diff options
Diffstat (limited to 'src/pkg')
381 files changed, 73008 insertions, 0 deletions
diff --git a/src/pkg/Make.deps b/src/pkg/Make.deps new file mode 100644 index 000000000..dd83e8b1c --- /dev/null +++ b/src/pkg/Make.deps @@ -0,0 +1,52 @@ +archive/tar.install: bufio.install bytes.install io.install os.install strconv.install +bignum.install: fmt.install +bufio.install: io.install os.install utf8.install +bytes.install: utf8.install +compress/flate.install: bufio.install io.install os.install strconv.install +compress/gzip.install: bufio.install compress/flate.install hash.install hash/crc32.install io.install os.install +container/list.install: +container/vector.install: +crypto/aes.install: os.install +crypto/block.install: fmt.install io.install os.install +crypto/hmac.install: crypto/md5.install crypto/sha1.install hash.install os.install +crypto/md5.install: hash.install os.install +crypto/sha1.install: hash.install os.install +datafmt.install: container/vector.install fmt.install go/scanner.install go/token.install io.install os.install reflect.install runtime.install strconv.install strings.install +exec.install: os.install strings.install +exvar.install: fmt.install http.install io.install log.install strconv.install sync.install +flag.install: fmt.install os.install strconv.install +fmt.install: io.install os.install reflect.install strconv.install utf8.install +go/ast.install: datafmt.install go/token.install io.install os.install unicode.install utf8.install +go/doc.install: container/vector.install fmt.install go/ast.install go/token.install io.install once.install regexp.install sort.install strings.install template.install +go/parser.install: container/vector.install fmt.install go/ast.install go/scanner.install go/token.install io.install os.install +go/scanner.install: go/token.install strconv.install unicode.install utf8.install +go/token.install: strconv.install +hash.install: io.install +hash/adler32.install: hash.install os.install +hash/crc32.install: hash.install os.install +http.install: bufio.install fmt.install io.install log.install net.install os.install path.install strconv.install strings.install utf8.install +io.install: bytes.install os.install sync.install +json.install: container/vector.install fmt.install io.install math.install reflect.install strconv.install strings.install utf8.install +log.install: fmt.install io.install os.install runtime.install time.install +malloc.install: +math.install: +net.install: fmt.install io.install once.install os.install reflect.install strconv.install strings.install sync.install syscall.install +once.install: sync.install +os.install: once.install syscall.install +path.install: io.install +rand.install: +reflect.install: strconv.install sync.install utf8.install +regexp.install: container/vector.install os.install runtime.install utf8.install +runtime.install: +sort.install: +strconv.install: bytes.install math.install os.install utf8.install +strings.install: utf8.install +sync.install: +syscall.install: sync.install +tabwriter.install: container/vector.install io.install os.install utf8.install +template.install: container/vector.install fmt.install io.install os.install reflect.install runtime.install strings.install +testing.install: flag.install fmt.install os.install regexp.install runtime.install +testing/iotest.install: io.install log.install os.install +time.install: io.install once.install os.install syscall.install +unicode.install: +utf8.install: diff --git a/src/pkg/Makefile b/src/pkg/Makefile new file mode 100644 index 000000000..036a82e38 --- /dev/null +++ b/src/pkg/Makefile @@ -0,0 +1,139 @@ +# Copyright 2009 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. + +# After editing the DIRS= list or adding imports to any Go files +# in any of those directories, run: +# +# ./deps.bash +# +# to rebuild the dependency information in Make.deps. + +all: install + +DIRS=\ + archive/tar\ + bignum\ + bufio\ + bytes\ + compress/flate\ + compress/gzip\ + container/list\ + container/vector\ + crypto/aes\ + crypto/block\ + crypto/hmac\ + crypto/md5\ + crypto/sha1\ + datafmt\ + exec\ + exvar\ + flag\ + fmt\ + go/ast\ + go/doc\ + go/parser\ + go/scanner\ + go/token\ + hash\ + hash/adler32\ + hash/crc32\ + http\ + io\ + json\ + log\ + malloc\ + math\ + net\ + once\ + os\ + path\ + rand\ + reflect\ + regexp\ + runtime\ + sort\ + strconv\ + strings\ + sync\ + syscall\ + tabwriter\ + template\ + testing\ + testing/iotest\ + time\ + unicode\ + utf8\ + +TEST=\ + archive/tar\ + bignum\ + bufio\ + compress/flate\ + compress/gzip\ + container/list\ + container/vector\ + crypto/aes\ + crypto/block\ + crypto/md5\ + crypto/sha1\ + datafmt\ + exec\ + exvar\ + flag\ + fmt\ + go/parser\ + go/scanner\ + hash/adler32\ + hash/crc32\ + http\ + io\ + json\ + log\ + math\ + net\ + once\ + os\ + path\ + reflect\ + regexp\ + sort\ + strconv\ + strings\ + sync\ + tabwriter\ + template\ + time\ + unicode\ + utf8\ + +clean.dirs: $(addsuffix .clean, $(DIRS)) +install.dirs: $(addsuffix .install, $(DIRS)) +nuke.dirs: $(addsuffix .nuke, $(DIRS)) +test.dirs: $(addsuffix .test, $(TEST)) + +%.clean: + +cd $* && make clean + +%.install: + +cd $* && make install + +%.nuke: + +cd $* && make nuke + +%.test: + +cd $* && make test + +clean: clean.dirs + +install: install.dirs + +test: test.dirs + +nuke: nuke.dirs + rm -rf $(GOROOT)/pkg/* + +deps: + ./deps.bash + +include Make.deps diff --git a/src/pkg/archive/tar/Makefile b/src/pkg/archive/tar/Makefile new file mode 100644 index 000000000..579ed4c35 --- /dev/null +++ b/src/pkg/archive/tar/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/archive/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + untar.$O\ + + +phases: a1 +_obj$D/tar.a: phases + +a1: $(O1) + $(AR) grc _obj$D/tar.a untar.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/tar.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/tar.a + +packages: _obj$D/tar.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/tar.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/tar.a diff --git a/src/pkg/archive/tar/testdata/small.txt b/src/pkg/archive/tar/testdata/small.txt new file mode 100644 index 000000000..b249bfc51 --- /dev/null +++ b/src/pkg/archive/tar/testdata/small.txt @@ -0,0 +1 @@ +Kilts
\ No newline at end of file diff --git a/src/pkg/archive/tar/testdata/small2.txt b/src/pkg/archive/tar/testdata/small2.txt new file mode 100644 index 000000000..394ee3ecd --- /dev/null +++ b/src/pkg/archive/tar/testdata/small2.txt @@ -0,0 +1 @@ +Google.com diff --git a/src/pkg/archive/tar/testdata/test.tar b/src/pkg/archive/tar/testdata/test.tar Binary files differnew file mode 100644 index 000000000..fc899dc8d --- /dev/null +++ b/src/pkg/archive/tar/testdata/test.tar diff --git a/src/pkg/archive/tar/untar.go b/src/pkg/archive/tar/untar.go new file mode 100644 index 000000000..300c0f932 --- /dev/null +++ b/src/pkg/archive/tar/untar.go @@ -0,0 +1,242 @@ +// Copyright 2009 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. + +// The tar package implements access to tar archives. +// It aims to cover most of the variations, including those produced +// by GNU and BSD tars (not yet started). +// +// References: +// http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5 +// http://www.gnu.org/software/tar/manual/html_node/Standard.html +package tar + +// TODO(dsymonds): +// - Make it seekable. +// - Extensions. + +import ( + "bufio"; + "bytes"; + "io"; + "os"; + "strconv"; +) + +var ( + HeaderError os.Error = os.ErrorString("invalid tar header"); +) + +// A tar archive consists of a sequence of files. +// A Reader provides sequential access to the contents of a tar archive. +// The Next method advances to the next file in the archive (including the first), +// and then it can be treated as an io.Reader to access the file's data. +// +// Example: +// tr := NewTarReader(r); +// for { +// hdr, err := tr.Next(); +// if err != nil { +// // handle error +// } +// if hdr == nil { +// // end of tar archive +// break +// } +// io.Copy(tr, somewhere); +// } +type Reader struct { + r io.Reader; + err os.Error; + nb int64; // number of unread bytes for current file entry + pad int64; // amount of padding (ignored) after current file entry +} + +// A Header represents a single header in a tar archive. +// Only some fields may be populated. +type Header struct { + Name string; + Mode int64; + Uid int64; + Gid int64; + Size int64; + Mtime int64; + Typeflag byte; + Linkname string; + Uname string; + Gname string; + Devmajor int64; + Devminor int64; +} + +func (tr *Reader) skipUnread() +func (tr *Reader) readHeader() *Header + +// NewReader creates a new Reader reading the given io.Reader. +func NewReader(r io.Reader) *Reader { + return &Reader{ r: r } +} + +// Next advances to the next entry in the tar archive. +func (tr *Reader) Next() (*Header, os.Error) { + var hdr *Header; + if tr.err == nil { + tr.skipUnread(); + } + if tr.err == nil { + hdr = tr.readHeader(); + } + return hdr, tr.err +} + +const ( + blockSize = 512; + + // Types + TypeReg = '0'; + TypeRegA = '\x00'; + TypeLink = '1'; + TypeSymlink = '2'; + TypeChar = '3'; + TypeBlock = '4'; + TypeDir = '5'; + TypeFifo = '6'; + TypeCont = '7'; + TypeXHeader = 'x'; + TypeXGlobalHeader = 'g'; +) + +var zeroBlock = make([]byte, blockSize); + +// Parse bytes as a NUL-terminated C-style string. +// If a NUL byte is not found then the whole slice is returned as a string. +func cString(b []byte) string { + n := 0; + for n < len(b) && b[n] != 0 { + n++; + } + return string(b[0:n]) +} + +func (tr *Reader) octalNumber(b []byte) int64 { + x, err := strconv.Btoui64(cString(b), 8); + if err != nil { + tr.err = err; + } + return int64(x) +} + +type ignoreWriter struct {} +func (ignoreWriter) Write(b []byte) (n int, err os.Error) { + return len(b), nil +} + +type seeker interface { + Seek(offset int64, whence int) (ret int64, err os.Error); +} + +// Skip any unread bytes in the existing file entry, as well as any alignment padding. +func (tr *Reader) skipUnread() { + nr := tr.nb + tr.pad; // number of bytes to skip + + var n int64; + if sr, ok := tr.r.(seeker); ok { + n, tr.err = sr.Seek(nr, 1); + } else { + n, tr.err = io.Copyn(tr.r, ignoreWriter{}, nr); + } + tr.nb, tr.pad = 0, 0; +} + +func (tr *Reader) verifyChecksum(header []byte) bool { + given := tr.octalNumber(header[148:156]); + if tr.err != nil { + return false + } + + var computed int64; + for i := 0; i < len(header); i++ { + if i == 148 { + // The chksum field is special: it should be treated as space bytes. + computed += ' ' * 8; + i += 7; + continue + } + computed += int64(header[i]); + } + + return given == computed +} + +type slicer []byte +func (s *slicer) next(n int) (b []byte) { + b, *s = s[0:n], s[n:len(s)]; + return +} + +func (tr *Reader) readHeader() *Header { + header := make([]byte, blockSize); + var n int; + if n, tr.err = io.FullRead(tr.r, header); tr.err != nil { + return nil + } + + // Two blocks of zero bytes marks the end of the archive. + if bytes.Equal(header, zeroBlock[0:blockSize]) { + if n, tr.err = io.FullRead(tr.r, header); tr.err != nil { + return nil + } + if !bytes.Equal(header, zeroBlock[0:blockSize]) { + tr.err = HeaderError; + } + return nil + } + + if !tr.verifyChecksum(header) { + tr.err = HeaderError; + return nil + } + + // Unpack + hdr := new(Header); + s := slicer(header); + + // TODO(dsymonds): The format of the header depends on the value of magic (hdr[257:262]), + // so use that value to do the correct parsing below. + + hdr.Name = cString(s.next(100)); + hdr.Mode = tr.octalNumber(s.next(8)); + hdr.Uid = tr.octalNumber(s.next(8)); + hdr.Gid = tr.octalNumber(s.next(8)); + hdr.Size = tr.octalNumber(s.next(12)); + hdr.Mtime = tr.octalNumber(s.next(12)); + s.next(8); // chksum + hdr.Typeflag = s.next(1)[0]; + hdr.Linkname = cString(s.next(100)); + s.next(8); // magic, version + + if tr.err != nil { + tr.err = HeaderError; + return nil + } + + // Maximum value of hdr.Size is 64 GB (12 octal digits), + // so there's no risk of int64 overflowing. + tr.nb = int64(hdr.Size); + tr.pad = -tr.nb & (blockSize - 1); // blockSize is a power of two + + return hdr +} + +// Read reads from the current entry in the tar archive. +// It returns 0, nil when it reaches the end of that entry, +// until Next is called to advance to the next entry. +func (tr *Reader) Read(b []uint8) (n int, err os.Error) { + if int64(len(b)) > tr.nb { + b = b[0:tr.nb]; + } + n, err = tr.r.Read(b); + tr.nb -= int64(n); + tr.err = err; + return +} diff --git a/src/pkg/archive/tar/untar_test.go b/src/pkg/archive/tar/untar_test.go new file mode 100644 index 000000000..a9c92dbf0 --- /dev/null +++ b/src/pkg/archive/tar/untar_test.go @@ -0,0 +1,69 @@ +// Copyright 2009 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 tar + +import ( + "archive/tar"; + "bytes"; + "fmt"; + "io"; + "os"; + "testing"; +) + +func TestUntar(t *testing.T) { + f, err := os.Open("testdata/test.tar", os.O_RDONLY, 0444); + if err != nil { + t.Fatalf("Unexpected error: %v", err); + } + defer f.Close(); + + tr := NewReader(f); + + // First file + hdr, err := tr.Next(); + if err != nil || hdr == nil { + t.Fatalf("Didn't get first file: %v", err); + } + if hdr.Name != "small.txt" { + t.Errorf(`hdr.Name = %q, want "small.txt"`, hdr.Name); + } + if hdr.Mode != 0640 { + t.Errorf("hdr.Mode = %v, want 0640", hdr.Mode); + } + if hdr.Size != 5 { + t.Errorf("hdr.Size = %v, want 5", hdr.Size); + } + + // Read the first four bytes; Next() should skip the last one. + buf := make([]byte, 4); + if n, err := io.FullRead(tr, buf); err != nil { + t.Fatalf("Unexpected error: %v", err); + } + if expected := io.StringBytes("Kilt"); !bytes.Equal(buf, expected) { + t.Errorf("Contents = %v, want %v", buf, expected); + } + + // Second file + hdr, err = tr.Next(); + if err != nil { + t.Fatalf("Didn't get second file: %v", err); + } + if hdr.Name != "small2.txt" { + t.Errorf(`hdr.Name = %q, want "small2.txt"`, hdr.Name); + } + if hdr.Mode != 0640 { + t.Errorf("hdr.Mode = %v, want 0640", hdr.Mode); + } + if hdr.Size != 11 { + t.Errorf("hdr.Size = %v, want 11", hdr.Size); + } + + + hdr, err = tr.Next(); + if hdr != nil || err != nil { + t.Fatalf("Unexpected third file or error: %v", err); + } +} diff --git a/src/pkg/bignum/Makefile b/src/pkg/bignum/Makefile new file mode 100644 index 000000000..098b90975 --- /dev/null +++ b/src/pkg/bignum/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + bignum.$O\ + + +phases: a1 +_obj$D/bignum.a: phases + +a1: $(O1) + $(AR) grc _obj$D/bignum.a bignum.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/bignum.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/bignum.a + +packages: _obj$D/bignum.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/bignum.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/bignum.a diff --git a/src/pkg/bignum/bignum.go b/src/pkg/bignum/bignum.go new file mode 100755 index 000000000..b9ea66587 --- /dev/null +++ b/src/pkg/bignum/bignum.go @@ -0,0 +1,1464 @@ +// Copyright 2009 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. + +// A package for arbitrary precision arithmethic. +// It implements the following numeric types: +// +// - Natural unsigned integers +// - Integer signed integers +// - Rational rational numbers +// +package bignum + +import "fmt" + + +// ---------------------------------------------------------------------------- +// Internal representation +// +// A natural number of the form +// +// x = x[n-1]*B^(n-1) + x[n-2]*B^(n-2) + ... + x[1]*B + x[0] +// +// with 0 <= x[i] < B and 0 <= i < n is stored in a slice of length n, +// with the digits x[i] as the slice elements. +// +// A natural number is normalized if the slice contains no leading 0 digits. +// During arithmetic operations, denormalized values may occur but are +// always normalized before returning the final result. The normalized +// representation of 0 is the empty slice (length = 0). +// +// The operations for all other numeric types are implemented on top of +// the operations for natural numbers. +// +// The base B is chosen as large as possible on a given platform but there +// are a few constraints besides the size of the largest unsigned integer +// type available: +// +// 1) To improve conversion speed between strings and numbers, the base B +// is chosen such that division and multiplication by 10 (for decimal +// string representation) can be done without using extended-precision +// arithmetic. This makes addition, subtraction, and conversion routines +// twice as fast. It requires a ``buffer'' of 4 bits per operand digit. +// That is, the size of B must be 4 bits smaller then the size of the +// type (digit) in which these operations are performed. Having this +// buffer also allows for trivial (single-bit) carry computation in +// addition and subtraction (optimization suggested by Ken Thompson). +// +// 2) Long division requires extended-precision (2-digit) division per digit. +// Instead of sacrificing the largest base type for all other operations, +// for division the operands are unpacked into ``half-digits'', and the +// results are packed again. For faster unpacking/packing, the base size +// in bits must be even. + +type ( + digit uint64; + digit2 uint32; // half-digits for division +) + + +const ( + _LogW = 64; + _LogH = 4; // bits for a hex digit (= small number) + _LogB = _LogW - _LogH; // largest bit-width available + + // half-digits + _W2 = _LogB / 2; // width + _B2 = 1 << _W2; // base + _M2 = _B2 - 1; // mask + + // full digits + _W = _W2 * 2; // width + _B = 1 << _W; // base + _M = _B - 1; // mask +) + + +// ---------------------------------------------------------------------------- +// Support functions + +func assert(p bool) { + if !p { + panic("assert failed"); + } +} + + +func isSmall(x digit) bool { + return x < 1<<_LogH; +} + + +// For debugging. +func dump(x []digit) { + print("[", len(x), "]"); + for i := len(x) - 1; i >= 0; i-- { + print(" ", x[i]); + } + println(); +} + + +// ---------------------------------------------------------------------------- +// Natural numbers + +// Natural represents an unsigned integer value of arbitrary precision. +// +type Natural []digit; + +var ( + natZero Natural = Natural{}; + natOne Natural = Natural{1}; + natTwo Natural = Natural{2}; + natTen Natural = Natural{10}; +) + + +// Nat creates a small natural number with value x. +// Implementation restriction: At the moment, only values +// x < (1<<60) are supported. +// +func Nat(x uint) Natural { + switch x { + case 0: return natZero; + case 1: return natOne; + case 2: return natTwo; + case 10: return natTen; + } + assert(digit(x) < _B); + return Natural{digit(x)}; +} + + +// IsEven returns true iff x is divisible by 2. +// +func (x Natural) IsEven() bool { + return len(x) == 0 || x[0]&1 == 0; +} + + +// IsOdd returns true iff x is not divisible by 2. +// +func (x Natural) IsOdd() bool { + return len(x) > 0 && x[0]&1 != 0; +} + + +// IsZero returns true iff x == 0. +// +func (x Natural) IsZero() bool { + return len(x) == 0; +} + + +// Operations +// +// Naming conventions +// +// c carry +// x, y operands +// z result +// n, m len(x), len(y) + +func normalize(x Natural) Natural { + n := len(x); + for n > 0 && x[n - 1] == 0 { n-- } + if n < len(x) { + x = x[0 : n]; // trim leading 0's + } + return x; +} + + +// Add returns the sum x + y. +// +func (x Natural) Add(y Natural) Natural { + n := len(x); + m := len(y); + if n < m { + return y.Add(x); + } + + c := digit(0); + z := make(Natural, n + 1); + i := 0; + for i < m { + t := c + x[i] + y[i]; + c, z[i] = t>>_W, t&_M; + i++; + } + for i < n { + t := c + x[i]; + c, z[i] = t>>_W, t&_M; + i++; + } + if c != 0 { + z[i] = c; + i++; + } + + return z[0 : i]; +} + + +// Sub returns the difference x - y for x >= y. +// If x < y, an underflow run-time error occurs (use Cmp to test if x >= y). +// +func (x Natural) Sub(y Natural) Natural { + n := len(x); + m := len(y); + if n < m { + panic("underflow") + } + + c := digit(0); + z := make(Natural, n); + i := 0; + for i < m { + t := c + x[i] - y[i]; + c, z[i] = digit(int64(t)>>_W), t&_M; // requires arithmetic shift! + i++; + } + for i < n { + t := c + x[i]; + c, z[i] = digit(int64(t)>>_W), t&_M; // requires arithmetic shift! + i++; + } + for i > 0 && z[i - 1] == 0 { // normalize + i--; + } + + return z[0 : i]; +} + + +// Returns c = x*y div B, z = x*y mod B. +// +func mul11(x, y digit) (digit, digit) { + // Split x and y into 2 sub-digits each, + // multiply the digits separately while avoiding overflow, + // and return the product as two separate digits. + + // This code also works for non-even bit widths W + // which is why there are separate constants below + // for half-digits. + const W2 = (_W + 1)/2; + const DW = W2*2 - _W; // 0 or 1 + const B2 = 1<<W2; + const M2 = _B2 - 1; + + // split x and y into sub-digits + // x = (x1*B2 + x0) + // y = (y1*B2 + y0) + x1, x0 := x>>W2, x&M2; + y1, y0 := y>>W2, y&M2; + + // x*y = t2*B2^2 + t1*B2 + t0 + t0 := x0*y0; + t1 := x1*y0 + x0*y1; + t2 := x1*y1; + + // compute the result digits but avoid overflow + // z = z1*B + z0 = x*y + z0 := (t1<<W2 + t0)&_M; + z1 := t2<<DW + (t1 + t0>>W2)>>(_W-W2); + + return z1, z0; +} + + +// Mul returns the product x * y. +// +func (x Natural) Mul(y Natural) Natural { + n := len(x); + m := len(y); + + z := make(Natural, n + m); + for j := 0; j < m; j++ { + d := y[j]; + if d != 0 { + c := digit(0); + for i := 0; i < n; i++ { + // z[i+j] += c + x[i]*d; + z1, z0 := mul11(x[i], d); + t := c + z[i+j] + z0; + c, z[i+j] = t>>_W, t&_M; + c += z1; + } + z[n+j] = c; + } + } + + return normalize(z); +} + + +// DivMod needs multi-precision division, which is not available if digit +// is already using the largest uint size. Instead, unpack each operand +// into operands with twice as many digits of half the size (digit2), do +// DivMod, and then pack the results again. + +func unpack(x Natural) []digit2 { + n := len(x); + z := make([]digit2, n*2 + 1); // add space for extra digit (used by DivMod) + for i := 0; i < n; i++ { + t := x[i]; + z[i*2] = digit2(t & _M2); + z[i*2 + 1] = digit2(t >> _W2 & _M2); + } + + // normalize result + k := 2*n; + for k > 0 && z[k - 1] == 0 { k-- } + return z[0 : k]; // trim leading 0's +} + + +func pack(x []digit2) Natural { + n := (len(x) + 1) / 2; + z := make(Natural, n); + if len(x) & 1 == 1 { + // handle odd len(x) + n--; + z[n] = digit(x[n*2]); + } + for i := 0; i < n; i++ { + z[i] = digit(x[i*2 + 1]) << _W2 | digit(x[i*2]); + } + return normalize(z); +} + + +func mul1(z, x []digit2, y digit2) digit2 { + n := len(x); + c := digit(0); + f := digit(y); + for i := 0; i < n; i++ { + t := c + digit(x[i])*f; + c, z[i] = t>>_W2, digit2(t&_M2); + } + return digit2(c); +} + + +func div1(z, x []digit2, y digit2) digit2 { + n := len(x); + c := digit(0); + d := digit(y); + for i := n-1; i >= 0; i-- { + t := c*_B2 + digit(x[i]); + c, z[i] = t%d, digit2(t/d); + } + return digit2(c); +} + + +// divmod returns q and r with x = y*q + r and 0 <= r < y. +// x and y are destroyed in the process. +// +// The algorithm used here is based on 1). 2) describes the same algorithm +// in C. A discussion and summary of the relevant theorems can be found in +// 3). 3) also describes an easier way to obtain the trial digit - however +// it relies on tripple-precision arithmetic which is why Knuth's method is +// used here. +// +// 1) D. Knuth, The Art of Computer Programming. Volume 2. Seminumerical +// Algorithms. Addison-Wesley, Reading, 1969. +// (Algorithm D, Sec. 4.3.1) +// +// 2) Henry S. Warren, Jr., Hacker's Delight. Addison-Wesley, 2003. +// (9-2 Multiword Division, p.140ff) +// +// 3) P. Brinch Hansen, ``Multiple-length division revisited: A tour of the +// minefield''. Software - Practice and Experience 24, (June 1994), +// 579-601. John Wiley & Sons, Ltd. + +func divmod(x, y []digit2) ([]digit2, []digit2) { + n := len(x); + m := len(y); + if m == 0 { + panic("division by zero"); + } + assert(n+1 <= cap(x)); // space for one extra digit + x = x[0 : n + 1]; + assert(x[n] == 0); + + if m == 1 { + // division by single digit + // result is shifted left by 1 in place! + x[0] = div1(x[1 : n+1], x[0 : n], y[0]); + + } else if m > n { + // y > x => quotient = 0, remainder = x + // TODO in this case we shouldn't even unpack x and y + m = n; + + } else { + // general case + assert(2 <= m && m <= n); + + // normalize x and y + // TODO Instead of multiplying, it would be sufficient to + // shift y such that the normalization condition is + // satisfied (as done in Hacker's Delight). + f := _B2 / (digit(y[m-1]) + 1); + if f != 1 { + mul1(x, x, digit2(f)); + mul1(y, y, digit2(f)); + } + assert(_B2/2 <= y[m-1] && y[m-1] < _B2); // incorrect scaling + + y1, y2 := digit(y[m-1]), digit(y[m-2]); + d2 := digit(y1)<<_W2 + digit(y2); + for i := n-m; i >= 0; i-- { + k := i+m; + + // compute trial digit (Knuth) + var q digit; + { x0, x1, x2 := digit(x[k]), digit(x[k-1]), digit(x[k-2]); + if x0 != y1 { + q = (x0<<_W2 + x1)/y1; + } else { + q = _B2 - 1; + } + for y2*q > (x0<<_W2 + x1 - y1*q)<<_W2 + x2 { + q-- + } + } + + // subtract y*q + c := digit(0); + for j := 0; j < m; j++ { + t := c + digit(x[i+j]) - digit(y[j])*q; + c, x[i+j] = digit(int64(t) >> _W2), digit2(t & _M2); // requires arithmetic shift! + } + + // correct if trial digit was too large + if c + digit(x[k]) != 0 { + // add y + c := digit(0); + for j := 0; j < m; j++ { + t := c + digit(x[i+j]) + digit(y[j]); + c, x[i+j] = t >> _W2, digit2(t & _M2) + } + assert(c + digit(x[k]) == 0); + // correct trial digit + q--; + } + + x[k] = digit2(q); + } + + // undo normalization for remainder + if f != 1 { + c := div1(x[0 : m], x[0 : m], digit2(f)); + assert(c == 0); + } + } + + return x[m : n+1], x[0 : m]; +} + + +// Div returns the quotient q = x / y for y > 0, +// with x = y*q + r and 0 <= r < y. +// If y == 0, a division-by-zero run-time error occurs. +// +func (x Natural) Div(y Natural) Natural { + q, r := divmod(unpack(x), unpack(y)); + return pack(q); +} + + +// Mod returns the modulus r of the division x / y for y > 0, +// with x = y*q + r and 0 <= r < y. +// If y == 0, a division-by-zero run-time error occurs. +// +func (x Natural) Mod(y Natural) Natural { + q, r := divmod(unpack(x), unpack(y)); + return pack(r); +} + + +// DivMod returns the pair (x.Div(y), x.Mod(y)) for y > 0. +// If y == 0, a division-by-zero run-time error occurs. +// +func (x Natural) DivMod(y Natural) (Natural, Natural) { + q, r := divmod(unpack(x), unpack(y)); + return pack(q), pack(r); +} + + +func shl(z, x []digit, s uint) digit { + assert(s <= _W); + n := len(x); + c := digit(0); + for i := 0; i < n; i++ { + c, z[i] = x[i] >> (_W-s), x[i] << s & _M | c; + } + return c; +} + + +// Shl implements ``shift left'' x << s. It returns x * 2^s. +// +func (x Natural) Shl(s uint) Natural { + n := uint(len(x)); + m := n + s/_W; + z := make(Natural, m+1); + + z[m] = shl(z[m-n : m], x, s%_W); + + return normalize(z); +} + + +func shr(z, x []digit, s uint) digit { + assert(s <= _W); + n := len(x); + c := digit(0); + for i := n - 1; i >= 0; i-- { + c, z[i] = x[i] << (_W-s) & _M, x[i] >> s | c; + } + return c; +} + + +// Shr implements ``shift right'' x >> s. It returns x / 2^s. +// +func (x Natural) Shr(s uint) Natural { + n := uint(len(x)); + m := n - s/_W; + if m > n { // check for underflow + m = 0; + } + z := make(Natural, m); + + shr(z, x[n-m : n], s%_W); + + return normalize(z); +} + + +// And returns the ``bitwise and'' x & y for the binary representation of x and y. +// +func (x Natural) And(y Natural) Natural { + n := len(x); + m := len(y); + if n < m { + return y.And(x); + } + + z := make(Natural, m); + for i := 0; i < m; i++ { + z[i] = x[i] & y[i]; + } + // upper bits are 0 + + return normalize(z); +} + + +func copy(z, x []digit) { + for i, e := range x { + z[i] = e + } +} + + +// Or returns the ``bitwise or'' x | y for the binary representation of x and y. +// +func (x Natural) Or(y Natural) Natural { + n := len(x); + m := len(y); + if n < m { + return y.Or(x); + } + + z := make(Natural, n); + for i := 0; i < m; i++ { + z[i] = x[i] | y[i]; + } + copy(z[m : n], x[m : n]); + + return z; +} + + +// Xor returns the ``bitwise exclusive or'' x ^ y for the binary representation of x and y. +// +func (x Natural) Xor(y Natural) Natural { + n := len(x); + m := len(y); + if n < m { + return y.Xor(x); + } + + z := make(Natural, n); + for i := 0; i < m; i++ { + z[i] = x[i] ^ y[i]; + } + copy(z[m : n], x[m : n]); + + return normalize(z); +} + + +// Cmp compares x and y. The result is an int value +// +// < 0 if x < y +// == 0 if x == y +// > 0 if x > y +// +func (x Natural) Cmp(y Natural) int { + n := len(x); + m := len(y); + + if n != m || n == 0 { + return n - m; + } + + i := n - 1; + for i > 0 && x[i] == y[i] { i--; } + + d := 0; + switch { + case x[i] < y[i]: d = -1; + case x[i] > y[i]: d = 1; + } + + return d; +} + + +func log2(x digit) uint { + assert(x > 0); + n := uint(0); + for x > 0 { + x >>= 1; + n++; + } + return n - 1; +} + + +// Log2 computes the binary logarithm of x for x > 0. +// The result is the integer n for which 2^n <= x < 2^(n+1). +// If x == 0 a run-time error occurs. +// +func (x Natural) Log2() uint { + n := len(x); + if n > 0 { + return (uint(n) - 1)*_W + log2(x[n - 1]); + } + panic("Log2(0)"); +} + + +// Computes x = x div d in place (modifies x) for small d's. +// Returns updated x and x mod d. +// +func divmod1(x Natural, d digit) (Natural, digit) { + assert(0 < d && isSmall(d - 1)); + + c := digit(0); + for i := len(x) - 1; i >= 0; i-- { + t := c<<_W + x[i]; + c, x[i] = t%d, t/d; + } + + return normalize(x), c; +} + + +// ToString converts x to a string for a given base, with 2 <= base <= 16. +// +func (x Natural) ToString(base uint) string { + if len(x) == 0 { + return "0"; + } + + // allocate buffer for conversion + assert(2 <= base && base <= 16); + n := (x.Log2() + 1) / log2(digit(base)) + 1; // +1: round up + s := make([]byte, n); + + // don't destroy x + t := make(Natural, len(x)); + copy(t, x); + + // convert + i := n; + for !t.IsZero() { + i--; + var d digit; + t, d = divmod1(t, digit(base)); + s[i] = "0123456789abcdef"[d]; + }; + + return string(s[i : n]); +} + + +// String converts x to its decimal string representation. +// x.String() is the same as x.ToString(10). +// +func (x Natural) String() string { + return x.ToString(10); +} + + +func fmtbase(c int) uint { + switch c { + case 'b': return 2; + case 'o': return 8; + case 'x': return 16; + } + return 10; +} + + +// Format is a support routine for fmt.Formatter. It accepts +// the formats 'b' (binary), 'o' (octal), and 'x' (hexadecimal). +// +func (x Natural) Format(h fmt.Formatter, c int) { + fmt.Fprintf(h, "%s", x.ToString(fmtbase(c))); +} + + +func hexvalue(ch byte) uint { + d := uint(1 << _LogH); + switch { + case '0' <= ch && ch <= '9': d = uint(ch - '0'); + case 'a' <= ch && ch <= 'f': d = uint(ch - 'a') + 10; + case 'A' <= ch && ch <= 'F': d = uint(ch - 'A') + 10; + } + return d; +} + + +// Computes x = x*d + c for small d's. +// +func muladd1(x Natural, d, c digit) Natural { + assert(isSmall(d-1) && isSmall(c)); + n := len(x); + z := make(Natural, n + 1); + + for i := 0; i < n; i++ { + t := c + x[i]*d; + c, z[i] = t>>_W, t&_M; + } + z[n] = c; + + return normalize(z); +} + + +// NatFromString returns the natural number corresponding to the +// longest possible prefix of s representing a natural number in a +// given conversion base, the actual conversion base used, and the +// prefix length. +// +// If the base argument is 0, the string prefix determines the actual +// conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the +// ``0'' prefix selects base 8. Otherwise the selected base is 10. +// +func NatFromString(s string, base uint) (Natural, uint, int) { + // determine base if necessary + i, n := 0, len(s); + if base == 0 { + base = 10; + if n > 0 && s[0] == '0' { + if n > 1 && (s[1] == 'x' || s[1] == 'X') { + base, i = 16, 2; + } else { + base, i = 8, 1; + } + } + } + + // convert string + assert(2 <= base && base <= 16); + x := Nat(0); + for ; i < n; i++ { + d := hexvalue(s[i]); + if d < base { + x = muladd1(x, digit(base), digit(d)); + } else { + break; + } + } + + return x, base, i; +} + + +// Natural number functions + +func pop1(x digit) uint { + n := uint(0); + for x != 0 { + x &= x-1; + n++; + } + return n; +} + + +// Pop computes the ``population count'' of (the number of 1 bits in) x. +// +func (x Natural) Pop() uint { + n := uint(0); + for i := len(x) - 1; i >= 0; i-- { + n += pop1(x[i]); + } + return n; +} + + +// Pow computes x to the power of n. +// +func (xp Natural) Pow(n uint) Natural { + z := Nat(1); + x := xp; + for n > 0 { + // z * x^n == x^n0 + if n&1 == 1 { + z = z.Mul(x); + } + x, n = x.Mul(x), n/2; + } + return z; +} + + +// MulRange computes the product of all the unsigned integers +// in the range [a, b] inclusively. +// +func MulRange(a, b uint) Natural { + switch { + case a > b: return Nat(1); + case a == b: return Nat(a); + case a + 1 == b: return Nat(a).Mul(Nat(b)); + } + m := (a + b)>>1; + assert(a <= m && m < b); + return MulRange(a, m).Mul(MulRange(m + 1, b)); +} + + +// Fact computes the factorial of n (Fact(n) == MulRange(2, n)). +// +func Fact(n uint) Natural { + // Using MulRange() instead of the basic for-loop + // lead to faster factorial computation. + return MulRange(2, n); +} + + +// Binomial computes the binomial coefficient of (n, k). +// +func Binomial(n, k uint) Natural { + return MulRange(n-k+1, n).Div(MulRange(1, k)); +} + + +// Gcd computes the gcd of x and y. +// +func (x Natural) Gcd(y Natural) Natural { + // Euclidean algorithm. + a, b := x, y; + for !b.IsZero() { + a, b = b, a.Mod(b); + } + return a; +} + + +// ---------------------------------------------------------------------------- +// Integer numbers +// +// Integers are normalized if the mantissa is normalized and the sign is +// false for mant == 0. Use MakeInt to create normalized Integers. + +// Integer represents a signed integer value of arbitrary precision. +// +type Integer struct { + sign bool; + mant Natural; +} + + +// MakeInt makes an integer given a sign and a mantissa. +// The number is positive (>= 0) if sign is false or the +// mantissa is zero; it is negative otherwise. +// +func MakeInt(sign bool, mant Natural) *Integer { + if mant.IsZero() { + sign = false; // normalize + } + return &Integer{sign, mant}; +} + + +// Int creates a small integer with value x. +// Implementation restriction: At the moment, only values +// with an absolute value |x| < (1<<60) are supported. +// +func Int(x int) *Integer { + sign := false; + var ux uint; + if x < 0 { + sign = true; + if -x == x { + // smallest negative integer + t := ^0; + ux = ^(uint(t) >> 1); + } else { + ux = uint(-x); + } + } else { + ux = uint(x); + } + return MakeInt(sign, Nat(ux)); +} + + +// Predicates + +// IsEven returns true iff x is divisible by 2. +// +func (x *Integer) IsEven() bool { + return x.mant.IsEven(); +} + + +// IsOdd returns true iff x is not divisible by 2. +// +func (x *Integer) IsOdd() bool { + return x.mant.IsOdd(); +} + + +// IsZero returns true iff x == 0. +// +func (x *Integer) IsZero() bool { + return x.mant.IsZero(); +} + + +// IsNeg returns true iff x < 0. +// +func (x *Integer) IsNeg() bool { + return x.sign && !x.mant.IsZero() +} + + +// IsPos returns true iff x >= 0. +// +func (x *Integer) IsPos() bool { + return !x.sign && !x.mant.IsZero() +} + + +// Operations + +// Neg returns the negated value of x. +// +func (x *Integer) Neg() *Integer { + return MakeInt(!x.sign, x.mant); +} + + +// Add returns the sum x + y. +// +func (x *Integer) Add(y *Integer) *Integer { + var z *Integer; + if x.sign == y.sign { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z = MakeInt(x.sign, x.mant.Add(y.mant)); + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.mant.Cmp(y.mant) >= 0 { + z = MakeInt(false, x.mant.Sub(y.mant)); + } else { + z = MakeInt(true, y.mant.Sub(x.mant)); + } + } + if x.sign { + z.sign = !z.sign; + } + return z; +} + + +// Sub returns the difference x - y. +// +func (x *Integer) Sub(y *Integer) *Integer { + var z *Integer; + if x.sign != y.sign { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z = MakeInt(false, x.mant.Add(y.mant)); + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.mant.Cmp(y.mant) >= 0 { + z = MakeInt(false, x.mant.Sub(y.mant)); + } else { + z = MakeInt(true, y.mant.Sub(x.mant)); + } + } + if x.sign { + z.sign = !z.sign; + } + return z; +} + + +// Mul returns the product x * y. +// +func (x *Integer) Mul(y *Integer) *Integer { + // x * y == x * y + // x * (-y) == -(x * y) + // (-x) * y == -(x * y) + // (-x) * (-y) == x * y + return MakeInt(x.sign != y.sign, x.mant.Mul(y.mant)); +} + + +// MulNat returns the product x * y, where y is a (unsigned) natural number. +// +func (x *Integer) MulNat(y Natural) *Integer { + // x * y == x * y + // (-x) * y == -(x * y) + return MakeInt(x.sign, x.mant.Mul(y)); +} + + +// Quo returns the quotient q = x / y for y != 0. +// If y == 0, a division-by-zero run-time error occurs. +// +// Quo and Rem implement T-division and modulus (like C99): +// +// q = x.Quo(y) = trunc(x/y) (truncation towards zero) +// r = x.Rem(y) = x - y*q +// +// (Daan Leijen, ``Division and Modulus for Computer Scientists''.) +// +func (x *Integer) Quo(y *Integer) *Integer { + // x / y == x / y + // x / (-y) == -(x / y) + // (-x) / y == -(x / y) + // (-x) / (-y) == x / y + return MakeInt(x.sign != y.sign, x.mant.Div(y.mant)); +} + + +// Rem returns the remainder r of the division x / y for y != 0, +// with r = x - y*x.Quo(y). Unless r is zero, its sign corresponds +// to the sign of x. +// If y == 0, a division-by-zero run-time error occurs. +// +func (x *Integer) Rem(y *Integer) *Integer { + // x % y == x % y + // x % (-y) == x % y + // (-x) % y == -(x % y) + // (-x) % (-y) == -(x % y) + return MakeInt(x.sign, x.mant.Mod(y.mant)); +} + + +// QuoRem returns the pair (x.Quo(y), x.Rem(y)) for y != 0. +// If y == 0, a division-by-zero run-time error occurs. +// +func (x *Integer) QuoRem(y *Integer) (*Integer, *Integer) { + q, r := x.mant.DivMod(y.mant); + return MakeInt(x.sign != y.sign, q), MakeInt(x.sign, r); +} + + +// Div returns the quotient q = x / y for y != 0. +// If y == 0, a division-by-zero run-time error occurs. +// +// Div and Mod implement Euclidian division and modulus: +// +// q = x.Div(y) +// r = x.Mod(y) with: 0 <= r < |q| and: y = x*q + r +// +// (Raymond T. Boute, ``The Euclidian definition of the functions +// div and mod''. ACM Transactions on Programming Languages and +// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. +// ACM press.) +// +func (x *Integer) Div(y *Integer) *Integer { + q, r := x.QuoRem(y); + if r.IsNeg() { + if y.IsPos() { + q = q.Sub(Int(1)); + } else { + q = q.Add(Int(1)); + } + } + return q; +} + + +// Mod returns the modulus r of the division x / y for y != 0, +// with r = x - y*x.Div(y). r is always positive. +// If y == 0, a division-by-zero run-time error occurs. +// +func (x *Integer) Mod(y *Integer) *Integer { + r := x.Rem(y); + if r.IsNeg() { + if y.IsPos() { + r = r.Add(y); + } else { + r = r.Sub(y); + } + } + return r; +} + + +// DivMod returns the pair (x.Div(y), x.Mod(y)). +// +func (x *Integer) DivMod(y *Integer) (*Integer, *Integer) { + q, r := x.QuoRem(y); + if r.IsNeg() { + if y.IsPos() { + q = q.Sub(Int(1)); + r = r.Add(y); + } else { + q = q.Add(Int(1)); + r = r.Sub(y); + } + } + return q, r; +} + + +// Shl implements ``shift left'' x << s. It returns x * 2^s. +// +func (x *Integer) Shl(s uint) *Integer { + return MakeInt(x.sign, x.mant.Shl(s)); +} + + +// Shr implements ``shift right'' x >> s. It returns x / 2^s. +// Implementation restriction: Shl is not yet implemented for negative x. +// +func (x *Integer) Shr(s uint) *Integer { + z := MakeInt(x.sign, x.mant.Shr(s)); + if x.IsNeg() { + panic("UNIMPLEMENTED Integer.Shr of negative values"); + } + return z; +} + + +// And returns the ``bitwise and'' x & y for the binary representation of x and y. +// Implementation restriction: And is not implemented for negative x. +// +func (x *Integer) And(y *Integer) *Integer { + var z *Integer; + if !x.sign && !y.sign { + z = MakeInt(false, x.mant.And(y.mant)); + } else { + panic("UNIMPLEMENTED Integer.And of negative values"); + } + return z; +} + + +// Or returns the ``bitwise or'' x | y for the binary representation of x and y. +// Implementation restriction: Or is not implemented for negative x. +// +func (x *Integer) Or(y *Integer) *Integer { + var z *Integer; + if !x.sign && !y.sign { + z = MakeInt(false, x.mant.Or(y.mant)); + } else { + panic("UNIMPLEMENTED Integer.Or of negative values"); + } + return z; +} + + +// Xor returns the ``bitwise xor'' x | y for the binary representation of x and y. +// Implementation restriction: Xor is not implemented for negative integers. +// +func (x *Integer) Xor(y *Integer) *Integer { + var z *Integer; + if !x.sign && !y.sign { + z = MakeInt(false, x.mant.Xor(y.mant)); + } else { + panic("UNIMPLEMENTED Integer.Xor of negative values"); + } + return z; +} + + +// Cmp compares x and y. The result is an int value +// +// < 0 if x < y +// == 0 if x == y +// > 0 if x > y +// +func (x *Integer) Cmp(y *Integer) int { + // x cmp y == x cmp y + // x cmp (-y) == x + // (-x) cmp y == y + // (-x) cmp (-y) == -(x cmp y) + var r int; + switch { + case x.sign == y.sign: + r = x.mant.Cmp(y.mant); + if x.sign { + r = -r; + } + case x.sign: r = -1; + case y.sign: r = 1; + } + return r; +} + + +// ToString converts x to a string for a given base, with 2 <= base <= 16. +// +func (x *Integer) ToString(base uint) string { + if x.mant.IsZero() { + return "0"; + } + var s string; + if x.sign { + s = "-"; + } + return s + x.mant.ToString(base); +} + + +// String converts x to its decimal string representation. +// x.String() is the same as x.ToString(10). +// +func (x *Integer) String() string { + return x.ToString(10); +} + + +// Format is a support routine for fmt.Formatter. It accepts +// the formats 'b' (binary), 'o' (octal), and 'x' (hexadecimal). +// +func (x *Integer) Format(h fmt.Formatter, c int) { + fmt.Fprintf(h, "%s", x.ToString(fmtbase(c))); +} + + +// IntFromString returns the integer corresponding to the +// longest possible prefix of s representing an integer in a +// given conversion base, the actual conversion base used, and +// the prefix length. +// +// If the base argument is 0, the string prefix determines the actual +// conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the +// ``0'' prefix selects base 8. Otherwise the selected base is 10. +// +func IntFromString(s string, base uint) (*Integer, uint, int) { + // skip sign, if any + i0 := 0; + if len(s) > 0 && (s[0] == '-' || s[0] == '+') { + i0 = 1; + } + + mant, base, slen := NatFromString(s[i0 : len(s)], base); + + return MakeInt(i0 > 0 && s[0] == '-', mant), base, i0 + slen; +} + + +// ---------------------------------------------------------------------------- +// Rational numbers + +// Rational represents a quotient a/b of arbitrary precision. +// +type Rational struct { + a *Integer; // numerator + b Natural; // denominator +} + + +// MakeRat makes a rational number given a numerator a and a denominator b. +// +func MakeRat(a *Integer, b Natural) *Rational { + f := a.mant.Gcd(b); // f > 0 + if f.Cmp(Nat(1)) != 0 { + a = MakeInt(a.sign, a.mant.Div(f)); + b = b.Div(f); + } + return &Rational{a, b}; +} + + +// Rat creates a small rational number with value a0/b0. +// Implementation restriction: At the moment, only values a0, b0 +// with an absolute value |a0|, |b0| < (1<<60) are supported. +// +func Rat(a0 int, b0 int) *Rational { + a, b := Int(a0), Int(b0); + if b.sign { + a = a.Neg(); + } + return MakeRat(a, b.mant); +} + + +// Predicates + +// IsZero returns true iff x == 0. +// +func (x *Rational) IsZero() bool { + return x.a.IsZero(); +} + + +// IsNeg returns true iff x < 0. +// +func (x *Rational) IsNeg() bool { + return x.a.IsNeg(); +} + + +// IsPos returns true iff x > 0. +// +func (x *Rational) IsPos() bool { + return x.a.IsPos(); +} + + +// IsInt returns true iff x can be written with a denominator 1 +// in the form x == x'/1; i.e., if x is an integer value. +// +func (x *Rational) IsInt() bool { + return x.b.Cmp(Nat(1)) == 0; +} + + +// Operations + +// Neg returns the negated value of x. +// +func (x *Rational) Neg() *Rational { + return MakeRat(x.a.Neg(), x.b); +} + + +// Add returns the sum x + y. +// +func (x *Rational) Add(y *Rational) *Rational { + return MakeRat((x.a.MulNat(y.b)).Add(y.a.MulNat(x.b)), x.b.Mul(y.b)); +} + + +// Sub returns the difference x - y. +// +func (x *Rational) Sub(y *Rational) *Rational { + return MakeRat((x.a.MulNat(y.b)).Sub(y.a.MulNat(x.b)), x.b.Mul(y.b)); +} + + +// Mul returns the product x * y. +// +func (x *Rational) Mul(y *Rational) *Rational { + return MakeRat(x.a.Mul(y.a), x.b.Mul(y.b)); +} + + +// Quo returns the quotient x / y for y != 0. +// If y == 0, a division-by-zero run-time error occurs. +// +func (x *Rational) Quo(y *Rational) *Rational { + a := x.a.MulNat(y.b); + b := y.a.MulNat(x.b); + if b.IsNeg() { + a = a.Neg(); + } + return MakeRat(a, b.mant); +} + + +// Cmp compares x and y. The result is an int value +// +// < 0 if x < y +// == 0 if x == y +// > 0 if x > y +// +func (x *Rational) Cmp(y *Rational) int { + return (x.a.MulNat(y.b)).Cmp(y.a.MulNat(x.b)); +} + + +// ToString converts x to a string for a given base, with 2 <= base <= 16. +// The string representation is of the form "n" if x is an integer; otherwise +// it is of form "n/d". +// +func (x *Rational) ToString(base uint) string { + s := x.a.ToString(base); + if !x.IsInt() { + s += "/" + x.b.ToString(base); + } + return s; +} + + +// String converts x to its decimal string representation. +// x.String() is the same as x.ToString(10). +// +func (x *Rational) String() string { + return x.ToString(10); +} + + +// Format is a support routine for fmt.Formatter. It accepts +// the formats 'b' (binary), 'o' (octal), and 'x' (hexadecimal). +// +func (x *Rational) Format(h fmt.Formatter, c int) { + fmt.Fprintf(h, "%s", x.ToString(fmtbase(c))); +} + + +// RatFromString returns the rational number corresponding to the +// longest possible prefix of s representing a rational number in a +// given conversion base, the actual conversion base used, and the +// prefix length. +// +// If the base argument is 0, the string prefix determines the actual +// conversion base. A prefix of ``0x'' or ``0X'' selects base 16; the +// ``0'' prefix selects base 8. Otherwise the selected base is 10. +// +func RatFromString(s string, base uint) (*Rational, uint, int) { + // read nominator + a, abase, alen := IntFromString(s, base); + b := Nat(1); + + // read denominator or fraction, if any + var blen int; + if alen < len(s) { + ch := s[alen]; + if ch == '/' { + alen++; + b, base, blen = NatFromString(s[alen : len(s)], base); + } else if ch == '.' { + alen++; + b, base, blen = NatFromString(s[alen : len(s)], abase); + assert(base == abase); + f := Nat(base).Pow(uint(blen)); + a = MakeInt(a.sign, a.mant.Mul(f).Add(b)); + b = f; + } + } + + return MakeRat(a, b), base, alen + blen; +} diff --git a/src/pkg/bignum/bignum_test.go b/src/pkg/bignum/bignum_test.go new file mode 100644 index 000000000..9351c2ebf --- /dev/null +++ b/src/pkg/bignum/bignum_test.go @@ -0,0 +1,482 @@ +// Copyright 2009 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 bignum_test + +import ( + bignum "bignum"; + fmt "fmt"; + testing "testing"; +) + +const ( + sa = "991"; + sb = "2432902008176640000"; // 20! + sc = "933262154439441526816992388562667004907159682643816214685929" + "638952175999932299156089414639761565182862536979208272237582" + "51185210916864000000000000000000000000"; // 100! + sp = "170141183460469231731687303715884105727"; // prime +) + +func natFromString(s string, base uint, slen *int) bignum.Natural { + x, _, len := bignum.NatFromString(s, base); + if slen != nil { + *slen = len; + } + return x; +} + + +func intFromString(s string, base uint, slen *int) *bignum.Integer { + x, _, len := bignum.IntFromString(s, base); + if slen != nil { + *slen = len; + } + return x; +} + + +func ratFromString(s string, base uint, slen *int) *bignum.Rational { + x, _, len := bignum.RatFromString(s, base); + if slen != nil { + *slen = len; + } + return x; +} + + +var ( + nat_zero = bignum.Nat(0); + nat_one = bignum.Nat(1); + nat_two = bignum.Nat(2); + + a = natFromString(sa, 10, nil); + b = natFromString(sb, 10, nil); + c = natFromString(sc, 10, nil); + p = natFromString(sp, 10, nil); + + int_zero = bignum.Int(0); + int_one = bignum.Int(1); + int_two = bignum.Int(2); + + ip = intFromString(sp, 10, nil); + + rat_zero = bignum.Rat(0, 1); + rat_half = bignum.Rat(1, 2); + rat_one = bignum.Rat(1, 1); + rat_two = bignum.Rat(2, 1); +) + + +var test_msg string; +var tester *testing.T; + +func test(n uint, b bool) { + if !b { + tester.Fatalf("TEST failed: %s (%d)", test_msg, n); + } +} + + +func nat_eq(n uint, x, y bignum.Natural) { + if x.Cmp(y) != 0 { + tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, &x, &y); + } +} + + +func int_eq(n uint, x, y *bignum.Integer) { + if x.Cmp(y) != 0 { + tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, x, y); + } +} + + +func rat_eq(n uint, x, y *bignum.Rational) { + if x.Cmp(y) != 0 { + tester.Fatalf("TEST failed: %s (%d)\nx = %v\ny = %v", test_msg, n, x, y); + } +} + +func TestNatConv(t *testing.T) { + tester = t; + test_msg = "NatConvA"; + nat_eq(0, a, bignum.Nat(991)); + nat_eq(1, b, bignum.Fact(20)); + nat_eq(2, c, bignum.Fact(100)); + test(3, a.String() == sa); + test(4, b.String() == sb); + test(5, c.String() == sc); + + test_msg = "NatConvB"; + var slen int; + nat_eq(10, natFromString("0", 0, nil), nat_zero); + nat_eq(11, natFromString("123", 0, nil), bignum.Nat(123)); + nat_eq(12, natFromString("077", 0, nil), bignum.Nat(7*8 + 7)); + nat_eq(13, natFromString("0x1f", 0, nil), bignum.Nat(1*16 + 15)); + nat_eq(14, natFromString("0x1fg", 0, &slen), bignum.Nat(1*16 + 15)); + test(4, slen == 4); + + test_msg = "NatConvC"; + tmp := c.Mul(c); + for base := uint(2); base <= 16; base++ { + nat_eq(base, natFromString(tmp.ToString(base), base, nil), tmp); + } + + test_msg = "NatConvD"; + x := bignum.Nat(100); + y, b, _ := bignum.NatFromString(fmt.Sprintf("%b", &x), 2); + nat_eq(100, y, x); +} + + +func TestIntConv(t *testing.T) { + tester = t; + test_msg = "IntConv"; + var slen int; + int_eq(0, intFromString("0", 0, nil), int_zero); + int_eq(1, intFromString("-0", 0, nil), int_zero); + int_eq(2, intFromString("123", 0, nil), bignum.Int(123)); + int_eq(3, intFromString("-123", 0, nil), bignum.Int(-123)); + int_eq(4, intFromString("077", 0, nil), bignum.Int(7*8 + 7)); + int_eq(5, intFromString("-077", 0, nil), bignum.Int(-(7*8 + 7))); + int_eq(6, intFromString("0x1f", 0, nil), bignum.Int(1*16 + 15)); + int_eq(7, intFromString("-0x1f", 0, &slen), bignum.Int(-(1*16 + 15))); + test(7, slen == 5); + int_eq(8, intFromString("+0x1f", 0, &slen), bignum.Int(+(1*16 + 15))); + test(8, slen == 5); + int_eq(9, intFromString("0x1fg", 0, &slen), bignum.Int(1*16 + 15)); + test(9, slen == 4); + int_eq(10, intFromString("-0x1fg", 0, &slen), bignum.Int(-(1*16 + 15))); + test(10, slen == 5); +} + + +func TestRatConv(t *testing.T) { + tester = t; + test_msg = "RatConv"; + var slen int; + rat_eq(0, ratFromString("0", 0, nil), rat_zero); + rat_eq(1, ratFromString("0/1", 0, nil), rat_zero); + rat_eq(2, ratFromString("0/01", 0, nil), rat_zero); + rat_eq(3, ratFromString("0x14/10", 0, &slen), rat_two); + test(4, slen == 7); + rat_eq(5, ratFromString("0.", 0, nil), rat_zero); + rat_eq(6, ratFromString("0.001f", 10, nil), bignum.Rat(1, 1000)); + rat_eq(7, ratFromString("10101.0101", 2, nil), bignum.Rat(0x155, 1<<4)); + rat_eq(8, ratFromString("-0003.145926", 10, &slen), bignum.Rat(-3145926, 1000000)); + test(9, slen == 12); +} + + +func add(x, y bignum.Natural) bignum.Natural { + z1 := x.Add(y); + z2 := y.Add(x); + if z1.Cmp(z2) != 0 { + tester.Fatalf("addition not symmetric:\n\tx = %v\n\ty = %t", x, y); + } + return z1; +} + + +func sum(n uint, scale bignum.Natural) bignum.Natural { + s := nat_zero; + for ; n > 0; n-- { + s = add(s, bignum.Nat(n).Mul(scale)); + } + return s; +} + + +func TestNatAdd(t *testing.T) { + tester = t; + test_msg = "NatAddA"; + nat_eq(0, add(nat_zero, nat_zero), nat_zero); + nat_eq(1, add(nat_zero, c), c); + + test_msg = "NatAddB"; + for i := uint(0); i < 100; i++ { + t := bignum.Nat(i); + nat_eq(i, sum(i, c), t.Mul(t).Add(t).Shr(1).Mul(c)); + } +} + + +func mul(x, y bignum.Natural) bignum.Natural { + z1 := x.Mul(y); + z2 := y.Mul(x); + if z1.Cmp(z2) != 0 { + tester.Fatalf("multiplication not symmetric:\n\tx = %v\n\ty = %t", x, y); + } + if !x.IsZero() && z1.Div(x).Cmp(y) != 0 { + tester.Fatalf("multiplication/division not inverse (A):\n\tx = %v\n\ty = %t", x, y); + } + if !y.IsZero() && z1.Div(y).Cmp(x) != 0 { + tester.Fatalf("multiplication/division not inverse (B):\n\tx = %v\n\ty = %t", x, y); + } + return z1; +} + + +func TestNatSub(t *testing.T) { + tester = t; + test_msg = "NatSubA"; + nat_eq(0, nat_zero.Sub(nat_zero), nat_zero); + nat_eq(1, c.Sub(nat_zero), c); + + test_msg = "NatSubB"; + for i := uint(0); i < 100; i++ { + t := sum(i, c); + for j := uint(0); j <= i; j++ { + t = t.Sub(mul(bignum.Nat(j), c)); + } + nat_eq(i, t, nat_zero); + } +} + + +func TestNatMul(t *testing.T) { + tester = t; + test_msg = "NatMulA"; + nat_eq(0, mul(c, nat_zero), nat_zero); + nat_eq(1, mul(c, nat_one), c); + + test_msg = "NatMulB"; + nat_eq(0, b.Mul(bignum.MulRange(0, 100)), nat_zero); + nat_eq(1, b.Mul(bignum.MulRange(21, 100)), c); + + test_msg = "NatMulC"; + const n = 100; + p := b.Mul(c).Shl(n); + for i := uint(0); i < n; i++ { + nat_eq(i, mul(b.Shl(i), c.Shl(n-i)), p); + } +} + + +func TestNatDiv(t *testing.T) { + tester = t; + test_msg = "NatDivA"; + nat_eq(0, c.Div(nat_one), c); + nat_eq(1, c.Div(bignum.Nat(100)), bignum.Fact(99)); + nat_eq(2, b.Div(c), nat_zero); + nat_eq(4, nat_one.Shl(100).Div(nat_one.Shl(90)), nat_one.Shl(10)); + nat_eq(5, c.Div(b), bignum.MulRange(21, 100)); + + test_msg = "NatDivB"; + const n = 100; + p := bignum.Fact(n); + for i := uint(0); i < n; i++ { + nat_eq(100+i, p.Div(bignum.MulRange(1, i)), bignum.MulRange(i+1, n)); + } +} + + +func TestIntQuoRem(t *testing.T) { + tester = t; + test_msg = "IntQuoRem"; + type T struct { x, y, q, r int }; + a := []T{ + T{+8, +3, +2, +2}, + T{+8, -3, -2, +2}, + T{-8, +3, -2, -2}, + T{-8, -3, +2, -2}, + T{+1, +2, 0, +1}, + T{+1, -2, 0, +1}, + T{-1, +2, 0, -1}, + T{-1, -2, 0, -1}, + }; + for i := uint(0); i < uint(len(a)); i++ { + e := &a[i]; + x, y := bignum.Int(e.x).Mul(ip), bignum.Int(e.y).Mul(ip); + q, r := bignum.Int(e.q), bignum.Int(e.r).Mul(ip); + qq, rr := x.QuoRem(y); + int_eq(4*i+0, x.Quo(y), q); + int_eq(4*i+1, x.Rem(y), r); + int_eq(4*i+2, qq, q); + int_eq(4*i+3, rr, r); + } +} + + +func TestIntDivMod(t *testing.T) { + tester = t; + test_msg = "IntDivMod"; + type T struct { x, y, q, r int }; + a := []T{ + T{+8, +3, +2, +2}, + T{+8, -3, -2, +2}, + T{-8, +3, -3, +1}, + T{-8, -3, +3, +1}, + T{+1, +2, 0, +1}, + T{+1, -2, 0, +1}, + T{-1, +2, -1, +1}, + T{-1, -2, +1, +1}, + }; + for i := uint(0); i < uint(len(a)); i++ { + e := &a[i]; + x, y := bignum.Int(e.x).Mul(ip), bignum.Int(e.y).Mul(ip); + q, r := bignum.Int(e.q), bignum.Int(e.r).Mul(ip); + qq, rr := x.DivMod(y); + int_eq(4*i+0, x.Div(y), q); + int_eq(4*i+1, x.Mod(y), r); + int_eq(4*i+2, qq, q); + int_eq(4*i+3, rr, r); + } +} + + +func TestNatMod(t *testing.T) { + tester = t; + test_msg = "NatModA"; + for i := uint(0); ; i++ { + d := nat_one.Shl(i); + if d.Cmp(c) < 0 { + nat_eq(i, c.Add(d).Mod(c), d); + } else { + nat_eq(i, c.Add(d).Div(c), nat_two); + nat_eq(i, c.Add(d).Mod(c), d.Sub(c)); + break; + } + } +} + + +func TestNatShift(t *testing.T) { + tester = t; + test_msg = "NatShift1L"; + test(0, b.Shl(0).Cmp(b) == 0); + test(1, c.Shl(1).Cmp(c) > 0); + + test_msg = "NatShift1R"; + test(3, b.Shr(0).Cmp(b) == 0); + test(4, c.Shr(1).Cmp(c) < 0); + + test_msg = "NatShift2"; + for i := uint(0); i < 100; i++ { + test(i, c.Shl(i).Shr(i).Cmp(c) == 0); + } + + test_msg = "NatShift3L"; + { const m = 3; + p := b; + f := bignum.Nat(1<<m); + for i := uint(0); i < 100; i++ { + nat_eq(i, b.Shl(i*m), p); + p = mul(p, f); + } + } + + test_msg = "NatShift3R"; + { p := c; + for i := uint(0); !p.IsZero(); i++ { + nat_eq(i, c.Shr(i), p); + p = p.Shr(1); + } + } +} + + +func TestIntShift(t *testing.T) { + tester = t; + test_msg = "IntShift1L"; + test(0, ip.Shl(0).Cmp(ip) == 0); + test(1, ip.Shl(1).Cmp(ip) > 0); + + test_msg = "IntShift1R"; + test(0, ip.Shr(0).Cmp(ip) == 0); + test(1, ip.Shr(1).Cmp(ip) < 0); + + test_msg = "IntShift2"; + for i := uint(0); i < 100; i++ { + test(i, ip.Shl(i).Shr(i).Cmp(ip) == 0); + } + + test_msg = "IntShift3L"; + { const m = 3; + p := ip; + f := bignum.Int(1<<m); + for i := uint(0); i < 100; i++ { + int_eq(i, ip.Shl(i*m), p); + p = p.Mul(f); + } + } + + test_msg = "IntShift3R"; + { p := ip; + for i := uint(0); p.IsPos(); i++ { + int_eq(i, ip.Shr(i), p); + p = p.Shr(1); + } + } + + test_msg = "IntShift4R"; + //int_eq(0, bignum.Int(-43).Shr(1), bignum.Int(-43 >> 1)); + //int_eq(1, ip.Neg().Shr(10), ip.Neg().Div(bignum.Int(1).Shl(10))); +} + + +func TestNatCmp(t *testing.T) { + tester = t; + test_msg = "NatCmp"; + test(0, a.Cmp(a) == 0); + test(1, a.Cmp(b) < 0); + test(2, b.Cmp(a) > 0); + test(3, a.Cmp(c) < 0); + d := c.Add(b); + test(4, c.Cmp(d) < 0); + test(5, d.Cmp(c) > 0); +} + + +func TestNatLog2(t *testing.T) { + tester = t; + test_msg = "NatLog2A"; + test(0, nat_one.Log2() == 0); + test(1, nat_two.Log2() == 1); + test(2, bignum.Nat(3).Log2() == 1); + test(3, bignum.Nat(4).Log2() == 2); + + test_msg = "NatLog2B"; + for i := uint(0); i < 100; i++ { + test(i, nat_one.Shl(i).Log2() == i); + } +} + + +func TestNatGcd(t *testing.T) { + tester = t; + test_msg = "NatGcdA"; + f := bignum.Nat(99991); + nat_eq(0, b.Mul(f).Gcd(c.Mul(f)), bignum.MulRange(1, 20).Mul(f)); +} + + +func TestNatPow(t *testing.T) { + tester = t; + test_msg = "NatPowA"; + nat_eq(0, nat_two.Pow(0), nat_one); + + test_msg = "NatPowB"; + for i := uint(0); i < 100; i++ { + nat_eq(i, nat_two.Pow(i), nat_one.Shl(i)); + } +} + + +func TestNatPop(t *testing.T) { + tester = t; + test_msg = "NatPopA"; + test(0, nat_zero.Pop() == 0); + test(1, nat_one.Pop() == 1); + test(2, bignum.Nat(10).Pop() == 2); + test(3, bignum.Nat(30).Pop() == 4); + test(4, bignum.Nat(0x1248f).Shl(33).Pop() == 8); + + test_msg = "NatPopB"; + for i := uint(0); i < 100; i++ { + test(i, nat_one.Shl(i).Sub(nat_one).Pop() == i); + } +} + diff --git a/src/pkg/bufio/Makefile b/src/pkg/bufio/Makefile new file mode 100644 index 000000000..abb826e7f --- /dev/null +++ b/src/pkg/bufio/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + bufio.$O\ + + +phases: a1 +_obj$D/bufio.a: phases + +a1: $(O1) + $(AR) grc _obj$D/bufio.a bufio.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/bufio.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/bufio.a + +packages: _obj$D/bufio.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/bufio.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/bufio.a diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go new file mode 100644 index 000000000..7bfbb089f --- /dev/null +++ b/src/pkg/bufio/bufio.go @@ -0,0 +1,515 @@ +// Copyright 2009 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. + +// This package implements buffered I/O. It wraps an io.Reader or io.Writer +// object, creating another object (Reader or Writer) that also implements +// the interface but provides buffering and some help for textual I/O. +package bufio + +import ( + "io"; + "os"; + "utf8"; +) + + +// TODO: +// - maybe define an interface +// - Reader: ReadRune, UnreadRune ? +// could make ReadRune generic if we dropped UnreadRune +// - buffered output + +const ( + defaultBufSize = 4096 +) + +// Errors introduced by this package. +type Error struct { + os.ErrorString; +} + +var ( + PhaseError os.Error = &Error{"bufio: phase error"}; + BufferFull os.Error = &Error{"bufio: buffer full"}; + InternalError os.Error = &Error{"bufio: internal error"}; + BadBufSize os.Error = &Error{"bufio: bad buffer size"}; +) + +func copySlice(dst []byte, src []byte) { + for i := 0; i < len(dst); i++ { + dst[i] = src[i] + } +} + + +// Buffered input. + +// Reader implements buffering for an io.Reader object. +type Reader struct { + buf []byte; + rd io.Reader; + r, w int; + err os.Error; + lastbyte int; +} + +// NewReaderSize creates a new Reader whose buffer has the specified size, +// which must be greater than zero. If the argument io.Reader is already a +// Reader with large enough size, it returns the underlying Reader. +// It returns the Reader and any error. +func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) { + if size <= 0 { + return nil, BadBufSize + } + // Is it already a Reader? + b, ok := rd.(*Reader); + if ok && len(b.buf) >= size { + return b, nil + } + b = new(Reader); + b.buf = make([]byte, size); + b.rd = rd; + b.lastbyte = -1; + return b, nil +} + +// NewReader returns a new Reader whose buffer has the default size. +func NewReader(rd io.Reader) *Reader { + b, err := NewReaderSize(rd, defaultBufSize); + if err != nil { + // cannot happen - defaultBufSize is a valid size + panic("bufio: NewReader: ", err.String()); + } + return b; +} + +//.fill reads a new chunk into the buffer. +func (b *Reader) fill() os.Error { + if b.err != nil { + return b.err + } + + // Slide existing data to beginning. + if b.w > b.r { + copySlice(b.buf[0:b.w-b.r], b.buf[b.r:b.w]); + b.w -= b.r; + } else { + b.w = 0 + } + b.r = 0; + + // Read new data. + n, e := b.rd.Read(b.buf[b.w:len(b.buf)]); + if e != nil { + b.err = e; + return e + } + b.w += n; + return nil +} + +// Read reads data into p. +// It returns the number of bytes read into p. +// If nn < len(p), also returns an error explaining +// why the read is short. At EOF, the count will be +// zero and err will be io.ErrEOF. +func (b *Reader) Read(p []byte) (nn int, err os.Error) { + nn = 0; + for len(p) > 0 { + n := len(p); + if b.w == b.r { + if len(p) >= len(b.buf) { + // Large read, empty buffer. + // Read directly into p to avoid copy. + n, b.err = b.rd.Read(p); + if n > 0 { + b.lastbyte = int(p[n-1]); + } + p = p[n:len(p)]; + nn += n; + if b.err != nil { + return nn, b.err + } + if n == 0 { + return nn, io.ErrEOF + } + continue; + } + b.fill(); + if b.err != nil { + return nn, b.err + } + if b.w == b.r { + return nn, io.ErrEOF + } + } + if n > b.w - b.r { + n = b.w - b.r + } + copySlice(p[0:n], b.buf[b.r:b.r+n]); + p = p[n:len(p)]; + b.r += n; + b.lastbyte = int(b.buf[b.r-1]); + nn += n + } + return nn, nil +} + +// ReadByte reads and returns a single byte. +// If no byte is available, returns an error. +func (b *Reader) ReadByte() (c byte, err os.Error) { + if b.w == b.r { + b.fill(); + if b.err != nil { + return 0, b.err + } + if b.w == b.r { + return 0, io.ErrEOF + } + } + c = b.buf[b.r]; + b.r++; + b.lastbyte = int(c); + return c, nil +} + +// UnreadByte unreads the last byte. Only one byte may be unread at a given time. +func (b *Reader) UnreadByte() os.Error { + if b.err != nil { + return b.err + } + if b.r == b.w && b.lastbyte >= 0 { + b.w = 1; + b.r = 0; + b.buf[0] = byte(b.lastbyte); + b.lastbyte = -1; + return nil; + } + if b.r <= 0 { + return PhaseError + } + b.r--; + b.lastbyte = -1; + return nil +} + +// ReadRune reads a single UTF-8 encoded Unicode character and returns the +// rune and its size in bytes. +func (b *Reader) ReadRune() (rune int, size int, err os.Error) { + for b.r + utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) { + n := b.w - b.r; + b.fill(); + if b.err != nil { + return 0, 0, b.err + } + if b.w - b.r == n { + // no bytes read + if b.r == b.w { + return 0, 0, io.ErrEOF + } + break; + } + } + rune, size = int(b.buf[b.r]), 1; + if rune >= 0x80 { + rune, size = utf8.DecodeRune(b.buf[b.r:b.w]); + } + b.r += size; + b.lastbyte = int(b.buf[b.r-1]); + return rune, size, nil +} + +// Helper function: look for byte c in array p, +// returning its index or -1. +func findByte(p []byte, c byte) int { + for i := 0; i < len(p); i++ { + if p[i] == c { + return i + } + } + return -1 +} + +// Buffered returns the number of bytes that can be read from the current buffer. +func (b *Reader) Buffered() int { + return b.w - b.r; +} + +// ReadLineSlice reads until the first occurrence of delim in the input, +// returning a slice pointing at the bytes in the buffer. +// The bytes stop being valid at the next read call. +// Fails if the line doesn't fit in the buffer. +// For internal or advanced use only; most uses should +// call ReadLineString or ReadLineBytes instead. +func (b *Reader) ReadLineSlice(delim byte) (line []byte, err os.Error) { + if b.err != nil { + return nil, b.err + } + + // Look in buffer. + if i := findByte(b.buf[b.r:b.w], delim); i >= 0 { + line1 := b.buf[b.r:b.r+i+1]; + b.r += i+1; + return line1, nil + } + + // Read more into buffer, until buffer fills or we find delim. + for { + n := b.Buffered(); + b.fill(); + if b.err != nil { + return nil, b.err + } + if b.Buffered() == n { // no data added; end of file + line := b.buf[b.r:b.w]; + b.r = b.w; + return line, io.ErrEOF + } + + // Search new part of buffer + if i := findByte(b.buf[n:b.w], delim); i >= 0 { + line := b.buf[0:n+i+1]; + b.r = n+i+1; + return line, nil + } + + // Buffer is full? + if b.Buffered() >= len(b.buf) { + return nil, BufferFull + } + } + + // BUG 6g bug100 + return nil, nil +} + +// ReadLineBytes reads until the first occurrence of delim in the input, +// returning a new byte array containing the line. +// If an error happens, returns the data (without a delimiter) +// and the error. (It can't leave the data in the buffer because +// it might have read more than the buffer size.) +func (b *Reader) ReadLineBytes(delim byte) (line []byte, err os.Error) { + if b.err != nil { + return nil, b.err + } + + // Use ReadLineSlice to look for array, + // accumulating full buffers. + var frag []byte; + var full [][]byte; + nfull := 0; + err = nil; + + for { + var e os.Error; + frag, e = b.ReadLineSlice(delim); + if e == nil { // got final fragment + break + } + if e != BufferFull { // unexpected error + err = e; + break + } + + // Read bytes out of buffer. + buf := make([]byte, b.Buffered()); + var n int; + n, e = b.Read(buf); + if e != nil { + frag = buf[0:n]; + err = e; + break + } + if n != len(buf) { + frag = buf[0:n]; + err = InternalError; + break + } + + // Grow list if needed. + if full == nil { + full = make([][]byte, 16); + } else if nfull >= len(full) { + newfull := make([][]byte, len(full)*2); + // BUG slice assignment + for i := 0; i < len(full); i++ { + newfull[i] = full[i]; + } + full = newfull + } + + // Save buffer + full[nfull] = buf; + nfull++; + } + + // Allocate new buffer to hold the full pieces and the fragment. + n := 0; + for i := 0; i < nfull; i++ { + n += len(full[i]) + } + n += len(frag); + + // Copy full pieces and fragment in. + buf := make([]byte, n); + n = 0; + for i := 0; i < nfull; i++ { + copySlice(buf[n:n+len(full[i])], full[i]); + n += len(full[i]) + } + copySlice(buf[n:n+len(frag)], frag); + return buf, err +} + +// ReadLineString reads until the first occurrence of delim in the input, +// returning a new string containing the line. +// If savedelim, keep delim in the result; otherwise drop it. +func (b *Reader) ReadLineString(delim byte, savedelim bool) (line string, err os.Error) { + bytes, e := b.ReadLineBytes(delim); + if e != nil { + return string(bytes), e + } + if !savedelim { + bytes = bytes[0:len(bytes)-1] + } + return string(bytes), nil +} + + +// buffered output + +// Writer implements buffering for an io.Writer object. +type Writer struct { + err os.Error; + buf []byte; + n int; + wr io.Writer; +} + +// NewWriterSize creates a new Writer whose buffer has the specified size, +// which must be greater than zero. If the argument io.Writer is already a +// Writer with large enough size, it returns the underlying Writer. +// It returns the Writer and any error. +func NewWriterSize(wr io.Writer, size int) (*Writer, os.Error) { + if size <= 0 { + return nil, BadBufSize + } + // Is it already a Writer? + b, ok := wr.(*Writer); + if ok && len(b.buf) >= size { + return b, nil + } + b = new(Writer); + b.buf = make([]byte, size); + b.wr = wr; + return b, nil +} + +// NewWriter returns a new Writer whose buffer has the default size. +func NewWriter(wr io.Writer) *Writer { + b, err := NewWriterSize(wr, defaultBufSize); + if err != nil { + // cannot happen - defaultBufSize is valid size + panic("bufio: NewWriter: ", err.String()); + } + return b; +} + +// Flush writes any buffered data to the underlying io.Writer. +func (b *Writer) Flush() os.Error { + if b.err != nil { + return b.err + } + n, e := b.wr.Write(b.buf[0:b.n]); + if n < b.n && e == nil { + e = io.ErrShortWrite; + } + if e != nil { + if n > 0 && n < b.n { + copySlice(b.buf[0:b.n-n], b.buf[n:b.n]) + } + b.n -= n; + b.err = e; + return e + } + b.n = 0; + return nil +} + +// Available returns how many bytes are unused in the buffer. +func (b *Writer) Available() int { + return len(b.buf) - b.n +} + +// Buffered returns the number of bytes that have been written into the current buffer. +func (b *Writer) Buffered() int { + return b.n +} + +// Write writes the contents of p into the buffer. +// It returns the number of bytes written. +// If nn < len(p), also returns an error explaining +// why the write is short. +func (b *Writer) Write(p []byte) (nn int, err os.Error) { + if b.err != nil { + return 0, b.err + } + nn = 0; + for len(p) > 0 { + n := b.Available(); + if n <= 0 { + if b.Flush(); b.err != nil { + break + } + n = b.Available() + } + if b.Available() == 0 && len(p) >= len(b.buf) { + // Large write, empty buffer. + // Write directly from p to avoid copy. + n, b.err = b.wr.Write(p); + nn += n; + p = p[n:len(p)]; + if b.err != nil { + break; + } + continue; + } + if n > len(p) { + n = len(p) + } + copySlice(b.buf[b.n:b.n+n], p[0:n]); + b.n += n; + nn += n; + p = p[n:len(p)] + } + return nn, b.err +} + +// WriteByte writes a single byte. +func (b *Writer) WriteByte(c byte) os.Error { + if b.err != nil { + return b.err + } + if b.Available() <= 0 && b.Flush() != nil { + return b.err + } + b.buf[b.n] = c; + b.n++; + return nil +} + +// buffered input and output + +// ReadWriter stores pointers to a Reader and a Writer. +// It implements io.ReadWriter. +type ReadWriter struct { + *Reader; + *Writer; +} + +// NewReadWriter allocates a new ReadWriter that dispatches to r and w. +func NewReadWriter(r *Reader, w *Writer) *ReadWriter { + return &ReadWriter{r, w} +} + diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go new file mode 100644 index 000000000..6e5135df7 --- /dev/null +++ b/src/pkg/bufio/bufio_test.go @@ -0,0 +1,298 @@ +// Copyright 2009 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 bufio + +import ( + "bufio"; + "fmt"; + "io"; + "os"; + "testing"; + "testing/iotest"; +) + +// Reads from a reader and rot13s the result. +type rot13Reader struct { + r io.Reader +} + +func newRot13Reader(r io.Reader) *rot13Reader { + r13 := new(rot13Reader); + r13.r = r; + return r13 +} + +func (r13 *rot13Reader) Read(p []byte) (int, os.Error) { + n, e := r13.r.Read(p); + if e != nil { + return n, e + } + for i := 0; i < n; i++ { + c := p[i] | 0x20; // lowercase byte + if 'a' <= c && c <= 'm' { + p[i] += 13; + } else if 'n' <= c && c <= 'z' { + p[i] -= 13; + } + } + return n, nil +} + +// Call ReadByte to accumulate the text of a file +func readBytes(buf *Reader) string { + var b [1000]byte; + nb := 0; + for { + c, e := buf.ReadByte(); + if e == io.ErrEOF { + break + } + if e != nil { + panic("Data: "+e.String()) + } + b[nb] = c; + nb++; + } + return string(b[0:nb]) +} + +func TestReaderSimple(t *testing.T) { + data := io.StringBytes("hello world"); + b := NewReader(io.NewByteReader(data)); + if s := readBytes(b); s != "hello world" { + t.Errorf("simple hello world test failed: got %q", s); + } + + b = NewReader(newRot13Reader(io.NewByteReader(data))); + if s := readBytes(b); s != "uryyb jbeyq" { + t.Error("rot13 hello world test failed: got %q", s); + } +} + + +type readMaker struct { + name string; + fn func(io.Reader) io.Reader; +} +var readMakers = []readMaker { + readMaker{ "full", func(r io.Reader) io.Reader { return r } }, + readMaker{ "byte", iotest.OneByteReader }, + readMaker{ "half", iotest.HalfReader }, +} + +// Call ReadLineString (which ends up calling everything else) +// to accumulate the text of a file. +func readLines(b *Reader) string { + s := ""; + for { + s1, e := b.ReadLineString('\n', true); + if e == io.ErrEOF { + break + } + if e != nil { + panic("GetLines: "+e.String()) + } + s += s1 + } + return s +} + +// Call Read to accumulate the text of a file +func reads(buf *Reader, m int) string { + var b [1000]byte; + nb := 0; + for { + n, e := buf.Read(b[nb:nb+m]); + nb += n; + if e == io.ErrEOF { + break + } + } + return string(b[0:nb]) +} + +type bufReader struct { + name string; + fn func(*Reader) string; +} +var bufreaders = []bufReader { + bufReader{ "1", func(b *Reader) string { return reads(b, 1) } }, + bufReader{ "2", func(b *Reader) string { return reads(b, 2) } }, + bufReader{ "3", func(b *Reader) string { return reads(b, 3) } }, + bufReader{ "4", func(b *Reader) string { return reads(b, 4) } }, + bufReader{ "5", func(b *Reader) string { return reads(b, 5) } }, + bufReader{ "7", func(b *Reader) string { return reads(b, 7) } }, + bufReader{ "bytes", readBytes }, + bufReader{ "lines", readLines }, +} + +var bufsizes = []int { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 23, 32, 46, 64, 93, 128, 1024, 4096 +} + +func TestReader(t *testing.T) { + var texts [31]string; + str := ""; + all := ""; + for i := 0; i < len(texts)-1; i++ { + texts[i] = str + "\n"; + all += texts[i]; + str += string(i%26+'a') + } + texts[len(texts)-1] = all; + + for h := 0; h < len(texts); h++ { + text := texts[h]; + textbytes := io.StringBytes(text); + for i := 0; i < len(readMakers); i++ { + for j := 0; j < len(bufreaders); j++ { + for k := 0; k < len(bufsizes); k++ { + readmaker := readMakers[i]; + bufreader := bufreaders[j]; + bufsize := bufsizes[k]; + read := readmaker.fn(io.NewByteReader(textbytes)); + buf, e := NewReaderSize(read, bufsize); + s := bufreader.fn(buf); + if s != text { + t.Errorf("reader=%s fn=%s bufsize=%d want=%q got=%q", + readmaker.name, bufreader.name, bufsize, text, s); + } + } + } + } + } +} + +func TestWriter(t *testing.T) { + var data [8192]byte; + + for i := 0; i < len(data); i++ { + data[i] = byte(' '+ i%('~'-' ')); + } + w := new(io.ByteBuffer); + for i := 0; i < len(bufsizes); i++ { + for j := 0; j < len(bufsizes); j++ { + nwrite := bufsizes[i]; + bs := bufsizes[j]; + + // Write nwrite bytes using buffer size bs. + // Check that the right amount makes it out + // and that the data is correct. + + w.Reset(); + buf, e := NewWriterSize(w, bs); + context := fmt.Sprintf("nwrite=%d bufsize=%d", nwrite, bs); + if e != nil { + t.Errorf("%s: NewWriterSize %d: %v", context, bs, e); + continue; + } + n, e1 := buf.Write(data[0:nwrite]); + if e1 != nil || n != nwrite { + t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1); + continue; + } + if e = buf.Flush(); e != nil { + t.Errorf("%s: buf.Flush = %v", context, e); + } + + written := w.Data(); + if len(written) != nwrite { + t.Errorf("%s: %d bytes written", context, len(written)); + } + for l := 0; l < len(written); l++ { + if written[i] != data[i] { + t.Errorf("%s: wrong bytes written"); + t.Errorf("want=%s", data[0:len(written)]); + t.Errorf("have=%s", written); + } + } + } + } +} + +// Check that write errors are returned properly. + +type errorWriterTest struct { + n, m int; + err os.Error; + expect os.Error; +} + +func (w errorWriterTest) Write(p []byte) (int, os.Error) { + return len(p)*w.n/w.m, w.err; +} + +var errorWriterTests = []errorWriterTest { + errorWriterTest{ 0, 1, nil, io.ErrShortWrite }, + errorWriterTest{ 1, 2, nil, io.ErrShortWrite }, + errorWriterTest{ 1, 1, nil, nil }, + errorWriterTest{ 0, 1, os.EPIPE, os.EPIPE }, + errorWriterTest{ 1, 2, os.EPIPE, os.EPIPE }, + errorWriterTest{ 1, 1, os.EPIPE, os.EPIPE }, +} + +func TestWriteErrors(t *testing.T) { + for i, w := range errorWriterTests { + buf := NewWriter(w); + n, e := buf.Write(io.StringBytes("hello world")); + if e != nil { + t.Errorf("Write hello to %v: %v", w, e); + continue; + } + e = buf.Flush(); + if e != w.expect { + t.Errorf("Flush %v: got %v, wanted %v", w, e, w.expect); + } + } +} + +func TestNewReaderSizeIdempotent(t *testing.T) { + const BufSize = 1000; + b, err := NewReaderSize(io.NewByteReader(io.StringBytes("hello world")), BufSize); + if err != nil { + t.Error("NewReaderSize create fail", err); + } + // Does it recognize itself? + b1, err2 := NewReaderSize(b, BufSize); + if err2 != nil { + t.Error("NewReaderSize #2 create fail", err2); + } + if b1 != b { + t.Error("NewReaderSize did not detect underlying Reader"); + } + // Does it wrap if existing buffer is too small? + b2, err3 := NewReaderSize(b, 2*BufSize); + if err3 != nil { + t.Error("NewReaderSize #3 create fail", err3); + } + if b2 == b { + t.Error("NewReaderSize did not enlarge buffer"); + } +} + +func TestNewWriterSizeIdempotent(t *testing.T) { + const BufSize = 1000; + b, err := NewWriterSize(new(io.ByteBuffer), BufSize); + if err != nil { + t.Error("NewWriterSize create fail", err); + } + // Does it recognize itself? + b1, err2 := NewWriterSize(b, BufSize); + if err2 != nil { + t.Error("NewWriterSize #2 create fail", err2); + } + if b1 != b { + t.Error("NewWriterSize did not detect underlying Writer"); + } + // Does it wrap if existing buffer is too small? + b2, err3 := NewWriterSize(b, 2*BufSize); + if err3 != nil { + t.Error("NewWriterSize #3 create fail", err3); + } + if b2 == b { + t.Error("NewWriterSize did not enlarge buffer"); + } +} diff --git a/src/pkg/bytes/Makefile b/src/pkg/bytes/Makefile new file mode 100644 index 000000000..5220d2880 --- /dev/null +++ b/src/pkg/bytes/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + bytes.$O\ + + +phases: a1 +_obj$D/bytes.a: phases + +a1: $(O1) + $(AR) grc _obj$D/bytes.a bytes.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/bytes.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/bytes.a + +packages: _obj$D/bytes.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/bytes.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/bytes.a diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go new file mode 100644 index 000000000..dd299a82e --- /dev/null +++ b/src/pkg/bytes/bytes.go @@ -0,0 +1,164 @@ +// Copyright 2009 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. + +// A package of simple functions to manipulate arrays of bytes. +// Analagous to the facilities of the strings package. +package bytes + +import "utf8" + +// Compare returns an integer comparing the two byte arrays lexicographically. +// The result will be 0 if a==b, -1 if a < b, and +1 if a > b +func Compare(a, b []byte) int { + for i := 0; i < len(a) && i < len(b); i++ { + switch { + case a[i] > b[i]: + return 1 + case a[i] < b[i]: + return -1 + } + } + switch { + case len(a) < len(b): + return -1 + case len(a) > len(b): + return 1 + } + return 0 +} + +// Equal returns a boolean reporting whether a == b. +func Equal(a, b []byte) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + return true +} + +// Copy copies the source to the destination, stopping when the source +// is all transferred. The caller must guarantee that there is enough +// room in the destination. It returns the number of bytes copied +func Copy(dst, src []byte) int { + for i, x := range src { + dst[i] = x + } + return len(src) +} + +// Explode splits s into an array of UTF-8 sequences, one per Unicode character (still arrays of bytes). +// Invalid UTF-8 sequences become correct encodings of U+FFF8. +func Explode(s []byte) [][]byte { + a := make([][]byte, utf8.RuneCount(s)); + var size, rune int; + i := 0; + for len(s) > 0 { + rune, size = utf8.DecodeRune(s); + a[i] = s[0:size]; + s = s[size:len(s)]; + i++; + } + return a +} + +// Count counts the number of non-overlapping instances of sep in s. +func Count(s, sep []byte) int { + if len(sep) == 0 { + return utf8.RuneCount(s)+1 + } + c := sep[0]; + n := 0; + for i := 0; i+len(sep) <= len(s); i++ { + if s[i] == c && (len(sep) == 1 || Equal(s[i:i+len(sep)], sep)) { + n++; + i += len(sep)-1 + } + } + return n +} + +// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. +func Index(s, sep []byte) int { + n := len(sep); + if n == 0 { + return 0 + } + c := sep[0]; + for i := 0; i+n <= len(s); i++ { + if s[i] == c && (n == 1 || Equal(s[i:i+n], sep)) { + return i + } + } + return -1 +} + +// Split returns the array representing the subarrays of s separated by sep. Adjacent +// occurrences of sep produce empty subarrays. If sep is empty, it is the same as Explode. +func Split(s, sep []byte) [][]byte { + if len(sep) == 0 { + return Explode(s) + } + c := sep[0]; + start := 0; + n := Count(s, sep)+1; + a := make([][]byte, n); + na := 0; + for i := 0; i+len(sep) <= len(s); i++ { + if s[i] == c && (len(sep) == 1 || Equal(s[i:i+len(sep)], sep)) { + a[na] = s[start:i]; + na++; + start = i+len(sep); + i += len(sep)-1 + } + } + a[na] = s[start:len(s)]; + return a +} + +// Join concatenates the elements of a to create a single byte array. The separator +// sep is placed between elements in the resulting array. +func Join(a [][]byte, sep []byte) []byte { + if len(a) == 0 { + return []byte{} + } + if len(a) == 1 { + return a[0] + } + n := len(sep) * (len(a)-1); + for i := 0; i < len(a); i++ { + n += len(a[i]) + } + + b := make([]byte, n); + bp := 0; + for i := 0; i < len(a); i++ { + s := a[i]; + for j := 0; j < len(s); j++ { + b[bp] = s[j]; + bp++ + } + if i + 1 < len(a) { + s = sep; + for j := 0; j < len(s); j++ { + b[bp] = s[j]; + bp++ + } + } + } + return b +} + +// HasPrefix tests whether the byte array s begins with prefix. +func HasPrefix(s, prefix []byte) bool { + return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix) +} + +// HasSuffix tests whether the byte array s ends with suffix. +func HasSuffix(s, suffix []byte) bool { + return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):len(s)], suffix) +} diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go new file mode 100644 index 000000000..4e7cdfad6 --- /dev/null +++ b/src/pkg/bytes/bytes_test.go @@ -0,0 +1,157 @@ +// Copyright 2009 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 bytes + +import ( + "bytes"; + "io"; + "testing"; +) + +func eq(a, b []string) bool { + if len(a) != len(b) { + return false; + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false; + } + } + return true; +} + +func arrayOfString(a [][]byte) []string { + result := make([]string, len(a)); + for j := 0; j < len(a); j++ { + result[j] = string(a[j]) + } + return result +} + +// For ease of reading, the test cases use strings that are converted to byte +// arrays before invoking the functions. + +var abcd = "abcd" +var faces = "☺☻☹" +var commas = "1,2,3,4" +var dots = "1....2....3....4" + +type CompareTest struct { + a string; + b string; + cmp int; +} +var comparetests = []CompareTest { + CompareTest{ "", "", 0 }, + CompareTest{ "a", "", 1 }, + CompareTest{ "", "a", -1 }, + CompareTest{ "abc", "abc", 0 }, + CompareTest{ "ab", "abc", -1 }, + CompareTest{ "abc", "ab", 1 }, + CompareTest{ "x", "ab", 1 }, + CompareTest{ "ab", "x", -1 }, + CompareTest{ "x", "a", 1 }, + CompareTest{ "b", "x", -1 }, +} + +func TestCompare(t *testing.T) { + for i := 0; i < len(comparetests); i++ { + tt := comparetests[i]; + a := io.StringBytes(tt.a); + b := io.StringBytes(tt.b); + cmp := Compare(a, b); + eql := Equal(a, b); + if cmp != tt.cmp { + t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp); + } + if eql != (tt.cmp==0) { + t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql); + } + } +} + + +type ExplodeTest struct { + s string; + a []string; +} +var explodetests = []ExplodeTest { + ExplodeTest{ abcd, []string{"a", "b", "c", "d"} }, + ExplodeTest{ faces, []string{"☺", "☻", "☹" } }, +} +func TestExplode(t *testing.T) { + for i := 0; i < len(explodetests); i++ { + tt := explodetests[i]; + a := Explode(io.StringBytes(tt.s)); + result := arrayOfString(a); + if !eq(result, tt.a) { + t.Errorf(`Explode("%s") = %v; want %v`, tt.s, result, tt.a); + continue; + } + s := Join(a, []byte{}); + if string(s) != tt.s { + t.Errorf(`Join(Explode("%s"), "") = "%s"`, tt.s, s); + } + } +} + + +type SplitTest struct { + s string; + sep string; + a []string; +} +var splittests = []SplitTest { + SplitTest{ abcd, "a", []string{"", "bcd"} }, + SplitTest{ abcd, "z", []string{"abcd"} }, + SplitTest{ abcd, "", []string{"a", "b", "c", "d"} }, + SplitTest{ commas, ",", []string{"1", "2", "3", "4"} }, + SplitTest{ dots, "...", []string{"1", ".2", ".3", ".4"} }, + SplitTest{ faces, "☹", []string{"☺☻", ""} }, + SplitTest{ faces, "~", []string{faces} }, + SplitTest{ faces, "", []string{"☺", "☻", "☹"} }, +} +func TestSplit(t *testing.T) { + for i := 0; i < len(splittests); i++ { + tt := splittests[i]; + a := Split(io.StringBytes(tt.s), io.StringBytes(tt.sep)); + result := arrayOfString(a); + if !eq(result, tt.a) { + t.Errorf(`Split("%s", "%s") = %v; want %v`, tt.s, tt.sep, result, tt.a); + continue; + } + s := Join(a, io.StringBytes(tt.sep)); + if string(s) != tt.s { + t.Errorf(`Join(Split("%s", "%s"), "%s") = "%s"`, tt.s, tt.sep, tt.sep, s); + } + } +} + +type CopyTest struct { + a string; + b string; + res string; +} +var copytests = []CopyTest { + CopyTest{ "", "", "" }, + CopyTest{ "a", "", "a" }, + CopyTest{ "a", "a", "a" }, + CopyTest{ "a", "b", "b" }, + CopyTest{ "xyz", "abc", "abc" }, + CopyTest{ "wxyz", "abc", "abcz" }, +} + +func TestCopy(t *testing.T) { + for i := 0; i < len(copytests); i++ { + tt := copytests[i]; + dst := io.StringBytes(tt.a); + Copy(dst, io.StringBytes(tt.b)); + result := string(dst); + if result != tt.res { + t.Errorf(`Copy("%s", "%s") = "%s"; want "%s"`, tt.a, tt.b, result, tt.res); + continue; + } + } +} diff --git a/src/pkg/compress/flate/Makefile b/src/pkg/compress/flate/Makefile new file mode 100644 index 000000000..3f73a1932 --- /dev/null +++ b/src/pkg/compress/flate/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/compress/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + inflate.$O\ + + +phases: a1 +_obj$D/flate.a: phases + +a1: $(O1) + $(AR) grc _obj$D/flate.a inflate.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/flate.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/flate.a + +packages: _obj$D/flate.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/flate.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/flate.a diff --git a/src/pkg/compress/flate/flate_test.go b/src/pkg/compress/flate/flate_test.go new file mode 100644 index 000000000..309606ecb --- /dev/null +++ b/src/pkg/compress/flate/flate_test.go @@ -0,0 +1,131 @@ +// Copyright 2009 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. + +// This test tests some internals of the flate package. +// The tests in package compress/gzip serve as the +// end-to-end test of the inflater. + +package flate + +import ( + "bufio"; + "compress/flate"; + "io"; + "os"; + "reflect"; + "strconv"; + "testing"; +) + +// The Huffman code lengths used by the fixed-format Huffman blocks. +var fixedHuffmanBits = [...]int { + // 0-143 length 8 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + + // 144-255 length 9 + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + + // 256-279 length 7 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + + // 280-287 length 8 + 8, 8, 8, 8, 8, 8, 8, 8, +} + +type InitDecoderTest struct { + in []int; + out huffmanDecoder; + ok bool; +} + +var initDecoderTests = []*InitDecoderTest{ + // Example from Connell 1973, + &InitDecoderTest{ + []int{ 3, 5, 2, 4, 3, 5, 5, 4, 4, 3, 4, 5 }, + huffmanDecoder { + 2, 5, + [maxCodeLen+1]int{ 2: 0, 4, 13, 31 }, + [maxCodeLen+1]int{ 2: 0, 1, 6, 20 }, + // Paper used different code assignment: + // 2, 9, 4, 0, 10, 8, 3, 7, 1, 5, 11, 6 + // Reordered here so that codes of same length + // are assigned to increasing numbers. + []int{ 2, 0, 4, 9, 3, 7, 8, 10, 1, 5, 6, 11 }, + }, + true, + }, + + // Example from RFC 1951 section 3.2.2 + &InitDecoderTest{ + []int{ 2, 1, 3, 3 }, + huffmanDecoder { + 1, 3, + [maxCodeLen+1]int{ 1: 0, 2, 7, }, + [maxCodeLen+1]int{ 1: 0, 1, 4, }, + []int{ 1, 0, 2, 3 }, + }, + true, + }, + + // Second example from RFC 1951 section 3.2.2 + &InitDecoderTest{ + []int{ 3, 3, 3, 3, 3, 2, 4, 4 }, + huffmanDecoder{ + 2, 4, + [maxCodeLen+1]int{ 2: 0, 6, 15, }, + [maxCodeLen+1]int{ 2: 0, 1, 8, }, + []int{ 5, 0, 1, 2, 3, 4, 6, 7 }, + }, + true, + }, + + // Static Huffman codes (RFC 1951 section 3.2.6) + &InitDecoderTest{ + &fixedHuffmanBits, + fixedHuffmanDecoder, + true, + }, + + // Illegal input. + &InitDecoderTest{ + []int{ }, + huffmanDecoder{ }, + false, + }, + + // Illegal input. + &InitDecoderTest{ + []int{ 0, 0, 0, 0, 0, 0, 0, }, + huffmanDecoder{ }, + false, + }, +} + +func TestInitDecoder(t *testing.T) { + for i, tt := range initDecoderTests { + var h huffmanDecoder; + if h.init(tt.in) != tt.ok { + t.Errorf("test %d: init = %v", i, !tt.ok); + continue; + } + if !reflect.DeepEqual(&h, &tt.out) { + t.Errorf("test %d:\nhave %v\nwant %v", i, h, tt.out); + } + } +} diff --git a/src/pkg/compress/flate/inflate.go b/src/pkg/compress/flate/inflate.go new file mode 100644 index 000000000..c07c94c6e --- /dev/null +++ b/src/pkg/compress/flate/inflate.go @@ -0,0 +1,673 @@ +// Copyright 2009 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. + +// The flate package implements the DEFLATE compressed data +// format, described in RFC 1951. The gzip and zlib packages +// implement access to DEFLATE-based file formats. +package flate + +import ( + "bufio"; + "io"; + "os"; + "strconv"; +) + +const ( + maxCodeLen = 16; // max length of Huffman code + maxHist = 32768; // max history required + maxLit = 286; + maxDist = 32; + numCodes = 19; // number of codes in Huffman meta-code +) + +// TODO(rsc): Publish in another package? +var reverseByte = [256]byte { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +} + +// A CorruptInputError reports the presence of corrupt input at a given offset. +type CorruptInputError int64 +func (e CorruptInputError) String() string { + return "flate: corrupt input at offset " + strconv.Itoa64(int64(e)); +} + +// An InternalError reports an error in the flate code itself. +type InternalError string +func (e InternalError) String() string { + return "flate: internal error: " + string(e); +} + +// A ReadError reports an error encountered while reading input. +type ReadError struct { + Offset int64; // byte offset where error occurred + Error os.Error; // error returned by underlying Read +} + +func (e *ReadError) String() string { + return "flate: read error at offset " + strconv.Itoa64(e.Offset) + + ": " + e.Error.String(); +} + +// A WriteError reports an error encountered while writing output. +type WriteError struct { + Offset int64; // byte offset where error occurred + Error os.Error; // error returned by underlying Read +} + +func (e *WriteError) String() string { + return "flate: write error at offset " + strconv.Itoa64(e.Offset) + + ": " + e.Error.String(); +} + +// Huffman decoder is based on +// J. Brian Connell, ``A Huffman-Shannon-Fano Code,'' +// Proceedings of the IEEE, 61(7) (July 1973), pp 1046-1047. +type huffmanDecoder struct { + // min, max code length + min, max int; + + // limit[i] = largest code word of length i + // Given code v of length n, + // need more bits if v > limit[n]. + limit [maxCodeLen+1]int; + + // base[i] = smallest code word of length i - seq number + base [maxCodeLen+1]int; + + // codes[seq number] = output code. + // Given code v of length n, value is + // codes[v - base[n]]. + codes []int; +} + +// Initialize Huffman decoding tables from array of code lengths. +func (h *huffmanDecoder) init(bits []int) bool { + // TODO(rsc): Return false sometimes. + + // Count number of codes of each length, + // compute min and max length. + var count [maxCodeLen+1]int; + var min, max int; + for i, n := range bits { + if n == 0 { + continue; + } + if min == 0 || n < min { + min = n; + } + if n > max { + max = n; + } + count[n]++; + } + if max == 0 { + return false; + } + + h.min = min; + h.max = max; + + + // For each code range, compute + // nextcode (first code of that length), + // limit (last code of that length), and + // base (offset from first code to sequence number). + code := 0; + seq := 0; + var nextcode [maxCodeLen]int; + for i := min; i <= max; i++ { + n := count[i]; + nextcode[i] = code; + h.base[i] = code - seq; + code += n; + seq += n; + h.limit[i] = code - 1; + code <<= 1; + } + + // Make array mapping sequence numbers to codes. + if len(h.codes) < len(bits) { + h.codes = make([]int, len(bits)); + } + for i, n := range bits { + if n == 0 { + continue; + } + code := nextcode[n]; + nextcode[n]++; + seq := code - h.base[n]; + h.codes[seq] = i; + } + return true; +} + +// Hard-coded Huffman tables for DEFLATE algorithm. +// See RFC 1951, section 3.2.6. +var fixedHuffmanDecoder = huffmanDecoder{ + 7, 9, + [maxCodeLen+1]int{ 7: 23, 199, 511, }, + [maxCodeLen+1]int{ 7: 0, 24, 224, }, + []int{ + // length 7: 256-279 + 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, + + // length 8: 0-143 + 0, 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, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, + + // length 8: 280-287 + 280, 281, 282, 283, 284, 285, 286, 287, + + // length 9: 144-255 + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, + } +} + +// The actual read interface needed by NewInflater. +// If the passed in io.Reader does not also have ReadByte, +// the NewInflater will introduce its own buffering. +type Reader interface { + io.Reader; + ReadByte() (c byte, err os.Error); +} + +// Inflate state. +// TODO(rsc): Expose this or not? +type inflater struct { + // Input/output sources. + r Reader; + w io.Writer; + roffset int64; + woffset int64; + + // Input bits, in top of b. + b uint32; + nb uint; + + // Huffman decoders for literal/length, distance. + h1, h2 huffmanDecoder; + + // Length arrays used to define Huffman codes. + bits [maxLit+maxDist]int; + codebits [numCodes]int; + + // Output history, buffer. + hist [maxHist]byte; + hp int; // current output position in buffer + hfull bool; // buffer has filled at least once + + // Temporary buffer (avoids repeated allocation). + buf [4]byte; +} + +// TODO(rsc): This works around a 6g bug. +func (f *inflater) getRoffset() int64 { + return f.roffset; +} + +func (f *inflater) dataBlock() os.Error +func (f *inflater) readHuffman() os.Error +func (f *inflater) decodeBlock(hl, hd *huffmanDecoder) os.Error +func (f *inflater) moreBits() os.Error +func (f *inflater) huffSym(h *huffmanDecoder) (int, os.Error) +func (f *inflater) flush() os.Error + +func (f *inflater) inflate() (err os.Error) { + final := false; + for err == nil && !final { + for f.nb < 1+2 { + if err = f.moreBits(); err != nil { + return; + } + } + final = f.b & 1 == 1; + f.b >>= 1; + typ := f.b & 3; + f.b >>= 2; + f.nb -= 1+2; + switch typ { + case 0: + err = f.dataBlock(); + case 1: + // compressed, fixed Huffman tables + err = f.decodeBlock(&fixedHuffmanDecoder, nil); + case 2: + // compressed, dynamic Huffman tables + if err = f.readHuffman(); err == nil { + err = f.decodeBlock(&f.h1, &f.h2); + } + default: + // 3 is reserved. + // TODO(rsc): Works around the same 6g bug. + var i int64 = f.getRoffset(); + i--; + err = CorruptInputError(i); + } + } + return; +} + +// RFC 1951 section 3.2.7. +// Compression with dynamic Huffman codes + +var codeOrder = [...]int { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +} + +func (f *inflater) readHuffman() os.Error { + // HLIT[5], HDIST[5], HCLEN[4]. + for f.nb < 5+5+4 { + if err := f.moreBits(); err != nil { + return err; + } + } + nlit := int(f.b & 0x1F) + 257; + f.b >>= 5; + ndist := int(f.b & 0x1F) + 1; + f.b >>= 5; + nclen := int(f.b & 0xF) + 4; + f.b >>= 4; + f.nb -= 5+5+4; + + // (HCLEN+4)*3 bits: code lengths in the magic codeOrder order. + for i := 0; i < nclen; i++ { + for f.nb < 3 { + if err := f.moreBits(); err != nil { + return err; + } + } + f.codebits[codeOrder[i]] = int(f.b & 0x7); + f.b >>= 3; + f.nb -= 3; + } + for i := nclen; i < len(codeOrder); i++ { + f.codebits[codeOrder[i]] = 0; + } + if !f.h1.init(&f.codebits) { + return os.ErrorString("huff and puff"); + } + + // HLIT + 257 code lengths, HDIST + 1 code lengths, + // using the code length Huffman code. + for i, n := 0, nlit+ndist; i < n; { + x, err := f.huffSym(&f.h1); + if err != nil { + return err; + } + if x < 16 { + // Actual length. + f.bits[i] = x; + i++; + continue; + } + // Repeat previous length or zero. + var rep int; + var nb uint; + var b int; + switch x { + default: + return InternalError("unexpected length code"); + case 16: + rep = 3; + nb = 2; + if i == 0 { + return CorruptInputError(f.getRoffset()); + } + b = f.bits[i-1]; + case 17: + rep = 3; + nb = 3; + b = 0; + case 18: + rep = 11; + nb = 7; + b = 0; + } + for f.nb < nb { + if err := f.moreBits(); err != nil { + return err; + } + } + rep += int(f.b & uint32(1<<nb - 1)); + f.b >>= nb; + f.nb -= nb; + if i+rep > n { + return CorruptInputError(f.getRoffset()); + } + for j := 0; j < rep; j++ { + f.bits[i] = b; + i++; + } + } + + if !f.h1.init(f.bits[0:nlit]) || !f.h2.init(f.bits[nlit:nlit+ndist]) { + return CorruptInputError(f.getRoffset()); + } + + return nil; +} + +// Decode a single Huffman block from f. +// hl and hd are the Huffman states for the lit/length values +// and the distance values, respectively. If hd == nil, using the +// fixed distance encoding assocated with fixed Huffman blocks. +func (f *inflater) decodeBlock(hl, hd *huffmanDecoder) os.Error { + for { + v, err := f.huffSym(hl); + if err != nil { + return err; + } + var n uint; // number of bits extra + var length int; + switch { + case v < 256: + f.hist[f.hp] = byte(v); + f.hp++; + if f.hp == len(f.hist) { + if err = f.flush(); err != nil { + return err; + } + } + continue; + case v == 256: + return nil; + // otherwise, reference to older data + case v < 265: + length = v - (257 - 3); + n = 0; + case v < 269: + length = v*2 - (265*2 - 11); + n = 1; + case v < 273: + length = v*4 - (269*4 - 19); + n = 2; + case v < 277: + length = v*8 - (273*8 - 35); + n = 3; + case v < 281: + length = v*16 - (277*16 - 67); + n = 4; + case v < 285: + length = v*32 - (281*32 - 131); + n = 5; + default: + length = 258; + n = 0; + } + if n > 0 { + for f.nb < n { + if err = f.moreBits(); err != nil { + return err; + } + } + length += int(f.b & uint32(1<<n - 1)); + f.b >>= n; + f.nb -= n; + } + + var dist int; + if hd == nil { + for f.nb < 5 { + if err = f.moreBits(); err != nil { + return err; + } + } + dist = int(f.b & 0x1F); + f.b >>= 5; + f.nb -= 5; + } else { + if dist, err = f.huffSym(hd); err != nil { + return err; + } + } + + switch { + case dist < 4: + dist++; + case dist >= 30: + return CorruptInputError(f.getRoffset()); + default: + nb := uint(dist - 2) >> 1; + // have 1 bit in bottom of dist, need nb more. + extra := (dist&1) << nb; + for f.nb < nb { + if err = f.moreBits(); err != nil { + return err; + } + } + extra |= int(f.b & uint32(1<<nb - 1)); + f.b >>= nb; + f.nb -= nb; + dist = 1<<(nb+1) + 1 + extra; + } + + // Copy history[-dist:-dist+length] into output. + if dist > len(f.hist) { + return InternalError("bad history distance"); + } + + // No check on length; encoding can be prescient. + if !f.hfull && dist > f.hp { + return CorruptInputError(f.getRoffset()); + } + + p := f.hp - dist; + if p < 0 { + p += len(f.hist); + } + for i := 0; i < length; i++ { + f.hist[f.hp] = f.hist[p]; + f.hp++; + p++; + if f.hp == len(f.hist) { + if err = f.flush(); err != nil { + return err; + } + } + if p == len(f.hist) { + p = 0; + } + } + } + panic("unreached"); +} + +// Copy a single uncompressed data block from input to output. +func (f *inflater) dataBlock() os.Error { + // Uncompressed. + // Discard current half-byte. + f.nb = 0; + f.b = 0; + + // Length then ones-complement of length. + nr, err := f.r.Read(f.buf[0:4]); + f.roffset += int64(nr); + if nr < 4 && err == nil { + err = io.ErrEOF; + } + if err != nil { + return &ReadError{f.roffset, err}; + } + n := int(f.buf[0]) | int(f.buf[1])<<8; + nn := int(f.buf[2]) | int(f.buf[3])<<8; + if nn != ^n { + return CorruptInputError(f.getRoffset()); + } + + // Read len bytes into history, + // writing as history fills. + for n > 0 { + m := len(f.hist) - f.hp; + if m > n { + m = n; + } + m, err := f.r.Read(f.hist[f.hp:f.hp+m]); + f.roffset += int64(m); + if m == 0 && err == nil { + err = io.ErrEOF; + } + if err != nil { + return &ReadError{f.roffset, err}; + } + n -= m; + f.hp += m; + if f.hp == len(f.hist) { + if err = f.flush(); err != nil { + return err; + } + } + } + return nil; +} + +func (f *inflater) moreBits() os.Error { + c, err := f.r.ReadByte(); + if err != nil { + return err; + } + f.roffset++; + f.b |= uint32(c) << f.nb; + f.nb += 8; + return nil; +} + +// Read the next Huffman-encoded symbol from f according to h. +func (f *inflater) huffSym(h *huffmanDecoder) (int, os.Error) { + for n := uint(h.min); n <= uint(h.max); n++ { + lim := h.limit[n]; + if lim == -1 { + continue; + } + for f.nb < n { + if err := f.moreBits(); err != nil { + return 0, err; + } + } + v := int(f.b & uint32(1<<n - 1)); + v <<= 16 - n; + v = int(reverseByte[v>>8]) | int(reverseByte[v&0xFF])<<8; // reverse bits + if v <= lim { + f.b >>= n; + f.nb -= n; + return h.codes[v - h.base[n]], nil; + } + } + return 0, CorruptInputError(f.getRoffset()); +} + +// Flush any buffered output to the underlying writer. +func (f *inflater) flush() os.Error { + if f.hp == 0 { + return nil; + } + n, err := f.w.Write(f.hist[0:f.hp]); + if n != f.hp && err == nil { + err = io.ErrShortWrite; + } + if err != nil { + return &WriteError{f.woffset, err}; + } + f.woffset += int64(f.hp); + f.hp = 0; + f.hfull = true; + return nil; +} + +func makeReader(r io.Reader) Reader { + if rr, ok := r.(Reader); ok { + return rr; + } + return bufio.NewReader(r); +} + +// Inflate reads DEFLATE-compressed data from r and writes +// the uncompressed data to w. +func (f *inflater) inflater(r io.Reader, w io.Writer) os.Error { + var ok bool; // TODO(rsc): why not := on next line? + f.r = makeReader(r); + f.w = w; + f.woffset = 0; + if err := f.inflate(); err != nil { + return err; + } + if err := f.flush(); err != nil { + return err; + } + return nil; +} + +// NewInflater returns a new ReadCloser that can be used +// to read the uncompressed version of r. It is the caller's +// responsibility to call Close on the ReadClosed when +// finished reading. +func NewInflater(r io.Reader) io.ReadCloser { + var f inflater; + pr, pw := io.Pipe(); + go func() { + pw.CloseWithError(f.inflater(r, pw)); + }(); + return pr; +} diff --git a/src/pkg/compress/gzip/Makefile b/src/pkg/compress/gzip/Makefile new file mode 100644 index 000000000..514118ae0 --- /dev/null +++ b/src/pkg/compress/gzip/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/compress/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + gunzip.$O\ + + +phases: a1 +_obj$D/gzip.a: phases + +a1: $(O1) + $(AR) grc _obj$D/gzip.a gunzip.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/gzip.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/gzip.a + +packages: _obj$D/gzip.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/gzip.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/gzip.a diff --git a/src/pkg/compress/gzip/gunzip.go b/src/pkg/compress/gzip/gunzip.go new file mode 100644 index 000000000..32cc3ecb0 --- /dev/null +++ b/src/pkg/compress/gzip/gunzip.go @@ -0,0 +1,236 @@ +// Copyright 2009 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. + +// The gzip package implements reading (and eventually writing) of +// gzip format compressed files, as specified in RFC 1952. +package gzip + +import ( + "bufio"; + "compress/flate"; + "hash"; + "hash/crc32"; + "io"; + "os"; +) + +const ( + gzipID1 = 0x1f; + gzipID2 = 0x8b; + + gzipDeflate = 8; + + flagText = 1<<0; + flagHdrCrc = 1<<1; + flagExtra = 1<<2; + flagName = 1<<3; + flagComment = 1<<4; +) + +func makeReader(r io.Reader) flate.Reader { + if rr, ok := r.(flate.Reader); ok { + return rr; + } + return bufio.NewReader(r); +} + +var HeaderError os.Error = os.ErrorString("invalid gzip header") +var ChecksumError os.Error = os.ErrorString("gzip checksum error") + +// A GzipInflater is an io.Reader that can be read to retrieve +// uncompressed data from a gzip-format compressed file. +// The gzip file stores a header giving metadata about the compressed file. +// That header is exposed as the fields of the GzipInflater struct. +// +// In general, a gzip file can be a concatenation of gzip files, +// each with its own header. Reads from the GzipInflater +// return the concatenation of the uncompressed data of each. +// Only the first header is recorded in the GzipInflater fields. +// +// Gzip files store a length and checksum of the uncompressed data. +// The GzipInflater will return a ChecksumError when Read +// reaches the end of the uncompressed data if it does not +// have the expected length or checksum. Clients should treat data +// returned by Read as tentative until they receive the successful +// (zero length, nil error) Read marking the end of the data. +type GzipInflater struct { + Comment string; // comment + Extra []byte; // "extra data" + Mtime uint32; // modification time (seconds since January 1, 1970) + Name string; // file name + OS byte; // operating system type + + r flate.Reader; + inflater io.Reader; + digest hash.Hash32; + size uint32; + flg byte; + buf [512]byte; + err os.Error; + eof bool; +} + +func (z *GzipInflater) readHeader(save bool) os.Error + +// NewGzipInflater creates a new GzipInflater reading the given reader. +// The implementation buffers input and may read more data than necessary from r. +func NewGzipInflater(r io.Reader) (*GzipInflater, os.Error) { + z := new(GzipInflater); + z.r = makeReader(r); + z.digest = crc32.NewIEEE(); + if err := z.readHeader(true); err != nil { + z.err = err; + return nil, err; + } + return z, nil; +} + +func get4(p []byte) uint32 { + return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24; +} + +func (z *GzipInflater) readString() (string, os.Error) { + var err os.Error; + for i := 0;; i++ { + if i >= len(z.buf) { + return "", HeaderError; + } + z.buf[i], err = z.r.ReadByte(); + if err != nil { + return "", err; + } + if z.buf[i] == 0 { + return string(z.buf[0:i]), nil; + } + } + panic("not reached"); +} + +func (z *GzipInflater) read2() (uint32, os.Error) { + _, err := z.r.Read(z.buf[0:2]); + if err != nil { + return 0, err; + } + return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil; +} + +func (z *GzipInflater) readHeader(save bool) os.Error { + n, err := io.FullRead(z.r, z.buf[0:10]); + if err != nil { + if n != 0 && err == io.ErrEOF { + return HeaderError; + } + return err; + } + if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate { + return HeaderError; + } + z.flg = z.buf[3]; + if save { + z.Mtime = get4(z.buf[4:8]); + // z.buf[8] is xfl, ignored + z.OS = z.buf[9]; + } + z.digest.Reset(); + z.digest.Write(z.buf[0:10]); + + if z.flg & flagExtra != 0{ + n, err := z.read2(); + if err != nil { + return err; + } + data := make([]byte, n); + var nn int; + if nn, err = io.FullRead(z.r, data); err != nil { + return err; + } + if save { + z.Extra = data; + } + } + + var s string; + if z.flg & flagName != 0 { + if s, err = z.readString(); err != nil { + return err; + } + if save { + z.Name = s; + } + } + + if z.flg & flagComment != 0 { + if s, err = z.readString(); err != nil { + return err; + } + if save { + z.Comment = s; + } + } + + if z.flg & flagHdrCrc != 0 { + n, err := z.read2(); + if err != nil { + return err; + } + sum := z.digest.Sum32() & 0xFFFF; + if n != sum { + return HeaderError; + } + } + + z.digest.Reset(); + z.inflater = flate.NewInflater(z.r); + return nil; +} + +func (z *GzipInflater) Read(p []byte) (n int, err os.Error) { + if z.err != nil { + return 0, z.err; + } + if z.eof || len(p) == 0 { + return 0, nil; + } + + n, err = z.inflater.Read(p); + z.digest.Write(p[0:n]); + z.size += uint32(n); + if n != 0 || err != nil { + z.err = err; + return; + } + + // Finished file; check checksum + size. + if _, err := io.FullRead(z.r, z.buf[0:8]); err != nil { + z.err = err; + return 0, err; + } + if err != nil { + z.err = err; + return 0, err; + } + crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8]); + sum := z.digest.Sum32(); + if sum != crc32 || isize != z.size { + z.err = ChecksumError; + return 0, z.err; + } + + // File is ok; is there another? + switch err = z.readHeader(false); { + case err == io.ErrEOF: + err = nil; + z.eof = true; + return; + case err != nil: + z.err = err; + return; + } + + // Yes. Reset and read from it. + z.digest.Reset(); + z.size = 0; + return z.Read(p); +} + diff --git a/src/pkg/compress/gzip/gunzip_test.go b/src/pkg/compress/gzip/gunzip_test.go new file mode 100644 index 000000000..a481de927 --- /dev/null +++ b/src/pkg/compress/gzip/gunzip_test.go @@ -0,0 +1,268 @@ +// Copyright 2009 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 gzip + +import ( + "compress/gzip"; + "fmt"; + "io"; + "testing"; + "os"; +) + +type gzipTest struct { + name string; + raw string; + gzip []byte; + err os.Error; +} + +var gzipTests = []gzipTest { + gzipTest { // has 1 empty fixed-huffman block + "empty.txt", + "", + []byte { + 0x1f, 0x8b, 0x08, 0x08, 0xf7, 0x5e, 0x14, 0x4a, + 0x00, 0x03, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + nil + }, + gzipTest { // has 1 non-empty fixed huffman block + "hello.txt", + "hello world\n", + []byte { + 0x1f, 0x8b, 0x08, 0x08, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, + 0x00, 0x00, + }, + nil + }, + gzipTest { // concatenation + "hello.txt", + "hello world\n" + "hello world\n", + []byte { + 0x1f, 0x8b, 0x08, 0x08, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, + 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x08, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, + 0x00, 0x00, + }, + nil + }, + gzipTest { // has dynamic huffman blocks + "gettysburg", + " Four score and seven years ago our fathers brought forth on\n" + "this continent, a new nation, conceived in Liberty, and dedicated\n" + "to the proposition that all men are created equal.\n" + " Now we are engaged in a great Civil War, testing whether that\n" + "nation, or any nation so conceived and so dedicated, can long\n" + "endure.\n" + " We are met on a great battle-field of that war.\n" + " We have come to dedicate a portion of that field, as a final\n" + "resting place for those who here gave their lives that that\n" + "nation might live. It is altogether fitting and proper that\n" + "we should do this.\n" + " But, in a larger sense, we can not dedicate — we can not\n" + "consecrate — we can not hallow — this ground.\n" + " The brave men, living and dead, who struggled here, have\n" + "consecrated it, far above our poor power to add or detract.\n" + "The world will little note, nor long remember what we say here,\n" + "but it can never forget what they did here.\n" + " It is for us the living, rather, to be dedicated here to the\n" + "unfinished work which they who fought here have thus far so\n" + "nobly advanced. It is rather for us to be here dedicated to\n" + "the great task remaining before us — that from these honored\n" + "dead we take increased devotion to that cause for which they\n" + "gave the last full measure of devotion —\n" + " that we here highly resolve that these dead shall not have\n" + "died in vain — that this nation, under God, shall have a new\n" + "birth of freedom — and that government of the people, by the\n" + "people, for the people, shall not perish from this earth.\n" + "\n" + "Abraham Lincoln, November 19, 1863, Gettysburg, Pennsylvania\n", + []byte { + 0x1f, 0x8b, 0x08, 0x08, 0xd1, 0x12, 0x2b, 0x4a, + 0x00, 0x03, 0x67, 0x65, 0x74, 0x74, 0x79, 0x73, + 0x62, 0x75, 0x72, 0x67, 0x00, 0x65, 0x54, 0xcd, + 0x6e, 0xd4, 0x30, 0x10, 0xbe, 0xfb, 0x29, 0xe6, + 0x01, 0x42, 0xa5, 0x0a, 0x09, 0xc1, 0x11, 0x90, + 0x40, 0x48, 0xa8, 0xe2, 0x80, 0xd4, 0xf3, 0x24, + 0x9e, 0x24, 0x56, 0xbd, 0x9e, 0xc5, 0x76, 0x76, + 0x95, 0x1b, 0x0f, 0xc1, 0x13, 0xf2, 0x24, 0x7c, + 0x63, 0x77, 0x9b, 0x4a, 0x5c, 0xaa, 0x6e, 0x6c, + 0xcf, 0x7c, 0x7f, 0x33, 0x44, 0x5f, 0x74, 0xcb, + 0x54, 0x26, 0xcd, 0x42, 0x9c, 0x3c, 0x15, 0xb9, + 0x48, 0xa2, 0x5d, 0x38, 0x17, 0xe2, 0x45, 0xc9, + 0x4e, 0x67, 0xae, 0xab, 0xe0, 0xf7, 0x98, 0x75, + 0x5b, 0xd6, 0x4a, 0xb3, 0xe6, 0xba, 0x92, 0x26, + 0x57, 0xd7, 0x50, 0x68, 0xd2, 0x54, 0x43, 0x92, + 0x54, 0x07, 0x62, 0x4a, 0x72, 0xa5, 0xc4, 0x35, + 0x68, 0x1a, 0xec, 0x60, 0x92, 0x70, 0x11, 0x4f, + 0x21, 0xd1, 0xf7, 0x30, 0x4a, 0xae, 0xfb, 0xd0, + 0x9a, 0x78, 0xf1, 0x61, 0xe2, 0x2a, 0xde, 0x55, + 0x25, 0xd4, 0xa6, 0x73, 0xd6, 0xb3, 0x96, 0x60, + 0xef, 0xf0, 0x9b, 0x2b, 0x71, 0x8c, 0x74, 0x02, + 0x10, 0x06, 0xac, 0x29, 0x8b, 0xdd, 0x25, 0xf9, + 0xb5, 0x71, 0xbc, 0x73, 0x44, 0x0f, 0x7a, 0xa5, + 0xab, 0xb4, 0x33, 0x49, 0x0b, 0x2f, 0xbd, 0x03, + 0xd3, 0x62, 0x17, 0xe9, 0x73, 0xb8, 0x84, 0x48, + 0x8f, 0x9c, 0x07, 0xaa, 0x52, 0x00, 0x6d, 0xa1, + 0xeb, 0x2a, 0xc6, 0xa0, 0x95, 0x76, 0x37, 0x78, + 0x9a, 0x81, 0x65, 0x7f, 0x46, 0x4b, 0x45, 0x5f, + 0xe1, 0x6d, 0x42, 0xe8, 0x01, 0x13, 0x5c, 0x38, + 0x51, 0xd4, 0xb4, 0x38, 0x49, 0x7e, 0xcb, 0x62, + 0x28, 0x1e, 0x3b, 0x82, 0x93, 0x54, 0x48, 0xf1, + 0xd2, 0x7d, 0xe4, 0x5a, 0xa3, 0xbc, 0x99, 0x83, + 0x44, 0x4f, 0x3a, 0x77, 0x36, 0x57, 0xce, 0xcf, + 0x2f, 0x56, 0xbe, 0x80, 0x90, 0x9e, 0x84, 0xea, + 0x51, 0x1f, 0x8f, 0xcf, 0x90, 0xd4, 0x60, 0xdc, + 0x5e, 0xb4, 0xf7, 0x10, 0x0b, 0x26, 0xe0, 0xff, + 0xc4, 0xd1, 0xe5, 0x67, 0x2e, 0xe7, 0xc8, 0x93, + 0x98, 0x05, 0xb8, 0xa8, 0x45, 0xc0, 0x4d, 0x09, + 0xdc, 0x84, 0x16, 0x2b, 0x0d, 0x9a, 0x21, 0x53, + 0x04, 0x8b, 0xd2, 0x0b, 0xbd, 0xa2, 0x4c, 0xa7, + 0x60, 0xee, 0xd9, 0xe1, 0x1d, 0xd1, 0xb7, 0x4a, + 0x30, 0x8f, 0x63, 0xd5, 0xa5, 0x8b, 0x33, 0x87, + 0xda, 0x1a, 0x18, 0x79, 0xf3, 0xe3, 0xa6, 0x17, + 0x94, 0x2e, 0xab, 0x6e, 0xa0, 0xe3, 0xcd, 0xac, + 0x50, 0x8c, 0xca, 0xa7, 0x0d, 0x76, 0x37, 0xd1, + 0x23, 0xe7, 0x05, 0x57, 0x8b, 0xa4, 0x22, 0x83, + 0xd9, 0x62, 0x52, 0x25, 0xad, 0x07, 0xbb, 0xbf, + 0xbf, 0xff, 0xbc, 0xfa, 0xee, 0x20, 0x73, 0x91, + 0x29, 0xff, 0x7f, 0x02, 0x71, 0x62, 0x84, 0xb5, + 0xf6, 0xb5, 0x25, 0x6b, 0x41, 0xde, 0x92, 0xb7, + 0x76, 0x3f, 0x91, 0x91, 0x31, 0x1b, 0x41, 0x84, + 0x62, 0x30, 0x0a, 0x37, 0xa4, 0x5e, 0x18, 0x3a, + 0x99, 0x08, 0xa5, 0xe6, 0x6d, 0x59, 0x22, 0xec, + 0x33, 0x39, 0x86, 0x26, 0xf5, 0xab, 0x66, 0xc8, + 0x08, 0x20, 0xcf, 0x0c, 0xd7, 0x47, 0x45, 0x21, + 0x0b, 0xf6, 0x59, 0xd5, 0xfe, 0x5c, 0x8d, 0xaa, + 0x12, 0x7b, 0x6f, 0xa1, 0xf0, 0x52, 0x33, 0x4f, + 0xf5, 0xce, 0x59, 0xd3, 0xab, 0x66, 0x10, 0xbf, + 0x06, 0xc4, 0x31, 0x06, 0x73, 0xd6, 0x80, 0xa2, + 0x78, 0xc2, 0x45, 0xcb, 0x03, 0x65, 0x39, 0xc9, + 0x09, 0xd1, 0x06, 0x04, 0x33, 0x1a, 0x5a, 0xf1, + 0xde, 0x01, 0xb8, 0x71, 0x83, 0xc4, 0xb5, 0xb3, + 0xc3, 0x54, 0x65, 0x33, 0x0d, 0x5a, 0xf7, 0x9b, + 0x90, 0x7c, 0x27, 0x1f, 0x3a, 0x58, 0xa3, 0xd8, + 0xfd, 0x30, 0x5f, 0xb7, 0xd2, 0x66, 0xa2, 0x93, + 0x1c, 0x28, 0xb7, 0xe9, 0x1b, 0x0c, 0xe1, 0x28, + 0x47, 0x26, 0xbb, 0xe9, 0x7d, 0x7e, 0xdc, 0x96, + 0x10, 0x92, 0x50, 0x56, 0x7c, 0x06, 0xe2, 0x27, + 0xb4, 0x08, 0xd3, 0xda, 0x7b, 0x98, 0x34, 0x73, + 0x9f, 0xdb, 0xf6, 0x62, 0xed, 0x31, 0x41, 0x13, + 0xd3, 0xa2, 0xa8, 0x4b, 0x3a, 0xc6, 0x1d, 0xe4, + 0x2f, 0x8c, 0xf8, 0xfb, 0x97, 0x64, 0xf4, 0xb6, + 0x2f, 0x80, 0x5a, 0xf3, 0x56, 0xe0, 0x40, 0x50, + 0xd5, 0x19, 0xd0, 0x1e, 0xfc, 0xca, 0xe5, 0xc9, + 0xd4, 0x60, 0x00, 0x81, 0x2e, 0xa3, 0xcc, 0xb6, + 0x52, 0xf0, 0xb4, 0xdb, 0x69, 0x99, 0xce, 0x7a, + 0x32, 0x4c, 0x08, 0xed, 0xaa, 0x10, 0x10, 0xe3, + 0x6f, 0xee, 0x99, 0x68, 0x95, 0x9f, 0x04, 0x71, + 0xb2, 0x49, 0x2f, 0x62, 0xa6, 0x5e, 0xb4, 0xef, + 0x02, 0xed, 0x4f, 0x27, 0xde, 0x4a, 0x0f, 0xfd, + 0xc1, 0xcc, 0xdd, 0x02, 0x8f, 0x08, 0x16, 0x54, + 0xdf, 0xda, 0xca, 0xe0, 0x82, 0xf1, 0xb4, 0x31, + 0x7a, 0xa9, 0x81, 0xfe, 0x90, 0xb7, 0x3e, 0xdb, + 0xd3, 0x35, 0xc0, 0x20, 0x80, 0x33, 0x46, 0x4a, + 0x63, 0xab, 0xd1, 0x0d, 0x29, 0xd2, 0xe2, 0x84, + 0xb8, 0xdb, 0xfa, 0xe9, 0x89, 0x44, 0x86, 0x7c, + 0xe8, 0x0b, 0xe6, 0x02, 0x6a, 0x07, 0x9b, 0x96, + 0xd0, 0xdb, 0x2e, 0x41, 0x4c, 0xa1, 0xd5, 0x57, + 0x45, 0x14, 0xfb, 0xe3, 0xa6, 0x72, 0x5b, 0x87, + 0x6e, 0x0c, 0x6d, 0x5b, 0xce, 0xe0, 0x2f, 0xe2, + 0x21, 0x81, 0x95, 0xb0, 0xe8, 0xb6, 0x32, 0x0b, + 0xb2, 0x98, 0x13, 0x52, 0x5d, 0xfb, 0xec, 0x63, + 0x17, 0x8a, 0x9e, 0x23, 0x22, 0x36, 0xee, 0xcd, + 0xda, 0xdb, 0xcf, 0x3e, 0xf1, 0xc7, 0xf1, 0x01, + 0x12, 0x93, 0x0a, 0xeb, 0x6f, 0xf2, 0x02, 0x15, + 0x96, 0x77, 0x5d, 0xef, 0x9c, 0xfb, 0x88, 0x91, + 0x59, 0xf9, 0x84, 0xdd, 0x9b, 0x26, 0x8d, 0x80, + 0xf9, 0x80, 0x66, 0x2d, 0xac, 0xf7, 0x1f, 0x06, + 0xba, 0x7f, 0xff, 0xee, 0xed, 0x40, 0x5f, 0xa5, + 0xd6, 0xbd, 0x8c, 0x5b, 0x46, 0xd2, 0x7e, 0x48, + 0x4a, 0x65, 0x8f, 0x08, 0x42, 0x60, 0xf7, 0x0f, + 0xb9, 0x16, 0x0b, 0x0c, 0x1a, 0x06, 0x00, 0x00, + }, + nil + }, + gzipTest { // has 1 non-empty fixed huffman block then garbage + "hello.txt", + "hello world\n", + []byte { + 0x1f, 0x8b, 0x08, 0x08, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0x0c, 0x00, + 0x00, 0x00, 'g', 'a', 'r', 'b', 'a', 'g', 'e', '!', + }, + HeaderError, + }, + gzipTest { // has 1 non-empty fixed huffman block but corrupt checksum + "hello.txt", + "hello world\n", + []byte { + 0x1f, 0x8b, 0x08, 0x08, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, + 0x00, 0x00, + }, + ChecksumError, + }, + gzipTest { // has 1 non-empty fixed huffman block but corrupt size + "hello.txt", + "hello world\n", + []byte { + 0x1f, 0x8b, 0x08, 0x08, 0xc8, 0x58, 0x13, 0x4a, + 0x00, 0x03, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0xcb, 0x48, 0xcd, 0xc9, + 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, 0x49, 0xe1, + 0x02, 0x00, 0x2d, 0x3b, 0x08, 0xaf, 0xff, 0x00, + 0x00, 0x00, + }, + ChecksumError, + }, +} + +func TestGzipInflater(t *testing.T) { + b := new(io.ByteBuffer); + for i, tt := range gzipTests { + in := io.NewByteReader(tt.gzip); + gzip, err := NewGzipInflater(in); + if err != nil { + t.Errorf("%s: NewGzipInflater: %s", tt.name, err); + continue; + } + if tt.name != gzip.Name { + t.Errorf("%s: got name %s", tt.name, gzip.Name); + } + b.Reset(); + n, err := io.Copy(gzip, b); + if err != tt.err { + t.Errorf("%s: io.Copy: %s want %s", tt.name, err, tt.err); + } + s := string(b.Data()); + if s != tt.raw { + t.Errorf("%s: got %d-byte %q want %d-byte %q", tt.name, n, s, len(tt.raw), tt.raw); + } + } +} + diff --git a/src/pkg/container/list/Makefile b/src/pkg/container/list/Makefile new file mode 100644 index 000000000..2a647eb2a --- /dev/null +++ b/src/pkg/container/list/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/container/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + list.$O\ + + +phases: a1 +_obj$D/list.a: phases + +a1: $(O1) + $(AR) grc _obj$D/list.a list.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/list.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/list.a + +packages: _obj$D/list.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/list.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/list.a diff --git a/src/pkg/container/list/list.go b/src/pkg/container/list/list.go new file mode 100755 index 000000000..7e8daa65a --- /dev/null +++ b/src/pkg/container/list/list.go @@ -0,0 +1,130 @@ +// Copyright 2009 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. + +// The list package implements a doubly linked list. +package list + +// Element is an element in the linked list. +type Element struct { + // Next and previous pointers in the doubly-linked list of elements. + // The front of the list has prev = nil, and the back has next = nil. + next, prev *Element; + + // The contents of this list element. + Value interface {}; +} + +// List represents a doubly linked list. +type List struct { + front, back *Element; +} + +// Init initializes or clears a List. +func (l *List) Init() *List { + l.front = nil; + l.back = nil; + return l +} + +// New returns an initialized list. +func New() *List { + return new(List).Init() +} + +// Front returns the first element in the list. +func (l *List) Front() *Element { + return l.front +} + +// Back returns the last element in the list. +func (l *List) Back() *Element { + return l.back +} + +// Remove removes the element from the list. +func (l *List) Remove(e *Element) { + if e.prev == nil { + l.front = e.next; + } else { + e.prev.next = e.next; + } + if e.next == nil { + l.back = e.prev; + } else { + e.next.prev = e.prev; + } + + e.prev = nil; + e.next = nil; +} + +func (l *List) insertFront(e *Element) { + e.prev = nil; + e.next = l.front; + l.front = e; + if e.next != nil { + e.next.prev = e; + } else { + l.back = e; + } +} + +func (l *List) insertBack(e *Element) { + e.next = nil; + e.prev = l.back; + l.back = e; + if e.prev != nil { + e.prev.next = e; + } else { + l.front = e; + } +} + +// PushFront inserts the value at the front of the list, and returns a new Element containing it. +func (l *List) PushFront(value interface {}) *Element { + e := &Element{ nil, nil, value }; + l.insertFront(e); + return e +} + +// PushBack inserts the value at the back of the list, and returns a new Element containing it. +func (l *List) PushBack(value interface {}) *Element { + e := &Element{ nil, nil, value }; + l.insertBack(e); + return e +} + +// MoveToFront moves the element to the front of the list. +func (l *List) MoveToFront(e *Element) { + if l.front == e { + return + } + l.Remove(e); + l.insertFront(e); +} + +// MoveToBack moves the element to the back of the list. +func (l *List) MoveToBack(e *Element) { + if l.back == e { + return + } + l.Remove(e); + l.insertBack(e); +} + +func (l *List) iterate(c chan <- *Element) { + var next *Element; + for e := l.front; e != nil; e = next { + // Save next in case reader of c changes e. + next = e.next; + c <- e; + } + close(c); +} + +func (l *List) Iter() <-chan *Element { + c := make(chan *Element); + go l.iterate(c); + return c +} diff --git a/src/pkg/container/list/list_test.go b/src/pkg/container/list/list_test.go new file mode 100755 index 000000000..d5b2672e0 --- /dev/null +++ b/src/pkg/container/list/list_test.go @@ -0,0 +1,91 @@ +// Copyright 2009 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 list + +import ( + "container/list"; + "testing"; +) + +func checkListPointers(t *testing.T, l *List, es []*Element) { + if len(es) == 0 { + if l.front != nil || l.back != nil { + t.Errorf("l.front/l.back = %v/%v should be nil/nil", l.front, l.back); + } + return + } + + if l.front != es[0] { + t.Errorf("l.front = %v, want %v", l.front, es[0]); + } + if last := es[len(es)-1]; l.back != last { + t.Errorf("l.back = %v, want %v", l.back, last); + } + + for i := 0; i < len(es); i++ { + e := es[i]; + var e_prev, e_next *Element = nil, nil; + if i > 0 { + e_prev = es[i-1]; + } + if i < len(es) - 1 { + e_next = es[i+1]; + } + if e.prev != e_prev { + t.Errorf("elt #%d (%v) has prev=%v, want %v", i, e, e.prev, e_prev); + } + if e.next != e_next { + t.Errorf("elt #%d (%v) has next=%v, want %v", i, e, e.next, e_next); + } + } +} + +func TestList(t *testing.T) { + l := New(); + checkListPointers(t, l, []*Element{}); + + // Single element list + e := l.PushFront("a"); + checkListPointers(t, l, []*Element{ e }); + l.MoveToFront(e); + checkListPointers(t, l, []*Element{ e }); + l.MoveToBack(e); + checkListPointers(t, l, []*Element{ e }); + l.Remove(e); + checkListPointers(t, l, []*Element{}); + + // Bigger list + e2 := l.PushFront(2); + e1 := l.PushFront(1); + e3 := l.PushBack(3); + e4 := l.PushBack("banana"); + checkListPointers(t, l, []*Element{ e1, e2, e3, e4 }); + + l.Remove(e2); + checkListPointers(t, l, []*Element{ e1, e3, e4 }); + + l.MoveToFront(e3); // move from middle + checkListPointers(t, l, []*Element{ e3, e1, e4 }); + + l.MoveToFront(e1); + l.MoveToBack(e3); // move from middle + checkListPointers(t, l, []*Element{ e1, e4, e3 }); + + l.MoveToFront(e3); // move from back + checkListPointers(t, l, []*Element{ e3, e1, e4 }); + l.MoveToFront(e3); // should be no-op + checkListPointers(t, l, []*Element{ e3, e1, e4 }); + + l.MoveToBack(e3); // move from front + checkListPointers(t, l, []*Element{ e1, e4, e3 }); + l.MoveToBack(e3); // should be no-op + checkListPointers(t, l, []*Element{ e1, e4, e3 }); + + // Clear all elements by iterating + for e := range l.Iter() { + l.Remove(e); + } + checkListPointers(t, l, []*Element{}); +} diff --git a/src/pkg/container/vector/Makefile b/src/pkg/container/vector/Makefile new file mode 100644 index 000000000..20490549d --- /dev/null +++ b/src/pkg/container/vector/Makefile @@ -0,0 +1,69 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/container/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + vector.$O\ + +O2=\ + intvector.$O\ + stringvector.$O\ + + +phases: a1 a2 +_obj$D/vector.a: phases + +a1: $(O1) + $(AR) grc _obj$D/vector.a vector.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/vector.a intvector.$O stringvector.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/vector.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/vector.a + +packages: _obj$D/vector.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/vector.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/vector.a diff --git a/src/pkg/container/vector/intvector.go b/src/pkg/container/vector/intvector.go new file mode 100644 index 000000000..c3b62f256 --- /dev/null +++ b/src/pkg/container/vector/intvector.go @@ -0,0 +1,118 @@ +// Copyright 2009 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 vector + +import "container/vector" + +// IntVector is a specialization of Vector that hides the wrapping of Elements around ints. +type IntVector struct { + vector.Vector; +} + + +// Init initializes a new or resized vector. The initial length may be <= 0 to +// request a default length. If initial_len is shorter than the current +// length of the IntVector, trailing elements of the IntVector will be cleared. +func (p *IntVector) Init(len int) *IntVector { + p.Vector.Init(len); + return p; +} + + +// NewIntVector returns an initialized new IntVector with length at least len. +func NewIntVector(len int) *IntVector { + return new(IntVector).Init(len) +} + + +// At returns the i'th element of the vector. +func (p *IntVector) At(i int) int { + return p.Vector.At(i).(int) +} + + +// Set sets the i'th element of the vector to value x. +func (p *IntVector) Set(i int, x int) { + p.a[i] = x +} + + +// Last returns the element in the vector of highest index. +func (p *IntVector) Last() int { + return p.Vector.Last().(int) +} + + +// Data returns all the elements as a slice. +func (p *IntVector) Data() []int { + arr := make([]int, p.Len()); + for i, v := range p.a { + arr[i] = v.(int) + } + return arr +} + + +// Insert inserts into the vector an element of value x before +// the current element at index i. +func (p *IntVector) Insert(i int, x int) { + p.Vector.Insert(i, x) +} + + +// InsertVector inserts into the vector the contents of the Vector +// x such that the 0th element of x appears at index i after insertion. +func (p *IntVector) InsertVector(i int, x *IntVector) { + p.Vector.InsertVector(i, &x.Vector) +} + + +// Slice returns a new IntVector by slicing the old one to extract slice [i:j]. +// The elements are copied. The original vector is unchanged. +func (p *IntVector) Slice(i, j int) *IntVector { + return &IntVector{ *p.Vector.Slice(i, j) }; +} + + +// Push appends x to the end of the vector. +func (p *IntVector) Push(x int) { + p.Vector.Push(x) +} + + +// Pop deletes and returns the last element of the vector. +func (p *IntVector) Pop() int { + return p.Vector.Pop().(int) +} + + +// AppendVector appends the entire IntVector x to the end of this vector. +func (p *IntVector) AppendVector(x *IntVector) { + p.Vector.InsertVector(len(p.a), &x.Vector); +} + + +// SortInterface support +// Less returns a boolean denoting whether the i'th element is less than the j'th element. +func (p *IntVector) Less(i, j int) bool { + return p.At(i) < p.At(j) +} + + +// Iterate over all elements; driver for range +func (p *IntVector) iterate(c chan int) { + for i, v := range p.a { + c <- v.(int) + } + close(c); +} + + +// Channel iterator for range. +func (p *IntVector) Iter() chan int { + c := make(chan int); + go p.iterate(c); + return c; +} diff --git a/src/pkg/container/vector/stringvector.go b/src/pkg/container/vector/stringvector.go new file mode 100644 index 000000000..18ca11a3f --- /dev/null +++ b/src/pkg/container/vector/stringvector.go @@ -0,0 +1,118 @@ +// Copyright 2009 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 vector + +import "container/vector" + +// StringVector is a specialization of Vector that hides the wrapping of Elements around strings. +type StringVector struct { + vector.Vector; +} + + +// Init initializes a new or resized vector. The initial length may be <= 0 to +// request a default length. If initial_len is shorter than the current +// length of the StringVector, trailing elements of the StringVector will be cleared. +func (p *StringVector) Init(len int) *StringVector { + p.Vector.Init(len); + return p; +} + + +// NewStringVector returns an initialized new StringVector with length at least len. +func NewStringVector(len int) *StringVector { + return new(StringVector).Init(len) +} + + +// At returns the i'th element of the vector. +func (p *StringVector) At(i int) string { + return p.Vector.At(i).(string) +} + + +// Set sets the i'th element of the vector to value x. +func (p *StringVector) Set(i int, x string) { + p.a[i] = x +} + + +// Last returns the element in the vector of highest index. +func (p *StringVector) Last() string { + return p.Vector.Last().(string) +} + + +// Data returns all the elements as a slice. +func (p *StringVector) Data() []string { + arr := make([]string, p.Len()); + for i, v := range p.a { + arr[i] = v.(string) + } + return arr +} + + +// Insert inserts into the vector an element of value x before +// the current element at index i. +func (p *StringVector) Insert(i int, x string) { + p.Vector.Insert(i, x) +} + + +// InsertVector inserts into the vector the contents of the Vector +// x such that the 0th element of x appears at index i after insertion. +func (p *StringVector) InsertVector(i int, x *StringVector) { + p.Vector.InsertVector(i, &x.Vector) +} + + +// Slice returns a new StringVector by slicing the old one to extract slice [i:j]. +// The elements are copied. The original vector is unchanged. +func (p *StringVector) Slice(i, j int) *StringVector { + return &StringVector{ *p.Vector.Slice(i, j) }; +} + + +// Push appends x to the end of the vector. +func (p *StringVector) Push(x string) { + p.Vector.Push(x) +} + + +// Pop deletes and returns the last element of the vector. +func (p *StringVector) Pop() string { + return p.Vector.Pop().(string) +} + + +// AppendVector appends the entire StringVector x to the end of this vector. +func (p *StringVector) AppendVector(x *StringVector) { + p.Vector.InsertVector(len(p.a), &x.Vector); +} + + +// SortInterface support +// Less returns a boolean denoting whether the i'th element is less than the j'th element. +func (p *StringVector) Less(i, j int) bool { + return p.At(i) < p.At(j) +} + + +// Iterate over all elements; driver for range +func (p *StringVector) iterate(c chan string) { + for i, v := range p.a { + c <- v.(string) + } + close(c); +} + + +// Channel iterator for range. +func (p *StringVector) Iter() chan string { + c := make(chan string); + go p.iterate(c); + return c; +} diff --git a/src/pkg/container/vector/vector.go b/src/pkg/container/vector/vector.go new file mode 100644 index 000000000..5b5cad21c --- /dev/null +++ b/src/pkg/container/vector/vector.go @@ -0,0 +1,244 @@ +// Copyright 2009 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. + +// The vector package implements an efficient container for managing +// linear arrays of elements. Unlike arrays, vectors can change size dynamically. +package vector + +// Element is an empty-interface object representing the contents of +// a cell in the vector. +type Element interface {} + + +// Vector is the container itself. +type Vector struct { + a []Element +} + + +func copy(dst, src []Element) { + for i := 0; i < len(src); i++ { + dst[i] = src[i] + } +} + + +// Insert n elements at position i. +func expand(a []Element, i, n int) []Element { + // make sure we have enough space + len0 := len(a); + len1 := len0 + n; + if len1 < cap(a) { + // enough space - just expand + a = a[0 : len1] + } else { + // not enough space - double capacity + capb := cap(a)*2; + if capb < len1 { + // still not enough - use required length + capb = len1 + } + // capb >= len1 + b := make([]Element, len1, capb); + copy(b, a); + a = b + } + + // make a hole + for j := len0-1; j >= i ; j-- { + a[j+n] = a[j] + } + return a +} + + +// Init initializes a new or resized vector. The initial_len may be <= 0 to +// request a default length. If initial_len is shorter than the current +// length of the Vector, trailing elements of the Vector will be cleared. +func (p *Vector) Init(initial_len int) *Vector { + a := p.a; + + if cap(a) == 0 || cap(a) < initial_len { + n := 8; // initial capacity + if initial_len > n { + n = initial_len + } + a = make([]Element, n); + } else { + // nil out entries + for j := len(a) - 1; j >= 0; j-- { + a[j] = nil + } + } + + p.a = a[0 : initial_len]; + return p +} + + +// New returns an initialized new Vector with length at least len. +func New(len int) *Vector { + return new(Vector).Init(len) +} + + +// Len returns the number of elements in the vector. +// Len is 0 if p == nil. +func (p *Vector) Len() int { + if p == nil { + return 0; + } + return len(p.a) +} + + +// At returns the i'th element of the vector. +func (p *Vector) At(i int) Element { + return p.a[i] +} + + +// Set sets the i'th element of the vector to value x. +func (p *Vector) Set(i int, x Element) { + p.a[i] = x +} + + +// Last returns the element in the vector of highest index. +func (p *Vector) Last() Element { + return p.a[len(p.a) - 1] +} + + +// Data returns all the elements as a slice. +func (p *Vector) Data() []Element { + arr := make([]Element, p.Len()); + for i, v := range p.a { + arr[i] = v + } + return arr +} + + +// Insert inserts into the vector an element of value x before +// the current element at index i. +func (p *Vector) Insert(i int, x Element) { + p.a = expand(p.a, i, 1); + p.a[i] = x; +} + + +// Delete deletes the i'th element of the vector. The gap is closed so the old +// element at index i+1 has index i afterwards. +func (p *Vector) Delete(i int) { + a := p.a; + n := len(a); + + copy(a[i : n-1], a[i+1 : n]); + a[n-1] = nil; // support GC, nil out entry + p.a = a[0 : n-1]; +} + + +// InsertVector inserts into the vector the contents of the Vector +// x such that the 0th element of x appears at index i after insertion. +func (p *Vector) InsertVector(i int, x *Vector) { + p.a = expand(p.a, i, len(x.a)); + copy(p.a[i : i + len(x.a)], x.a); +} + + +// Cut deletes elements i through j-1, inclusive. +func (p *Vector) Cut(i, j int) { + a := p.a; + n := len(a); + m := n - (j - i); + + copy(a[i : m], a[j : n]); + for k := m; k < n; k++ { + a[k] = nil // support GC, nil out entries + } + + p.a = a[0 : m]; +} + + +// Slice returns a new Vector by slicing the old one to extract slice [i:j]. +// The elements are copied. The original vector is unchanged. +func (p *Vector) Slice(i, j int) *Vector { + s := New(j - i); // will fail in Init() if j < j + copy(s.a, p.a[i : j]); + return s; +} + + +// Do calls function f for each element of the vector, in order. +// The function should not change the indexing of the vector underfoot. +func (p *Vector) Do(f func(elem Element)) { + for i := 0; i < len(p.a); i++ { + f(p.a[i]) // not too safe if f changes the Vector + } +} + + +// Convenience wrappers + +// Push appends x to the end of the vector. +func (p *Vector) Push(x Element) { + p.Insert(len(p.a), x) +} + + +// Pop deletes the last element of the vector. +func (p *Vector) Pop() Element { + i := len(p.a) - 1; + x := p.a[i]; + p.a[i] = nil; // support GC, nil out entry + p.a = p.a[0 : i]; + return x; +} + + +// AppendVector appends the entire Vector x to the end of this vector. +func (p *Vector) AppendVector(x *Vector) { + p.InsertVector(len(p.a), x); +} + + +// Partial SortInterface support + +// LessInterface provides partial support of the SortInterface. +type LessInterface interface { + Less(y Element) bool +} + + +// Less returns a boolean denoting whether the i'th element is less than the j'th element. +func (p *Vector) Less(i, j int) bool { + return p.a[i].(LessInterface).Less(p.a[j]) +} + + +// Swap exchanges the elements at indexes i and j. +func (p *Vector) Swap(i, j int) { + a := p.a; + a[i], a[j] = a[j], a[i] +} + + +// Iterate over all elements; driver for range +func (p *Vector) iterate(c chan Element) { + for i, v := range p.a { + c <- v + } + close(c); +} + + +// Channel iterator for range. +func (p *Vector) Iter() chan Element { + c := make(chan Element); + go p.iterate(c); + return c; +} diff --git a/src/pkg/container/vector/vector_test.go b/src/pkg/container/vector/vector_test.go new file mode 100644 index 000000000..2a9819394 --- /dev/null +++ b/src/pkg/container/vector/vector_test.go @@ -0,0 +1,209 @@ +// Copyright 2009 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 vector + +import "container/vector" +import "testing" +import "sort" +import "fmt" + + +func TestZeroLen(t *testing.T) { + var a *vector.Vector; + if a.Len() != 0 { t.Errorf("A) expected 0, got %d", a.Len()); } + a = vector.New(0); + if a.Len() != 0 { t.Errorf("B) expected 0, got %d", a.Len()); } +} + + +func TestInit(t *testing.T) { + var a vector.Vector; + if a.Init(0).Len() != 0 { t.Error("A") } + if a.Init(1).Len() != 1 { t.Error("B") } + if a.Init(10).Len() != 10 { t.Error("C") } +} + + +func TestNew(t *testing.T) { + if vector.New(0).Len() != 0 { t.Error("A") } + if vector.New(1).Len() != 1 { t.Error("B") } + if vector.New(10).Len() != 10 { t.Error("C") } +} + + +func val(i int) int { + return i*991 - 1234 +} + + +func TestAccess(t *testing.T) { + const n = 100; + var a vector.Vector; + a.Init(n); + for i := 0; i < n; i++ { + a.Set(i, val(i)); + } + for i := 0; i < n; i++ { + if a.At(i).(int) != val(i) { t.Error(i) } + } +} + + +func TestInsertDeleteClear(t *testing.T) { + const n = 100; + a := vector.New(0); + + for i := 0; i < n; i++ { + if a.Len() != i { t.Errorf("A) wrong len %d (expected %d)", a.Len(), i) } + a.Insert(0, val(i)); + if a.Last().(int) != val(0) { t.Error("B") } + } + for i := n-1; i >= 0; i-- { + if a.Last().(int) != val(0) { t.Error("C") } + if a.At(0).(int) != val(i) { t.Error("D") } + a.Delete(0); + if a.Len() != i { t.Errorf("E) wrong len %d (expected %d)", a.Len(), i) } + } + + if a.Len() != 0 { t.Errorf("F) wrong len %d (expected 0)", a.Len()) } + for i := 0; i < n; i++ { + a.Push(val(i)); + if a.Len() != i+1 { t.Errorf("G) wrong len %d (expected %d)", a.Len(), i+1) } + if a.Last().(int) != val(i) { t.Error("H") } + } + a.Init(0); + if a.Len() != 0 { t.Errorf("I wrong len %d (expected 0)", a.Len()) } + + const m = 5; + for j := 0; j < m; j++ { + a.Push(j); + for i := 0; i < n; i++ { + x := val(i); + a.Push(x); + if a.Pop().(int) != x { t.Error("J") } + if a.Len() != j+1 { t.Errorf("K) wrong len %d (expected %d)", a.Len(), j+1) } + } + } + if a.Len() != m { t.Errorf("L) wrong len %d (expected %d)", a.Len(), m) } +} + + +func verify_slice(t *testing.T, x *vector.Vector, elt, i, j int) { + for k := i; k < j; k++ { + if x.At(k).(int) != elt { + t.Errorf("M) wrong [%d] element %d (expected %d)", k, x.At(k).(int), elt) + } + } + + s := x.Slice(i, j); + for k, n := 0, j-i; k < n; k++ { + if s.At(k).(int) != elt { + t.Errorf("N) wrong [%d] element %d (expected %d)", k, x.At(k).(int), elt) + } + } +} + + +func verify_pattern(t *testing.T, x *vector.Vector, a, b, c int) { + n := a + b + c; + if x.Len() != n { + t.Errorf("O) wrong len %d (expected %d)", x.Len(), n) + } + verify_slice(t, x, 0, 0, a); + verify_slice(t, x, 1, a, a + b); + verify_slice(t, x, 0, a + b, n); +} + + +func make_vector(elt, len int) *vector.Vector { + x := vector.New(len); + for i := 0; i < len; i++ { + x.Set(i, elt); + } + return x; +} + + +func TestInsertVector(t *testing.T) { + // 1 + a := make_vector(0, 0); + b := make_vector(1, 10); + a.InsertVector(0, b); + verify_pattern(t, a, 0, 10, 0); + // 2 + a = make_vector(0, 10); + b = make_vector(1, 0); + a.InsertVector(5, b); + verify_pattern(t, a, 5, 0, 5); + // 3 + a = make_vector(0, 10); + b = make_vector(1, 3); + a.InsertVector(3, b); + verify_pattern(t, a, 3, 3, 7); + // 4 + a = make_vector(0, 10); + b = make_vector(1, 1000); + a.InsertVector(8, b); + verify_pattern(t, a, 8, 1000, 2); +} + + +// This also tests IntVector and StringVector +func TestSorting(t *testing.T) { + const n = 100; + + a := vector.NewIntVector(n); + for i := n-1; i >= 0; i-- { + a.Set(i, n-1-i); + } + if sort.IsSorted(a) { t.Error("int vector not sorted") } + + b := vector.NewStringVector(n); + for i := n-1; i >= 0; i-- { + b.Set(i, fmt.Sprint(n-1-i)); + } + if sort.IsSorted(b) { t.Error("string vector not sorted") } +} + + +func TestDo(t *testing.T) { + const n = 25; + const salt = 17; + a := vector.NewIntVector(n); + for i := 0; i < n; i++ { + a.Set(i, salt * i); + } + count := 0; + a.Do( + func(e vector.Element) { + i := e.(int); + if i != count*salt { + t.Error("value at", count, "should be", count*salt, "not", i) + } + count++; + } + ); + if count != n { + t.Error("should visit", n, "values; did visit", count) + } +} + +func TestIter(t *testing.T) { + const Len = 100; + x := vector.New(Len); + for i := 0; i < Len; i++ { + x.Set(i, i*i); + } + i := 0; + for v := range x.Iter() { + if v.(int) != i*i { + t.Error("Iter expected", i*i, "got", v.(int)) + } + i++; + } + if i != Len { + t.Error("Iter stopped at", i, "not", Len) + } +} diff --git a/src/pkg/crypto/aes/Makefile b/src/pkg/crypto/aes/Makefile new file mode 100644 index 000000000..c62275b3c --- /dev/null +++ b/src/pkg/crypto/aes/Makefile @@ -0,0 +1,76 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/crypto/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + const.$O\ + +O2=\ + block.$O\ + +O3=\ + cipher.$O\ + + +phases: a1 a2 a3 +_obj$D/aes.a: phases + +a1: $(O1) + $(AR) grc _obj$D/aes.a const.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/aes.a block.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/aes.a cipher.$O + rm -f $(O3) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/aes.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/aes.a + +packages: _obj$D/aes.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/aes.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/aes.a diff --git a/src/pkg/crypto/aes/aes_test.go b/src/pkg/crypto/aes/aes_test.go new file mode 100644 index 000000000..2f6cb4a92 --- /dev/null +++ b/src/pkg/crypto/aes/aes_test.go @@ -0,0 +1,353 @@ +// Copyright 2009 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 aes + +import ( + "crypto/aes"; + "fmt"; + "testing"; +) + +// See const.go for overview of math here. + +// Test that powx is initialized correctly. +// (Can adapt this code to generate it too.) +func TestPowx(t *testing.T) { + p := 1; + for i := 0; i < len(powx); i++ { + if powx[i] != byte(p) { + t.Errorf("powx[%d] = %#x, want %#x", i, powx[i], p); + } + p <<= 1; + if p & 0x100 != 0 { + p ^= poly; + } + } +} + +// Multiply b and c as GF(2) polynomials modulo poly +func mul(b, c uint32) uint32 { + i := b; + j := c; + s := uint32(0); + for k := uint32(1); k < 0x100 && j != 0; k <<= 1 { + // Invariant: k == 1<<n, i == b * xⁿ + + if j & k != 0 { + // s += i in GF(2); xor in binary + s ^= i; + j ^= k; // turn off bit to end loop early + } + + // i *= x in GF(2) modulo the polynomial + i <<= 1; + if i & 0x100 != 0 { + i ^= poly; + } + } + return s; +} + +// Test all mul inputs against bit-by-bit n² algorithm. +func TestMul(t *testing.T) { + for i := uint32(0); i < 256; i++ { + for j := uint32(0); j < 256; j++ { + // Multiply i, j bit by bit. + s := uint8(0); + for k := uint(0); k < 8; k++ { + for l := uint(0); l < 8; l++ { + if i & (1<<k) != 0 && j & (1<<l) != 0 { + s ^= powx[k+l]; + } + } + } + if x := mul(i, j); x != uint32(s) { + t.Fatalf("mul(%#x, %#x) = %#x, want %#x", i, j, x, s); + } + } + } +} + +// Check that S-boxes are inverses of each other. +// They have more structure that we could test, +// but if this sanity check passes, we'll assume +// the cut and paste from the FIPS PDF worked. +func TestSboxes(t *testing.T) { + for i := 0; i < 256; i++ { + if j := sbox0[sbox1[i]]; j != byte(i) { + t.Errorf("sbox0[sbox1[%#x]] = %#x", i, j); + } + if j := sbox1[sbox0[i]]; j != byte(i) { + t.Errorf("sbox1[sbox0[%#x]] = %#x", i, j); + } + } +} + +// Test that encryption tables are correct. +// (Can adapt this code to generate them too.) +func TestTe(t *testing.T) { + for i := 0; i < 256; i++ { + s := uint32(sbox0[i]); + s2 := mul(s, 2); + s3 := mul(s, 3); + w := s2<<24 | s<<16 | s<<8 | s3; + for j := 0; j < 4; j++ { + if x := te[j][i]; x != w { + t.Fatalf("te[%d][%d] = %#x, want %#x", j, i, x, w); + } + w = w<<24 | w>>8; + } + } +} + +// Test that decryption tables are correct. +// (Can adapt this code to generate them too.) +func TestTd(t *testing.T) { + for i := 0; i < 256; i++ { + s := uint32(sbox1[i]); + s9 := mul(s, 0x9); + sb := mul(s, 0xb); + sd := mul(s, 0xd); + se := mul(s, 0xe); + w := se<<24 | s9<<16 | sd<<8 | sb; + for j := 0; j < 4; j++ { + if x := td[j][i]; x != w { + t.Fatalf("td[%d][%d] = %#x, want %#x", j, i, x, w); + } + w = w<<24 | w>>8; + } + } +} + +// Test vectors are from FIPS 197: +// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +// Appendix A of FIPS 197: Key expansion examples +type KeyTest struct { + key []byte; + enc []uint32; + dec []uint32; // decryption expansion; not in FIPS 197, computed from C implementation. +} + +var keyTests = []KeyTest { + KeyTest { + // A.1. Expansion of a 128-bit Cipher Key + []byte { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }, + []uint32 { + 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c, + 0xa0fafe17, 0x88542cb1, 0x23a33939, 0x2a6c7605, + 0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f, + 0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b, + 0xef44a541, 0xa8525b7f, 0xb671253b, 0xdb0bad00, + 0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc, + 0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd, + 0x4e54f70e, 0x5f5fc9f3, 0x84a64fb2, 0x4ea6dc4f, + 0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f, + 0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e, + 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6, + }, + []uint32 { + 0xd014f9a8, 0xc9ee2589, 0xe13f0cc8, 0xb6630ca6, + 0xc7b5a63, 0x1319eafe, 0xb0398890, 0x664cfbb4, + 0xdf7d925a, 0x1f62b09d, 0xa320626e, 0xd6757324, + 0x12c07647, 0xc01f22c7, 0xbc42d2f3, 0x7555114a, + 0x6efcd876, 0xd2df5480, 0x7c5df034, 0xc917c3b9, + 0x6ea30afc, 0xbc238cf6, 0xae82a4b4, 0xb54a338d, + 0x90884413, 0xd280860a, 0x12a12842, 0x1bc89739, + 0x7c1f13f7, 0x4208c219, 0xc021ae48, 0x969bf7b, + 0xcc7505eb, 0x3e17d1ee, 0x82296c51, 0xc9481133, + 0x2b3708a7, 0xf262d405, 0xbc3ebdbf, 0x4b617d62, + 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x9cf4f3c, + }, + }, + KeyTest { + // A.2. Expansion of a 192-bit Cipher Key + []byte { + 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, + 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b, + }, + []uint32 { + 0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5, + 0x62f8ead2, 0x522c6b7b, 0xfe0c91f7, 0x2402f5a5, + 0xec12068e, 0x6c827f6b, 0x0e7a95b9, 0x5c56fec2, + 0x4db7b4bd, 0x69b54118, 0x85a74796, 0xe92538fd, + 0xe75fad44, 0xbb095386, 0x485af057, 0x21efb14f, + 0xa448f6d9, 0x4d6dce24, 0xaa326360, 0x113b30e6, + 0xa25e7ed5, 0x83b1cf9a, 0x27f93943, 0x6a94f767, + 0xc0a69407, 0xd19da4e1, 0xec1786eb, 0x6fa64971, + 0x485f7032, 0x22cb8755, 0xe26d1352, 0x33f0b7b3, + 0x40beeb28, 0x2f18a259, 0x6747d26b, 0x458c553e, + 0xa7e1466c, 0x9411f1df, 0x821f750a, 0xad07d753, + 0xca400538, 0x8fcc5006, 0x282d166a, 0xbc3ce7b5, + 0xe98ba06f, 0x448c773c, 0x8ecc7204, 0x01002202, + }, + nil, + }, + KeyTest { + // A.3. Expansion of a 256-bit Cipher Key + []byte { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4, + }, + []uint32 { + 0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781, + 0x1f352c07, 0x3b6108d7, 0x2d9810a3, 0x0914dff4, + 0x9ba35411, 0x8e6925af, 0xa51a8b5f, 0x2067fcde, + 0xa8b09c1a, 0x93d194cd, 0xbe49846e, 0xb75d5b9a, + 0xd59aecb8, 0x5bf3c917, 0xfee94248, 0xde8ebe96, + 0xb5a9328a, 0x2678a647, 0x98312229, 0x2f6c79b3, + 0x812c81ad, 0xdadf48ba, 0x24360af2, 0xfab8b464, + 0x98c5bfc9, 0xbebd198e, 0x268c3ba7, 0x09e04214, + 0x68007bac, 0xb2df3316, 0x96e939e4, 0x6c518d80, + 0xc814e204, 0x76a9fb8a, 0x5025c02d, 0x59c58239, + 0xde136967, 0x6ccc5a71, 0xfa256395, 0x9674ee15, + 0x5886ca5d, 0x2e2f31d7, 0x7e0af1fa, 0x27cf73c3, + 0x749c47ab, 0x18501dda, 0xe2757e4f, 0x7401905a, + 0xcafaaae3, 0xe4d59b34, 0x9adf6ace, 0xbd10190d, + 0xfe4890d1, 0xe6188d0b, 0x046df344, 0x706c631e, + }, + nil, + }, +} + +// Test key expansion against FIPS 197 examples. +func TestExpandKey(t *testing.T) { +L: + for i, tt := range keyTests { + enc := make([]uint32, len(tt.enc)); + var dec []uint32; + if tt.dec != nil { + dec = make([]uint32, len(tt.dec)); + } + expandKey(tt.key, enc, dec); + for j, v := range enc { + if v != tt.enc[j] { + t.Errorf("key %d: enc[%d] = %#x, want %#x", i, j, v, tt.enc[j]); + continue L; + } + } + if dec != nil { + for j, v := range dec { + if v != tt.dec[j] { + t.Errorf("key %d: dec[%d] = %#x, want %#x", i, j, v, tt.dec[j]); + continue L; + } + } + } + } +} + +// Appendix B, C of FIPS 197: Cipher examples, Example vectors. +type CryptTest struct { + key []byte; + in []byte; + out []byte; +} + +var encryptTests = []CryptTest { + CryptTest { + // Appendix B. + []byte { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, }, + []byte { 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34, }, + []byte { 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32, }, + }, + CryptTest { + // Appendix C.1. AES-128 + []byte { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, }, + []byte { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, }, + []byte { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a, }, + }, + CryptTest { + // Appendix C.2. AES-192 + []byte { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, }, + []byte { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, }, + []byte { 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0, 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91, }, + }, + CryptTest { + // Appendix C.3. AES-256 + []byte { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, }, + []byte { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, }, + []byte { 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89, }, + }, +} + +// Test encryptBlock against FIPS 197 examples. +func TestEncryptBlock(t *testing.T) { + for i, tt := range encryptTests { + n := len(tt.key) + 28; + enc := make([]uint32, n); + dec := make([]uint32, n); + expandKey(tt.key, enc, dec); + out := make([]byte, len(tt.in)); + encryptBlock(enc, tt.in, out); + for j, v := range out { + if v != tt.out[j] { + t.Errorf("encryptBlock %d: out[%d] = %#x, want %#x", i, j, v, tt.out[j]); + break; + } + } + } +} + +// Test decryptBlock against FIPS 197 examples. +func TestDecryptBlock(t *testing.T) { + for i, tt := range encryptTests { + n := len(tt.key) + 28; + enc := make([]uint32, n); + dec := make([]uint32, n); + expandKey(tt.key, enc, dec); + plain := make([]byte, len(tt.in)); + decryptBlock(dec, tt.out, plain); + for j, v := range plain { + if v != tt.in[j] { + t.Errorf("decryptBlock %d: plain[%d] = %#x, want %#x", i, j, v, tt.in[j]); + break; + } + } + } +} + +// Test Cipher Encrypt method against FIPS 197 examples. +func TestCipherEncrypt(t *testing.T) { + for i, tt := range encryptTests { + c, err := NewCipher(tt.key); + if err != nil { + t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err); + continue; + } + out := make([]byte, len(tt.in)); + c.Encrypt(tt.in, out); + for j, v := range out { + if v != tt.out[j] { + t.Errorf("Cipher.Encrypt %d: out[%d] = %#x, want %#x", i, j, v, tt.out[j]); + break; + } + } + } +} + +// Test Cipher Decrypt against FIPS 197 examples. +func TestCipherDecrypt(t *testing.T) { + for i, tt := range encryptTests { + c, err := NewCipher(tt.key); + if err != nil { + t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err); + continue; + } + plain := make([]byte, len(tt.in)); + c.Decrypt(tt.out, plain); + for j, v := range plain { + if v != tt.in[j] { + t.Errorf("decryptBlock %d: plain[%d] = %#x, want %#x", i, j, v, tt.in[j]); + break; + } + } + } +} + diff --git a/src/pkg/crypto/aes/block.go b/src/pkg/crypto/aes/block.go new file mode 100644 index 000000000..3c67d1c3c --- /dev/null +++ b/src/pkg/crypto/aes/block.go @@ -0,0 +1,182 @@ +// Copyright 2009 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. + +// This Go implementation is derived in part from the reference +// ANSI C implementation, which carries the following notice: +// +// rijndael-alg-fst.c +// +// @version 3.0 (December 2000) +// +// Optimised ANSI C code for the Rijndael cipher (now AES) +// +// @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> +// @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> +// @author Paulo Barreto <paulo.barreto@terra.com.br> +// +// This code is hereby placed in the public domain. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS +// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// See FIPS 197 for specification, and see Daemen and Rijmen's Rijndael submission +// for implementation details. +// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf +// http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf + +package aes + +import "crypto/aes" + +// Encrypt one block from src into dst, using the expanded key xk. +func encryptBlock(xk []uint32, src, dst []byte) { + var s0, s1, s2, s3, t0, t1, t2, t3 uint32; + + s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]); + s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]); + s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11]); + s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15]); + + // First round just XORs input with key. + s0 ^= xk[0]; + s1 ^= xk[1]; + s2 ^= xk[2]; + s3 ^= xk[3]; + + // Middle rounds shuffle using tables. + // Number of rounds is set by length of expanded key. + nr := len(xk)/4 - 2; // - 2: one above, one more below + k := 4; + for r := 0; r < nr; r++ { + t0 = xk[k+0] ^ te[0][s0>>24] ^ te[1][s1>>16 & 0xff] ^ te[2][s2>>8 & 0xff] ^ te[3][s3 & 0xff]; + t1 = xk[k+1] ^ te[0][s1>>24] ^ te[1][s2>>16 & 0xff] ^ te[2][s3>>8 & 0xff] ^ te[3][s0 & 0xff]; + t2 = xk[k+2] ^ te[0][s2>>24] ^ te[1][s3>>16 & 0xff] ^ te[2][s0>>8 & 0xff] ^ te[3][s1 & 0xff]; + t3 = xk[k+3] ^ te[0][s3>>24] ^ te[1][s0>>16 & 0xff] ^ te[2][s1>>8 & 0xff] ^ te[3][s2 & 0xff]; + k += 4; + s0, s1, s2, s3 = t0, t1, t2, t3; + } + + // Last round uses s-box directly and XORs to produce output. + s0 = uint32(sbox0[t0>>24])<<24 | uint32(sbox0[t1>>16 & 0xff])<<16 | uint32(sbox0[t2>>8 & 0xff])<<8 | uint32(sbox0[t3 & 0xff]); + s1 = uint32(sbox0[t1>>24])<<24 | uint32(sbox0[t2>>16 & 0xff])<<16 | uint32(sbox0[t3>>8 & 0xff])<<8 | uint32(sbox0[t0 & 0xff]); + s2 = uint32(sbox0[t2>>24])<<24 | uint32(sbox0[t3>>16 & 0xff])<<16 | uint32(sbox0[t0>>8 & 0xff])<<8 | uint32(sbox0[t1 & 0xff]); + s3 = uint32(sbox0[t3>>24])<<24 | uint32(sbox0[t0>>16 & 0xff])<<16 | uint32(sbox0[t1>>8 & 0xff])<<8 | uint32(sbox0[t2 & 0xff]); + + s0 ^= xk[k+0]; + s1 ^= xk[k+1]; + s2 ^= xk[k+2]; + s3 ^= xk[k+3]; + + dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0); + dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1); + dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2); + dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3); +} + +// Decrypt one block from src into dst, using the expanded key xk. +func decryptBlock(xk []uint32, src, dst []byte) { + var s0, s1, s2, s3, t0, t1, t2, t3 uint32; + + s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]); + s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]); + s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11]); + s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15]); + + // First round just XORs input with key. + s0 ^= xk[0]; + s1 ^= xk[1]; + s2 ^= xk[2]; + s3 ^= xk[3]; + + // Middle rounds shuffle using tables. + // Number of rounds is set by length of expanded key. + nr := len(xk)/4 - 2; // - 2: one above, one more below + k := 4; + for r := 0; r < nr; r++ { + t0 = xk[k+0] ^ td[0][s0>>24] ^ td[1][s3>>16 & 0xff] ^ td[2][s2>>8 & 0xff] ^ td[3][s1 & 0xff]; + t1 = xk[k+1] ^ td[0][s1>>24] ^ td[1][s0>>16 & 0xff] ^ td[2][s3>>8 & 0xff] ^ td[3][s2 & 0xff]; + t2 = xk[k+2] ^ td[0][s2>>24] ^ td[1][s1>>16 & 0xff] ^ td[2][s0>>8 & 0xff] ^ td[3][s3 & 0xff]; + t3 = xk[k+3] ^ td[0][s3>>24] ^ td[1][s2>>16 & 0xff] ^ td[2][s1>>8 & 0xff] ^ td[3][s0 & 0xff]; + k += 4; + s0, s1, s2, s3 = t0, t1, t2, t3; + } + + // Last round uses s-box directly and XORs to produce output. + s0 = uint32(sbox1[t0>>24])<<24 | uint32(sbox1[t3>>16 & 0xff])<<16 | uint32(sbox1[t2>>8 & 0xff])<<8 | uint32(sbox1[t1 & 0xff]); + s1 = uint32(sbox1[t1>>24])<<24 | uint32(sbox1[t0>>16 & 0xff])<<16 | uint32(sbox1[t3>>8 & 0xff])<<8 | uint32(sbox1[t2 & 0xff]); + s2 = uint32(sbox1[t2>>24])<<24 | uint32(sbox1[t1>>16 & 0xff])<<16 | uint32(sbox1[t0>>8 & 0xff])<<8 | uint32(sbox1[t3 & 0xff]); + s3 = uint32(sbox1[t3>>24])<<24 | uint32(sbox1[t2>>16 & 0xff])<<16 | uint32(sbox1[t1>>8 & 0xff])<<8 | uint32(sbox1[t0 & 0xff]); + + s0 ^= xk[k+0]; + s1 ^= xk[k+1]; + s2 ^= xk[k+2]; + s3 ^= xk[k+3]; + + dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0); + dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1); + dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2); + dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3); +} + +// Apply sbox0 to each byte in w. +func subw(w uint32) uint32 { + return + uint32(sbox0[w>>24])<<24 | + uint32(sbox0[w>>16 & 0xff])<<16 | + uint32(sbox0[w>>8 & 0xff])<<8 | + uint32(sbox0[w & 0xff]); +} + +// Rotate +func rotw(w uint32) uint32 { + return w<<8 | w>>24; +} + +// Key expansion algorithm. See FIPS-197, Figure 11. +// Their rcon[i] is our powx[i-1] << 24. +func expandKey(key []byte, enc, dec []uint32) { + // Encryption key setup. + var i int; + nk := len(key) / 4; + for i = 0; i < nk; i++ { + enc[i] = uint32(key[4*i])<<24 | uint32(key[4*i+1])<<16 | uint32(key[4*i+2])<<8 | uint32(key[4*i+3]); + } + for ; i < len(enc); i++ { + t := enc[i-1]; + if i % nk == 0 { + t = subw(rotw(t)) ^ (uint32(powx[i/nk - 1]) << 24); + } else if nk > 6 && i % nk == 4 { + t = subw(t); + } + enc[i] = enc[i-nk] ^ t; + } + + // Derive decryption key from encryption key. + // Reverse the 4-word round key sets from enc to produce dec. + // All sets but the first and last get the MixColumn transform applied. + if dec == nil { + return; + } + n := len(enc); + for i := 0; i < n; i += 4 { + ei := n - i - 4; + for j := 0; j < 4; j++ { + x := enc[ei+j]; + if i > 0 && i+4 < n { + x = td[0][sbox0[x>>24]] ^ td[1][sbox0[x>>16 & 0xff]] ^ td[2][sbox0[x>>8 & 0xff]] ^ td[3][sbox0[x & 0xff]]; + } + dec[i+j] = x; + } + } +} + diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go new file mode 100644 index 000000000..fd8e43e16 --- /dev/null +++ b/src/pkg/crypto/aes/cipher.go @@ -0,0 +1,71 @@ +// Copyright 2009 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 aes + +import ( + "crypto/aes"; + "os"; +) + +// The AES block size in bytes. +const BlockSize = 16; + +// A Cipher is an instance of AES encryption using a particular key. +type Cipher struct { + enc []uint32; + dec []uint32; +} + +// NewCipher creates and returns a new Cipher. +// The key argument should be the AES key, +// either 16, 24, or 32 bytes to select +// AES-128, AES-192, or AES-256. +func NewCipher(key []byte) (*Cipher, os.Error) { + switch len(key) { + default: + return nil, os.ErrorString("crypto/aes: invalid key size"); + case 16, 24, 32: + break; + } + + n := len(key) + 28; + c := &Cipher{make([]uint32, n), make([]uint32, n)}; + expandKey(key, c.enc, c.dec); + return c, nil; +} + +// BlockSize returns the AES block size, 16 bytes. +// It is necessary to satisfy the Key interface in the +// package "crypto/modes". +func (c *Cipher) BlockSize() int { + return BlockSize; +} + +// Encrypt encrypts the 16-byte buffer src using the key k +// and stores the result in dst. +// Note that for amounts of data larger than a block, +// it is not safe to just call Encrypt on successive blocks; +// instead, use an encryption mode like AESCBC (see modes.go). +func (c *Cipher) Encrypt(src, dst []byte) { + encryptBlock(c.enc, src, dst); +} + +// Decrypt decrypts the 16-byte buffer src using the key k +// and stores the result in dst. +func (c *Cipher) Decrypt(src, dst []byte) { + decryptBlock(c.dec, src, dst); +} + +// Reset zeros the key data, so that it will no longer +// appear in the process's memory. +func (c *Cipher) Reset() { + for i := 0; i < len(c.enc); i++ { + c.enc[i] = 0; + } + for i := 0; i < len(c.dec); i++ { + c.dec[i] = 0; + } +} + diff --git a/src/pkg/crypto/aes/const.go b/src/pkg/crypto/aes/const.go new file mode 100644 index 000000000..9167d602d --- /dev/null +++ b/src/pkg/crypto/aes/const.go @@ -0,0 +1,363 @@ +// Copyright 2009 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. + +// AES constants - 8720 bytes of initialized data. + +// This package implements AES encryption (formerly Rijndael), +// as defined in U.S. Federal Information Processing Standards Publication 197. +package aes + +// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +// AES is based on the mathematical behavior of binary polynomials +// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x² + x + 1. +// Addition of these binary polynomials corresponds to binary xor. +// Reducing mod poly corresponds to binary xor with poly every +// time a 0x100 bit appears. +const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0; // x⁸ + x⁴ + x² + x + 1 + +// Powers of x mod poly in GF(2). +var powx = [16]byte{ + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, +} + +// FIPS-197 Figure 7. S-box substitution values in hexadecimal format. +var sbox0 = [256]byte { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +} + +// FIPS-197 Figure 14. Inverse S-box substitution values in hexadecimal format. +var sbox1 = [256]byte { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, +} + +// Lookup tables for encryption. +// These can be recomputed by adapting the tests in aes_test.go. + +var te = [4][256]uint32 { + [256]uint32 { + 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, + 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, + 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, + 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, + 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, + 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, + 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, + 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, + 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, + 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, + 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, + 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, + 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, + 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, + 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, + 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, + 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, + 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, + 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, + 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, + }, + [256]uint32 { + 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, + 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, + 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, + 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, + 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, + 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, + 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, + 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, + 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, + 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, + 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, + 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, + 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, + 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, + 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, + 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, + 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, + 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, + 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, + }, + [256]uint32 { + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, + 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, + 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, + 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, + 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, + 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, + 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, + 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, + 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, + 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, + 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, + 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, + 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, + 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, + 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, + 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, + 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, + 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, + 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, + 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, + 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, + }, + [256]uint32 { + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, + 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, + 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, + 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, + 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, + 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, + 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, + 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, + 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, + 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, + 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, + 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, + 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, + 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, + 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, + 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, + 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, + 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, + 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, + 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, + 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, + }, +} + +// Lookup tables for decryption. +// These can be recomputed by adapting the tests in aes_test.go. + +var td = [4][256]uint32 { + [256]uint32 { + 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, + 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, + 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, + 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, + 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, + 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, + 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, + 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, + 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, + 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, + 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, + 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, + 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, + 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, + 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, + 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, + 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, + 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, + 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, + 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, + 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, + 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, + 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, + 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, + 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, + 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, + 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, + 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, + 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, + 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, + 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, + }, + [256]uint32 { + 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, + 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, + 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, + 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, + 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, + 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, + 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, + 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, + 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, + 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, + 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, + 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, + 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, + 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, + 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, + 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, + 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, + 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, + 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, + 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, + 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, + 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, + 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, + 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, + 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, + 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, + 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, + 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, + 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, + 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, + 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, + }, + [256]uint32 { + 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, + 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, + 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, + 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, + 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, + 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, + 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, + 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, + 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, + 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, + 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, + 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, + 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, + 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, + 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, + 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, + 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, + 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, + 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, + 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, + 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, + 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, + 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, + 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, + 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, + 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, + 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, + 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, + }, + [256]uint32 { + 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, + 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, + 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, + 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, + 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, + 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, + 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, + 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, + 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, + 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, + 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, + 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, + 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, + 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, + 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, + 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, + 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, + 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, + 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, + 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, + 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, + 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, + 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, + 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, + 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, + 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, + 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, + 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, + 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, + 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, + 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, + }, +} + diff --git a/src/pkg/crypto/block/Makefile b/src/pkg/crypto/block/Makefile new file mode 100644 index 000000000..e8bc8e907 --- /dev/null +++ b/src/pkg/crypto/block/Makefile @@ -0,0 +1,82 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/crypto/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + cipher.$O\ + xor.$O\ + +O2=\ + cmac.$O\ + ctr.$O\ + ecb.$O\ + ofb.$O\ + +O3=\ + cbc.$O\ + cfb.$O\ + eax.$O\ + + +phases: a1 a2 a3 +_obj$D/block.a: phases + +a1: $(O1) + $(AR) grc _obj$D/block.a cipher.$O xor.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/block.a cmac.$O ctr.$O ecb.$O ofb.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/block.a cbc.$O cfb.$O eax.$O + rm -f $(O3) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/block.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/block.a + +packages: _obj$D/block.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/block.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/block.a diff --git a/src/pkg/crypto/block/cbc.go b/src/pkg/crypto/block/cbc.go new file mode 100644 index 000000000..85a746b72 --- /dev/null +++ b/src/pkg/crypto/block/cbc.go @@ -0,0 +1,75 @@ +// Copyright 2009 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. + +// Cipher block chaining (CBC) mode. + +// CBC provides confidentiality by xoring (chaining) each plaintext block +// with the previous ciphertext block before applying the block cipher. + +// See NIST SP 800-38A, pp 10-11 + +package block + +import ( + "crypto/block"; + "io"; +) + +type cbcCipher struct { + c Cipher; + blockSize int; + iv []byte; + tmp []byte; +} + +func newCBC(c Cipher, iv []byte) *cbcCipher { + n := c.BlockSize(); + x := new(cbcCipher); + x.c = c; + x.blockSize = n; + x.iv = copy(iv); + x.tmp = make([]byte, n); + return x; +} + +func (x *cbcCipher) BlockSize() int { + return x.blockSize; +} + +func (x *cbcCipher) Encrypt(src, dst []byte) { + for i := 0; i < x.blockSize; i++ { + x.iv[i] ^= src[i]; + } + x.c.Encrypt(x.iv, x.iv); + for i := 0; i < x.blockSize; i++ { + dst[i] = x.iv[i]; + } +} + +func (x *cbcCipher) Decrypt(src, dst []byte) { + x.c.Decrypt(src, x.tmp); + for i := 0; i < x.blockSize; i++ { + x.tmp[i] ^= x.iv[i]; + x.iv[i] = src[i]; + dst[i] = x.tmp[i]; + } +} + +// NewCBCDecrypter returns a reader that reads data from r and decrypts it using c +// in cipher block chaining (CBC) mode with the initialization vector iv. +// The returned Reader does not buffer or read ahead except +// as required by the cipher's block size. +func NewCBCDecrypter(c Cipher, iv []byte, r io.Reader) io.Reader { + return NewECBDecrypter(newCBC(c, iv), r); +} + +// NewCBCEncrypter returns a writer that encrypts data using c +// in cipher block chaining (CBC) mode with the initialization vector iv +// and writes the encrypted data to w. +// The returned Writer does no buffering except as required +// by the cipher's block size, so there is no need for a Flush method. +func NewCBCEncrypter(c Cipher, iv []byte, w io.Writer) io.Writer { + return NewECBEncrypter(newCBC(c, iv), w); +} + diff --git a/src/pkg/crypto/block/cbc_aes_test.go b/src/pkg/crypto/block/cbc_aes_test.go new file mode 100644 index 000000000..4681c1c07 --- /dev/null +++ b/src/pkg/crypto/block/cbc_aes_test.go @@ -0,0 +1,107 @@ +// Copyright 2009 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. + +// CBC AES test vectors. + +// See U.S. National Institute of Standards and Technology (NIST) +// Special Publication 800-38A, ``Recommendation for Block Cipher +// Modes of Operation,'' 2001 Edition, pp. 24-29. + +package block + +// gobuild: $GC ecb_aes_test.go + +import ( + "crypto/aes"; + "crypto/block"; + "io"; + "os"; + "testing"; + + "./ecb_aes_test"; +) + +type cbcTest struct { + name string; + key []byte; + iv []byte; + in []byte; + out []byte; +} + +var cbcAESTests = []cbcTest { + // NIST SP 800-38A pp 27-29 + cbcTest { + "CBC-AES128", + commonKey128, + commonIV, + commonInput, + []byte { + 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, + 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, + 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7, + }, + }, + cbcTest { + "CBC-AES192", + commonKey192, + commonIV, + commonInput, + []byte { + 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, + 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, + 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, + 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd, + }, + }, + cbcTest { + "CBC-AES256", + commonKey256, + commonIV, + commonInput, + []byte { + 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, + 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, + 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, + 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b, + }, + }, +} + +func TestCBC_AES(t *testing.T) { + for i, tt := range cbcAESTests { + test := tt.name; + + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err); + continue; + } + + var crypt io.ByteBuffer; + w := NewCBCEncrypter(c, tt.iv, &crypt); + var r io.Reader = io.NewByteReader(tt.in); + n, err := io.Copy(r, w); + if n != int64(len(tt.in)) || err != nil { + t.Errorf("%s: CBCEncrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.in)); + } else if d := crypt.Data(); !same(tt.out, d) { + t.Errorf("%s: CBCEncrypter\nhave %x\nwant %x", test, d, tt.out); + } + + var plain io.ByteBuffer; + r = NewCBCDecrypter(c, tt.iv, io.NewByteReader(tt.out)); + w = &plain; + n, err = io.Copy(r, w); + if n != int64(len(tt.out)) || err != nil { + t.Errorf("%s: CBCDecrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.out)); + } else if d := plain.Data(); !same(tt.in, d) { + t.Errorf("%s: CBCDecrypter\nhave %x\nwant %x", test, d, tt.in); + } + + if t.Failed() { + break; + } + } +} diff --git a/src/pkg/crypto/block/cfb.go b/src/pkg/crypto/block/cfb.go new file mode 100644 index 000000000..5c4c09a1b --- /dev/null +++ b/src/pkg/crypto/block/cfb.go @@ -0,0 +1,100 @@ +// Copyright 2009 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. + +// Cipher feedback (CFB) mode. + +// CFB provides confidentiality by feeding a fraction of +// the previous ciphertext in as the plaintext for the next +// block operation. + +// See NIST SP 800-38A, pp 11-13 + +package block + +import ( + "crypto/block"; + "io"; +) + +type cfbCipher struct { + c Cipher; + blockSize int; // our block size (s/8) + cipherSize int; // underlying cipher block size + iv []byte; + tmp []byte; +} + +func newCFB(c Cipher, s int, iv []byte) *cfbCipher { + if s == 0 || s % 8 != 0 { + panicln("crypto/block: invalid CFB mode", s); + } + b := c.BlockSize(); + x := new(cfbCipher); + x.c = c; + x.blockSize = s/8; + x.cipherSize = b; + x.iv = copy(iv); + x.tmp = make([]byte, b); + return x; +} + +func (x *cfbCipher) BlockSize() int { + return x.blockSize; +} + +func (x *cfbCipher) Encrypt(src, dst []byte) { + // Encrypt old IV and xor prefix with src to make dst. + x.c.Encrypt(x.iv, x.tmp); + for i := 0; i < x.blockSize; i++ { + dst[i] = src[i] ^ x.tmp[i]; + } + + // Slide unused IV pieces down and insert dst at end. + for i := 0; i < x.cipherSize - x.blockSize; i++ { + x.iv[i] = x.iv[i + x.blockSize]; + } + off := x.cipherSize - x.blockSize; + for i := off; i < x.cipherSize; i++ { + x.iv[i] = dst[i - off]; + } +} + +func (x *cfbCipher) Decrypt(src, dst []byte) { + // Encrypt [sic] old IV and xor prefix with src to make dst. + x.c.Encrypt(x.iv, x.tmp); + for i := 0; i < x.blockSize; i++ { + dst[i] = src[i] ^ x.tmp[i]; + } + + // Slide unused IV pieces down and insert src at top. + for i := 0; i < x.cipherSize - x.blockSize; i++ { + x.iv[i] = x.iv[i + x.blockSize]; + } + off := x.cipherSize - x.blockSize; + for i := off; i < x.cipherSize; i++ { + // Reconstruct src = dst ^ x.tmp + // in case we overwrote src (src == dst). + x.iv[i] = dst[i - off] ^ x.tmp[i - off]; + } +} + +// NewCFBDecrypter returns a reader that reads data from r and decrypts it using c +// in s-bit cipher feedback (CFB) mode with the initialization vector iv. +// The returned Reader does not buffer or read ahead except +// as required by the cipher's block size. +// Modes for s not a multiple of 8 are unimplemented. +func NewCFBDecrypter(c Cipher, s int, iv []byte, r io.Reader) io.Reader { + return NewECBDecrypter(newCFB(c, s, iv), r); +} + +// NewCFBEncrypter returns a writer that encrypts data using c +// in s-bit cipher feedback (CFB) mode with the initialization vector iv +// and writes the encrypted data to w. +// The returned Writer does no buffering except as required +// by the cipher's block size, so there is no need for a Flush method. +// Modes for s not a multiple of 8 are unimplemented. +func NewCFBEncrypter(c Cipher, s int, iv []byte, w io.Writer) io.Writer { + return NewECBEncrypter(newCFB(c, s, iv), w); +} + diff --git a/src/pkg/crypto/block/cfb_aes_test.go b/src/pkg/crypto/block/cfb_aes_test.go new file mode 100644 index 000000000..6c793dba8 --- /dev/null +++ b/src/pkg/crypto/block/cfb_aes_test.go @@ -0,0 +1,316 @@ +// Copyright 2009 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. + +// CFB AES test vectors. + +// See U.S. National Institute of Standards and Technology (NIST) +// Special Publication 800-38A, ``Recommendation for Block Cipher +// Modes of Operation,'' 2001 Edition, pp. 29-52. + +package block + +// gobuild: $GC ecb_aes_test.go + +import ( + "crypto/aes"; + "crypto/block"; + "io"; + "os"; + "testing"; + + "./ecb_aes_test"; +) + +type cfbTest struct { + name string; + s int; + key []byte; + iv []byte; + in []byte; + out []byte; +} + +var cfbAESTests = []cfbTest { + cfbTest { + "CFB1-AES128", + 1, + commonKey128, + commonIV, + []byte{ + 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 1<<1, + 1<<7 | 1<<6 | 0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1, + }, + []byte{ + 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 0<<1, + 1<<7 | 0<<6 | 1<<5 | 1<<4 | 0<<3 | 0<<2 | 1<<1, + }, + }, + cfbTest { + "CFB1-AES192", + 1, + commonKey192, + commonIV, + []byte{ + 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 1<<1, + 1<<7 | 1<<6 | 0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1, + }, + []byte{ + 1<<7 | 0<<6 | 0<<5 | 1<<4 | 0<<3 | 0<<2 | 1<<1, + 0<<7 | 1<<6 | 0<<5 | 1<<4 | 1<<3 | 0<<2 | 0<<1, + }, + }, + cfbTest { + "CFB1-AES256", + 1, + commonKey256, + commonIV, + []byte{ + 0<<7 | 1<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 1<<1, + 1<<7 | 1<<6 | 0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1, + }, + []byte{ + 1<<7 | 0<<6 | 0<<5 | 1<<4 | 0<<3 | 0<<2 | 0<<1, + 0<<7 | 0<<6 | 1<<5 | 0<<4 | 1<<3 | 0<<2 | 0<<1, + }, + }, + + cfbTest { + "CFB8-AES128", + 8, + commonKey128, + commonIV, + []byte{ + 0x6b, + 0xc1, + 0xbe, + 0xe2, + 0x2e, + 0x40, + 0x9f, + 0x96, + 0xe9, + 0x3d, + 0x7e, + 0x11, + 0x73, + 0x93, + 0x17, + 0x2a, + 0xae, + 0x2d, + }, + []byte{ + 0x3b, + 0x79, + 0x42, + 0x4c, + 0x9c, + 0x0d, + 0xd4, + 0x36, + 0xba, + 0xce, + 0x9e, + 0x0e, + 0xd4, + 0x58, + 0x6a, + 0x4f, + 0x32, + 0xb9, + }, + }, + + cfbTest { + "CFB8-AES192", + 8, + commonKey192, + commonIV, + []byte{ + 0x6b, + 0xc1, + 0xbe, + 0xe2, + 0x2e, + 0x40, + 0x9f, + 0x96, + 0xe9, + 0x3d, + 0x7e, + 0x11, + 0x73, + 0x93, + 0x17, + 0x2a, + 0xae, + 0x2d, + }, + []byte{ + 0xcd, + 0xa2, + 0x52, + 0x1e, + 0xf0, + 0xa9, + 0x05, + 0xca, + 0x44, + 0xcd, + 0x05, + 0x7c, + 0xbf, + 0x0d, + 0x47, + 0xa0, + 0x67, + 0x8a, + }, + }, + + cfbTest { + "CFB8-AES256", + 8, + commonKey256, + commonIV, + []byte{ + 0x6b, + 0xc1, + 0xbe, + 0xe2, + 0x2e, + 0x40, + 0x9f, + 0x96, + 0xe9, + 0x3d, + 0x7e, + 0x11, + 0x73, + 0x93, + 0x17, + 0x2a, + 0xae, + 0x2d, + }, + []byte{ + 0xdc, + 0x1f, + 0x1a, + 0x85, + 0x20, + 0xa6, + 0x4d, + 0xb5, + 0x5f, + 0xcc, + 0x8a, + 0xc5, + 0x54, + 0x84, + 0x4e, + 0x88, + 0x97, + 0x00, + }, + }, + + cfbTest { + "CFB128-AES128", + 128, + commonKey128, + commonIV, + []byte{ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }, + []byte{ + 0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a, + 0xc8, 0xa6, 0x45, 0x37, 0xa0, 0xb3, 0xa9, 0x3f, 0xcd, 0xe3, 0xcd, 0xad, 0x9f, 0x1c, 0xe5, 0x8b, + 0x26, 0x75, 0x1f, 0x67, 0xa3, 0xcb, 0xb1, 0x40, 0xb1, 0x80, 0x8c, 0xf1, 0x87, 0xa4, 0xf4, 0xdf, + 0xc0, 0x4b, 0x05, 0x35, 0x7c, 0x5d, 0x1c, 0x0e, 0xea, 0xc4, 0xc6, 0x6f, 0x9f, 0xf7, 0xf2, 0xe6, + }, + }, + + cfbTest { + "CFB128-AES192", + 128, + commonKey192, + commonIV, + []byte{ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }, + []byte{ + 0xcd, 0xc8, 0x0d, 0x6f, 0xdd, 0xf1, 0x8c, 0xab, 0x34, 0xc2, 0x59, 0x09, 0xc9, 0x9a, 0x41, 0x74, + 0x67, 0xce, 0x7f, 0x7f, 0x81, 0x17, 0x36, 0x21, 0x96, 0x1a, 0x2b, 0x70, 0x17, 0x1d, 0x3d, 0x7a, + 0x2e, 0x1e, 0x8a, 0x1d, 0xd5, 0x9b, 0x88, 0xb1, 0xc8, 0xe6, 0x0f, 0xed, 0x1e, 0xfa, 0xc4, 0xc9, + 0xc0, 0x5f, 0x9f, 0x9c, 0xa9, 0x83, 0x4f, 0xa0, 0x42, 0xae, 0x8f, 0xba, 0x58, 0x4b, 0x09, 0xff, + }, + }, + + cfbTest { + "CFB128-AES256", + 128, + commonKey256, + commonIV, + []byte{ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }, + []byte{ + 0xdc, 0x7e, 0x84, 0xbf, 0xda, 0x79, 0x16, 0x4b, 0x7e, 0xcd, 0x84, 0x86, 0x98, 0x5d, 0x38, 0x60, + 0x39, 0xff, 0xed, 0x14, 0x3b, 0x28, 0xb1, 0xc8, 0x32, 0x11, 0x3c, 0x63, 0x31, 0xe5, 0x40, 0x7b, + 0xdf, 0x10, 0x13, 0x24, 0x15, 0xe5, 0x4b, 0x92, 0xa1, 0x3e, 0xd0, 0xa8, 0x26, 0x7a, 0xe2, 0xf9, + 0x75, 0xa3, 0x85, 0x74, 0x1a, 0xb9, 0xce, 0xf8, 0x20, 0x31, 0x62, 0x3d, 0x55, 0xb1, 0xe4, 0x71, + }, + }, +} + +func TestCFB_AES(t *testing.T) { + for i, tt := range cfbAESTests { + test := tt.name; + + if tt.s == 1 { + // 1-bit CFB not implemented + continue; + } + + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err); + continue; + } + + var crypt io.ByteBuffer; + w := NewCFBEncrypter(c, tt.s, tt.iv, &crypt); + var r io.Reader = io.NewByteReader(tt.in); + n, err := io.Copy(r, w); + if n != int64(len(tt.in)) || err != nil { + t.Errorf("%s: CFBEncrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.in)); + } else if d := crypt.Data(); !same(tt.out, d) { + t.Errorf("%s: CFBEncrypter\nhave %x\nwant %x", test, d, tt.out); + } + + var plain io.ByteBuffer; + r = NewCFBDecrypter(c, tt.s, tt.iv, io.NewByteReader(tt.out)); + w = &plain; + n, err = io.Copy(r, w); + if n != int64(len(tt.out)) || err != nil { + t.Errorf("%s: CFBDecrypter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.out)); + } else if d := plain.Data(); !same(tt.in, d) { + t.Errorf("%s: CFBDecrypter\nhave %x\nwant %x", test, d, tt.in); + } + + if t.Failed() { + break; + } + } +} diff --git a/src/pkg/crypto/block/cipher.go b/src/pkg/crypto/block/cipher.go new file mode 100644 index 000000000..7ea035db9 --- /dev/null +++ b/src/pkg/crypto/block/cipher.go @@ -0,0 +1,74 @@ +// Copyright 2009 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. + +// The block package implements standard block cipher modes +// that can be wrapped around low-level block cipher implementations. +// See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html +// and NIST Special Publication 800-38A. +package block + +import "io"; + +// A Cipher represents an implementation of block cipher +// using a given key. It provides the capability to encrypt +// or decrypt individual blocks. The mode implementations +// extend that capability to streams of blocks. +type Cipher interface { + // BlockSize returns the cipher's block size. + BlockSize() int; + + // Encrypt encrypts the first block in src into dst. + // Src and dst may point at the same memory. + Encrypt(src, dst []byte); + + // Decrypt decrypts the first block in src into dst. + // Src and dst may point at the same memory. + Decrypt(src, dst []byte); +} + +// TODO(rsc): Digest belongs elsewhere. + +// A Digest is an implementation of a message digest algorithm. +// Write data to it and then call Sum to retreive the digest. +// Calling Reset resets the internal state, as though no data has +// been written. +type Digest interface { + io.Writer; + Sum() []byte; + Reset(); +} + + + +// Utility routines + +func shift1(src, dst []byte) byte { + var b byte; + for i := len(src)-1; i >= 0; i-- { + bb := src[i]>>7; + dst[i] = src[i]<<1 | b; + b = bb; + } + return b; +} + +func same(p, q []byte) bool { + if len(p) != len(q) { + return false; + } + for i := 0; i < len(p); i++ { + if p[i] != q[i] { + return false; + } + } + return true; +} + +func copy(p []byte) []byte { + q := make([]byte, len(p)); + for i, b := range p { + q[i] = b; + } + return q; +} diff --git a/src/pkg/crypto/block/cmac.go b/src/pkg/crypto/block/cmac.go new file mode 100644 index 000000000..40697cabd --- /dev/null +++ b/src/pkg/crypto/block/cmac.go @@ -0,0 +1,105 @@ +// Copyright 2009 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. + +// CMAC message authentication code, defined in +// NIST Special Publication SP 800-38B. + +package block + +import ( + "crypto/block"; + "io"; + "os"; +) + +const ( + // minimal irreducible polynomial of degree b + r64 = 0x1b; + r128 = 0x87; +) + +type cmac struct { + k1, k2, ci, digest []byte; + p int; // position in ci + c Cipher; +} + +// TODO(rsc): Should this return an error instead of panic? + +// NewCMAC returns a new instance of a CMAC message authentication code +// digest using the given Cipher. +func NewCMAC(c Cipher) Digest { + var r byte; + n := c.BlockSize(); + switch n { + case 64/8: + r = r64; + case 128/8: + r = r128; + default: + panic("crypto/block: NewCMAC: invalid cipher block size", n); + } + + d := new(cmac); + d.c = c; + d.k1 = make([]byte, n); + d.k2 = make([]byte, n); + d.ci = make([]byte, n); + d.digest = make([]byte, n); + + // Subkey generation, p. 7 + c.Encrypt(d.k1, d.k1); + if shift1(d.k1, d.k1) != 0 { + d.k1[n-1] ^= r; + } + if shift1(d.k1, d.k2) != 0 { + d.k2[n-1] ^= r; + } + + return d; +} + +// Reset clears the digest state, starting a new digest. +func (d *cmac) Reset() { + for i := range d.ci { + d.ci[i] = 0; + } + d.p = 0; +} + +// Write adds the given data to the digest state. +func (d *cmac) Write(p []byte) (n int, err os.Error) { + // Xor input into ci. + for i, c := range p { + // If ci is full, encrypt and start over. + if d.p >= len(d.ci) { + d.c.Encrypt(d.ci, d.ci); + d.p = 0; + } + d.ci[d.p] ^= c; + d.p++; + } + return len(p), nil; +} + +// Sum returns the CMAC digest, one cipher block in length, +// of the data written with Write. +func (d *cmac) Sum() []byte { + // Finish last block, mix in key, encrypt. + // Don't edit ci, in case caller wants + // to keep digesting after call to Sum. + k := d.k1; + if d.p < len(d.digest) { + k = d.k2; + } + for i := 0; i < len(d.ci); i++ { + d.digest[i] = d.ci[i] ^ k[i]; + } + if d.p < len(d.digest) { + d.digest[d.p] ^= 0x80; + } + d.c.Encrypt(d.digest, d.digest); + return d.digest; +} + diff --git a/src/pkg/crypto/block/cmac_aes_test.go b/src/pkg/crypto/block/cmac_aes_test.go new file mode 100644 index 000000000..9284ac40a --- /dev/null +++ b/src/pkg/crypto/block/cmac_aes_test.go @@ -0,0 +1,165 @@ +// Copyright 2009 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. + +// CMAC test vectors. See NIST SP 800-38B, Appendix D. + +package block + +// gobuild: $GC ecb_aes_test.go + +import ( + "crypto/aes"; + "crypto/block"; + "testing"; + + "./ecb_aes_test"; +) + +type cmacAESTest struct { + key []byte; + in []byte; + digest []byte; +} + +var cmacAESTests = []cmacAESTest { + cmacAESTest { + commonKey128, + nil, + []byte { + 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46, + } + }, + cmacAESTest { + commonKey128, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + }, + []byte { + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c, + } + }, + cmacAESTest { + commonKey128, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + }, + []byte { + 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27, + } + }, + cmacAESTest { + commonKey128, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }, + []byte { + 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe, + } + }, + cmacAESTest { + commonKey192, + nil, + []byte { + 0xd1, 0x7d, 0xdf, 0x46, 0xad, 0xaa, 0xcd, 0xe5, 0x31, 0xca, 0xc4, 0x83, 0xde, 0x7a, 0x93, 0x67, + } + }, + cmacAESTest { + commonKey192, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + }, + []byte { + 0x9e, 0x99, 0xa7, 0xbf, 0x31, 0xe7, 0x10, 0x90, 0x06, 0x62, 0xf6, 0x5e, 0x61, 0x7c, 0x51, 0x84, + } + }, + cmacAESTest { + commonKey192, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + }, + []byte { + 0x8a, 0x1d, 0xe5, 0xbe, 0x2e, 0xb3, 0x1a, 0xad, 0x08, 0x9a, 0x82, 0xe6, 0xee, 0x90, 0x8b, 0x0e, + } + }, + cmacAESTest { + commonKey192, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }, + []byte { + 0xa1, 0xd5, 0xdf, 0x0e, 0xed, 0x79, 0x0f, 0x79, 0x4d, 0x77, 0x58, 0x96, 0x59, 0xf3, 0x9a, 0x11, + } + }, + cmacAESTest { + commonKey256, + nil, + []byte { + 0x02, 0x89, 0x62, 0xf6, 0x1b, 0x7b, 0xf8, 0x9e, 0xfc, 0x6b, 0x55, 0x1f, 0x46, 0x67, 0xd9, 0x83, + } + }, + cmacAESTest { + commonKey256, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + }, + []byte { + 0x28, 0xa7, 0x02, 0x3f, 0x45, 0x2e, 0x8f, 0x82, 0xbd, 0x4b, 0xf2, 0x8d, 0x8c, 0x37, 0xc3, 0x5c, + } + }, + cmacAESTest { + commonKey256, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + }, + []byte { + 0xaa, 0xf3, 0xd8, 0xf1, 0xde, 0x56, 0x40, 0xc2, 0x32, 0xf5, 0xb1, 0x69, 0xb9, 0xc9, 0x11, 0xe6, + } + }, + cmacAESTest { + commonKey256, + []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }, + []byte { + 0xe1, 0x99, 0x21, 0x90, 0x54, 0x9f, 0x6e, 0xd5, 0x69, 0x6a, 0x2c, 0x05, 0x6c, 0x31, 0x54, 0x10, + } + } +} + +func TestCMAC_AES(t *testing.T) { + for i, tt := range cmacAESTests { + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Errorf("test %d: NewCipher: %s", i, err); + continue; + } + d := NewCMAC(c); + n, err := d.Write(tt.in); + if err != nil || n != len(tt.in) { + t.Errorf("test %d: Write %d: %d, %s", i, len(tt.in), n, err); + continue; + } + sum := d.Sum(); + if !same(sum, tt.digest) { + x := d.(*cmac); + t.Errorf("test %d: digest mismatch\n\twant %x\n\thave %x\n\tk1 %x\n\tk2 %x", i, tt.digest, sum, x.k1, x.k2); + continue; + } + } +} diff --git a/src/pkg/crypto/block/ctr.go b/src/pkg/crypto/block/ctr.go new file mode 100644 index 000000000..eecb615ad --- /dev/null +++ b/src/pkg/crypto/block/ctr.go @@ -0,0 +1,69 @@ +// Copyright 2009 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. + +// Counter (CTR) mode. + +// CTR converts a block cipher into a stream cipher by +// repeatedly encrypting an incrementing counter and +// xoring the resulting stream of data with the input. + +// See NIST SP 800-38A, pp 13-15 + +package block + +import ( + "crypto/block"; + "io"; +) + +type ctrStream struct { + c Cipher; + ctr []byte; + out []byte; +} + +func newCTRStream(c Cipher, ctr []byte) *ctrStream { + x := new(ctrStream); + x.c = c; + x.ctr = copy(ctr); + x.out = make([]byte, len(ctr)); + return x; +} + +func (x *ctrStream) Next() []byte { + // Next block is encryption of counter. + x.c.Encrypt(x.ctr, x.out); + + // Increment counter + for i := len(x.ctr) - 1; i >= 0; i-- { + x.ctr[i]++; + if x.ctr[i] != 0 { + break; + } + } + + return x.out; +} + +// NewCTRReader returns a reader that reads data from r, decrypts (or encrypts) +// it using c in counter (CTR) mode with the initialization vector iv. +// The returned Reader does not buffer and has no block size. +// In CTR mode, encryption and decryption are the same operation: +// a CTR reader applied to an encrypted stream produces a decrypted +// stream and vice versa. +func NewCTRReader(c Cipher, iv []byte, r io.Reader) io.Reader { + return newXorReader(newCTRStream(c, iv), r); +} + +// NewCTRWriter returns a writer that encrypts (or decrypts) data using c +// in counter (CTR) mode with the initialization vector iv +// and writes the encrypted data to w. +// The returned Writer does not buffer and has no block size. +// In CTR mode, encryption and decryption are the same operation: +// a CTR writer applied to an decrypted stream produces an encrypted +// stream and vice versa. +func NewCTRWriter(c Cipher, iv []byte, w io.Writer) io.Writer { + return newXorWriter(newCTRStream(c, iv), w); +} + diff --git a/src/pkg/crypto/block/ctr_aes_test.go b/src/pkg/crypto/block/ctr_aes_test.go new file mode 100644 index 000000000..a3da1b5bf --- /dev/null +++ b/src/pkg/crypto/block/ctr_aes_test.go @@ -0,0 +1,115 @@ +// Copyright 2009 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. + +// CTR AES test vectors. + +// See U.S. National Institute of Standards and Technology (NIST) +// Special Publication 800-38A, ``Recommendation for Block Cipher +// Modes of Operation,'' 2001 Edition, pp. 55-58. + +package block + +import ( + "crypto/aes"; + "crypto/block"; + "io"; + "os"; + "testing"; + + "./ecb_aes_test"; +) + +type ctrTest struct { + name string; + key []byte; + iv []byte; + in []byte; + out []byte; +} + +var commonCounter = []byte { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +} + +var ctrAESTests = []ctrTest { + // NIST SP 800-38A pp 55-58 + ctrTest { + "CTR-AES128", + commonKey128, + commonCounter, + commonInput, + []byte { + 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, + 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, + 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, + 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee, + }, + }, + ctrTest { + "CTR-AES192", + commonKey192, + commonCounter, + commonInput, + []byte { + 0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b, + 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94, + 0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7, + 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50, + }, + }, + ctrTest { + "CTR-AES256", + commonKey256, + commonCounter, + commonInput, + []byte { + 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28, + 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5, + 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d, + 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6, + } + }, +} + +func TestCTR_AES(t *testing.T) { + for i, tt := range ctrAESTests { + test := tt.name; + + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err); + continue; + } + + for j := 0; j <= 5; j += 5 { + var crypt io.ByteBuffer; + in := tt.in[0:len(tt.in) - j]; + w := block.NewCTRWriter(c, tt.iv, &crypt); + var r io.Reader = io.NewByteReader(in); + n, err := io.Copy(r, w); + if n != int64(len(in)) || err != nil { + t.Errorf("%s/%d: CTRWriter io.Copy = %d, %v want %d, nil", test, len(in), n, err, len(in)); + } else if d, out := crypt.Data(), tt.out[0:len(in)]; !same(out, d) { + t.Errorf("%s/%d: CTRWriter\ninpt %x\nhave %x\nwant %x", test, len(in), in, d, out); + } + } + + for j := 0; j <= 7; j += 7 { + var plain io.ByteBuffer; + out := tt.out[0:len(tt.out) - j]; + r := block.NewCTRReader(c, tt.iv, io.NewByteReader(out)); + w := &plain; + n, err := io.Copy(r, w); + if n != int64(len(out)) || err != nil { + t.Errorf("%s/%d: CTRReader io.Copy = %d, %v want %d, nil", test, len(out), n, err, len(out)); + } else if d, in := plain.Data(), tt.in[0:len(out)]; !same(in, d) { + t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), d, in); + } + } + + if t.Failed() { + break; + } + } +} diff --git a/src/pkg/crypto/block/eax.go b/src/pkg/crypto/block/eax.go new file mode 100644 index 000000000..7e1d7475c --- /dev/null +++ b/src/pkg/crypto/block/eax.go @@ -0,0 +1,254 @@ +// Copyright 2009 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. + +// EAX mode, not a NIST standard (yet). +// EAX provides encryption and authentication. +// EAX targets the same uses as NIST's CCM mode, +// but EAX adds the ability to run in streaming mode. + +// See +// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/eax/eax-spec.pdf +// http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf +// What those papers call OMAC is now called CMAC. + +package block + +import ( + "crypto/block"; + "fmt"; + "io"; + "os"; +) + +// An EAXTagError is returned when the message has failed to authenticate, +// because the tag at the end of the message stream (Read) does not match +// the tag computed from the message itself (Computed). +type EAXTagError struct { + Read []byte; + Computed []byte; +} + +func (e *EAXTagError) String() string { + return fmt.Sprintf("crypto/block: EAX tag mismatch: read %x but computed %x", e.Read, e.Computed); +} + +func setupEAX(c Cipher, iv, hdr []byte, tagBytes int) (ctrIV, tag []byte, cmac Digest) { + n := len(iv); + if n != c.BlockSize() { + panicln("crypto/block: EAX: iv length", n, "!=", c.BlockSize()); + } + buf := make([]byte, n); // zeroed + + // tag = CMAC(0 + iv) ^ CMAC(1 + hdr) ^ CMAC(2 + data) + cmac = NewCMAC(c); + cmac.Write(buf); // 0 + cmac.Write(iv); + sum := cmac.Sum(); + ctrIV = copy(sum); + tag = copy(sum[0:tagBytes]); + + cmac.Reset(); + buf[n-1] = 1; + cmac.Write(buf); // 1 + cmac.Write(hdr); + sum = cmac.Sum(); + for i := 0; i < tagBytes; i++ { + tag[i] ^= sum[i]; + } + + cmac.Reset(); + buf[n-1] = 2; // 2 + cmac.Write(buf); + + return; +} + +func finishEAX(tag []byte, cmac Digest) { + // Finish CMAC #2 and xor into tag. + sum := cmac.Sum(); + for i := range tag { + tag[i] ^= sum[i]; + } +} + +// Writer adapter. Tees writes into both w and cmac. +// Knows that cmac never returns write errors. +type cmacWriter struct { + w io.Writer; + cmac Digest; +} + +func (cw *cmacWriter) Write(p []byte) (n int, err os.Error) { + n, err = cw.w.Write(p); + cw.cmac.Write(p[0:n]); + return; +} + +// An eaxEncrypter implements the EAX encryption mode. +type eaxEncrypter struct { + ctr io.Writer; // CTR encrypter + cw cmacWriter; // CTR's output stream + tag []byte; +} + +// NewEAXEncrypter creates and returns a new EAX encrypter +// using the given cipher c, initialization vector iv, associated data hdr, +// and tag length tagBytes. The encrypter's Write method encrypts +// the data it receives and writes that data to w. +// The encrypter's Close method writes a final authenticating tag to w. +func NewEAXEncrypter(c Cipher, iv []byte, hdr []byte, tagBytes int, w io.Writer) io.WriteCloser { + x := new(eaxEncrypter); + + // Create new CTR instance writing to both + // w for encrypted output and cmac for digesting. + x.cw.w = w; + var ctrIV []byte; + ctrIV, x.tag, x.cw.cmac = setupEAX(c, iv, hdr, tagBytes); + x.ctr = NewCTRWriter(c, ctrIV, &x.cw); + return x; +} + +func (x *eaxEncrypter) Write(p []byte) (n int, err os.Error) { + return x.ctr.Write(p); +} + +func (x *eaxEncrypter) Close() os.Error { + x.ctr = nil; // crash if Write is called again + + // Write tag. + finishEAX(x.tag, x.cw.cmac); + n, err := x.cw.w.Write(x.tag); + if n != len(x.tag) && err == nil { + err = io.ErrShortWrite; + } + + return err; +} + +// Reader adapter. Returns data read from r but hangs +// on to the last len(tag) bytes for itself (returns EOF len(tag) +// bytes early). Also tees all data returned from Read into +// the cmac digest. The "don't return the last t bytes" +// and the "tee into digest" functionality could be separated, +// but the latter half is trivial. +type cmacReader struct { + r io.Reader; + cmac Digest; + tag []byte; + tmp []byte; +} + +func (cr *cmacReader) Read(p []byte) (n int, err os.Error) { + // TODO(rsc): Maybe fall back to simpler code if + // we recognize the underlying r as a ByteBuffer + // or ByteReader. Then we can just take the last piece + // off at the start. + + // First, read a tag-sized chunk. + // It's probably not the tag (unless there's no data). + tag := cr.tag; + if len(tag) < cap(tag) { + nt := len(tag); + nn, err1 := io.FullRead(cr.r, tag[nt:cap(tag)]); + tag = tag[0:nt+nn]; + cr.tag = tag; + if err1 != nil { + return 0, err1; + } + } + + tagBytes := len(tag); + if len(p) > 4*tagBytes { + // If p is big, try to read directly into p to avoid a copy. + n, err = cr.r.Read(p[tagBytes:len(p)]); + if n == 0 { + goto out; + } + // copy old tag into p + for i := 0; i < tagBytes; i++ { + p[i] = tag[i]; + } + // copy new tag out of p + for i := 0; i < tagBytes; i++ { + tag[i] = p[n+i]; + } + goto out; + } + + // Otherwise, read into p and then slide data + n, err = cr.r.Read(p); + if n == 0 { + goto out; + } + + // copy tag+p into p+tmp and then swap tmp, tag + tmp := cr.tmp; + for i := n + tagBytes - 1; i >= 0; i-- { + var c byte; + if i < tagBytes { + c = tag[i]; + } else { + c = p[i - tagBytes]; + } + if i < n { + p[i] = c; + } else { + tmp[i] = c; + } + } + cr.tmp, cr.tag = tag, tmp; + +out: + cr.cmac.Write(p[0:n]); + return; +} + +type eaxDecrypter struct { + ctr io.Reader; + cr cmacReader; + tag []byte; +} + +// NewEAXDecrypter creates and returns a new EAX decrypter +// using the given cipher c, initialization vector iv, associated data hdr, +// and tag length tagBytes. The encrypter's Read method decrypts and +// returns data read from r. At r's EOF, the encrypter checks the final +// authenticating tag and returns an EAXTagError if the tag is invalid. +// In that case, the message should be discarded. +// Note that the data stream returned from Read cannot be +// assumed to be valid, authenticated data until Read returns +// 0, nil to signal the end of the data. +func NewEAXDecrypter(c Cipher, iv []byte, hdr []byte, tagBytes int, r io.Reader) io.Reader { + x := new(eaxDecrypter); + + x.cr.r = r; + x.cr.tag = make([]byte, 0, tagBytes); + x.cr.tmp = make([]byte, 0, tagBytes); + var ctrIV []byte; + ctrIV, x.tag, x.cr.cmac = setupEAX(c, iv, hdr, tagBytes); + x.ctr = NewCTRReader(c, ctrIV, &x.cr); + return x; +} + +func (x *eaxDecrypter) checkTag() os.Error { + x.ctr = nil; // crash if Read is called again + + finishEAX(x.tag, x.cr.cmac); + if !same(x.tag, x.cr.tag) { + e := new(EAXTagError); + e.Computed = copy(x.tag); + e.Read = copy(x.cr.tag); + return e; + } + return nil; +} + +func (x *eaxDecrypter) Read(p []byte) (n int, err os.Error) { + n, err = x.ctr.Read(p); + if n == 0 && err == nil { + err = x.checkTag(); + } + return n, err; +} + diff --git a/src/pkg/crypto/block/eax_aes_test.go b/src/pkg/crypto/block/eax_aes_test.go new file mode 100644 index 000000000..f0453be80 --- /dev/null +++ b/src/pkg/crypto/block/eax_aes_test.go @@ -0,0 +1,239 @@ +// Copyright 2009 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 block + +import ( + "crypto/aes"; + "crypto/block"; + "fmt"; + "io"; + "testing"; +) + +// Test vectors from http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf + +type eaxAESTest struct { + msg []byte; + key []byte; + nonce []byte; + header []byte; + cipher []byte; +} + +var eaxAESTests = []eaxAESTest { + eaxAESTest { + []byte { + }, + []byte { + 0x23, 0x39, 0x52, 0xDE, 0xE4, 0xD5, 0xED, 0x5F, 0x9B, 0x9C, 0x6D, 0x6F, 0xF8, 0x0F, 0xF4, 0x78, + }, + []byte { + 0x62, 0xEC, 0x67, 0xF9, 0xC3, 0xA4, 0xA4, 0x07, 0xFC, 0xB2, 0xA8, 0xC4, 0x90, 0x31, 0xA8, 0xB3, + }, + []byte { + 0x6B, 0xFB, 0x91, 0x4F, 0xD0, 0x7E, 0xAE, 0x6B, + }, + []byte { + 0xE0, 0x37, 0x83, 0x0E, 0x83, 0x89, 0xF2, 0x7B, 0x02, 0x5A, 0x2D, 0x65, 0x27, 0xE7, 0x9D, 0x01, + }, + }, + eaxAESTest { + []byte { + 0xF7, 0xFB, + }, + []byte { + 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4, + }, + []byte { + 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD, + }, + []byte { + 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA, + }, + []byte { + 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, 0x67, 0xE5, + }, + }, + eaxAESTest { + []byte { + 0x1A, 0x47, 0xCB, 0x49, 0x33, + }, + []byte { + 0x01, 0xF7, 0x4A, 0xD6, 0x40, 0x77, 0xF2, 0xE7, 0x04, 0xC0, 0xF6, 0x0A, 0xDA, 0x3D, 0xD5, 0x23, + }, + []byte { + 0x70, 0xC3, 0xDB, 0x4F, 0x0D, 0x26, 0x36, 0x84, 0x00, 0xA1, 0x0E, 0xD0, 0x5D, 0x2B, 0xFF, 0x5E, + }, + []byte { + 0x23, 0x4A, 0x34, 0x63, 0xC1, 0x26, 0x4A, 0xC6, + }, + []byte { + 0xD8, 0x51, 0xD5, 0xBA, 0xE0, 0x3A, 0x59, 0xF2, 0x38, 0xA2, 0x3E, 0x39, 0x19, 0x9D, 0xC9, 0x26, 0x66, 0x26, 0xC4, 0x0F, 0x80, + }, + }, + eaxAESTest { + []byte { + 0x48, 0x1C, 0x9E, 0x39, 0xB1, + }, + []byte { + 0xD0, 0x7C, 0xF6, 0xCB, 0xB7, 0xF3, 0x13, 0xBD, 0xDE, 0x66, 0xB7, 0x27, 0xAF, 0xD3, 0xC5, 0xE8, + }, + []byte { + 0x84, 0x08, 0xDF, 0xFF, 0x3C, 0x1A, 0x2B, 0x12, 0x92, 0xDC, 0x19, 0x9E, 0x46, 0xB7, 0xD6, 0x17, + }, + []byte { + 0x33, 0xCC, 0xE2, 0xEA, 0xBF, 0xF5, 0xA7, 0x9D, + }, + []byte { + 0x63, 0x2A, 0x9D, 0x13, 0x1A, 0xD4, 0xC1, 0x68, 0xA4, 0x22, 0x5D, 0x8E, 0x1F, 0xF7, 0x55, 0x93, 0x99, 0x74, 0xA7, 0xBE, 0xDE, + }, + }, + eaxAESTest { + []byte { + 0x40, 0xD0, 0xC0, 0x7D, 0xA5, 0xE4, + }, + []byte { + 0x35, 0xB6, 0xD0, 0x58, 0x00, 0x05, 0xBB, 0xC1, 0x2B, 0x05, 0x87, 0x12, 0x45, 0x57, 0xD2, 0xC2, + }, + []byte { + 0xFD, 0xB6, 0xB0, 0x66, 0x76, 0xEE, 0xDC, 0x5C, 0x61, 0xD7, 0x42, 0x76, 0xE1, 0xF8, 0xE8, 0x16, + }, + []byte { + 0xAE, 0xB9, 0x6E, 0xAE, 0xBE, 0x29, 0x70, 0xE9, + }, + []byte { + 0x07, 0x1D, 0xFE, 0x16, 0xC6, 0x75, 0xCB, 0x06, 0x77, 0xE5, 0x36, 0xF7, 0x3A, 0xFE, 0x6A, 0x14, 0xB7, 0x4E, 0xE4, 0x98, 0x44, 0xDD, + }, + }, + eaxAESTest { + []byte { + 0x4D, 0xE3, 0xB3, 0x5C, 0x3F, 0xC0, 0x39, 0x24, 0x5B, 0xD1, 0xFB, 0x7D, + }, + []byte { + 0xBD, 0x8E, 0x6E, 0x11, 0x47, 0x5E, 0x60, 0xB2, 0x68, 0x78, 0x4C, 0x38, 0xC6, 0x2F, 0xEB, 0x22, + }, + []byte { + 0x6E, 0xAC, 0x5C, 0x93, 0x07, 0x2D, 0x8E, 0x85, 0x13, 0xF7, 0x50, 0x93, 0x5E, 0x46, 0xDA, 0x1B, + }, + []byte { + 0xD4, 0x48, 0x2D, 0x1C, 0xA7, 0x8D, 0xCE, 0x0F, + }, + []byte { + 0x83, 0x5B, 0xB4, 0xF1, 0x5D, 0x74, 0x3E, 0x35, 0x0E, 0x72, 0x84, 0x14, 0xAB, 0xB8, 0x64, 0x4F, 0xD6, 0xCC, 0xB8, 0x69, 0x47, 0xC5, 0xE1, 0x05, 0x90, 0x21, 0x0A, 0x4F, + }, + }, + eaxAESTest { + []byte { + 0x8B, 0x0A, 0x79, 0x30, 0x6C, 0x9C, 0xE7, 0xED, 0x99, 0xDA, 0xE4, 0xF8, 0x7F, 0x8D, 0xD6, 0x16, 0x36, + }, + []byte { + 0x7C, 0x77, 0xD6, 0xE8, 0x13, 0xBE, 0xD5, 0xAC, 0x98, 0xBA, 0xA4, 0x17, 0x47, 0x7A, 0x2E, 0x7D, + }, + []byte { + 0x1A, 0x8C, 0x98, 0xDC, 0xD7, 0x3D, 0x38, 0x39, 0x3B, 0x2B, 0xF1, 0x56, 0x9D, 0xEE, 0xFC, 0x19, + }, + []byte { + 0x65, 0xD2, 0x01, 0x79, 0x90, 0xD6, 0x25, 0x28, + }, + []byte { + 0x02, 0x08, 0x3E, 0x39, 0x79, 0xDA, 0x01, 0x48, 0x12, 0xF5, 0x9F, 0x11, 0xD5, 0x26, 0x30, 0xDA, 0x30, 0x13, 0x73, 0x27, 0xD1, 0x06, 0x49, 0xB0, 0xAA, 0x6E, 0x1C, 0x18, 0x1D, 0xB6, 0x17, 0xD7, 0xF2, + }, + }, + eaxAESTest { + []byte { + 0x1B, 0xDA, 0x12, 0x2B, 0xCE, 0x8A, 0x8D, 0xBA, 0xF1, 0x87, 0x7D, 0x96, 0x2B, 0x85, 0x92, 0xDD, 0x2D, 0x56, + }, + []byte { + 0x5F, 0xFF, 0x20, 0xCA, 0xFA, 0xB1, 0x19, 0xCA, 0x2F, 0xC7, 0x35, 0x49, 0xE2, 0x0F, 0x5B, 0x0D, + }, + []byte { + 0xDD, 0xE5, 0x9B, 0x97, 0xD7, 0x22, 0x15, 0x6D, 0x4D, 0x9A, 0xFF, 0x2B, 0xC7, 0x55, 0x98, 0x26, + }, + []byte { + 0x54, 0xB9, 0xF0, 0x4E, 0x6A, 0x09, 0x18, 0x9A, + }, + []byte { + 0x2E, 0xC4, 0x7B, 0x2C, 0x49, 0x54, 0xA4, 0x89, 0xAF, 0xC7, 0xBA, 0x48, 0x97, 0xED, 0xCD, 0xAE, 0x8C, 0xC3, 0x3B, 0x60, 0x45, 0x05, 0x99, 0xBD, 0x02, 0xC9, 0x63, 0x82, 0x90, 0x2A, 0xEF, 0x7F, 0x83, 0x2A, + }, + }, + eaxAESTest { + []byte { + 0x6C, 0xF3, 0x67, 0x20, 0x87, 0x2B, 0x85, 0x13, 0xF6, 0xEA, 0xB1, 0xA8, 0xA4, 0x44, 0x38, 0xD5, 0xEF, 0x11, + }, + []byte { + 0xA4, 0xA4, 0x78, 0x2B, 0xCF, 0xFD, 0x3E, 0xC5, 0xE7, 0xEF, 0x6D, 0x8C, 0x34, 0xA5, 0x61, 0x23, + }, + []byte { + 0xB7, 0x81, 0xFC, 0xF2, 0xF7, 0x5F, 0xA5, 0xA8, 0xDE, 0x97, 0xA9, 0xCA, 0x48, 0xE5, 0x22, 0xEC, + }, + []byte { + 0x89, 0x9A, 0x17, 0x58, 0x97, 0x56, 0x1D, 0x7E, + }, + []byte { + 0x0D, 0xE1, 0x8F, 0xD0, 0xFD, 0xD9, 0x1E, 0x7A, 0xF1, 0x9F, 0x1D, 0x8E, 0xE8, 0x73, 0x39, 0x38, 0xB1, 0xE8, 0xE7, 0xF6, 0xD2, 0x23, 0x16, 0x18, 0x10, 0x2F, 0xDB, 0x7F, 0xE5, 0x5F, 0xF1, 0x99, 0x17, 0x00, + }, + }, + eaxAESTest { + []byte { + 0xCA, 0x40, 0xD7, 0x44, 0x6E, 0x54, 0x5F, 0xFA, 0xED, 0x3B, 0xD1, 0x2A, 0x74, 0x0A, 0x65, 0x9F, 0xFB, 0xBB, 0x3C, 0xEA, 0xB7, + }, + []byte { + 0x83, 0x95, 0xFC, 0xF1, 0xE9, 0x5B, 0xEB, 0xD6, 0x97, 0xBD, 0x01, 0x0B, 0xC7, 0x66, 0xAA, 0xC3, + }, + []byte { + 0x22, 0xE7, 0xAD, 0xD9, 0x3C, 0xFC, 0x63, 0x93, 0xC5, 0x7E, 0xC0, 0xB3, 0xC1, 0x7D, 0x6B, 0x44, + }, + []byte { + 0x12, 0x67, 0x35, 0xFC, 0xC3, 0x20, 0xD2, 0x5A, + }, + []byte { + 0xCB, 0x89, 0x20, 0xF8, 0x7A, 0x6C, 0x75, 0xCF, 0xF3, 0x96, 0x27, 0xB5, 0x6E, 0x3E, 0xD1, 0x97, 0xC5, 0x52, 0xD2, 0x95, 0xA7, 0xCF, 0xC4, 0x6A, 0xFC, 0x25, 0x3B, 0x46, 0x52, 0xB1, 0xAF, 0x37, 0x95, 0xB1, 0x24, 0xAB, 0x6E, + }, + }, +} + +func TestEAXEncrypt_AES(t *testing.T) { + b := new(io.ByteBuffer); + for i, tt := range eaxAESTests { + test := fmt.Sprintf("test %d", i); + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Fatalf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err); + } + b.Reset(); + enc := NewEAXEncrypter(c, tt.nonce, tt.header, 16, b); + n, err := io.Copy(io.NewByteReader(tt.msg), enc); + if n != int64(len(tt.msg)) || err != nil { + t.Fatalf("%s: io.Copy into encrypter: %d, %s", test, n, err); + } + err = enc.Close(); + if err != nil { + t.Fatalf("%s: enc.Close: %s", test, err); + } + if d := b.Data(); !same(d, tt.cipher) { + t.Fatalf("%s: got %x want %x", test, d, tt.cipher); + } + } +} + +func TestEAXDecrypt_AES(t *testing.T) { + b := new(io.ByteBuffer); + for i, tt := range eaxAESTests { + test := fmt.Sprintf("test %d", i); + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Fatalf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err); + } + b.Reset(); + dec := NewEAXDecrypter(c, tt.nonce, tt.header, 16, io.NewByteReader(tt.cipher)); + n, err := io.Copy(dec, b); + if n != int64(len(tt.msg)) || err != nil { + t.Fatalf("%s: io.Copy into decrypter: %d, %s", test, n, err); + } + if d := b.Data(); !same(d, tt.msg) { + t.Fatalf("%s: got %x want %x", test, d, tt.msg); + } + } +} diff --git a/src/pkg/crypto/block/ecb.go b/src/pkg/crypto/block/ecb.go new file mode 100644 index 000000000..141d38cc8 --- /dev/null +++ b/src/pkg/crypto/block/ecb.go @@ -0,0 +1,271 @@ +// Copyright 2009 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. + +// Electronic codebook (ECB) mode. +// ECB is a fancy name for ``encrypt and decrypt each block separately.'' +// It's a pretty bad thing to do for any large amount of data (more than one block), +// because the individual blocks can still be identified, duplicated, and reordered. +// The ECB implementation exists mainly to provide buffering for +// the other modes, which wrap it by providing modified Ciphers. + +// See NIST SP 800-38A, pp 9-10 + +package block + +import ( + "crypto/block"; + "io"; + "os"; +) + +type ecbDecrypter struct { + c Cipher; + r io.Reader; + blockSize int; // block size + + // Buffered data. + // The buffer buf is used as storage for both + // plain or crypt; at least one of those is nil at any given time. + buf []byte; + plain []byte; // plain text waiting to be read + crypt []byte; // ciphertext waiting to be decrypted +} + +// Read into x.crypt until it has a full block or EOF or an error happens. +func (x *ecbDecrypter) fillCrypt() os.Error { + var err os.Error; + for len(x.crypt) < x.blockSize { + off := len(x.crypt); + var m int; + m, err = x.r.Read(x.crypt[off:x.blockSize]); + x.crypt = x.crypt[0:off+m]; + if m == 0 { + break; + } + + // If an error happened but we got enough + // data to do some decryption, we can decrypt + // first and report the error (with some data) later. + // But if we don't have enough to decrypt, + // have to stop now. + if err != nil && len(x.crypt) < x.blockSize { + break; + } + } + return err; +} + +// Read from plain text buffer into p. +func (x *ecbDecrypter) readPlain(p []byte) int { + n := len(x.plain); + if n > len(p) { + n = len(p); + } + for i := 0; i < n; i++ { + p[i] = x.plain[i]; + } + if n < len(x.plain) { + x.plain = x.plain[n:len(x.plain)]; + } else { + x.plain = nil; + } + return n; +} + +func (x *ecbDecrypter) Read(p []byte) (n int, err os.Error) { + if len(p) == 0 { + return; + } + + // If there's no plaintext waiting and p is not big enough + // to hold a whole cipher block, we'll have to work in the + // cipher text buffer. Set it to non-nil so that the + // code below will fill it. + if x.plain == nil && len(p) < x.blockSize && x.crypt == nil { + x.crypt = x.buf[0:0]; + } + + // If there is a leftover cipher text buffer, + // try to accumulate a full block. + if x.crypt != nil { + err = x.fillCrypt(); + if err != nil || len(x.crypt) == 0 { + return; + } + x.c.Decrypt(x.crypt, x.crypt); + x.plain = x.crypt; + x.crypt = nil; + } + + // If there is a leftover plain text buffer, read from it. + if x.plain != nil { + n = x.readPlain(p); + return; + } + + // Read and decrypt directly in caller's buffer. + n, err = io.ReadAtLeast(x.r, p, x.blockSize); + if err == io.ErrEOF && n == 0 { + // EOF is okay on block boundary + err = nil; + return; + } + var i int; + for i = 0; i+x.blockSize <= n; i += x.blockSize { + a := p[i:i+x.blockSize]; + x.c.Decrypt(a, a); + } + + // There might be an encrypted fringe remaining. + // Save it for next time. + if i < n { + p = p[i:n]; + for j, v := range p { + x.buf[j] = p[j]; + } + x.crypt = x.buf[0:len(p)]; + n = i; + } + + return; +} + +// NewECBDecrypter returns a reader that reads data from r and decrypts it using c. +// It decrypts by calling c.Decrypt on each block in sequence; +// this mode is known as electronic codebook mode, or ECB. +// The returned Reader does not buffer or read ahead except +// as required by the cipher's block size. +func NewECBDecrypter(c Cipher, r io.Reader) io.Reader { + x := new(ecbDecrypter); + x.c = c; + x.r = r; + x.blockSize = c.BlockSize(); + x.buf = make([]byte, x.blockSize); + return x; +} + +type ecbEncrypter struct { + c Cipher; + w io.Writer; + blockSize int; + + // Buffered data. + // The buffer buf is used as storage for both + // plain or crypt. If both are non-nil, plain + // follows crypt in buf. + buf []byte; + plain []byte; // plain text waiting to be encrypted + crypt []byte; // encrypted text waiting to be written +} + +// Flush the x.crypt buffer to x.w. +func (x *ecbEncrypter) flushCrypt() os.Error { + if len(x.crypt) == 0 { + return nil; + } + n, err := x.w.Write(x.crypt); + if n < len(x.crypt) { + x.crypt = x.crypt[n:len(x.crypt)]; + if err == nil { + err = io.ErrShortWrite; + } + } + if err != nil { + return err; + } + x.crypt = nil; + return nil; +} + +// Slide x.plain down to the beginning of x.buf. +// Plain is known to have less than one block of data, +// so this is cheap enough. +func (x *ecbEncrypter) slidePlain() { + if len(x.plain) == 0 { + x.plain = x.buf[0:0]; + } else if cap(x.plain) < cap(x.buf) { + // plain and buf share same data, + // but buf is before plain, so forward loop is correct + for i := 0; i < len(x.plain); i++ { + x.buf[i] = x.plain[i]; + } + x.plain = x.buf[0:len(x.plain)]; + } +} + +// Fill x.plain from the data in p. +// Return the number of bytes copied. +func (x *ecbEncrypter) fillPlain(p []byte) int { + off := len(x.plain); + n := len(p); + if max := cap(x.plain) - off; n > max { + n = max; + } + x.plain = x.plain[0:off+n]; + for i := 0; i < n; i++ { + x.plain[off + i] = p[i]; + } + return n; +} + +// Encrypt x.plain; record encrypted range as x.crypt. +func (x *ecbEncrypter) encrypt() { + var i int; + n := len(x.plain); + for i = 0; i+x.blockSize <= n; i += x.blockSize { + a := x.plain[i:i+x.blockSize]; + x.c.Encrypt(a, a); + } + x.crypt = x.plain[0:i]; + x.plain = x.plain[i:n]; +} + +func (x *ecbEncrypter) Write(p []byte) (n int, err os.Error) { + for { + // If there is data waiting to be written, write it. + // This can happen on the first iteration + // if a write failed in an earlier call. + if err = x.flushCrypt(); err != nil { + return; + } + + // Now that encrypted data is gone (flush ran), + // perhaps we need to slide the plaintext down. + x.slidePlain(); + + // Fill plaintext buffer from p. + m := x.fillPlain(p); + if m == 0 { + break; + } + n += m; + p = p[m:len(p)]; + + // Encrypt, adjusting crypt and plain. + x.encrypt(); + + // Write x.crypt. + if err = x.flushCrypt(); err != nil { + break; + } + } + return; +} + +// NewECBEncrypter returns a writer that encrypts data using c and writes it to w. +// It encrypts by calling c.Encrypt on each block in sequence; +// this mode is known as electronic codebook mode, or ECB. +// The returned Writer does no buffering except as required +// by the cipher's block size, so there is no need for a Flush method. +func NewECBEncrypter(c Cipher, w io.Writer) io.Writer { + x := new(ecbEncrypter); + x.c = c; + x.w = w; + x.blockSize = c.BlockSize(); + + // Create a buffer that is an integral number of blocks. + x.buf = make([]byte, 8192/x.blockSize * x.blockSize); + return x; +} + diff --git a/src/pkg/crypto/block/ecb_aes_test.go b/src/pkg/crypto/block/ecb_aes_test.go new file mode 100644 index 000000000..de8a624b9 --- /dev/null +++ b/src/pkg/crypto/block/ecb_aes_test.go @@ -0,0 +1,136 @@ +// Copyright 2009 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. + +// ECB AES test vectors. + +// See U.S. National Institute of Standards and Technology (NIST) +// Special Publication 800-38A, ``Recommendation for Block Cipher +// Modes of Operation,'' 2001 Edition, pp. 24-27. + +package block + +import ( + "crypto/aes"; + "crypto/block"; + "io"; + "os"; + "testing"; +) + +type ecbTest struct { + name string; + key []byte; + in []byte; + out []byte; +} + +var commonInput = []byte { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, +} + +var commonKey128 = []byte { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, +} + +var commonKey192 = []byte { + 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, + 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b, +} + +var commonKey256 = []byte { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4, +} + +var commonIV = []byte { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +} + +var ecbAESTests = []ecbTest { + // FIPS 197, Appendix B, C + ecbTest { + "FIPS-197 Appendix B", + commonKey128, + []byte { + 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34, + }, + []byte { + 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32, + } + }, + + // NIST SP 800-38A pp 24-27 + ecbTest { + "ECB-AES128", + commonKey128, + commonInput, + []byte { + 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97, + 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf, + 0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88, + 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4, + } + }, + ecbTest { + "ECB-AES192", + commonKey192, + commonInput, + []byte { + 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc, + 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad, 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef, + 0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e, + 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e, + } + }, + ecbTest { + "ECB-AES256", + commonKey256, + commonInput, + []byte { + 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8, + 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70, + 0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d, + 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7, + } + } +} + +func TestECB_AES(t *testing.T) { + for i, tt := range ecbAESTests { + test := tt.name; + + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err); + continue; + } + + var crypt io.ByteBuffer; + w := NewECBEncrypter(c, &crypt); + var r io.Reader = io.NewByteReader(tt.in); + n, err := io.Copy(r, w); + if n != int64(len(tt.in)) || err != nil { + t.Errorf("%s: ECBReader io.Copy = %d, %v want %d, nil", test, n, err, len(tt.in)); + } else if d := crypt.Data(); !same(tt.out, d) { + t.Errorf("%s: ECBReader\nhave %x\nwant %x", test, d, tt.out); + } + + var plain io.ByteBuffer; + r = NewECBDecrypter(c, io.NewByteReader(tt.out)); + w = &plain; + n, err = io.Copy(r, w); + if n != int64(len(tt.out)) || err != nil { + t.Errorf("%s: ECBWriter io.Copy = %d, %v want %d, nil", test, n, err, len(tt.out)); + } else if d := plain.Data(); !same(tt.in, d) { + t.Errorf("%s: ECBWriter\nhave %x\nwant %x", test, d, tt.in); + } + + if t.Failed() { + break; + } + } +} diff --git a/src/pkg/crypto/block/ecb_test.go b/src/pkg/crypto/block/ecb_test.go new file mode 100644 index 000000000..968893a9b --- /dev/null +++ b/src/pkg/crypto/block/ecb_test.go @@ -0,0 +1,183 @@ +// Copyright 2009 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 block + +import ( + "crypto/block"; + "fmt"; + "io"; + "testing"; + "testing/iotest"; +) + +// Simple Cipher for testing: adds an incrementing amount +// to each byte in each +type IncCipher struct { + blockSize int; + delta byte; + encrypting bool; +} + +func (c *IncCipher) BlockSize() int { + return c.blockSize; +} + +func (c *IncCipher) Encrypt(src, dst []byte) { + if !c.encrypting { + panicln("encrypt: not encrypting"); + } + if len(src) != c.blockSize || len(dst) != c.blockSize { + panicln("encrypt: wrong block size", c.blockSize, len(src), len(dst)); + } + c.delta++; + for i, b := range src { + dst[i] = b + c.delta; + } +} + +func (c *IncCipher) Decrypt(src, dst []byte) { + if c.encrypting { + panicln("decrypt: not decrypting"); + } + if len(src) != c.blockSize || len(dst) != c.blockSize { + panicln("decrypt: wrong block size", c.blockSize, len(src), len(dst)); + } + c.delta--; + for i, b := range src { + dst[i] = b + c.delta; + } +} + +func TestECBEncrypter(t *testing.T) { + var plain, crypt [256]byte; + for i := 0; i < len(plain); i++ { + plain[i] = byte(i); + } + b := new(io.ByteBuffer); + for block := 1; block <= 64; block *= 2 { + // compute encrypted version + delta := byte(0); + for i := 0; i < len(crypt); i++ { + if i % block == 0 { + delta++; + } + crypt[i] = plain[i] + delta; + } + + for frag := 0; frag < 2; frag++ { + c := &IncCipher{block, 0, true}; + b.Reset(); + r := io.NewByteReader(&plain); + w := NewECBEncrypter(c, b); + + // copy plain into w in increasingly large chunks: 1, 1, 2, 4, 8, ... + // if frag != 0, move the 1 to the end to cause fragmentation. + if frag == 0 { + nn, err := io.Copyn(r, w, 1); + if err != nil { + t.Errorf("block=%d frag=0: first Copyn: %s", block, err); + continue; + } + } + for n := 1; n <= len(plain)/2; n *= 2 { + nn, err := io.Copyn(r, w, int64(n)); + if err != nil { + t.Errorf("block=%d frag=%d: Copyn %d: %s", block, frag, n, err); + } + } + if frag != 0 { + nn, err := io.Copyn(r, w, 1); + if err != nil { + t.Errorf("block=%d frag=1: last Copyn: %s", block, err); + continue; + } + } + + // check output + data := b.Data(); + if len(data) != len(crypt) { + t.Errorf("block=%d frag=%d: want %d bytes, got %d", block, frag, len(crypt), len(data)); + continue; + } + + if string(data) != string(&crypt) { + t.Errorf("block=%d frag=%d: want %x got %x", block, frag, data, crypt); + } + } + } +} + +func testECBDecrypter(t *testing.T, maxio int) { + var readers = []func(io.Reader) io.Reader { + func (r io.Reader) io.Reader { return r }, + iotest.OneByteReader, + iotest.HalfReader, + }; + var plain, crypt [256]byte; + for i := 0; i < len(plain); i++ { + plain[i] = byte(255 - i); + } + b := new(io.ByteBuffer); + for block := 1; block <= 64 && block <= maxio; block *= 2 { + // compute encrypted version + delta := byte(0); + for i := 0; i < len(crypt); i++ { + if i % block == 0 { + delta++; + } + crypt[i] = plain[i] + delta; + } + + for mode := 0; mode < len(readers); mode++ { + for frag := 0; frag < 2; frag++ { + test := fmt.Sprintf("block=%d mode=%d frag=%d maxio=%d", block, mode, frag, maxio); + c := &IncCipher{block, 0, false}; + b.Reset(); + r := NewECBDecrypter(c, readers[mode](io.NewByteReader(crypt[0:maxio]))); + + // read from crypt in increasingly large chunks: 1, 1, 2, 4, 8, ... + // if frag == 1, move the 1 to the end to cause fragmentation. + if frag == 0 { + nn, err := io.Copyn(r, b, 1); + if err != nil { + t.Errorf("%s: first Copyn: %s", test, err); + continue; + } + } + for n := 1; n <= maxio/2; n *= 2 { + nn, err := io.Copyn(r, b, int64(n)); + if err != nil { + t.Errorf("%s: Copyn %d: %s", test, n, err); + } + } + if frag != 0 { + nn, err := io.Copyn(r, b, 1); + if err != nil { + t.Errorf("%s: last Copyn: %s", test, err); + continue; + } + } + + // check output + data := b.Data(); + if len(data) != maxio { + t.Errorf("%s: want %d bytes, got %d", test, maxio, len(data)); + continue; + } + + if string(data) != string(plain[0:maxio]) { + t.Errorf("%s: input=%x want %x got %x", test, crypt[0:maxio], plain[0:maxio], data); + } + } + } + } +} + +func TestECBDecrypter(t *testing.T) { + // Do shorter I/O sizes first; they're easier to debug. + for n := 1; n <= 256 && !t.Failed(); n *= 2 { + testECBDecrypter(t, n); + } +} diff --git a/src/pkg/crypto/block/ofb.go b/src/pkg/crypto/block/ofb.go new file mode 100644 index 000000000..084274a08 --- /dev/null +++ b/src/pkg/crypto/block/ofb.go @@ -0,0 +1,61 @@ +// Copyright 2009 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. + +// Output feedback (OFB) mode. + +// OFB converts a block cipher into a stream cipher by +// repeatedly encrypting an initialization vector and +// xoring the resulting stream of data with the input. + +// See NIST SP 800-38A, pp 13-15 + +package block + +import ( + "crypto/block"; + "io"; +) + +type ofbStream struct { + c Cipher; + iv []byte; +} + +func newOFBStream(c Cipher, iv []byte) *ofbStream { + x := new(ofbStream); + x.c = c; + n := len(iv); + if n != c.BlockSize() { + panicln("crypto/block: newOFBStream: invalid iv size", n, "!=", c.BlockSize()); + } + x.iv = copy(iv); + return x; +} + +func (x *ofbStream) Next() []byte { + x.c.Encrypt(x.iv, x.iv); + return x.iv; +} + +// NewOFBReader returns a reader that reads data from r, decrypts (or encrypts) +// it using c in output feedback (OFB) mode with the initialization vector iv. +// The returned Reader does not buffer and has no block size. +// In OFB mode, encryption and decryption are the same operation: +// an OFB reader applied to an encrypted stream produces a decrypted +// stream and vice versa. +func NewOFBReader(c Cipher, iv []byte, r io.Reader) io.Reader { + return newXorReader(newOFBStream(c, iv), r); +} + +// NewOFBWriter returns a writer that encrypts (or decrypts) data using c +// in cipher feedback (OFB) mode with the initialization vector iv +// and writes the encrypted data to w. +// The returned Writer does not buffer and has no block size. +// In OFB mode, encryption and decryption are the same operation: +// an OFB writer applied to an decrypted stream produces an encrypted +// stream and vice versa. +func NewOFBWriter(c Cipher, iv []byte, w io.Writer) io.Writer { + return newXorWriter(newOFBStream(c, iv), w); +} + diff --git a/src/pkg/crypto/block/ofb_aes_test.go b/src/pkg/crypto/block/ofb_aes_test.go new file mode 100644 index 000000000..3f5f9f482 --- /dev/null +++ b/src/pkg/crypto/block/ofb_aes_test.go @@ -0,0 +1,113 @@ +// Copyright 2009 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. + +// OFB AES test vectors. + +// See U.S. National Institute of Standards and Technology (NIST) +// Special Publication 800-38A, ``Recommendation for Block Cipher +// Modes of Operation,'' 2001 Edition, pp. 52-55. + +package block + +// gotest: $GC ecb_aes_test.go + +import ( + "crypto/aes"; + "crypto/block"; + "io"; + "os"; + "testing"; + + "./ecb_aes_test"; +) + +type ofbTest struct { + name string; + key []byte; + iv []byte; + in []byte; + out []byte; +} + +var ofbAESTests = []ofbTest { + // NIST SP 800-38A pp 52-55 + ofbTest { + "OFB-AES128", + commonKey128, + commonIV, + commonInput, + []byte { + 0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a, + 0x77, 0x89, 0x50, 0x8d, 0x16, 0x91, 0x8f, 0x03, 0xf5, 0x3c, 0x52, 0xda, 0xc5, 0x4e, 0xd8, 0x25, + 0x97, 0x40, 0x05, 0x1e, 0x9c, 0x5f, 0xec, 0xf6, 0x43, 0x44, 0xf7, 0xa8, 0x22, 0x60, 0xed, 0xcc, + 0x30, 0x4c, 0x65, 0x28, 0xf6, 0x59, 0xc7, 0x78, 0x66, 0xa5, 0x10, 0xd9, 0xc1, 0xd6, 0xae, 0x5e, + }, + }, + ofbTest { + "OFB-AES192", + commonKey192, + commonIV, + commonInput, + []byte { + 0xcd, 0xc8, 0x0d, 0x6f, 0xdd, 0xf1, 0x8c, 0xab, 0x34, 0xc2, 0x59, 0x09, 0xc9, 0x9a, 0x41, 0x74, + 0xfc, 0xc2, 0x8b, 0x8d, 0x4c, 0x63, 0x83, 0x7c, 0x09, 0xe8, 0x17, 0x00, 0xc1, 0x10, 0x04, 0x01, + 0x8d, 0x9a, 0x9a, 0xea, 0xc0, 0xf6, 0x59, 0x6f, 0x55, 0x9c, 0x6d, 0x4d, 0xaf, 0x59, 0xa5, 0xf2, + 0x6d, 0x9f, 0x20, 0x08, 0x57, 0xca, 0x6c, 0x3e, 0x9c, 0xac, 0x52, 0x4b, 0xd9, 0xac, 0xc9, 0x2a, + }, + }, + ofbTest { + "OFB-AES256", + commonKey256, + commonIV, + commonInput, + []byte { + 0xdc, 0x7e, 0x84, 0xbf, 0xda, 0x79, 0x16, 0x4b, 0x7e, 0xcd, 0x84, 0x86, 0x98, 0x5d, 0x38, 0x60, + 0x4f, 0xeb, 0xdc, 0x67, 0x40, 0xd2, 0x0b, 0x3a, 0xc8, 0x8f, 0x6a, 0xd8, 0x2a, 0x4f, 0xb0, 0x8d, + 0x71, 0xab, 0x47, 0xa0, 0x86, 0xe8, 0x6e, 0xed, 0xf3, 0x9d, 0x1c, 0x5b, 0xba, 0x97, 0xc4, 0x08, + 0x01, 0x26, 0x14, 0x1d, 0x67, 0xf3, 0x7b, 0xe8, 0x53, 0x8f, 0x5a, 0x8b, 0xe7, 0x40, 0xe4, 0x84, + } + }, +} + +func TestOFB_AES(t *testing.T) { + for i, tt := range ofbAESTests { + test := tt.name; + + c, err := aes.NewCipher(tt.key); + if err != nil { + t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err); + continue; + } + + for j := 0; j <= 5; j += 5 { + var crypt io.ByteBuffer; + in := tt.in[0:len(tt.in) - j]; + w := NewOFBWriter(c, tt.iv, &crypt); + var r io.Reader = io.NewByteReader(in); + n, err := io.Copy(r, w); + if n != int64(len(in)) || err != nil { + t.Errorf("%s/%d: OFBWriter io.Copy = %d, %v want %d, nil", test, len(in), n, err, len(in)); + } else if d, out := crypt.Data(), tt.out[0:len(in)]; !same(out, d) { + t.Errorf("%s/%d: OFBWriter\ninpt %x\nhave %x\nwant %x", test, len(in), in, d, out); + } + } + + for j := 0; j <= 7; j += 7 { + var plain io.ByteBuffer; + out := tt.out[0:len(tt.out) - j]; + r := NewOFBReader(c, tt.iv, io.NewByteReader(out)); + w := &plain; + n, err := io.Copy(r, w); + if n != int64(len(out)) || err != nil { + t.Errorf("%s/%d: OFBReader io.Copy = %d, %v want %d, nil", test, len(out), n, err, len(out)); + } else if d, in := plain.Data(), tt.in[0:len(out)]; !same(in, d) { + t.Errorf("%s/%d: OFBReader\nhave %x\nwant %x", test, len(out), d, in); + } + } + + if t.Failed() { + break; + } + } +} diff --git a/src/pkg/crypto/block/xor.go b/src/pkg/crypto/block/xor.go new file mode 100644 index 000000000..63229dbb4 --- /dev/null +++ b/src/pkg/crypto/block/xor.go @@ -0,0 +1,126 @@ +// Copyright 2009 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. + +// Encrypt/decrypt data by xor with a pseudo-random data stream. + +package block + +import ( + "crypto/block"; + "io"; + "os"; +) + +// A dataStream is an interface to an unending stream of data, +// used by XorReader and XorWriter to model a pseudo-random generator. +// Calls to Next() return sequential blocks of data from the stream. +// Each call must return at least one byte: there is no EOF. +type dataStream interface { + Next() []byte +} + +type xorReader struct { + r io.Reader; + rand dataStream; // pseudo-random + buf []byte; // data available from last call to rand +} + +func newXorReader(rand dataStream, r io.Reader) io.Reader { + x := new(xorReader); + x.r = r; + x.rand = rand; + return x; +} + +func (x *xorReader) Read(p []byte) (n int, err os.Error) { + n, err = x.r.Read(p); + + // xor input with stream. + bp := 0; + buf := x.buf; + for i := 0; i < n; i++ { + if bp >= len(buf) { + buf = x.rand.Next(); + bp = 0; + } + p[i] ^= buf[bp]; + bp++; + } + x.buf = buf[bp:len(buf)]; + return n, err; +} + +type xorWriter struct { + w io.Writer; + rand dataStream; // pseudo-random + buf []byte; // last buffer returned by rand + extra []byte; // extra random data (use before buf) + work []byte; // work space +} + +func newXorWriter(rand dataStream, w io.Writer) io.Writer { + x := new(xorWriter); + x.w = w; + x.rand = rand; + x.work = make([]byte, 4096); + return x; +} + +func (x *xorWriter) Write(p []byte) (n int, err os.Error) { + for len(p) > 0 { + // Determine next chunk of random data + // and xor with p into x.work. + var chunk []byte; + m := len(p); + if nn := len(x.extra); nn > 0 { + // extra points into work, so edit directly + if m > nn { + m = nn; + } + for i := 0; i < m; i++ { + x.extra[i] ^= p[i]; + } + chunk = x.extra[0:m]; + } else { + // xor p ^ buf into work, refreshing buf as needed + if nn := len(x.work); m > nn { + m = nn; + } + bp := 0; + buf := x.buf; + for i := 0; i < m; i++ { + if bp >= len(buf) { + buf = x.rand.Next(); + bp = 0; + } + x.work[i] = buf[bp] ^ p[i]; + bp++; + } + x.buf = buf[bp:len(buf)]; + chunk = x.work[0:m]; + } + + // Write chunk. + var nn int; + nn, err = x.w.Write(chunk); + if nn != len(chunk) && err == nil { + err = io.ErrShortWrite; + } + if nn < len(chunk) { + // Reconstruct the random bits from the unwritten + // data and save them for next time. + for i := nn; i < m; i++ { + chunk[i] ^= p[i]; + } + x.extra = chunk[nn:len(chunk)]; + } + n += nn; + if err != nil { + return; + } + p = p[m:len(p)]; + } + return; +} + diff --git a/src/pkg/crypto/block/xor_test.go b/src/pkg/crypto/block/xor_test.go new file mode 100644 index 000000000..6e6d1a3ce --- /dev/null +++ b/src/pkg/crypto/block/xor_test.go @@ -0,0 +1,169 @@ +// Copyright 2009 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 block + +import ( + "crypto/block"; + "fmt"; + "io"; + "testing"; + "testing/iotest"; +) + +// Simple "pseudo-random" stream for testing. +type incStream struct { + buf []byte; + n byte; +} + +func newIncStream(blockSize int) *incStream { + x := new(incStream); + x.buf = make([]byte, blockSize); + return x; +} + +func (x *incStream) Next() []byte { + x.n++; + for i := range x.buf { + x.buf[i] = x.n; + x.n++; + } + return x.buf; +} + +func testXorWriter(t *testing.T, maxio int) { + var plain, crypt [256]byte; + for i := 0; i < len(plain); i++ { + plain[i] = byte(i); + } + b := new(io.ByteBuffer); + for block := 1; block <= 64 && block <= maxio; block *= 2 { + // compute encrypted version + n := byte(0); + for i := 0; i < len(crypt); i++ { + if i % block == 0 { + n++; + } + crypt[i] = plain[i] ^ n; + n++; + } + + for frag := 0; frag < 2; frag++ { + test := fmt.Sprintf("block=%d frag=%d maxio=%d", block, frag, maxio); + b.Reset(); + r := io.NewByteReader(&plain); + s := newIncStream(block); + w := newXorWriter(s, b); + + // copy plain into w in increasingly large chunks: 1, 1, 2, 4, 8, ... + // if frag != 0, move the 1 to the end to cause fragmentation. + if frag == 0 { + nn, err := io.Copyn(r, w, 1); + if err != nil { + t.Errorf("%s: first Copyn: %s", test, err); + continue; + } + } + for n := 1; n <= len(plain)/2; n *= 2 { + nn, err := io.Copyn(r, w, int64(n)); + if err != nil { + t.Errorf("%s: Copyn %d: %s", test, n, err); + } + } + + // check output + crypt := crypt[0:len(crypt) - frag]; + data := b.Data(); + if len(data) != len(crypt) { + t.Errorf("%s: want %d bytes, got %d", test, len(crypt), len(data)); + continue; + } + + if string(data) != string(crypt) { + t.Errorf("%s: want %x got %x", test, data, crypt); + } + } + } +} + + +func TestXorWriter(t *testing.T) { + // Do shorter I/O sizes first; they're easier to debug. + for n := 1; n <= 256 && !t.Failed(); n *= 2 { + testXorWriter(t, n); + } +} + +func testXorReader(t *testing.T, maxio int) { + var readers = []func(io.Reader) io.Reader { + func (r io.Reader) io.Reader { return r }, + iotest.OneByteReader, + iotest.HalfReader, + }; + var plain, crypt [256]byte; + for i := 0; i < len(plain); i++ { + plain[i] = byte(255 - i); + } + b := new(io.ByteBuffer); + for block := 1; block <= 64 && block <= maxio; block *= 2 { + // compute encrypted version + n := byte(0); + for i := 0; i < len(crypt); i++ { + if i % block == 0 { + n++; + } + crypt[i] = plain[i] ^ n; + n++; + } + + for mode := 0; mode < len(readers); mode++ { + for frag := 0; frag < 2; frag++ { + test := fmt.Sprintf("block=%d mode=%d frag=%d maxio=%d", block, mode, frag, maxio); + s := newIncStream(block); + b.Reset(); + r := newXorReader(s, readers[mode](io.NewByteReader(crypt[0:maxio]))); + + // read from crypt in increasingly large chunks: 1, 1, 2, 4, 8, ... + // if frag == 1, move the 1 to the end to cause fragmentation. + if frag == 0 { + nn, err := io.Copyn(r, b, 1); + if err != nil { + t.Errorf("%s: first Copyn: %s", test, err); + continue; + } + } + for n := 1; n <= maxio/2; n *= 2 { + nn, err := io.Copyn(r, b, int64(n)); + if err != nil { + t.Errorf("%s: Copyn %d: %s", test, n, err); + } + } + + // check output + data := b.Data(); + crypt := crypt[0:maxio - frag]; + plain := plain[0:maxio - frag]; + if len(data) != len(plain) { + t.Errorf("%s: want %d bytes, got %d", test, len(plain), len(data)); + continue; + } + + if string(data) != string(plain) { + t.Errorf("%s: input=%x want %x got %x", test, crypt, plain, data); + } + } + } + } +} + +func TestXorReader(t *testing.T) { + // Do shorter I/O sizes first; they're easier to debug. + for n := 1; n <= 256 && !t.Failed(); n *= 2 { + testXorReader(t, n); + } +} + +// TODO(rsc): Test handling of writes after write errors. + diff --git a/src/pkg/crypto/hmac/Makefile b/src/pkg/crypto/hmac/Makefile new file mode 100644 index 000000000..1da3f58dd --- /dev/null +++ b/src/pkg/crypto/hmac/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/crypto/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + hmac.$O\ + + +phases: a1 +_obj$D/hmac.a: phases + +a1: $(O1) + $(AR) grc _obj$D/hmac.a hmac.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/hmac.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/hmac.a + +packages: _obj$D/hmac.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/hmac.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/hmac.a diff --git a/src/pkg/crypto/hmac/hmac.go b/src/pkg/crypto/hmac/hmac.go new file mode 100644 index 000000000..a3f47ccc9 --- /dev/null +++ b/src/pkg/crypto/hmac/hmac.go @@ -0,0 +1,104 @@ +// Copyright 2009 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. + +// The hmac package implements the Keyed-Hash Message Authentication Code (HMAC) +// as defined in U.S. Federal Information Processing Standards Publication 198. +// An HMAC is a cryptographic hash that uses a key to sign a message. +// The receiver verifies the hash by recomputing it using the same key. +package hmac + +import ( + "crypto/md5"; + "crypto/sha1"; + "hash"; + "os"; +) + +// FIPS 198: +// http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf + +// key is zero padded to 64 bytes +// ipad = 0x36 byte repeated to 64 bytes +// opad = 0x5c byte repeated to 64 bytes +// hmac = H([key ^ opad] H([key ^ ipad] text)) + +const ( + // NOTE(rsc): This constant is actually the + // underlying hash function's block size. + // HMAC is only conventionally used with + // MD5 and SHA1, and both use 64-byte blocks. + // The hash.Hash interface doesn't provide a + // way to find out the block size. + padSize = 64; +) + +type hmac struct { + size int; + key []byte; + tmp []byte; + inner hash.Hash; +} + +func (h *hmac) tmpPad(xor byte) { + for i, k := range h.key { + h.tmp[i] = xor ^ k; + } + for i := len(h.key); i < padSize; i++ { + h.tmp[i] = xor; + } +} + +func (h *hmac) Sum() []byte { + h.tmpPad(0x5c); + sum := h.inner.Sum(); + for i, b := range sum { + h.tmp[padSize + i] = b; + } + h.inner.Reset(); + h.inner.Write(h.tmp); + return h.inner.Sum(); +} + +func (h *hmac) Write(p []byte) (n int, err os.Error) { + return h.inner.Write(p); +} + +func (h *hmac) Size() int { + return h.size; +} + +func (h *hmac) Reset() { + h.inner.Reset(); + h.tmpPad(0x36); + h.inner.Write(h.tmp[0:padSize]); +} + +// New returns a new HMAC hash using the given hash and key. +func New(h hash.Hash, key []byte) hash.Hash { + if len(key) > padSize { + // If key is too big, hash it. + h.Write(key); + key = h.Sum(); + } + hm := new(hmac); + hm.inner = h; + hm.size = h.Size(); + hm.key = make([]byte, len(key)); + for i, k := range key { + hm.key[i] = k; + } + hm.tmp = make([]byte, padSize + hm.size); + hm.Reset(); + return hm; +} + +// NewMD5 returns a new HMAC-MD5 hash using the given key. +func NewMD5(key []byte) hash.Hash { + return New(md5.New(), key); +} + +// NewSHA1 returns a new HMAC-SHA1 hash using the given key. +func NewSHA1(key []byte) hash.Hash { + return New(sha1.New(), key); +} diff --git a/src/pkg/crypto/hmac/hmac_test.go b/src/pkg/crypto/hmac/hmac_test.go new file mode 100644 index 000000000..01e532d9f --- /dev/null +++ b/src/pkg/crypto/hmac/hmac_test.go @@ -0,0 +1,100 @@ +// Copyright 2009 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 hmac + +// TODO(rsc): better test + +import ( + "hash"; + "crypto/hmac"; + "io"; + "fmt"; + "testing"; +) + +type hmacTest struct { + hash func([]byte) hash.Hash; + key []byte; + in []byte; + out string; +} + +// Tests from US FIPS 198 +// http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf +var hmacTests = []hmacTest { + hmacTest { + NewSHA1, + []byte { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }, + io.StringBytes("Sample #1"), + "4f4ca3d5d68ba7cc0a1208c9c61e9c5da0403c0a", + }, + hmacTest { + NewSHA1, + []byte { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, + }, + io.StringBytes("Sample #2"), + "0922d3405faa3d194f82a45830737d5cc6c75d24", + }, + hmacTest { + NewSHA1, + []byte { + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, + }, + io.StringBytes("Sample #3"), + "bcf41eab8bb2d802f3d05caf7cb092ecf8d1a3aa", + }, + + // Test from Plan 9. + hmacTest { + NewMD5, + io.StringBytes("Jefe"), + io.StringBytes("what do ya want for nothing?"), + "750c783e6ab0b503eaa86e310a5db738", + } +} + +func TestHMAC(t *testing.T) { + for i, tt := range hmacTests { + h := tt.hash(tt.key); + for j := 0; j < 2; j++ { + n, err := h.Write(tt.in); + if n != len(tt.in) || err != nil { + t.Errorf("test %d.%d: Write(%d) = %d, %v", i, j, len(tt.in), n, err); + continue; + } + sum := fmt.Sprintf("%x", h.Sum()); + if sum != tt.out { + t.Errorf("test %d.%d: have %s want %s\n", i, j, sum, tt.out); + } + + // Second iteration: make sure reset works. + h.Reset(); + } + } +} diff --git a/src/pkg/crypto/md5/Makefile b/src/pkg/crypto/md5/Makefile new file mode 100644 index 000000000..b6c88d45a --- /dev/null +++ b/src/pkg/crypto/md5/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/crypto/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + md5.$O\ + +O2=\ + md5block.$O\ + + +phases: a1 a2 +_obj$D/md5.a: phases + +a1: $(O1) + $(AR) grc _obj$D/md5.a md5.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/md5.a md5block.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/md5.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/md5.a + +packages: _obj$D/md5.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/md5.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/md5.a diff --git a/src/pkg/crypto/md5/md5.go b/src/pkg/crypto/md5/md5.go new file mode 100644 index 000000000..cbc007f01 --- /dev/null +++ b/src/pkg/crypto/md5/md5.go @@ -0,0 +1,117 @@ +// Copyright 2009 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. + +// This package implements the MD5 hash algorithm as defined in RFC 1321. +package md5 + +import ( + "hash"; + "os"; +) + +// The size of an MD5 checksum in bytes. +const Size = 16; + +const ( + _Chunk = 64; + + _Init0 = 0x67452301; + _Init1 = 0xEFCDAB89; + _Init2 = 0x98BADCFE; + _Init3 = 0x10325476; +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + s [4]uint32; + x [_Chunk]byte; + nx int; + len uint64; +} + +func (d *digest) Reset() { + d.s[0] = _Init0; + d.s[1] = _Init1; + d.s[2] = _Init2; + d.s[3] = _Init3; + d.nx = 0; + d.len = 0; +} + +// New returns a Hash computing the SHA1 checksum. +func New() hash.Hash { + d := new(digest); + d.Reset(); + return d; +} + +func (d *digest) Size() int { + return Size; +} + +func _Block(dig *digest, p []byte) int + +func (d *digest) Write(p []byte) (nn int, err os.Error) { + nn = len(p); + d.len += uint64(nn); + if d.nx > 0 { + n := len(p); + if n > _Chunk-d.nx { + n = _Chunk-d.nx; + } + for i := 0; i < n; i++ { + d.x[d.nx+i] = p[i]; + } + d.nx += n; + if d.nx == _Chunk { + _Block(d, &d.x); + d.nx = 0; + } + p = p[n:len(p)]; + } + n := _Block(d, p); + p = p[n:len(p)]; + if len(p) > 0 { + for i := 0; i < len(p); i++ { + d.x[i] = p[i]; + } + d.nx = len(p); + } + return; +} + +func (d *digest) Sum() []byte { + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + len := d.len; + var tmp [64]byte; + tmp[0] = 0x80; + if len%64 < 56 { + d.Write(tmp[0:56-len%64]); + } else { + d.Write(tmp[0:64+56-len%64]); + } + + // Length in bits. + len <<= 3; + for i := uint(0); i < 8; i++ { + tmp[i] = byte(len>>(8*i)); + } + d.Write(tmp[0:8]); + + if d.nx != 0 { + panicln("oops"); + } + + p := make([]byte, 16); + j := 0; + for i := 0; i < 4; i++ { + s := d.s[i]; + p[j] = byte(s); j++; + p[j] = byte(s>>8); j++; + p[j] = byte(s>>16); j++; + p[j] = byte(s>>24); j++; + } + return p; +} + diff --git a/src/pkg/crypto/md5/md5_test.go b/src/pkg/crypto/md5/md5_test.go new file mode 100644 index 000000000..f610f1143 --- /dev/null +++ b/src/pkg/crypto/md5/md5_test.go @@ -0,0 +1,68 @@ +// Copyright 2009 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 md5 + +import ( + "fmt"; + "crypto/md5"; + "io"; + "testing"; +) + +type md5Test struct { + out string; + in string; +} + +var golden = []md5Test { + md5Test{ "d41d8cd98f00b204e9800998ecf8427e", "" }, + md5Test{ "0cc175b9c0f1b6a831c399e269772661", "a" }, + md5Test{ "187ef4436122d1cc2f40dc2b92f0eba0", "ab" }, + md5Test{ "900150983cd24fb0d6963f7d28e17f72", "abc" }, + md5Test{ "e2fc714c4727ee9395f324cd2e7f331f", "abcd" }, + md5Test{ "ab56b4d92b40713acc5af89985d4b786", "abcde" }, + md5Test{ "e80b5017098950fc58aad83c8c14978e", "abcdef" }, + md5Test{ "7ac66c0f148de9519b8bd264312c4d64", "abcdefg" }, + md5Test{ "e8dc4081b13434b45189a720b77b6818", "abcdefgh" }, + md5Test{ "8aa99b1f439ff71293e95357bac6fd94", "abcdefghi" }, + md5Test{ "a925576942e94b2ef57a066101b48876", "abcdefghij" }, + md5Test{ "d747fc1719c7eacb84058196cfe56d57", "Discard medicine more than two years old." }, + md5Test{ "bff2dcb37ef3a44ba43ab144768ca837", "He who has a shady past knows that nice guys finish last." }, + md5Test{ "0441015ecb54a7342d017ed1bcfdbea5", "I wouldn't marry him with a ten foot pole." }, + md5Test{ "9e3cac8e9e9757a60c3ea391130d3689", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave" }, + md5Test{ "a0f04459b031f916a59a35cc482dc039", "The days of the digital watch are numbered. -Tom Stoppard" }, + md5Test{ "e7a48e0fe884faf31475d2a04b1362cc", "Nepal premier won't resign." }, + md5Test{ "637d2fe925c07c113800509964fb0e06", "For every action there is an equal and opposite government program." }, + md5Test{ "834a8d18d5c6562119cf4c7f5086cb71", "His money is twice tainted: 'taint yours and 'taint mine." }, + md5Test{ "de3a4d2fd6c73ec2db2abad23b444281", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977" }, + md5Test{ "acf203f997e2cf74ea3aff86985aefaf", "It's a tiny change to the code and not completely disgusting. - Bob Manchek" }, + md5Test{ "e1c1384cb4d2221dfdd7c795a4222c9a", "size: a.out: bad magic" }, + md5Test{ "c90f3ddecc54f34228c063d7525bf644", "The major problem is with sendmail. -Mark Horton" }, + md5Test{ "cdf7ab6c1fd49bd9933c43f3ea5af185", "Give me a rock, paper and scissors and I will move the world. CCFestoon" }, + md5Test{ "83bc85234942fc883c063cbd7f0ad5d0", "If the enemy is within range, then so are you." }, + md5Test{ "277cbe255686b48dd7e8f389394d9299", "It's well we cannot hear the screams/That we create in others' dreams." }, + md5Test{ "fd3fb0a7ffb8af16603f3d3af98f8e1f", "You remind me of a TV show, but that's all right: I watch it anyway." }, + md5Test{ "469b13a78ebf297ecda64d4723655154", "C is as portable as Stonehedge!!" }, + md5Test{ "63eb3a2f466410104731c4b037600110", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley" }, + md5Test{ "72c2ed7592debca1c90fc0100f931a2f", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule" }, + md5Test{ "132f7619d33b523b1d9e5bd8e0928355", "How can you write a big system without C++? -Paul Glick" }, +} + +func TestGolden(t *testing.T) { + for i := 0; i < len(golden); i++ { + g := golden[i]; + c := New(); + for j := 0; j < 2; j++ { + io.WriteString(c, g.in); + s := fmt.Sprintf("%x", c.Sum()); + if s != g.out { + t.Errorf("md5[%d](%s) = %s want %s", j, g.in, s, g.out); + t.FailNow(); + } + c.Reset(); + } + } +} + diff --git a/src/pkg/crypto/md5/md5block.go b/src/pkg/crypto/md5/md5block.go new file mode 100644 index 000000000..2776c8795 --- /dev/null +++ b/src/pkg/crypto/md5/md5block.go @@ -0,0 +1,178 @@ +// Copyright 2009 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. + +// MD5 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +package md5 + +import "crypto/md5" + +// table[i] = int((1<<32) * abs(sin(i+1 radians))). +var table = []uint32 { + // round 1 + 0xd76aa478, + 0xe8c7b756, + 0x242070db, + 0xc1bdceee, + 0xf57c0faf, + 0x4787c62a, + 0xa8304613, + 0xfd469501, + 0x698098d8, + 0x8b44f7af, + 0xffff5bb1, + 0x895cd7be, + 0x6b901122, + 0xfd987193, + 0xa679438e, + 0x49b40821, + + // round 2 + 0xf61e2562, + 0xc040b340, + 0x265e5a51, + 0xe9b6c7aa, + 0xd62f105d, + 0x2441453, + 0xd8a1e681, + 0xe7d3fbc8, + 0x21e1cde6, + 0xc33707d6, + 0xf4d50d87, + 0x455a14ed, + 0xa9e3e905, + 0xfcefa3f8, + 0x676f02d9, + 0x8d2a4c8a, + + // round3 + 0xfffa3942, + 0x8771f681, + 0x6d9d6122, + 0xfde5380c, + 0xa4beea44, + 0x4bdecfa9, + 0xf6bb4b60, + 0xbebfbc70, + 0x289b7ec6, + 0xeaa127fa, + 0xd4ef3085, + 0x4881d05, + 0xd9d4d039, + 0xe6db99e5, + 0x1fa27cf8, + 0xc4ac5665, + + // round 4 + 0xf4292244, + 0x432aff97, + 0xab9423a7, + 0xfc93a039, + 0x655b59c3, + 0x8f0ccc92, + 0xffeff47d, + 0x85845dd1, + 0x6fa87e4f, + 0xfe2ce6e0, + 0xa3014314, + 0x4e0811a1, + 0xf7537e82, + 0xbd3af235, + 0x2ad7d2bb, + 0xeb86d391, +} + +var shift1 = []uint { 7, 12, 17, 22 }; +var shift2 = []uint { 5, 9, 14, 20 }; +var shift3 = []uint { 4, 11, 16, 23 }; +var shift4 = []uint { 6, 10, 15, 21 }; + +func _Block(dig *digest, p []byte) int { + a := dig.s[0]; + b := dig.s[1]; + c := dig.s[2]; + d := dig.s[3]; + n := 0; + var X [16]uint32; + for len(p) >= _Chunk { + aa, bb, cc, dd := a, b, c, d; + + for i := 0; i < 16; i++ { + j := i*4; + X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24; + } + + // If this needs to be made faster in the future, + // the usual trick is to unroll each of these + // loops by a factor of 4; that lets you replace + // the shift[] lookups with constants and, + // with suitable variable renaming in each + // unrolled body, delete the a, b, c, d = d, a, b, c + // (or you can let the optimizer do the renaming). + + // Round 1. + for i := 0; i < 16; i++ { + x := i; + t := i; + s := shift1[i%4]; + f := ((c ^ d) & b) ^ d; + a += f + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + a, b, c, d = d, a, b, c; + } + + // Round 2. + for i := 0; i < 16; i++ { + x := (1+5*i)%16; + t := 16+i; + s := shift2[i%4]; + g := ((b ^ c) & d) ^ c; + a += g + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + a, b, c, d = d, a, b, c; + } + + // Round 3. + for i := 0; i < 16; i++ { + x := (5+3*i)%16; + t := 32+i; + s := shift3[i%4]; + h := b ^ c ^ d; + a += h + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + a, b, c, d = d, a, b, c; + } + + // Round 4. + for i := 0; i < 16; i++ { + x := (7*i)%16; + s := shift4[i%4]; + t := 48+i; + ii := c ^ (b | ^d); + a += ii + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + a, b, c, d = d, a, b, c; + } + + a += aa; + b += bb; + c += cc; + d += dd; + + p = p[_Chunk:len(p)]; + n += _Chunk; + } + + dig.s[0] = a; + dig.s[1] = b; + dig.s[2] = c; + dig.s[3] = d; + return n; +} diff --git a/src/pkg/crypto/sha1/Makefile b/src/pkg/crypto/sha1/Makefile new file mode 100644 index 000000000..03ffe4fd7 --- /dev/null +++ b/src/pkg/crypto/sha1/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/crypto/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + sha1.$O\ + +O2=\ + sha1block.$O\ + + +phases: a1 a2 +_obj$D/sha1.a: phases + +a1: $(O1) + $(AR) grc _obj$D/sha1.a sha1.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/sha1.a sha1block.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/sha1.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/sha1.a + +packages: _obj$D/sha1.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/sha1.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/sha1.a diff --git a/src/pkg/crypto/sha1/sha1.go b/src/pkg/crypto/sha1/sha1.go new file mode 100644 index 000000000..a4cccd7a3 --- /dev/null +++ b/src/pkg/crypto/sha1/sha1.go @@ -0,0 +1,119 @@ +// Copyright 2009 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. + +// This package implements the SHA1 hash algorithm as defined in RFC 3174. +package sha1 + +import ( + "hash"; + "os"; +) + +// The size of a SHA1 checksum in bytes. +const Size = 20; + +const ( + _Chunk = 64; + + _Init0 = 0x67452301; + _Init1 = 0xEFCDAB89; + _Init2 = 0x98BADCFE; + _Init3 = 0x10325476; + _Init4 = 0xC3D2E1F0; +) + +// digest represents the partial evaluation of a checksum. +type digest struct { + h [5]uint32; + x [_Chunk]byte; + nx int; + len uint64; +} + +func (d *digest) Reset() { + d.h[0] = _Init0; + d.h[1] = _Init1; + d.h[2] = _Init2; + d.h[3] = _Init3; + d.h[4] = _Init4; + d.nx = 0; + d.len = 0; +} + +// New returns a Hash computing the SHA1 checksum. +func New() hash.Hash { + d := new(digest); + d.Reset(); + return d; +} + +func (d *digest) Size() int { + return Size; +} + +func _Block(dig *digest, p []byte) int + +func (d *digest) Write(p []byte) (nn int, err os.Error) { + nn = len(p); + d.len += uint64(nn); + if d.nx > 0 { + n := len(p); + if n > _Chunk-d.nx { + n = _Chunk-d.nx; + } + for i := 0; i < n; i++ { + d.x[d.nx+i] = p[i]; + } + d.nx += n; + if d.nx == _Chunk { + _Block(d, &d.x); + d.nx = 0; + } + p = p[n:len(p)]; + } + n := _Block(d, p); + p = p[n:len(p)]; + if len(p) > 0 { + for i := 0; i < len(p); i++ { + d.x[i] = p[i]; + } + d.nx = len(p); + } + return; +} + +func (d *digest) Sum() []byte { + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + len := d.len; + var tmp [64]byte; + tmp[0] = 0x80; + if len%64 < 56 { + d.Write(tmp[0:56-len%64]); + } else { + d.Write(tmp[0:64+56-len%64]); + } + + // Length in bits. + len <<= 3; + for i := uint(0); i < 8; i++ { + tmp[i] = byte(len>>(56-8*i)); + } + d.Write(tmp[0:8]); + + if d.nx != 0 { + panicln("oops"); + } + + p := make([]byte, 20); + j := 0; + for i := 0; i < 5; i++ { + s := d.h[i]; + p[j] = byte(s>>24); j++; + p[j] = byte(s>>16); j++; + p[j] = byte(s>>8); j++; + p[j] = byte(s); j++; + } + return p; +} + diff --git a/src/pkg/crypto/sha1/sha1_test.go b/src/pkg/crypto/sha1/sha1_test.go new file mode 100644 index 000000000..381cc76ee --- /dev/null +++ b/src/pkg/crypto/sha1/sha1_test.go @@ -0,0 +1,70 @@ +// Copyright 2009 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. + +// SHA1 hash algorithm. See RFC 3174. + +package sha1 + +import ( + "fmt"; + "crypto/sha1"; + "io"; + "testing"; +) + +type sha1Test struct { + out string; + in string; +} + +var golden = []sha1Test { + sha1Test{ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "" }, + sha1Test{ "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a" }, + sha1Test{ "da23614e02469a0d7c7bd1bdab5c9c474b1904dc", "ab" }, + sha1Test{ "a9993e364706816aba3e25717850c26c9cd0d89d", "abc" }, + sha1Test{ "81fe8bfe87576c3ecb22426f8e57847382917acf", "abcd" }, + sha1Test{ "03de6c570bfe24bfc328ccd7ca46b76eadaf4334", "abcde" }, + sha1Test{ "1f8ac10f23c5b5bc1167bda84b833e5c057a77d2", "abcdef" }, + sha1Test{ "2fb5e13419fc89246865e7a324f476ec624e8740", "abcdefg" }, + sha1Test{ "425af12a0743502b322e93a015bcf868e324d56a", "abcdefgh" }, + sha1Test{ "c63b19f1e4c8b5f76b25c49b8b87f57d8e4872a1", "abcdefghi" }, + sha1Test{ "d68c19a0a345b7eab78d5e11e991c026ec60db63", "abcdefghij" }, + sha1Test{ "ebf81ddcbe5bf13aaabdc4d65354fdf2044f38a7", "Discard medicine more than two years old." }, + sha1Test{ "e5dea09392dd886ca63531aaa00571dc07554bb6", "He who has a shady past knows that nice guys finish last." }, + sha1Test{ "45988f7234467b94e3e9494434c96ee3609d8f8f", "I wouldn't marry him with a ten foot pole." }, + sha1Test{ "55dee037eb7460d5a692d1ce11330b260e40c988", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave" }, + sha1Test{ "b7bc5fb91080c7de6b582ea281f8a396d7c0aee8", "The days of the digital watch are numbered. -Tom Stoppard" }, + sha1Test{ "c3aed9358f7c77f523afe86135f06b95b3999797", "Nepal premier won't resign." }, + sha1Test{ "6e29d302bf6e3a5e4305ff318d983197d6906bb9", "For every action there is an equal and opposite government program." }, + sha1Test{ "597f6a540010f94c15d71806a99a2c8710e747bd", "His money is twice tainted: 'taint yours and 'taint mine." }, + sha1Test{ "6859733b2590a8a091cecf50086febc5ceef1e80", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977" }, + sha1Test{ "514b2630ec089b8aee18795fc0cf1f4860cdacad", "It's a tiny change to the code and not completely disgusting. - Bob Manchek" }, + sha1Test{ "c5ca0d4a7b6676fc7aa72caa41cc3d5df567ed69", "size: a.out: bad magic" }, + sha1Test{ "74c51fa9a04eadc8c1bbeaa7fc442f834b90a00a", "The major problem is with sendmail. -Mark Horton" }, + sha1Test{ "0b4c4ce5f52c3ad2821852a8dc00217fa18b8b66", "Give me a rock, paper and scissors and I will move the world. CCFestoon" }, + sha1Test{ "3ae7937dd790315beb0f48330e8642237c61550a", "If the enemy is within range, then so are you." }, + sha1Test{ "410a2b296df92b9a47412b13281df8f830a9f44b", "It's well we cannot hear the screams/That we create in others' dreams." }, + sha1Test{ "841e7c85ca1adcddbdd0187f1289acb5c642f7f5", "You remind me of a TV show, but that's all right: I watch it anyway." }, + sha1Test{ "163173b825d03b952601376b25212df66763e1db", "C is as portable as Stonehedge!!" }, + sha1Test{ "32b0377f2687eb88e22106f133c586ab314d5279", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley" }, + sha1Test{ "0885aaf99b569542fd165fa44e322718f4a984e0", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule" }, + sha1Test{ "6627d6904d71420b0bf3886ab629623538689f45", "How can you write a big system without C++? -Paul Glick" }, +} + +func TestGolden(t *testing.T) { + for i := 0; i < len(golden); i++ { + g := golden[i]; + c := New(); + for j := 0; j < 2; j++ { + io.WriteString(c, g.in); + s := fmt.Sprintf("%x", c.Sum()); + if s != g.out { + t.Errorf("sha1[%d](%s) = %s want %s", j, g.in, s, g.out); + t.FailNow(); + } + c.Reset(); + } + } +} + diff --git a/src/pkg/crypto/sha1/sha1block.go b/src/pkg/crypto/sha1/sha1block.go new file mode 100644 index 000000000..01ddd9506 --- /dev/null +++ b/src/pkg/crypto/sha1/sha1block.go @@ -0,0 +1,86 @@ +// Copyright 2009 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. + +// SHA1 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +package sha1 + +import "crypto/sha1" + +const ( + _K0 = 0x5A827999; + _K1 = 0x6ED9EBA1; + _K2 = 0x8F1BBCDC; + _K3 = 0xCA62C1D6; +) + +func _Block(dig *digest, p []byte) int { + var w [80]uint32; + + n := 0; + h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]; + for len(p) >= _Chunk { + // Can interlace the computation of w with the + // rounds below if needed for speed. + for i := 0; i < 16; i++ { + j := i*4; + w[i] = uint32(p[j])<<24 | + uint32(p[j+1])<<16 | + uint32(p[j+2])<<8 | + uint32(p[j+3]); + } + for i := 16; i < 80; i++ { + tmp := w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; + w[i] = tmp<<1 | tmp>>(32-1); + } + + a, b, c, d, e := h0, h1, h2, h3, h4; + + // Each of the four 20-iteration rounds + // differs only in the computation of f and + // the choice of K (_K0, _K1, etc). + for i := 0; i < 20; i++ { + f := b&c | (^b)&d; + a5 := a<<5 | a>>(32-5); + b30 := b<<30 | b>>(32-30); + t := a5 + f + e + w[i] + _K0; + a, b, c, d, e = t, a, b30, c, d; + } + for i := 20; i < 40; i++ { + f := b ^ c ^ d; + a5 := a<<5 | a>>(32-5); + b30 := b<<30 | b>>(32-30); + t := a5 + f + e + w[i] + _K1; + a, b, c, d, e = t, a, b30, c, d; + } + for i := 40; i < 60; i++ { + f := b&c | b&d | c&d; + a5 := a<<5 | a>>(32-5); + b30 := b<<30 | b>>(32-30); + t := a5 + f + e + w[i] + _K2; + a, b, c, d, e = t, a, b30, c, d; + } + for i := 60; i < 80; i++ { + f := b ^ c ^ d; + a5 := a<<5 | a>>(32-5); + b30 := b<<30 | b>>(32-30); + t := a5 + f + e + w[i] + _K3; + a, b, c, d, e = t, a, b30, c, d; + } + + h0 += a; + h1 += b; + h2 += c; + h3 += d; + h4 += e; + + p = p[_Chunk:len(p)]; + n += _Chunk; + } + + dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4; + return n; +} diff --git a/src/pkg/datafmt/Makefile b/src/pkg/datafmt/Makefile new file mode 100644 index 000000000..1546faf7e --- /dev/null +++ b/src/pkg/datafmt/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + datafmt.$O\ + +O2=\ + parser.$O\ + + +phases: a1 a2 +_obj$D/datafmt.a: phases + +a1: $(O1) + $(AR) grc _obj$D/datafmt.a datafmt.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/datafmt.a parser.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/datafmt.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/datafmt.a + +packages: _obj$D/datafmt.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/datafmt.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/datafmt.a diff --git a/src/pkg/datafmt/datafmt.go b/src/pkg/datafmt/datafmt.go new file mode 100644 index 000000000..0aedbbbb0 --- /dev/null +++ b/src/pkg/datafmt/datafmt.go @@ -0,0 +1,789 @@ +// Copyright 2009 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. + +/* The datafmt package implements syntax-directed, type-driven formatting + of arbitrary data structures. Formatting a data structure consists of + two phases: first, a parser reads a format specification and builds a + "compiled" format. Then, the format can be applied repeatedly to + arbitrary values. Applying a format to a value evaluates to a []byte + containing the formatted value bytes, or nil. + + A format specification is a set of package declarations and format rules: + + Format = [ Entry { ";" Entry } [ ";" ] ] . + Entry = PackageDecl | FormatRule . + + (The syntax of a format specification is presented in the same EBNF + notation as used in the Go language specification. The syntax of white + space, comments, identifiers, and string literals is the same as in Go.) + + A package declaration binds a package name (such as 'ast') to a + package import path (such as '"go/ast"'). Each package used (in + a type name, see below) must be declared once before use. + + PackageDecl = PackageName ImportPath . + PackageName = identifier . + ImportPath = string . + + A format rule binds a rule name to a format expression. A rule name + may be a type name or one of the special names 'default' or '/'. + A type name may be the name of a predeclared type (for example, 'int', + 'float32', etc.), the package-qualified name of a user-defined type + (for example, 'ast.MapType'), or an identifier indicating the structure + of unnamed composite types ('array', 'chan', 'func', 'interface', 'map', + or 'ptr'). Each rule must have a unique name; rules can be declared in + any order. + + FormatRule = RuleName "=" Expression . + RuleName = TypeName | "default" | "/" . + TypeName = [ PackageName "." ] identifier . + + To format a value, the value's type name is used to select the format rule + (there is an override mechanism, see below). The format expression of the + selected rule specifies how the value is formatted. Each format expression, + when applied to a value, evaluates to a byte sequence or nil. + + In its most general form, a format expression is a list of alternatives, + each of which is a sequence of operands: + + Expression = [ Sequence ] { "|" [ Sequence ] } . + Sequence = Operand { Operand } . + + The formatted result produced by an expression is the result of the first + alternative sequence that evaluates to a non-nil result; if there is no + such alternative, the expression evaluates to nil. The result produced by + an operand sequence is the concatenation of the results of its operands. + If any operand in the sequence evaluates to nil, the entire sequence + evaluates to nil. + + There are five kinds of operands: + + Operand = Literal | Field | Group | Option | Repetition . + + Literals evaluate to themselves, with two substitutions. First, + %-formats expand in the manner of fmt.Printf, with the current value + passed as the parameter. Second, the current indentation (see below) + is inserted after every newline or form feed character. + + Literal = string . + + This table shows string literals applied to the value 42 and the + corresponding formatted result: + + "foo" foo + "%x" 2a + "x = %d" x = 42 + "%#x = %d" 0x2a = 42 + + A field operand is a field name optionally followed by an alternate + rule name. The field name may be an identifier or one of the special + names @ or *. + + Field = FieldName [ ":" RuleName ] . + FieldName = identifier | "@" | "*" . + + If the field name is an identifier, the current value must be a struct, + and there must be a field with that name in the struct. The same lookup + rules apply as in the Go language (for instance, the name of an anonymous + field is the unqualified type name). The field name denotes the field + value in the struct. If the field is not found, formatting is aborted + and an error message is returned. (TODO consider changing the semantics + such that if a field is not found, it evaluates to nil). + + The special name '@' denotes the current value. + + The meaning of the special name '*' depends on the type of the current + value: + + array, slice types array, slice element (inside {} only, see below) + interfaces value stored in interface + pointers value pointed to by pointer + + (Implementation restriction: channel, function and map types are not + supported due to missing reflection support). + + Fields are evaluated as follows: If the field value is nil, or an array + or slice element does not exist, the result is nil (see below for details + on array/slice elements). If the value is not nil the field value is + formatted (recursively) using the rule corresponding to its type name, + or the alternate rule name, if given. + + The following example shows a complete format specification for a + struct 'myPackage.Point'. Assume the package + + package myPackage // in directory myDir/myPackage + type Point struct { + name string; + x, y int; + } + + Applying the format specification + + myPackage "myDir/myPackage"; + int = "%d"; + hexInt = "0x%x"; + string = "---%s---"; + myPackage.Point = name "{" x ", " y:hexInt "}"; + + to the value myPackage.Point{"foo", 3, 15} results in + + ---foo---{3, 0xf} + + Finally, an operand may be a grouped, optional, or repeated expression. + A grouped expression ("group") groups a more complex expression (body) + so that it can be used in place of a single operand: + + Group = "(" [ Indentation ">>" ] Body ")" . + Indentation = Expression . + Body = Expression . + + A group body may be prefixed by an indentation expression followed by '>>'. + The indentation expression is applied to the current value like any other + expression and the result, if not nil, is appended to the current indentation + during the evaluation of the body (see also formatting state, below). + + An optional expression ("option") is enclosed in '[]' brackets. + + Option = "[" Body "]" . + + An option evaluates to its body, except that if the body evaluates to nil, + the option expression evaluates to an empty []byte. Thus an option's purpose + is to protect the expression containing the option from a nil operand. + + A repeated expression ("repetition") is enclosed in '{}' braces. + + Repetition = "{" Body [ "/" Separator ] "}" . + Separator = Expression . + + A repeated expression is evaluated as follows: The body is evaluated + repeatedly and its results are concatenated until the body evaluates + to nil. The result of the repetition is the (possibly empty) concatenation, + but it is never nil. An implicit index is supplied for the evaluation of + the body: that index is used to address elements of arrays or slices. If + the corresponding elements do not exist, the field denoting the element + evaluates to nil (which in turn may terminate the repetition). + + The body of a repetition may be followed by a '/' and a "separator" + expression. If the separator is present, it is invoked between repetitions + of the body. + + The following example shows a complete format specification for formatting + a slice of unnamed type. Applying the specification + + int = "%b"; + array = { * / ", " }; // array is the type name for an unnamed slice + + to the value '[]int{2, 3, 5, 7}' results in + + 10, 11, 101, 111 + + Default rule: If a format rule named 'default' is present, it is used for + formatting a value if no other rule was found. A common default rule is + + default = "%v" + + to provide default formatting for basic types without having to specify + a specific rule for each basic type. + + Global separator rule: If a format rule named '/' is present, it is + invoked with the current value between literals. If the separator + expression evaluates to nil, it is ignored. + + For instance, a global separator rule may be used to punctuate a sequence + of values with commas. The rules: + + default = "%v"; + / = ", "; + + will format an argument list by printing each one in its default format, + separated by a comma and a space. +*/ +package datafmt + +import ( + "container/vector"; + "fmt"; + "go/token"; + "io"; + "os"; + "reflect"; + "runtime"; + "strconv"; + "strings"; +) + + +// ---------------------------------------------------------------------------- +// Format representation + +type State struct + +// Custom formatters implement the Formatter function type. +// A formatter is invoked with the current formatting state, the +// value to format, and the rule name under which the formatter +// was installed (the same formatter function may be installed +// under different names). The formatter may access the current state +// to guide formatting and use State.Write to append to the state's +// output. +// +// A formatter must return a boolean value indicating if it evaluated +// to a non-nil value (true), or a nil value (false). +// +type Formatter func(state *State, value interface{}, ruleName string) bool + + +// A FormatterMap is a set of custom formatters. +// It maps a rule name to a formatter function. +// +type FormatterMap map [string] Formatter; + + +// A parsed format expression is built from the following nodes. +// +type ( + expr interface {}; + + alternatives []expr; // x | y | z + + sequence []expr; // x y z + + literal [][]byte; // a list of string segments, possibly starting with '%' + + field struct { + fieldName string; // including "@", "*" + ruleName string; // "" if no rule name specified + }; + + group struct { + indent, body expr; // (indent >> body) + }; + + option struct { + body expr; // [body] + }; + + repetition struct { + body, separator expr; // {body / separator} + }; + + custom struct { + ruleName string; + fun Formatter + }; +) + + +// A Format is the result of parsing a format specification. +// The format may be applied repeatedly to format values. +// +type Format map [string] expr; + + +// ---------------------------------------------------------------------------- +// Formatting + +// An application-specific environment may be provided to Format.Apply; +// the environment is available inside custom formatters via State.Env(). +// Environments must implement copying; the Copy method must return an +// complete copy of the receiver. This is necessary so that the formatter +// can save and restore an environment (in case of an absent expression). +// +// If the Environment doesn't change during formatting (this is under +// control of the custom formatters), the Copy function can simply return +// the receiver, and thus can be very light-weight. +// +type Environment interface { + Copy() Environment +} + + +// State represents the current formatting state. +// It is provided as argument to custom formatters. +// +type State struct { + fmt Format; // format in use + env Environment; // user-supplied environment + errors chan os.Error; // not chan *Error (errors <- nil would be wrong!) + hasOutput bool; // true after the first literal has been written + indent io.ByteBuffer; // current indentation + output io.ByteBuffer; // format output + linePos token.Position; // position of line beginning (Column == 0) + default_ expr; // possibly nil + separator expr; // possibly nil +} + + +func newState(fmt Format, env Environment, errors chan os.Error) *State { + s := new(State); + s.fmt = fmt; + s.env = env; + s.errors = errors; + s.linePos = token.Position{Line: 1}; + + // if we have a default rule, cache it's expression for fast access + if x, found := fmt["default"]; found { + s.default_ = x; + } + + // if we have a global separator rule, cache it's expression for fast access + if x, found := fmt["/"]; found { + s.separator = x; + } + + return s; +} + + +// Env returns the environment passed to Format.Apply. +func (s *State) Env() interface{} { + return s.env; +} + + +// LinePos returns the position of the current line beginning +// in the state's output buffer. Line numbers start at 1. +// +func (s *State) LinePos() token.Position { + return s.linePos; +} + + +// Pos returns the position of the next byte to be written to the +// output buffer. Line numbers start at 1. +// +func (s *State) Pos() token.Position { + offs := s.output.Len(); + return token.Position{Line: s.linePos.Line, Column: offs - s.linePos.Offset, Offset: offs}; +} + + +// Write writes data to the output buffer, inserting the indentation +// string after each newline or form feed character. It cannot return an error. +// +func (s *State) Write(data []byte) (int, os.Error) { + n := 0; + i0 := 0; + for i, ch := range data { + if ch == '\n' || ch == '\f' { + // write text segment and indentation + n1, _ := s.output.Write(data[i0 : i+1]); + n2, _ := s.output.Write(s.indent.Data()); + n += n1 + n2; + i0 = i + 1; + s.linePos.Offset = s.output.Len(); + s.linePos.Line++; + } + } + n3, _ := s.output.Write(data[i0 : len(data)]); + return n + n3, nil; +} + + +type checkpoint struct { + env Environment; + hasOutput bool; + outputLen int; + linePos token.Position; +} + + +func (s *State) save() checkpoint { + saved := checkpoint{nil, s.hasOutput, s.output.Len(), s.linePos}; + if s.env != nil { + saved.env = s.env.Copy(); + } + return saved; +} + + +func (s *State) restore(m checkpoint) { + s.env = m.env; + s.output.Truncate(m.outputLen); +} + + +func (s *State) error(msg string) { + s.errors <- os.NewError(msg); + runtime.Goexit(); +} + + +// getField searches in val, which must be a struct, for a field +// with the given name. It returns the value and the embedded depth +// where it was found. +// +func getField(val reflect.Value, fieldname string) (reflect.Value, int) { + // do we have a struct in the first place? + if val.Kind() != reflect.StructKind { + return nil, 0; + } + + sval, styp := val.(reflect.StructValue), val.Type().(reflect.StructType); + + // look for field at the top level + for i := 0; i < styp.Len(); i++ { + name, typ, tag, offset := styp.Field(i); + if name == fieldname || name == "" && strings.HasSuffix(typ.Name(), "." + fieldname) /* anonymous field */ { + return sval.Field(i), 0; + } + } + + // look for field in anonymous fields + var field reflect.Value; + level := 1000; // infinity (no struct has that many levels) + for i := 0; i < styp.Len(); i++ { + name, typ, tag, offset := styp.Field(i); + if name == "" { + f, l := getField(sval.Field(i), fieldname); + // keep the most shallow field + if f != nil { + switch { + case l < level: + field, level = f, l; + case l == level: + // more than one field at the same level, + // possibly an error unless there is a more + // shallow field found later + field = nil; + } + } + } + } + + return field, level + 1; +} + + +// TODO At the moment, unnamed types are simply mapped to the default +// names below. For instance, all unnamed arrays are mapped to +// 'array' which is not really sufficient. Eventually one may want +// to be able to specify rules for say an unnamed slice of T. +// +var defaultNames = map[int]string { + reflect.ArrayKind: "array", + reflect.BoolKind: "bool", + reflect.ChanKind: "chan", + reflect.DotDotDotKind: "ellipsis", + reflect.FloatKind: "float", + reflect.Float32Kind: "float32", + reflect.Float64Kind: "float64", + reflect.FuncKind: "func", + reflect.IntKind: "int", + reflect.Int16Kind: "int16", + reflect.Int32Kind: "int32", + reflect.Int64Kind: "int64", + reflect.Int8Kind: "int8", + reflect.InterfaceKind: "interface", + reflect.MapKind: "map", + reflect.PtrKind: "ptr", + reflect.StringKind: "string", + reflect.StructKind: "struct", + reflect.UintKind: "uint", + reflect.Uint16Kind: "uint16", + reflect.Uint32Kind: "uint32", + reflect.Uint64Kind: "uint64", + reflect.Uint8Kind: "uint8", + reflect.UintptrKind: "uintptr", +} + + +func typename(value reflect.Value) string { + name := value.Type().Name(); + if name == "" { + if defaultName, found := defaultNames[value.Kind()]; found { + name = defaultName; + } + } + return name; +} + + +func (s *State) getFormat(name string) expr { + if fexpr, found := s.fmt[name]; found { + return fexpr; + } + + if s.default_ != nil { + return s.default_; + } + + s.error(fmt.Sprintf("no format rule for type: '%s'", name)); + return nil; +} + + +// eval applies a format expression fexpr to a value. If the expression +// evaluates internally to a non-nil []byte, that slice is appended to +// the state's output buffer and eval returns true. Otherwise, eval +// returns false and the state remains unchanged. +// +func (s *State) eval(fexpr expr, value reflect.Value, index int) bool { + // an empty format expression always evaluates + // to a non-nil (but empty) []byte + if fexpr == nil { + return true; + } + + switch t := fexpr.(type) { + case alternatives: + // append the result of the first alternative that evaluates to + // a non-nil []byte to the state's output + mark := s.save(); + for _, x := range t { + if s.eval(x, value, index) { + return true; + } + s.restore(mark); + } + return false; + + case sequence: + // append the result of all operands to the state's output + // unless a nil result is encountered + mark := s.save(); + for _, x := range t { + if !s.eval(x, value, index) { + s.restore(mark); + return false; + } + } + return true; + + case literal: + // write separator, if any + if s.hasOutput { + // not the first literal + if s.separator != nil { + sep := s.separator; // save current separator + s.separator = nil; // and disable it (avoid recursion) + mark := s.save(); + if !s.eval(sep, value, index) { + s.restore(mark); + } + s.separator = sep; // enable it again + } + } + s.hasOutput = true; + // write literal segments + for _, lit := range t { + if len(lit) > 1 && lit[0] == '%' { + // segment contains a %-format at the beginning + if lit[1] == '%' { + // "%%" is printed as a single "%" + s.Write(lit[1 : len(lit)]); + } else { + // use s instead of s.output to get indentation right + fmt.Fprintf(s, string(lit), value.Interface()); + } + } else { + // segment contains no %-formats + s.Write(lit); + } + } + return true; // a literal never evaluates to nil + + case *field: + // determine field value + switch t.fieldName { + case "@": + // field value is current value + + case "*": + // indirection: operation is type-specific + switch v := value.(type) { + case reflect.ArrayValue: + if v.IsNil() || v.Len() <= index { + return false; + } + value = v.Elem(index); + + case reflect.MapValue: + s.error("reflection support for maps incomplete"); + + case reflect.PtrValue: + if v.IsNil() { + return false; + } + value = v.Sub(); + + case reflect.InterfaceValue: + if v.IsNil() { + return false; + } + value = v.Value(); + + case reflect.ChanValue: + s.error("reflection support for chans incomplete"); + + case reflect.FuncValue: + s.error("reflection support for funcs incomplete"); + + default: + s.error(fmt.Sprintf("error: * does not apply to `%s`", value.Type().Name())); + } + + default: + // value is value of named field + field, _ := getField(value, t.fieldName); + if field == nil { + // TODO consider just returning false in this case + s.error(fmt.Sprintf("error: no field `%s` in `%s`", t.fieldName, value.Type().Name())); + } + value = field; + } + + // determine rule + ruleName := t.ruleName; + if ruleName == "" { + // no alternate rule name, value type determines rule + ruleName = typename(value) + } + fexpr = s.getFormat(ruleName); + + mark := s.save(); + if !s.eval(fexpr, value, index) { + s.restore(mark); + return false; + } + return true; + + case *group: + // remember current indentation + indentLen := s.indent.Len(); + + // update current indentation + mark := s.save(); + s.eval(t.indent, value, index); + // if the indentation evaluates to nil, the state's output buffer + // didn't change - either way it's ok to append the difference to + // the current identation + s.indent.Write(s.output.Data()[mark.outputLen : s.output.Len()]); + s.restore(mark); + + // format group body + mark = s.save(); + b := true; + if !s.eval(t.body, value, index) { + s.restore(mark); + b = false; + } + + // reset indentation + s.indent.Truncate(indentLen); + return b; + + case *option: + // evaluate the body and append the result to the state's output + // buffer unless the result is nil + mark := s.save(); + if !s.eval(t.body, value, 0) { // TODO is 0 index correct? + s.restore(mark); + } + return true; // an option never evaluates to nil + + case *repetition: + // evaluate the body and append the result to the state's output + // buffer until a result is nil + for i := 0; ; i++ { + mark := s.save(); + // write separator, if any + if i > 0 && t.separator != nil { + // nil result from separator is ignored + mark := s.save(); + if !s.eval(t.separator, value, i) { + s.restore(mark); + } + } + if !s.eval(t.body, value, i) { + s.restore(mark); + break; + } + } + return true; // a repetition never evaluates to nil + + case *custom: + // invoke the custom formatter to obtain the result + mark := s.save(); + if !t.fun(s, value.Interface(), t.ruleName) { + s.restore(mark); + return false; + } + return true; + } + + panic("unreachable"); + return false; +} + + +// Eval formats each argument according to the format +// f and returns the resulting []byte and os.Error. If +// an error occured, the []byte contains the partially +// formatted result. An environment env may be passed +// in which is available in custom formatters through +// the state parameter. +// +func (f Format) Eval(env Environment, args ...) ([]byte, os.Error) { + if f == nil { + return nil, os.NewError("format is nil"); + } + + errors := make(chan os.Error); + s := newState(f, env, errors); + + go func() { + value := reflect.NewValue(args).(reflect.StructValue); + for i := 0; i < value.Len(); i++ { + fld := value.Field(i); + mark := s.save(); + if !s.eval(s.getFormat(typename(fld)), fld, 0) { // TODO is 0 index correct? + s.restore(mark); + } + } + errors <- nil; // no errors + }(); + + return s.output.Data(), <- errors; +} + + +// ---------------------------------------------------------------------------- +// Convenience functions + +// Fprint formats each argument according to the format f +// and writes to w. The result is the total number of bytes +// written and an os.Error, if any. +// +func (f Format) Fprint(w io.Writer, env Environment, args ...) (int, os.Error) { + data, err := f.Eval(env, args); + if err != nil { + // TODO should we print partial result in case of error? + return 0, err; + } + return w.Write(data); +} + + +// Print formats each argument according to the format f +// and writes to standard output. The result is the total +// number of bytes written and an os.Error, if any. +// +func (f Format) Print(args ...) (int, os.Error) { + return f.Fprint(os.Stdout, nil, args); +} + + +// Sprint formats each argument according to the format f +// and returns the resulting string. If an error occurs +// during formatting, the result string contains the +// partially formatted result followed by an error message. +// +func (f Format) Sprint(args ...) string { + var buf io.ByteBuffer; + n, err := f.Fprint(&buf, nil, args); + if err != nil { + fmt.Fprintf(&buf, "--- Sprint(%s) failed: %v", fmt.Sprint(args), err); + } + return string(buf.Data()); +} diff --git a/src/pkg/datafmt/datafmt_test.go b/src/pkg/datafmt/datafmt_test.go new file mode 100644 index 000000000..788c013c6 --- /dev/null +++ b/src/pkg/datafmt/datafmt_test.go @@ -0,0 +1,375 @@ +// Copyright 2009 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 datafmt + +import ( + "fmt"; + "datafmt"; + "io"; + "os"; + "testing"; +) + + +func parse(t *testing.T, form string, fmap FormatterMap) Format { + f, err := Parse(io.StringBytes(form), fmap); + if err != nil { + t.Errorf("Parse(%s): %v", form, err); + return nil; + } + return f; +} + + +func verify(t *testing.T, f Format, expected string, args ...) { + if f == nil { + return; // allow other tests to run + } + result := f.Sprint(args); + if result != expected { + t.Errorf( + "result : `%s`\nexpected: `%s`\n\n", + result, expected + ) + } +} + + +func formatter(s *State, value interface{}, rule_name string) bool { + switch rule_name { + case "/": + fmt.Fprintf(s, "%d %d %d", s.Pos().Line, s.LinePos().Column, s.Pos().Column); + return true; + case "blank": + s.Write([]byte{' '}); + return true; + case "int": + if value.(int) & 1 == 0 { + fmt.Fprint(s, "even "); + } else { + fmt.Fprint(s, "odd "); + } + return true; + case "nil": + return false; + case "testing.T": + s.Write(io.StringBytes("testing.T")); + return true; + } + panic("unreachable"); + return false; +} + + +func TestCustomFormatters(t *testing.T) { + fmap0 := FormatterMap{ "/": formatter }; + fmap1 := FormatterMap{ "int": formatter, "blank": formatter, "nil": formatter }; + fmap2 := FormatterMap{ "testing.T": formatter }; + + f := parse(t, `int=`, fmap0); + verify(t, f, ``, 1, 2, 3); + + f = parse(t, `int="#"`, nil); + verify(t, f, `###`, 1, 2, 3); + + f = parse(t, `int="#";string="%s"`, fmap0); + verify(t, f, "#1 0 1#1 0 7#1 0 13\n2 0 0foo2 0 8\n", 1, 2, 3, "\n", "foo", "\n"); + + f = parse(t, ``, fmap1); + verify(t, f, `even odd even odd `, 0, 1, 2, 3); + + f = parse(t, `/ =@:blank; float="#"`, fmap1); + verify(t, f, `# # #`, 0.0, 1.0, 2.0); + + f = parse(t, `float=@:nil`, fmap1); + verify(t, f, ``, 0.0, 1.0, 2.0); + + f = parse(t, `testing "testing"; ptr=*`, fmap2); + verify(t, f, `testing.T`, t); + + // TODO needs more tests +} + + +// ---------------------------------------------------------------------------- +// Formatting of basic and simple composite types + +func check(t *testing.T, form, expected string, args ...) { + f := parse(t, form, nil); + if f == nil { + return; // allow other tests to run + } + result := f.Sprint(args); + if result != expected { + t.Errorf( + "format : %s\nresult : `%s`\nexpected: `%s`\n\n", + form, result, expected + ) + } +} + + +func TestBasicTypes(t *testing.T) { + check(t, ``, ``); + check(t, `bool=":%v"`, `:true:false`, true, false); + check(t, `int="%b %d %o 0x%x"`, `101010 42 52 0x2a`, 42); + + check(t, `int="%"`, `%`, 42); + check(t, `int="%%"`, `%`, 42); + check(t, `int="**%%**"`, `**%**`, 42); + check(t, `int="%%%%%%"`, `%%%`, 42); + check(t, `int="%%%d%%"`, `%42%`, 42); + + const i = -42; + const is = `-42`; + check(t, `int ="%d"`, is, i); + check(t, `int8 ="%d"`, is, int8(i)); + check(t, `int16="%d"`, is, int16(i)); + check(t, `int32="%d"`, is, int32(i)); + check(t, `int64="%d"`, is, int64(i)); + + const u = 42; + const us = `42`; + check(t, `uint ="%d"`, us, uint(u)); + check(t, `uint8 ="%d"`, us, uint8(u)); + check(t, `uint16="%d"`, us, uint16(u)); + check(t, `uint32="%d"`, us, uint32(u)); + check(t, `uint64="%d"`, us, uint64(u)); + + const f = 3.141592; + const fs = `3.141592`; + check(t, `float ="%g"`, fs, f); + check(t, `float32="%g"`, fs, float32(f)); + check(t, `float64="%g"`, fs, float64(f)); +} + + +func TestArrayTypes(t *testing.T) { + var a0 [10]int; + check(t, `array="array";`, `array`, a0); + + a1 := [...]int{1, 2, 3}; + check(t, `array="array";`, `array`, a1); + check(t, `array={*}; int="%d";`, `123`, a1); + check(t, `array={* / ", "}; int="%d";`, `1, 2, 3`, a1); + check(t, `array={* / *}; int="%d";`, `12233`, a1); + + a2 := []interface{}{42, "foo", 3.14}; + check(t, `array={* / ", "}; interface=*; string="bar"; default="%v";`, `42, bar, 3.14`, a2); +} + + +func TestChanTypes(t *testing.T) { + var c0 chan int; + check(t, `chan="chan"`, `chan`, c0); + + c1 := make(chan int); + go func(){ c1 <- 42 }(); + check(t, `chan="chan"`, `chan`, c1); + // check(t, `chan=*`, `42`, c1); // reflection support for chans incomplete +} + + +func TestFuncTypes(t *testing.T) { + var f0 func() int; + check(t, `func="func"`, `func`, f0); + + f1 := func() int { return 42; }; + check(t, `func="func"`, `func`, f1); + // check(t, `func=*`, `42`, f1); // reflection support for funcs incomplete +} + + +func TestInterfaceTypes(t *testing.T) { + var i0 interface{}; + check(t, `interface="interface"`, `interface`, i0); + + i0 = "foo"; + check(t, `interface="interface"`, `interface`, i0); + check(t, `interface=*; string="%s"`, `foo`, i0); +} + + +func TestMapTypes(t *testing.T) { + var m0 map[string]int; + check(t, `map="map"`, `map`, m0); + + m1 := map[string]int{}; + check(t, `map="map"`, `map`, m1); + // check(t, `map=*`, ``, m1); // reflection support for maps incomplete +} + + +func TestPointerTypes(t *testing.T) { + var p0 *int; + check(t, `ptr="ptr"`, `ptr`, p0); + check(t, `ptr=*`, ``, p0); + check(t, `ptr=*|"nil"`, `nil`, p0); + + x := 99991; + p1 := &x; + check(t, `ptr="ptr"`, `ptr`, p1); + check(t, `ptr=*; int="%d"`, `99991`, p1); +} + + +func TestDefaultRule(t *testing.T) { + check(t, `default="%v"`, `42foo3.14`, 42, "foo", 3.14); + check(t, `default="%v"; int="%x"`, `abcdef`, 10, 11, 12, 13, 14, 15); + check(t, `default="%v"; int="%x"`, `ab**ef`, 10, 11, "**", 14, 15); + check(t, `default="%x"; int=@:default`, `abcdef`, 10, 11, 12, 13, 14, 15); +} + + +func TestGlobalSeparatorRule(t *testing.T) { + check(t, `int="%d"; / ="-"`, `1-2-3-4`, 1, 2, 3, 4); + check(t, `int="%x%x"; / ="*"`, `aa*aa`, 10, 10); +} + + +// ---------------------------------------------------------------------------- +// Formatting of a struct + +type T1 struct { + a int; +} + +const F1 = + `datafmt "datafmt";` + `int = "%d";` + `datafmt.T1 = "<" a ">";` + +func TestStruct1(t *testing.T) { + check(t, F1, "<42>", T1{42}); +} + + +// ---------------------------------------------------------------------------- +// Formatting of a struct with an optional field (ptr) + +type T2 struct { + s string; + p *T1; +} + +const F2a = + F1 + + `string = "%s";` + `ptr = *;` + `datafmt.T2 = s ["-" p "-"];` + +const F2b = + F1 + + `string = "%s";` + `ptr = *;` + `datafmt.T2 = s ("-" p "-" | "empty");`; + +func TestStruct2(t *testing.T) { + check(t, F2a, "foo", T2{"foo", nil}); + check(t, F2a, "bar-<17>-", T2{"bar", &T1{17}}); + check(t, F2b, "fooempty", T2{"foo", nil}); +} + + +// ---------------------------------------------------------------------------- +// Formatting of a struct with a repetitive field (slice) + +type T3 struct { + s string; + a []int; +} + +const F3a = + `datafmt "datafmt";` + `default = "%v";` + `array = *;` + `datafmt.T3 = s {" " a a / ","};` + +const F3b = + `datafmt "datafmt";` + `int = "%d";` + `string = "%s";` + `array = *;` + `nil = ;` + `empty = *:nil;` + `datafmt.T3 = s [a:empty ": " {a / "-"}]` + +func TestStruct3(t *testing.T) { + check(t, F3a, "foo", T3{"foo", nil}); + check(t, F3a, "foo 00, 11, 22", T3{"foo", []int{0, 1, 2}}); + check(t, F3b, "bar", T3{"bar", nil}); + check(t, F3b, "bal: 2-3-5", T3{"bal", []int{2, 3, 5}}); +} + + +// ---------------------------------------------------------------------------- +// Formatting of a struct with alternative field + +type T4 struct { + x *int; + a []int; +} + +const F4a = + `datafmt "datafmt";` + `int = "%d";` + `ptr = *;` + `array = *;` + `nil = ;` + `empty = *:nil;` + `datafmt.T4 = "<" (x:empty x | "-") ">" ` + +const F4b = + `datafmt "datafmt";` + `int = "%d";` + `ptr = *;` + `array = *;` + `nil = ;` + `empty = *:nil;` + `datafmt.T4 = "<" (a:empty {a / ", "} | "-") ">" ` + +func TestStruct4(t *testing.T) { + x := 7; + check(t, F4a, "<->", T4{nil, nil}); + check(t, F4a, "<7>", T4{&x, nil}); + check(t, F4b, "<->", T4{nil, nil}); + check(t, F4b, "<2, 3, 7>", T4{nil, []int{2, 3, 7}}); +} + + +// ---------------------------------------------------------------------------- +// Formatting a struct (documentation example) + +type Point struct { + name string; + x, y int; +} + +const FPoint = + `datafmt "datafmt";` + `int = "%d";` + `hexInt = "0x%x";` + `string = "---%s---";` + `datafmt.Point = name "{" x ", " y:hexInt "}";` + +func TestStructPoint(t *testing.T) { + p := Point{"foo", 3, 15}; + check(t, FPoint, "---foo---{3, 0xf}", p); +} + + +// ---------------------------------------------------------------------------- +// Formatting a slice (documentation example) + +const FSlice = + `int = "%b";` + `array = { * / ", " }` + +func TestSlice(t *testing.T) { + check(t, FSlice, "10, 11, 101, 111", []int{2, 3, 5, 7}); +} + + +// TODO add more tests diff --git a/src/pkg/datafmt/parser.go b/src/pkg/datafmt/parser.go new file mode 100644 index 000000000..0d597dcb5 --- /dev/null +++ b/src/pkg/datafmt/parser.go @@ -0,0 +1,447 @@ +// Copyright 2009 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 datafmt + +import ( + "container/vector"; + "datafmt"; + "fmt"; + "go/scanner"; + "go/token"; + "io"; + "os"; + "strconv"; + "strings"; +) + +// ---------------------------------------------------------------------------- +// Error handling + +// Error describes an individual error. The position Pos, if valid, +// indicates the format source position the error relates to. The +// error is specified with the Msg string. +// +type Error struct { + Pos token.Position; + Msg string; +} + + +func (e *Error) String() string { + pos := ""; + if e.Pos.IsValid() { + pos = fmt.Sprintf("%d:%d: ", e.Pos.Line, e.Pos.Column); + } + return pos + e.Msg; +} + + +// An ErrorList is a list of errors encountered during parsing. +type ErrorList []*Error + + +// ErrorList implements SortInterface and the os.Error interface. + +func (p ErrorList) Len() int { return len(p); } +func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } +func (p ErrorList) Less(i, j int) bool { return p[i].Pos.Offset < p[j].Pos.Offset; } + + +func (p ErrorList) String() string { + switch len(p) { + case 0: return "unspecified error"; + case 1: return p[0].String(); + } + return fmt.Sprintf("%s (and %d more errors)", p[0].String(), len(p) - 1); +} + + +// ---------------------------------------------------------------------------- +// Parsing + +type parser struct { + errors vector.Vector; + scanner scanner.Scanner; + pos token.Position; // token position + tok token.Token; // one token look-ahead + lit []byte; // token literal + + packs map [string] string; // PackageName -> ImportPath + rules map [string] expr; // RuleName -> Expression +} + + +func (p *parser) next() { + p.pos, p.tok, p.lit = p.scanner.Scan(); + switch p.tok { + case token.CHAN, token.FUNC, token.INTERFACE, token.MAP, token.STRUCT: + // Go keywords for composite types are type names + // returned by reflect. Accept them as identifiers. + p.tok = token.IDENT; // p.lit is already set correctly + } +} + + +func (p *parser) init(src []byte) { + p.errors.Init(0); + p.scanner.Init(src, p, scanner.AllowIllegalChars); // return '@' as token.ILLEGAL w/o error message + p.next(); // initializes pos, tok, lit + p.packs = make(map [string] string); + p.rules = make(map [string] expr); +} + + +// The parser implements scanner.Error. +func (p *parser) Error(pos token.Position, msg string) { + // Don't collect errors that are on the same line as the previous error + // in the hope to reduce the number of spurious errors due to incorrect + // parser synchronization. + if p.errors.Len() == 0 || p.errors.Last().(*Error).Pos.Line != pos.Line { + p.errors.Push(&Error{pos, msg}); + } +} + + +func (p *parser) errorExpected(pos token.Position, msg string) { + msg = "expected " + msg; + if pos.Offset == p.pos.Offset { + // the error happened at the current position; + // make the error message more specific + msg += ", found '" + p.tok.String() + "'"; + if p.tok.IsLiteral() { + msg += " " + string(p.lit); + } + } + p.Error(pos, msg); +} + + +func (p *parser) expect(tok token.Token) token.Position { + pos := p.pos; + if p.tok != tok { + p.errorExpected(pos, "'" + tok.String() + "'"); + } + p.next(); // make progress in any case + return pos; +} + + +func (p *parser) parseIdentifier() string { + name := string(p.lit); + p.expect(token.IDENT); + return name; +} + + +func (p *parser) parseTypeName() (string, bool) { + pos := p.pos; + name, isIdent := p.parseIdentifier(), true; + if p.tok == token.PERIOD { + // got a package name, lookup package + if importPath, found := p.packs[name]; found { + name = importPath; + } else { + p.Error(pos, "package not declared: " + name); + } + p.next(); + name, isIdent = name + "." + p.parseIdentifier(), false; + } + return name, isIdent; +} + + +// Parses a rule name and returns it. If the rule name is +// a package-qualified type name, the package name is resolved. +// The 2nd result value is true iff the rule name consists of a +// single identifier only (and thus could be a package name). +// +func (p *parser) parseRuleName() (string, bool) { + name, isIdent := "", false; + switch p.tok { + case token.IDENT: + name, isIdent = p.parseTypeName(); + case token.DEFAULT: + name = "default"; + p.next(); + case token.QUO: + name = "/"; + p.next(); + default: + p.errorExpected(p.pos, "rule name"); + p.next(); // make progress in any case + } + return name, isIdent; +} + + +func (p *parser) parseString() string { + s := ""; + if p.tok == token.STRING { + var err os.Error; + s, err = strconv.Unquote(string(p.lit)); + // Unquote may fail with an error, but only if the scanner found + // an illegal string in the first place. In this case the error + // has already been reported. + p.next(); + return s; + } else { + p.expect(token.STRING); + } + return s; +} + + +func (p *parser) parseLiteral() literal { + s := io.StringBytes(p.parseString()); + + // A string literal may contain %-format specifiers. To simplify + // and speed up printing of the literal, split it into segments + // that start with "%" possibly followed by a last segment that + // starts with some other character. + var list vector.Vector; + list.Init(0); + i0 := 0; + for i := 0; i < len(s); i++ { + if s[i] == '%' && i+1 < len(s) { + // the next segment starts with a % format + if i0 < i { + // the current segment is not empty, split it off + list.Push(s[i0 : i]); + i0 = i; + } + i++; // skip %; let loop skip over char after % + } + } + // the final segment may start with any character + // (it is empty iff the string is empty) + list.Push(s[i0 : len(s)]); + + // convert list into a literal + lit := make(literal, list.Len()); + for i := 0; i < list.Len(); i++ { + lit[i] = list.At(i).([]byte); + } + + return lit; +} + + +func (p *parser) parseField() expr { + var fname string; + switch p.tok { + case token.ILLEGAL: + if string(p.lit) != "@" { + return nil; + } + fname = "@"; + p.next(); + case token.MUL: + fname = "*"; + p.next(); + case token.IDENT: + fname = p.parseIdentifier(); + default: + return nil; + } + + var ruleName string; + if p.tok == token.COLON { + p.next(); + var _ bool; + ruleName, _ = p.parseRuleName(); + } + + return &field{fname, ruleName}; +} + + +func (p *parser) parseExpression() expr + +func (p *parser) parseOperand() (x expr) { + switch p.tok { + case token.STRING: + x = p.parseLiteral(); + + case token.LPAREN: + p.next(); + x = p.parseExpression(); + if p.tok == token.SHR { + p.next(); + x = &group{x, p.parseExpression()}; + } + p.expect(token.RPAREN); + + case token.LBRACK: + p.next(); + x = &option{p.parseExpression()}; + p.expect(token.RBRACK); + + case token.LBRACE: + p.next(); + x = p.parseExpression(); + var div expr; + if p.tok == token.QUO { + p.next(); + div = p.parseExpression(); + } + x = &repetition{x, div}; + p.expect(token.RBRACE); + + default: + x = p.parseField(); // may be nil + } + + return x; +} + + +func (p *parser) parseSequence() expr { + var list vector.Vector; + list.Init(0); + + for x := p.parseOperand(); x != nil; x = p.parseOperand() { + list.Push(x); + } + + // no need for a sequence if list.Len() < 2 + switch list.Len() { + case 0: return nil; + case 1: return list.At(0).(expr); + } + + // convert list into a sequence + seq := make(sequence, list.Len()); + for i := 0; i < list.Len(); i++ { + seq[i] = list.At(i).(expr); + } + return seq; +} + + +func (p *parser) parseExpression() expr { + var list vector.Vector; + list.Init(0); + + for { + x := p.parseSequence(); + if x != nil { + list.Push(x); + } + if p.tok != token.OR { + break; + } + p.next(); + } + + // no need for an alternatives if list.Len() < 2 + switch list.Len() { + case 0: return nil; + case 1: return list.At(0).(expr); + } + + // convert list into a alternatives + alt := make(alternatives, list.Len()); + for i := 0; i < list.Len(); i++ { + alt[i] = list.At(i).(expr); + } + return alt; +} + + +func (p *parser) parseFormat() { + for p.tok != token.EOF { + pos := p.pos; + + name, isIdent := p.parseRuleName(); + switch p.tok { + case token.STRING: + // package declaration + importPath := p.parseString(); + + // add package declaration + if !isIdent { + p.Error(pos, "illegal package name: " + name); + } else if _, found := p.packs[name]; !found { + p.packs[name] = importPath; + } else { + p.Error(pos, "package already declared: " + name); + } + + case token.ASSIGN: + // format rule + p.next(); + x := p.parseExpression(); + + // add rule + if _, found := p.rules[name]; !found { + p.rules[name] = x; + } else { + p.Error(pos, "format rule already declared: " + name); + } + + default: + p.errorExpected(p.pos, "package declaration or format rule"); + p.next(); // make progress in any case + } + + if p.tok == token.SEMICOLON { + p.next(); + } else { + break; + } + } + p.expect(token.EOF); +} + + +func remap(p *parser, name string) string { + i := strings.Index(name, "."); + if i >= 0 { + packageName, suffix := name[0 : i], name[i : len(name)]; + // lookup package + if importPath, found := p.packs[packageName]; found { + name = importPath + suffix; + } else { + var invalidPos token.Position; + p.Error(invalidPos, "package not declared: " + packageName); + } + } + return name; +} + + +// Parse parses a set of format productions from source src. Custom +// formatters may be provided via a map of formatter functions. If +// there are no errors, the result is a Format and the error is nil. +// Otherwise the format is nil and a non-empty ErrorList is returned. +// +func Parse(src []byte, fmap FormatterMap) (Format, os.Error) { + // parse source + var p parser; + p.init(src); + p.parseFormat(); + + // add custom formatters, if any + for name, form := range fmap { + name = remap(&p, name); + if t, found := p.rules[name]; !found { + p.rules[name] = &custom{name, form}; + } else { + var invalidPos token.Position; + p.Error(invalidPos, "formatter already declared: " + name); + } + } + + // convert errors list, if any + if p.errors.Len() > 0 { + errors := make(ErrorList, p.errors.Len()); + for i := 0; i < p.errors.Len(); i++ { + errors[i] = p.errors.At(i).(*Error); + } + return nil, errors; + } + + return p.rules, nil; +} diff --git a/src/pkg/exec/Makefile b/src/pkg/exec/Makefile new file mode 100644 index 000000000..679cc39c0 --- /dev/null +++ b/src/pkg/exec/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + exec.$O\ + + +phases: a1 +_obj$D/exec.a: phases + +a1: $(O1) + $(AR) grc _obj$D/exec.a exec.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/exec.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/exec.a + +packages: _obj$D/exec.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/exec.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/exec.a diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go new file mode 100644 index 000000000..c2b7bdd59 --- /dev/null +++ b/src/pkg/exec/exec.go @@ -0,0 +1,228 @@ +// Copyright 2009 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. + +// The exec package runs external commands. +package exec + +import ( + "os"; + "strings"; +) + +// Arguments to Run. +const ( + DevNull = iota; + PassThrough; + Pipe; + MergeWithStdout; +) + +// A Cmd represents a running command. +// Stdin, Stdout, and Stderr are Files representing pipes +// connected to the running command's standard input, output, and error, +// or else nil, depending on the arguments to Run. +// Pid is the running command's operating system process ID. +type Cmd struct { + Stdin *os.File; + Stdout *os.File; + Stderr *os.File; + Pid int; +} + +// Given mode (DevNull, etc), return file for child +// and file to record in Cmd structure. +func modeToFiles(mode, fd int) (*os.File, *os.File, os.Error) { + switch mode { + case DevNull: + rw := os.O_WRONLY; + if fd == 0 { + rw = os.O_RDONLY; + } + f, err := os.Open("/dev/null", rw, 0); + return f, nil, err; + case PassThrough: + switch fd { + case 0: + return os.Stdin, nil, nil; + case 1: + return os.Stdout, nil, nil; + case 2: + return os.Stderr, nil, nil; + } + case Pipe: + r, w, err := os.Pipe(); + if err != nil { + return nil, nil, err; + } + if fd == 0 { + return r, w, nil; + } + return w, r, nil; + } + return nil, nil, os.EINVAL; +} + +// Run starts the binary prog running with +// arguments argv and environment envv. +// It returns a pointer to a new Cmd representing +// the command or an error. +// +// The parameters stdin, stdout, and stderr +// specify how to handle standard input, output, and error. +// The choices are DevNull (connect to /dev/null), +// PassThrough (connect to the current process's standard stream), +// Pipe (connect to an operating system pipe), and +// MergeWithStdout (only for standard error; use the same +// file descriptor as was used for standard output). +// If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr) +// of the returned Cmd is the other end of the pipe. +// Otherwise the field in Cmd is nil. +func Run(argv0 string, argv, envv []string, stdin, stdout, stderr int) (p *Cmd, err os.Error) +{ + p = new(Cmd); + var fd [3]*os.File; + + if fd[0], p.Stdin, err = modeToFiles(stdin, 0); err != nil { + goto Error; + } + if fd[1], p.Stdout, err = modeToFiles(stdout, 1); err != nil { + goto Error; + } + if stderr == MergeWithStdout { + p.Stderr = p.Stdout; + } else if fd[2], p.Stderr, err = modeToFiles(stderr, 2); err != nil { + goto Error; + } + + // Run command. + p.Pid, err = os.ForkExec(argv0, argv, envv, "", &fd); + if err != nil { + goto Error; + } + if fd[0] != os.Stdin { + fd[0].Close(); + } + if fd[1] != os.Stdout { + fd[1].Close(); + } + if fd[2] != os.Stderr && fd[2] != fd[1] { + fd[2].Close(); + } + return p, nil; + +Error: + if fd[0] != os.Stdin && fd[0] != nil { + fd[0].Close(); + } + if fd[1] != os.Stdout && fd[1] != nil { + fd[1].Close(); + } + if fd[2] != os.Stderr && fd[2] != nil && fd[2] != fd[1] { + fd[2].Close(); + } + if p.Stdin != nil { + p.Stdin.Close(); + } + if p.Stdout != nil { + p.Stdout.Close(); + } + if p.Stderr != nil { + p.Stderr.Close(); + } + return nil, err; +} + +// Wait waits for the running command p, +// returning the Waitmsg returned by os.Wait and an error. +// The options are passed through to os.Wait. +// Setting options to 0 waits for p to exit; +// other options cause Wait to return for other +// process events; see package os for details. +func (p *Cmd) Wait(options int) (*os.Waitmsg, os.Error) { + if p.Pid < 0 { + return nil, os.EINVAL; + } + w, err := os.Wait(p.Pid, options); + if w != nil && (w.Exited() || w.Signaled()) { + p.Pid = -1; + } + return w, err; +} + +// Close waits for the running command p to exit, +// if it hasn't already, and then closes the non-nil file descriptors +// p.Stdin, p.Stdout, and p.Stderr. +func (p *Cmd) Close() os.Error { + if p.Pid >= 0 { + // Loop on interrupt, but + // ignore other errors -- maybe + // caller has already waited for pid. + w, err := p.Wait(0); + for err == os.EINTR { + w, err = p.Wait(0); + } + } + + // Close the FDs that are still open. + var err os.Error; + if p.Stdin != nil && p.Stdin.Fd() >= 0 { + if err1 := p.Stdin.Close(); err1 != nil { + err = err1; + } + } + if p.Stdout != nil && p.Stdout.Fd() >= 0 { + if err1 := p.Stdout.Close(); err1 != nil && err != nil { + err = err1; + } + } + if p.Stderr != nil && p.Stderr != p.Stdout && p.Stderr.Fd() >= 0 { + if err1 := p.Stderr.Close(); err1 != nil && err != nil { + err = err1; + } + } + return err; +} + +func canExec(file string) bool{ + d, err := os.Stat(file); + if err != nil { + return false; + } + return d.IsRegular() && d.Permission() & 0111 != 0; +} + +// LookPath searches for an executable binary named file +// in the directories named by the PATH environment variable. +// If file contains a slash, it is tried directly and the PATH is not consulted. +// +// TODO(rsc): Does LookPath belong in os instead? +func LookPath(file string) (string, os.Error) { + // NOTE(rsc): I wish we could use the Plan 9 behavior here + // (only bypass the path if file begins with / or ./ or ../) + // but that would not match all the Unix shells. + + if strings.Index(file, "/") >= 0 { + if canExec(file) { + return file, nil; + } + return "", os.ENOENT; + } + pathenv, err := os.Getenv("PATH"); + if err != nil { + // Unix shell semantics: no $PATH means assume PATH="" + // (equivalent to PATH="."). + pathenv = ""; + } + for i, dir := range strings.Split(pathenv, ":") { + if dir == "" { + // Unix shell semantics: path element "" means "." + dir = "."; + } + if canExec(dir+"/"+file) { + return dir+"/"+file, nil; + } + } + return "", os.ENOENT; +} + diff --git a/src/pkg/exec/exec_test.go b/src/pkg/exec/exec_test.go new file mode 100644 index 000000000..a1bb1f50e --- /dev/null +++ b/src/pkg/exec/exec_test.go @@ -0,0 +1,51 @@ +// Copyright 2009 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 exec + +import ( + "exec"; + "io"; + "testing"; +) + +func TestRunCat(t *testing.T) { + cmd, err := exec.Run("/bin/cat", []string{"cat"}, nil, + exec.Pipe, exec.Pipe, exec.DevNull); + if err != nil { + t.Fatalf("opencmd /bin/cat: %v", err); + } + io.WriteString(cmd.Stdin, "hello, world\n"); + cmd.Stdin.Close(); + var buf [64]byte; + n, err1 := io.FullRead(cmd.Stdout, &buf); + if err1 != nil && err1 != io.ErrEOF { + t.Fatalf("reading from /bin/cat: %v", err1); + } + if string(buf[0:n]) != "hello, world\n" { + t.Fatalf("reading from /bin/cat: got %q", buf[0:n]); + } + if err1 = cmd.Close(); err1 != nil { + t.Fatalf("closing /bin/cat: %v", err1); + } +} + +func TestRunEcho(t *testing.T) { + cmd, err := Run("/bin/echo", []string{"echo", "hello", "world"}, nil, + exec.DevNull, exec.Pipe, exec.DevNull); + if err != nil { + t.Fatalf("opencmd /bin/echo: %v", err); + } + var buf [64]byte; + n, err1 := io.FullRead(cmd.Stdout, &buf); + if err1 != nil && err1 != io.ErrEOF { + t.Fatalf("reading from /bin/echo: %v", err1); + } + if string(buf[0:n]) != "hello world\n" { + t.Fatalf("reading from /bin/echo: got %q", buf[0:n]); + } + if err1 = cmd.Close(); err1 != nil { + t.Fatalf("closing /bin/echo: %v", err1); + } +} diff --git a/src/pkg/exvar/Makefile b/src/pkg/exvar/Makefile new file mode 100644 index 000000000..a65a1ee6b --- /dev/null +++ b/src/pkg/exvar/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + exvar.$O\ + + +phases: a1 +_obj$D/exvar.a: phases + +a1: $(O1) + $(AR) grc _obj$D/exvar.a exvar.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/exvar.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/exvar.a + +packages: _obj$D/exvar.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/exvar.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/exvar.a diff --git a/src/pkg/exvar/exvar.go b/src/pkg/exvar/exvar.go new file mode 100644 index 000000000..6473f7af6 --- /dev/null +++ b/src/pkg/exvar/exvar.go @@ -0,0 +1,214 @@ +// Copyright 2009 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. + +// The exvar package provides a standardized interface to public variables, +// such as operation counters in servers. It exposes these variables via +// HTTP at /debug/vars in JSON format. +package exvar + +import ( + "fmt"; + "http"; + "io"; + "log"; + "strconv"; + "sync"; +) + +// Var is an abstract type for all exported variables. +type Var interface { + String() string; +} + +// Int is a 64-bit integer variable, and satisfies the Var interface. +type Int struct { + i int64; + mu sync.Mutex; +} + +func (v *Int) String() string { + return strconv.Itoa64(v.i) +} + +func (v *Int) Add(delta int64) { + v.mu.Lock(); + defer v.mu.Unlock(); + v.i += delta; +} + +// Map is a string-to-Var map variable, and satisfies the Var interface. +type Map struct { + m map[string] Var; + mu sync.Mutex; +} + +// KeyValue represents a single entry in a Map. +type KeyValue struct { + Key string; + Value Var; +} + +func (v *Map) String() string { + v.mu.Lock(); + defer v.mu.Unlock(); + b := new(io.ByteBuffer); + fmt.Fprintf(b, "{"); + first := true; + for key, val := range v.m { + if !first { + fmt.Fprintf(b, ", "); + } + fmt.Fprintf(b, "\"%s\": %v", key, val.String()); + first = false; + } + fmt.Fprintf(b, "}"); + return string(b.Data()) +} + +func (v *Map) Init() *Map { + v.m = make(map[string] Var); + return v +} + +func (v *Map) Get(key string) Var { + v.mu.Lock(); + defer v.mu.Unlock(); + if av, ok := v.m[key]; ok { + return av + } + return nil +} + +func (v *Map) Set(key string, av Var) { + v.mu.Lock(); + defer v.mu.Unlock(); + v.m[key] = av; +} + +func (v *Map) Add(key string, delta int64) { + v.mu.Lock(); + defer v.mu.Unlock(); + av, ok := v.m[key]; + if !ok { + av = new(Int); + v.m[key] = av; + } + + // Add to Int; ignore otherwise. + if iv, ok := av.(*Int); ok { + iv.Add(delta); + } +} + +// TODO(rsc): Make sure map access in separate thread is safe. +func (v *Map) iterate(c chan<- KeyValue) { + for k, v := range v.m { + c <- KeyValue{ k, v }; + } + close(c); +} + +func (v *Map) Iter() <-chan KeyValue { + c := make(chan KeyValue); + go v.iterate(c); + return c +} + +// String is a string variable, and satisfies the Var interface. +type String struct { + s string; +} + +func (v *String) String() string { + return strconv.Quote(v.s) +} + +func (v *String) Set(value string) { + v.s = value; +} + +// IntFunc wraps a func() int64 to create a value that satisfies the Var interface. +// The function will be called each time the Var is evaluated. +type IntFunc func() int64; + +func (v IntFunc) String() string { + return strconv.Itoa64(v()) +} + + +// All published variables. +var vars map[string] Var = make(map[string] Var); +var mutex sync.Mutex; + +// Publish declares an named exported variable. This should be called from a +// package's init function when it creates its Vars. If the name is already +// registered then this will log.Crash. +func Publish(name string, v Var) { + mutex.Lock(); + defer mutex.Unlock(); + if _, existing := vars[name]; existing { + log.Crash("Reuse of exported var name:", name); + } + vars[name] = v; +} + +// Get retrieves a named exported variable. +func Get(name string) Var { + if v, ok := vars[name]; ok { + return v + } + return nil +} + +// Convenience functions for creating new exported variables. + +func NewInt(name string) *Int { + v := new(Int); + Publish(name, v); + return v +} + +func NewMap(name string) *Map { + v := new(Map).Init(); + Publish(name, v); + return v +} + +func NewString(name string) *String { + v := new(String); + Publish(name, v); + return v +} + +// TODO(rsc): Make sure map access in separate thread is safe. +func iterate(c chan<- KeyValue) { + for k, v := range vars { + c <- KeyValue{ k, v }; + } + close(c); +} + +func Iter() <-chan KeyValue { + c := make(chan KeyValue); + go iterate(c); + return c +} + +func exvarHandler(c *http.Conn, req *http.Request) { + c.SetHeader("content-type", "application/json; charset=utf-8"); + fmt.Fprintf(c, "{\n"); + first := true; + for name, value := range vars { + if !first { + fmt.Fprintf(c, ",\n"); + } + first = false; + fmt.Fprintf(c, " %q: %s", name, value); + } + fmt.Fprintf(c, "\n}\n"); +} + +func init() { + http.Handle("/debug/vars", http.HandlerFunc(exvarHandler)); +} diff --git a/src/pkg/exvar/exvar_test.go b/src/pkg/exvar/exvar_test.go new file mode 100644 index 000000000..8b028bccb --- /dev/null +++ b/src/pkg/exvar/exvar_test.go @@ -0,0 +1,93 @@ +// Copyright 2009 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 exvar + +import ( + "exvar"; + "fmt"; + "json"; + "testing"; +) + +func TestInt(t *testing.T) { + reqs := NewInt("requests"); + if reqs.i != 0 { + t.Errorf("reqs.i = %v, want 4", reqs.i) + } + if reqs != Get("requests").(*Int) { + t.Errorf("Get() failed.") + } + + reqs.Add(1); + reqs.Add(3); + if reqs.i != 4 { + t.Errorf("reqs.i = %v, want 4", reqs.i) + } + + if s := reqs.String(); s != "4" { + t.Errorf("reqs.String() = %q, want \"4\"", s); + } +} + +func TestString(t *testing.T) { + name := NewString("my-name"); + if name.s != "" { + t.Errorf("name.s = %q, want \"\"", name.s) + } + + name.Set("Mike"); + if name.s != "Mike" { + t.Errorf("name.s = %q, want \"Mike\"", name.s) + } + + if s := name.String(); s != "\"Mike\"" { + t.Errorf("reqs.String() = %q, want \"\"Mike\"\"", s); + } +} + +func TestMapCounter(t *testing.T) { + colours := NewMap("bike-shed-colours"); + + colours.Add("red", 1); + colours.Add("red", 2); + colours.Add("blue", 4); + if x := colours.m["red"].(*Int).i; x != 3 { + t.Errorf("colours.m[\"red\"] = %v, want 3", x) + } + if x := colours.m["blue"].(*Int).i; x != 4 { + t.Errorf("colours.m[\"blue\"] = %v, want 4", x) + } + + // colours.String() should be '{"red":3, "blue":4}', + // though the order of red and blue could vary. + s := colours.String(); + j, ok, errtok := json.StringToJson(s); + if !ok { + t.Errorf("colours.String() isn't valid JSON: %v", errtok) + } + if j.Kind() != json.MapKind { + t.Error("colours.String() didn't produce a map.") + } + red := j.Get("red"); + if red.Kind() != json.NumberKind { + t.Error("red.Kind() is not a NumberKind.") + } + if x := red.Number(); x != 3 { + t.Error("red = %v, want 3", x) + } +} + +func TestIntFunc(t *testing.T) { + x := int(4); + ix := IntFunc(func() int64 { return int64(x) }); + if s := ix.String(); s != "4" { + t.Errorf("ix.String() = %v, want 4", s); + } + + x++; + if s := ix.String(); s != "5" { + t.Errorf("ix.String() = %v, want 5", s); + } +} diff --git a/src/pkg/flag/Makefile b/src/pkg/flag/Makefile new file mode 100644 index 000000000..466e19564 --- /dev/null +++ b/src/pkg/flag/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + flag.$O\ + + +phases: a1 +_obj$D/flag.a: phases + +a1: $(O1) + $(AR) grc _obj$D/flag.a flag.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/flag.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/flag.a + +packages: _obj$D/flag.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/flag.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/flag.a diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go new file mode 100644 index 000000000..63d649a9b --- /dev/null +++ b/src/pkg/flag/flag.go @@ -0,0 +1,486 @@ +// Copyright 2009 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. + +/* + The flag package implements command-line flag parsing. + + Usage: + + 1) Define flags using flag.String(), Bool(), Int(), etc. Example: + import flag "flag" + var ip *int = flag.Int("flagname", 1234, "help message for flagname") + If you like, you can bind the flag to a variable using the Var() functions. + var flagvar int + func init() { + flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") + } + + 2) After all flags are defined, call + flag.Parse() + to parse the command line into the defined flags. + + 3) Flags may then be used directly. If you're using the flags themselves, + they are all pointers; if you bind to variables, they're values. + print("ip has value ", *ip, "\n"); + print("flagvar has value ", flagvar, "\n"); + + 4) After parsing, flag.Arg(i) is the i'th argument after the flags. + Args are indexed from 0 up to flag.NArg(). + + Command line flag syntax: + -flag + -flag=x + -flag x + One or two minus signs may be used; they are equivalent. + + Flag parsing stops just before the first non-flag argument + ("-" is a non-flag argument) or after the terminator "--". + + Integer flags accept 1234, 0664, 0x1234 and may be negative. + Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. + */ +package flag + +import ( + "fmt"; + "os"; + "strconv" +) + +// BUG: atob belongs elsewhere +func atob(str string) (value bool, ok bool) { + switch str { + case "1", "t", "T", "true", "TRUE", "True": + return true, true; + case "0", "f", "F", "false", "FALSE", "False": + return false, true + } + return false, false +} + +type ( + boolValue struct; + intValue struct; + int64Value struct; + uintValue struct; + uint64Value struct; + stringValue struct; +) + +// -- Bool Value +type boolValue struct { + p *bool; +} + +func newBoolValue(val bool, p *bool) *boolValue { + *p = val; + return &boolValue{p} +} + +func (b *boolValue) set(s string) bool { + v, ok := atob(s); + *b.p = v; + return ok +} + +func (b *boolValue) String() string { + return fmt.Sprintf("%v", *b.p) +} + +// -- Int Value +type intValue struct { + p *int; +} + +func newIntValue(val int, p *int) *intValue { + *p = val; + return &intValue{p} +} + +func (i *intValue) set(s string) bool { + v, err := strconv.Atoi(s); + *i.p = int(v); + return err == nil +} + +func (i *intValue) String() string { + return fmt.Sprintf("%v", *i.p) +} + +// -- Int64 Value +type int64Value struct { + p *int64; +} + +func newInt64Value(val int64, p *int64) *int64Value { + *p = val; + return &int64Value{p} +} + +func (i *int64Value) set(s string) bool { + v, err := strconv.Atoi64(s); + *i.p = v; + return err == nil; +} + +func (i *int64Value) String() string { + return fmt.Sprintf("%v", *i.p) +} + +// -- Uint Value +type uintValue struct { + p *uint; +} + +func newUintValue(val uint, p *uint) *uintValue { + *p = val; + return &uintValue{p} +} + +func (i *uintValue) set(s string) bool { + v, err := strconv.Atoui(s); + *i.p = uint(v); + return err == nil; +} + +func (i *uintValue) String() string { + return fmt.Sprintf("%v", *i.p) +} + +// -- uint64 Value +type uint64Value struct { + p *uint64; +} + +func newUint64Value(val uint64, p *uint64) *uint64Value { + *p = val; + return &uint64Value{p} +} + +func (i *uint64Value) set(s string) bool { + v, err := strconv.Atoui64(s); + *i.p = uint64(v); + return err == nil; +} + +func (i *uint64Value) String() string { + return fmt.Sprintf("%v", *i.p) +} + +// -- string Value +type stringValue struct { + p *string; +} + +func newStringValue(val string, p *string) *stringValue { + *p = val; + return &stringValue{p} +} + +func (s *stringValue) set(val string) bool { + *s.p = val; + return true; +} + +func (s *stringValue) String() string { + return fmt.Sprintf("%s", *s.p) +} + +// FlagValue is the interface to the dynamic value stored in a flag. +// (The default value is represented as a string.) +type FlagValue interface { + String() string; + set(string) bool; +} + +// A Flag represents the state of a flag. +type Flag struct { + Name string; // name as it appears on command line + Usage string; // help message + Value FlagValue; // value as set + DefValue string; // default value (as text); for usage message +} + +type allFlags struct { + actual map[string] *Flag; + formal map[string] *Flag; + first_arg int; // 0 is the program name, 1 is first arg +} + +var flags *allFlags = &allFlags{make(map[string] *Flag), make(map[string] *Flag), 1} + +// VisitAll visits the flags, calling fn for each. It visits all flags, even those not set. +func VisitAll(fn func(*Flag)) { + for k, f := range flags.formal { + fn(f) + } +} + +// Visit visits the flags, calling fn for each. It visits only those flags that have been set. +func Visit(fn func(*Flag)) { + for k, f := range flags.actual { + fn(f) + } +} + +// Lookup returns the Flag structure of the named flag, returning nil if none exists. +func Lookup(name string) *Flag { + f, ok := flags.formal[name]; + if !ok { + return nil + } + return f +} + +// Set sets the value of tne named flag. It returns true if the set succeeded; false if +// there is no such flag defined. +func Set(name, value string) bool { + f, ok := flags.formal[name]; + if !ok { + return false + } + ok = f.Value.set(value); + if !ok { + return false + } + flags.actual[name] = f; + return true; +} + +// PrintDefaults prints to standard error the default values of all defined flags. +func PrintDefaults() { + VisitAll(func(f *Flag) { + format := " -%s=%s: %s\n"; + if s, ok := f.Value.(*stringValue); ok { + // put quotes on the value + format = " -%s=%q: %s\n"; + } + fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage); + }) +} + +// Usage prints to standard error a default usage message documenting all defined flags and +// then calls os.Exit(1). +func Usage() { + if len(os.Args) > 0 { + fmt.Fprintln(os.Stderr, "Usage of", os.Args[0] + ":"); + } else { + fmt.Fprintln(os.Stderr, "Usage:"); + } + PrintDefaults(); + os.Exit(1); +} + +func NFlag() int { + return len(flags.actual) +} + +// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument +// after flags have been processed. +func Arg(i int) string { + i += flags.first_arg; + if i < 0 || i >= len(os.Args) { + return ""; + } + return os.Args[i] +} + +// NArg is the number of arguments remaining after flags have been processed. +func NArg() int { + return len(os.Args) - flags.first_arg +} + +// Args returns the non-flag command-line arguments. +func Args() []string { + return os.Args[flags.first_arg:len(os.Args)]; +} + +func add(name string, value FlagValue, usage string) { + // Remember the default value as a string; it won't change. + f := &Flag{name, usage, value, value.String()}; + dummy, alreadythere := flags.formal[name]; + if alreadythere { + print("flag redefined: ", name, "\n"); + panic("flag redefinition"); // Happens only if flags are declared with identical names + } + flags.formal[name] = f; +} + +// BoolVar defines a bool flag with specified name, default value, and usage string. +// The argument p points to a bool variable in which to store the value of the flag. +func BoolVar(p *bool, name string, value bool, usage string) { + add(name, newBoolValue(value, p), usage); +} + +// Bool defines a bool flag with specified name, default value, and usage string. +// The return value is the address of a bool variable that stores the value of the flag. +func Bool(name string, value bool, usage string) *bool { + p := new(bool); + BoolVar(p, name, value, usage); + return p; +} + +// IntVar defines an int flag with specified name, default value, and usage string. +// The argument p points to an int variable in which to store the value of the flag. +func IntVar(p *int, name string, value int, usage string) { + add(name, newIntValue(value, p), usage); +} + +// Int defines an int flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +func Int(name string, value int, usage string) *int { + p := new(int); + IntVar(p, name, value, usage); + return p; +} + +// Int64Var defines an int64 flag with specified name, default value, and usage string. +// The argument p points to an int64 variable in which to store the value of the flag. +func Int64Var(p *int64, name string, value int64, usage string) { + add(name, newInt64Value(value, p), usage); +} + +// Int64 defines an int64 flag with specified name, default value, and usage string. +// The return value is the address of an int64 variable that stores the value of the flag. +func Int64(name string, value int64, usage string) *int64 { + p := new(int64); + Int64Var(p, name, value, usage); + return p; +} + +// UintVar defines a uint flag with specified name, default value, and usage string. +// The argument p points to a uint variable in which to store the value of the flag. +func UintVar(p *uint, name string, value uint, usage string) { + add(name, newUintValue(value, p), usage); +} + +// Uint defines a uint flag with specified name, default value, and usage string. +// The return value is the address of a uint variable that stores the value of the flag. +func Uint(name string, value uint, usage string) *uint { + p := new(uint); + UintVar(p, name, value, usage); + return p; +} + +// Uint64Var defines a uint64 flag with specified name, default value, and usage string. +// The argument p points to a uint64 variable in which to store the value of the flag. +func Uint64Var(p *uint64, name string, value uint64, usage string) { + add(name, newUint64Value(value, p), usage); +} + +// Uint64 defines a uint64 flag with specified name, default value, and usage string. +// The return value is the address of a uint64 variable that stores the value of the flag. +func Uint64(name string, value uint64, usage string) *uint64 { + p := new(uint64); + Uint64Var(p, name, value, usage); + return p; +} + +// StringVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a string variable in which to store the value of the flag. +func StringVar(p *string, name, value string, usage string) { + add(name, newStringValue(value, p), usage); +} + +// String defines a string flag with specified name, default value, and usage string. +// The return value is the address of a string variable that stores the value of the flag. +func String(name, value string, usage string) *string { + p := new(string); + StringVar(p, name, value, usage); + return p; +} + +func (f *allFlags) parseOne(index int) (ok bool, next int) +{ + s := os.Args[index]; + f.first_arg = index; // until proven otherwise + if len(s) == 0 { + return false, -1 + } + if s[0] != '-' { + return false, -1 + } + num_minuses := 1; + if len(s) == 1 { + return false, index + } + if s[1] == '-' { + num_minuses++; + if len(s) == 2 { // "--" terminates the flags + return false, index + 1 + } + } + name := s[num_minuses : len(s)]; + if len(name) == 0 || name[0] == '-' || name[0] == '=' { + print("bad flag syntax: ", s, "\n"); + Usage(); + } + + // it's a flag. does it have an argument? + has_value := false; + value := ""; + for i := 1; i < len(name); i++ { // equals cannot be first + if name[i] == '=' { + value = name[i+1 : len(name)]; + has_value = true; + name = name[0 : i]; + break; + } + } + flag, alreadythere := flags.actual[name]; + if alreadythere { + print("flag specified twice: -", name, "\n"); + Usage(); + } + m := flags.formal; + flag, alreadythere = m[name]; // BUG + if !alreadythere { + print("flag provided but not defined: -", name, "\n"); + Usage(); + } + if f, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg + if has_value { + if !f.set(value) { + print("invalid boolean value ", value, " for flag: -", name, "\n"); + Usage(); + } + } else { + f.set("true") + } + } else { + // It must have a value, which might be the next argument. + if !has_value && index < len(os.Args)-1 { + // value is the next arg + has_value = true; + index++; + value = os.Args[index]; + } + if !has_value { + print("flag needs an argument: -", name, "\n"); + Usage(); + } + ok = flag.Value.set(value); + if !ok { + print("invalid value ", value, " for flag: -", name, "\n"); + Usage(); + } + } + flags.actual[name] = flag; + return true, index + 1 +} + +// Parse parses the command-line flags. Must be called after all flags are defined +// and before any are accessed by the program. +func Parse() { + for i := 1; i < len(os.Args); { + ok, next := flags.parseOne(i); + if next > 0 { + flags.first_arg = next; + i = next; + } + if !ok { + break + } + } +} diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go new file mode 100644 index 000000000..0d83fcf81 --- /dev/null +++ b/src/pkg/flag/flag_test.go @@ -0,0 +1,77 @@ +// Copyright 2009 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 flag + +import ( + "flag"; + "fmt"; + "testing"; +) + +var ( + test_bool = flag.Bool("test_bool", false, "bool value"); + test_int = flag.Int("test_int", 0, "int value"); + test_int64 = flag.Int64("test_int64", 0, "int64 value"); + test_uint = flag.Uint("test_uint", 0, "uint value"); + test_uint64 = flag.Uint64("test_uint64", 0, "uint64 value"); + test_string = flag.String("test_string", "0", "string value"); +) + +func boolString(s string) string { + if s == "0" { + return "false" + } + return "true" +} + +func TestEverything(t *testing.T) { + m := make(map[string] *flag.Flag); + desired := "0"; + visitor := func(f *flag.Flag) { + if len(f.Name) > 5 && f.Name[0:5] == "test_" { + m[f.Name] = f; + ok := false; + switch { + case f.Value.String() == desired: + ok = true; + case f.Name == "test_bool" && f.Value.String() == boolString(desired): + ok = true; + } + if !ok { + t.Error("flag.Visit: bad value", f.Value.String(), "for", f.Name); + } + } + }; + flag.VisitAll(visitor); + if len(m) != 6 { + t.Error("flag.VisitAll misses some flags"); + for k, v := range m { + t.Log(k, *v) + } + } + m = make(map[string] *flag.Flag); + flag.Visit(visitor); + if len(m) != 0 { + t.Errorf("flag.Visit sees unset flags"); + for k, v := range m { + t.Log(k, *v) + } + } + // Now set all flags + flag.Set("test_bool", "true"); + flag.Set("test_int", "1"); + flag.Set("test_int64", "1"); + flag.Set("test_uint", "1"); + flag.Set("test_uint64", "1"); + flag.Set("test_string", "1"); + desired = "1"; + flag.Visit(visitor); + if len(m) != 6 { + t.Error("flag.Visit fails after set"); + for k, v := range m { + t.Log(k, *v) + } + } +} diff --git a/src/pkg/fmt/Makefile b/src/pkg/fmt/Makefile new file mode 100644 index 000000000..5d0281a0c --- /dev/null +++ b/src/pkg/fmt/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + format.$O\ + +O2=\ + print.$O\ + + +phases: a1 a2 +_obj$D/fmt.a: phases + +a1: $(O1) + $(AR) grc _obj$D/fmt.a format.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/fmt.a print.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/fmt.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/fmt.a + +packages: _obj$D/fmt.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/fmt.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/fmt.a diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go new file mode 100644 index 000000000..e8abc2f0d --- /dev/null +++ b/src/pkg/fmt/fmt_test.go @@ -0,0 +1,247 @@ +// Copyright 2009 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 fmt + +import ( + "fmt"; + "io"; + "math"; + "testing"; +) + +func TestFmtInterface(t *testing.T) { + var i1 interface{}; + i1 = "abc"; + s := fmt.Sprintf("%s", i1); + if s != "abc" { + t.Errorf(`fmt.Sprintf("%%s", empty("abc")) = %q want %q`, s, "abc"); + } +} + +type fmtTest struct { + fmt string; + val interface { }; + out string; +} + +const b32 uint32 = 1<<32 - 1 +const b64 uint64 = 1<<64 - 1 +var array = []int{1, 2, 3, 4, 5} +var iarray = []interface{}{1, "hello", 2.5, nil} + +var fmttests = []fmtTest{ + // basic string + fmtTest{ "%s", "abc", "abc" }, + fmtTest{ "%x", "abc", "616263" }, + fmtTest{ "%x", "xyz", "78797a" }, + fmtTest{ "%X", "xyz", "78797A" }, + fmtTest{ "%q", "abc", `"abc"` }, + + // basic bytes + fmtTest{ "%s", io.StringBytes("abc"), "abc" }, + fmtTest{ "%x", io.StringBytes("abc"), "616263" }, + fmtTest{ "% x", io.StringBytes("abc"), "61 62 63" }, + fmtTest{ "%x", io.StringBytes("xyz"), "78797a" }, + fmtTest{ "%X", io.StringBytes("xyz"), "78797A" }, + fmtTest{ "%q", io.StringBytes("abc"), `"abc"` }, + + // escaped strings + fmtTest{ "%#q", `abc`, "`abc`" }, + fmtTest{ "%#q", `"`, "`\"`" }, + fmtTest{ "1 %#q", `\n`, "1 `\\n`" }, + fmtTest{ "2 %#q", "\n", `2 "\n"` }, + fmtTest{ "%q", `"`, `"\""` }, + fmtTest{ "%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"` }, + fmtTest{ "%q", "abc\xffdef", `"abc\xffdef"` }, + fmtTest{ "%q", "\u263a", `"\u263a"` }, + fmtTest{ "%q", "\U0010ffff", `"\U0010ffff"` }, + + // width + fmtTest{ "%5s", "abc", " abc" }, + fmtTest{ "%-5s", "abc", "abc " }, + fmtTest{ "%05s", "abc", "00abc" }, + + // integers + fmtTest{ "%d", 12345, "12345" }, + fmtTest{ "%d", -12345, "-12345" }, + fmtTest{ "%10d", 12345, " 12345" }, + fmtTest{ "%10d", -12345, " -12345" }, + fmtTest{ "%+10d", 12345, " +12345" }, + fmtTest{ "%010d", 12345, "0000012345" }, + fmtTest{ "%010d", -12345, "-000012345" }, + fmtTest{ "%-10d", 12345, "12345 " }, + fmtTest{ "%010.3d", 1, " 001" }, + fmtTest{ "%010.3d", -1, " -001" }, + fmtTest{ "%+d", 12345, "+12345" }, + fmtTest{ "%+d", -12345, "-12345" }, + fmtTest{ "% d", 12345, " 12345" }, + + // arrays + fmtTest{ "%v", array, "[1 2 3 4 5]" }, + fmtTest{ "%v", iarray, "[1 hello 2.5 <nil>]" }, + fmtTest{ "%v", &array, "&[1 2 3 4 5]" }, + fmtTest{ "%v", &iarray, "&[1 hello 2.5 <nil>]" }, + + // old test/fmt_test.go + fmtTest{ "%d", 1234, "1234" }, + fmtTest{ "%d", -1234, "-1234" }, + fmtTest{ "%d", uint(1234), "1234" }, + fmtTest{ "%d", uint32(b32), "4294967295" }, + fmtTest{ "%d", uint64(b64), "18446744073709551615" }, + fmtTest{ "%o", 01234, "1234" }, + fmtTest{ "%#o", 01234, "01234" }, + fmtTest{ "%o", uint32(b32), "37777777777" }, + fmtTest{ "%o", uint64(b64), "1777777777777777777777" }, + fmtTest{ "%x", 0x1234abcd, "1234abcd" }, + fmtTest{ "%#x", 0x1234abcd, "0x1234abcd" }, + fmtTest{ "%x", b32-0x1234567, "fedcba98" }, + fmtTest{ "%X", 0x1234abcd, "1234ABCD" }, + fmtTest{ "%X", b32-0x1234567, "FEDCBA98" }, + fmtTest{ "%#X", 0, "0X0" }, + fmtTest{ "%x", b64, "ffffffffffffffff" }, + fmtTest{ "%b", 7, "111" }, + fmtTest{ "%b", b64, "1111111111111111111111111111111111111111111111111111111111111111" }, + fmtTest{ "%e", float64(1), "1.000000e+00" }, + fmtTest{ "%e", float64(1234.5678e3), "1.234568e+06" }, + fmtTest{ "%e", float64(1234.5678e-8), "1.234568e-05" }, + fmtTest{ "%e", float64(-7), "-7.000000e+00" }, + fmtTest{ "%e", float64(-1e-9), "-1.000000e-09" }, + fmtTest{ "%f", float64(1234.5678e3), "1234567.800000" }, + fmtTest{ "%f", float64(1234.5678e-8), "0.000012" }, + fmtTest{ "%f", float64(-7), "-7.000000" }, + fmtTest{ "%f", float64(-1e-9), "-0.000000" }, + fmtTest{ "%g", float64(1234.5678e3), "1.2345678e+06" }, + fmtTest{ "%g", float32(1234.5678e3), "1.2345678e+06" }, + fmtTest{ "%g", float64(1234.5678e-8), "1.2345678e-05" }, + fmtTest{ "%g", float64(-7), "-7" }, + fmtTest{ "%g", float64(-1e-9), "-1e-09", }, + fmtTest{ "%g", float32(-1e-9), "-1e-09" }, + fmtTest{ "%c", 'x', "x" }, + fmtTest{ "%c", 0xe4, "ä" }, + fmtTest{ "%c", 0x672c, "本" }, + fmtTest{ "%c", '日', "日" }, + fmtTest{ "%20.8d", 1234, " 00001234" }, + fmtTest{ "%20.8d", -1234, " -00001234" }, + fmtTest{ "%20d", 1234, " 1234" }, + fmtTest{ "%-20.8d", 1234, "00001234 " }, + fmtTest{ "%-20.8d", -1234, "-00001234 " }, + fmtTest{ "%-#20.8x", 0x1234abc, "0x01234abc " }, + fmtTest{ "%-#20.8X", 0x1234abc, "0X01234ABC " }, + fmtTest{ "%-#20.8o", 01234, "00001234 " }, + fmtTest{ "%.20b", 7, "00000000000000000111" }, + fmtTest{ "%20.5s", "qwertyuiop", " qwert" }, + fmtTest{ "%.5s", "qwertyuiop", "qwert" }, + fmtTest{ "%-20.5s", "qwertyuiop", "qwert " }, + fmtTest{ "%20c", 'x', " x" }, + fmtTest{ "%-20c", 'x', "x " }, + fmtTest{ "%20.6e", 1.2345e3, " 1.234500e+03" }, + fmtTest{ "%20.6e", 1.2345e-3, " 1.234500e-03" }, + fmtTest{ "%20e", 1.2345e3, " 1.234500e+03" }, + fmtTest{ "%20e", 1.2345e-3, " 1.234500e-03" }, + fmtTest{ "%20.8e", 1.2345e3, " 1.23450000e+03" }, + fmtTest{ "%20f", float64(1.23456789e3), " 1234.567890" }, + fmtTest{ "%20f", float64(1.23456789e-3), " 0.001235" }, + fmtTest{ "%20f", float64(12345678901.23456789), " 12345678901.234568" }, + fmtTest{ "%-20f", float64(1.23456789e3), "1234.567890 " }, + fmtTest{ "%20.8f", float64(1.23456789e3), " 1234.56789000" }, + fmtTest{ "%20.8f", float64(1.23456789e-3), " 0.00123457" }, + fmtTest{ "%g", float64(1.23456789e3), "1234.56789" }, + fmtTest{ "%g", float64(1.23456789e-3), "0.00123456789" }, + fmtTest{ "%g", float64(1.23456789e20), "1.23456789e+20" }, + fmtTest{ "%20e", math.Inf(1), " +Inf" }, + fmtTest{ "%-20f", math.Inf(-1), "-Inf " }, + fmtTest{ "%20g", math.NaN(), " NaN" }, +} + +func TestSprintf(t *testing.T) { + for i := 0; i < len(fmttests); i++ { + tt := fmttests[i]; + s := fmt.Sprintf(tt.fmt, tt.val); + if s != tt.out { + if ss, ok := tt.val.(string); ok { + // Don't requote the already-quoted strings. + // It's too confusing to read the errors. + t.Errorf("fmt.Sprintf(%q, %q) = %s want %s", tt.fmt, tt.val, s, tt.out); + } else { + t.Errorf("fmt.Sprintf(%q, %v) = %q want %q", tt.fmt, tt.val, s, tt.out); + } + } + } +} + +type flagPrinter struct { } +func (*flagPrinter) Format(f fmt.Formatter, c int) { + s := "%"; + for i := 0; i < 128; i++ { + if f.Flag(i) { + s += string(i); + } + } + if w, ok := f.Width(); ok { + s += fmt.Sprintf("%d", w); + } + if p, ok := f.Precision(); ok { + s += fmt.Sprintf(".%d", p); + } + s += string(c); + io.WriteString(f, "["+s+"]"); +} + +type flagTest struct { + in string; + out string; +} + +var flagtests = []flagTest { + flagTest{ "%a", "[%a]" }, + flagTest{ "%-a", "[%-a]" }, + flagTest{ "%+a", "[%+a]" }, + flagTest{ "%#a", "[%#a]" }, + flagTest{ "% a", "[% a]" }, + flagTest{ "%0a", "[%0a]" }, + flagTest{ "%1.2a", "[%1.2a]" }, + flagTest{ "%-1.2a", "[%-1.2a]" }, + flagTest{ "%+1.2a", "[%+1.2a]" }, + flagTest{ "%-+1.2a", "[%+-1.2a]" }, + flagTest{ "%-+1.2abc", "[%+-1.2a]bc" }, + flagTest{ "%-1.2abc", "[%-1.2a]bc" }, +} + +func TestFlagParser(t *testing.T) { + var flagprinter flagPrinter; + for i := 0; i < len(flagtests); i++ { + tt := flagtests[i]; + s := fmt.Sprintf(tt.in, &flagprinter); + if s != tt.out { + t.Errorf("Sprintf(%q, &flagprinter) => %q, want %q", tt.in, s, tt.out); + } + } +} + +func TestStructPrinter(t *testing.T) { + var s struct { + a string; + b string; + c int; + }; + s.a = "abc"; + s.b = "def"; + s.c = 123; + type Test struct { + fmt string; + out string; + } + var tests = []Test { + Test{ "%v", "{abc def 123}" }, + Test{ "%+v", "{a=abc b=def c=123}" }, + }; + for i := 0; i < len(tests); i++ { + tt := tests[i]; + out := fmt.Sprintf(tt.fmt, s); + if out != tt.out { + t.Errorf("Sprintf(%q, &s) = %q, want %q", tt.fmt, out, tt.out); + } + } +} diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go new file mode 100644 index 000000000..3cd492980 --- /dev/null +++ b/src/pkg/fmt/format.go @@ -0,0 +1,533 @@ +// Copyright 2009 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 fmt + +import ( + "strconv"; +) + + +const nByte = 64; +const nPows10 = 160; + +var ldigits string = "0123456789abcdef" // var not const because we take its address +var udigits string = "0123456789ABCDEF" + +/* + Fmt is the raw formatter used by Printf etc. Not meant for normal use. + See print.go for a more palatable interface. + + The model is to accumulate operands into an internal buffer and then + retrieve the buffer in one hit using Str(), Putnl(), etc. The formatting + methods return ``self'' so the operations can be chained. + + f := fmt.New(); + print(f.Fmt_d(1234).Fmt_s("\n").Str()); // create string, print it + f.Fmt_d(-1234).Fmt_s("\n").Put(); // print string + f.Fmt_ud(1<<63).Putnl(); // print string with automatic newline +*/ +type Fmt struct { + buf string; + wid int; + wid_present bool; + prec int; + prec_present bool; + // flags + minus bool; + plus bool; + sharp bool; + space bool; + zero bool; +} + +func (f *Fmt) clearflags() { + f.wid = 0; + f.wid_present = false; + f.prec = 0; + f.prec_present = false; + f.minus = false; + f.plus = false; + f.sharp = false; + f.space = false; + f.zero = false; +} + +func (f *Fmt) clearbuf() { + f.buf = ""; +} + +func (f *Fmt) init() { + f.clearbuf(); + f.clearflags(); +} + +// New returns a new initialized Fmt +func New() *Fmt { + f := new(Fmt); + f.init(); + return f; +} + +// Str returns the buffered contents as a string and resets the Fmt. +func (f *Fmt) Str() string { + s := f.buf; + f.clearbuf(); + f.clearflags(); + f.buf = ""; + return s; +} + +// Put writes the buffered contents to stdout and resets the Fmt. +func (f *Fmt) Put() { + print(f.buf); + f.clearbuf(); + f.clearflags(); +} + +// Putnl writes the buffered contents to stdout, followed by a newline, and resets the Fmt. +func (f *Fmt) Putnl() { + print(f.buf, "\n"); + f.clearbuf(); + f.clearflags(); +} + +// Wp sets the width and precision for formatting the next item. +func (f *Fmt) Wp(w, p int) *Fmt { + f.wid_present = true; + f.wid = w; + f.prec_present = true; + f.prec = p; + return f; +} + +// P sets the precision for formatting the next item. +func (f *Fmt) P(p int) *Fmt { + f.prec_present = true; + f.prec = p; + return f; +} + +// W sets the width for formatting the next item. +func (f *Fmt) W(x int) *Fmt { + f.wid_present = true; + f.wid = x; + return f; +} + +// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus) +// padding is in bytes, not characters (agrees with ANSIC C, not Plan 9 C) +func (f *Fmt) pad(s string) { + if f.wid_present && f.wid != 0 { + left := !f.minus; + w := f.wid; + if w < 0 { + left = false; + w = -w; + } + w -= len(s); + padchar := byte(' '); + if left && f.zero { + padchar = '0'; + } + if w > 0 { + if w > nByte { + w = nByte; + } + buf := make([]byte, w); + for i := 0; i < w; i++ { + buf[i] = padchar; + } + if left { + s = string(buf) + s; + } else { + s = s + string(buf); + } + } + } + f.buf += s; +} + +// format val into buf, ending at buf[i]. (printing is easier right-to-left; +// that's why the bidi languages are right-to-left except for numbers. wait, +// never mind.) val is known to be unsigned. we could make things maybe +// marginally faster by splitting the 32-bit case out into a separate function +// but it's not worth the duplication, so val has 64 bits. +func putint(buf *[nByte]byte, i int, base, val uint64, digits *string) int { + for val >= base { + buf[i] = digits[val%base]; + i--; + val /= base; + } + buf[i] = digits[val]; + return i-1; +} + +// Fmt_boolean formats a boolean. +func (f *Fmt) Fmt_boolean(v bool) *Fmt { + if v { + f.pad("true"); + } else { + f.pad("false"); + } + f.clearflags(); + return f; +} + +// integer; interprets prec but not wid. +func (f *Fmt) integer(a int64, base uint, is_signed bool, digits *string) string { + var buf [nByte]byte; + negative := is_signed && a < 0; + if negative { + a = -a; + } + + // two ways to ask for extra leading zero digits: %.3d or %03d. + // apparently the first cancels the second. + prec := 0; + if f.prec_present { + prec = f.prec; + f.zero = false; + } else if f.zero && f.wid_present && !f.minus && f.wid > 0{ + prec = f.wid; + if negative || f.plus || f.space { + prec--; // leave room for sign + } + } + + i := putint(&buf, nByte-1, uint64(base), uint64(a), digits); + for i > 0 && prec > (nByte-1-i) { + buf[i] = '0'; + i--; + } + + if f.sharp { + switch base { + case 8: + if buf[i+1] != '0' { + buf[i] = '0'; + i--; + } + case 16: + buf[i] = 'x' + digits[10]-'a'; + i--; + buf[i] = '0'; + i--; + } + } + + if negative { + buf[i] = '-'; + i--; + } else if f.plus { + buf[i] = '+'; + i--; + } else if f.space { + buf[i] = ' '; + i--; + } + return string(buf[i+1:nByte]); +} + +// Fmt_d64 formats an int64 in decimal. +func (f *Fmt) Fmt_d64(v int64) *Fmt { + f.pad(f.integer(v, 10, true, &ldigits)); + f.clearflags(); + return f; +} + +// Fmt_d32 formats an int32 in decimal. +func (f *Fmt) Fmt_d32(v int32) *Fmt { + return f.Fmt_d64(int64(v)); +} + +// Fmt_d formats an int in decimal. +func (f *Fmt) Fmt_d(v int) *Fmt { + return f.Fmt_d64(int64(v)); +} + +// Fmt_ud64 formats a uint64 in decimal. +func (f *Fmt) Fmt_ud64(v uint64) *Fmt { + f.pad(f.integer(int64(v), 10, false, &ldigits)); + f.clearflags(); + return f; +} + +// Fmt_ud32 formats a uint32 in decimal. +func (f *Fmt) Fmt_ud32(v uint32) *Fmt { + return f.Fmt_ud64(uint64(v)); +} + +// Fmt_ud formats a uint in decimal. +func (f *Fmt) Fmt_ud(v uint) *Fmt { + return f.Fmt_ud64(uint64(v)); +} + +// Fmt_x64 formats an int64 in hexadecimal. +func (f *Fmt) Fmt_x64(v int64) *Fmt { + f.pad(f.integer(v, 16, true, &ldigits)); + f.clearflags(); + return f; +} + +// Fmt_x32 formats an int32 in hexadecimal. +func (f *Fmt) Fmt_x32(v int32) *Fmt { + return f.Fmt_x64(int64(v)); +} + +// Fmt_x formats an int in hexadecimal. +func (f *Fmt) Fmt_x(v int) *Fmt { + return f.Fmt_x64(int64(v)); +} + +// Fmt_ux64 formats a uint64 in hexadecimal. +func (f *Fmt) Fmt_ux64(v uint64) *Fmt { + f.pad(f.integer(int64(v), 16, false, &ldigits)); + f.clearflags(); + return f; +} + +// Fmt_ux32 formats a uint32 in hexadecimal. +func (f *Fmt) Fmt_ux32(v uint32) *Fmt { + return f.Fmt_ux64(uint64(v)); +} + +// Fmt_ux formats a uint in hexadecimal. +func (f *Fmt) Fmt_ux(v uint) *Fmt { + return f.Fmt_ux64(uint64(v)); +} + +// Fmt_X64 formats an int64 in upper case hexadecimal. +func (f *Fmt) Fmt_X64(v int64) *Fmt { + f.pad(f.integer(v, 16, true, &udigits)); + f.clearflags(); + return f; +} + +// Fmt_X32 formats an int32 in upper case hexadecimal. +func (f *Fmt) Fmt_X32(v int32) *Fmt { + return f.Fmt_X64(int64(v)); +} + +// Fmt_X formats an int in upper case hexadecimal. +func (f *Fmt) Fmt_X(v int) *Fmt { + return f.Fmt_X64(int64(v)); +} + +// Fmt_uX64 formats a uint64 in upper case hexadecimal. +func (f *Fmt) Fmt_uX64(v uint64) *Fmt { + f.pad(f.integer(int64(v), 16, false, &udigits)); + f.clearflags(); + return f; +} + +// Fmt_uX32 formats a uint32 in upper case hexadecimal. +func (f *Fmt) Fmt_uX32(v uint32) *Fmt { + return f.Fmt_uX64(uint64(v)); +} + +// Fmt_uX formats a uint in upper case hexadecimal. +func (f *Fmt) Fmt_uX(v uint) *Fmt { + return f.Fmt_uX64(uint64(v)); +} + +// Fmt_o64 formats an int64 in octal. +func (f *Fmt) Fmt_o64(v int64) *Fmt { + f.pad(f.integer(v, 8, true, &ldigits)); + f.clearflags(); + return f; +} + +// Fmt_o32 formats an int32 in octal. +func (f *Fmt) Fmt_o32(v int32) *Fmt { + return f.Fmt_o64(int64(v)); +} + +// Fmt_o formats an int in octal. +func (f *Fmt) Fmt_o(v int) *Fmt { + return f.Fmt_o64(int64(v)); +} + +// Fmt_uo64 formats a uint64 in octal. +func (f *Fmt) Fmt_uo64(v uint64) *Fmt { + f.pad(f.integer(int64(v), 8, false, &ldigits)); + f.clearflags(); + return f; +} + +// Fmt_uo32 formats a uint32 in octal. +func (f *Fmt) Fmt_uo32(v uint32) *Fmt { + return f.Fmt_uo64(uint64(v)); +} + +// Fmt_uo formats a uint in octal. +func (f *Fmt) Fmt_uo(v uint) *Fmt { + return f.Fmt_uo64(uint64(v)); +} + +// Fmt_b64 formats a uint64 in binary. +func (f *Fmt) Fmt_b64(v uint64) *Fmt { + f.pad(f.integer(int64(v), 2, false, &ldigits)); + f.clearflags(); + return f; +} + +// Fmt_b32 formats a uint32 in binary. +func (f *Fmt) Fmt_b32(v uint32) *Fmt { + return f.Fmt_b64(uint64(v)); +} + +// Fmt_b formats a uint in binary. +func (f *Fmt) Fmt_b(v uint) *Fmt { + return f.Fmt_b64(uint64(v)); +} + +// Fmt_c formats a Unicode character. +func (f *Fmt) Fmt_c(v int) *Fmt { + f.pad(string(v)); + f.clearflags(); + return f; +} + +// Fmt_s formats a string. +func (f *Fmt) Fmt_s(s string) *Fmt { + if f.prec_present { + if f.prec < len(s) { + s = s[0:f.prec]; + } + } + f.pad(s); + f.clearflags(); + return f; +} + +// Fmt_sx formats a string as a hexadecimal encoding of its bytes. +func (f *Fmt) Fmt_sx(s string) *Fmt { + t := ""; + for i := 0; i < len(s); i++ { + if i > 0 && f.space { + t += " "; + } + v := s[i]; + t += string(ldigits[v>>4]); + t += string(ldigits[v&0xF]); + } + f.pad(t); + f.clearflags(); + return f; +} + +// Fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes. +func (f *Fmt) Fmt_sX(s string) *Fmt { + t := ""; + for i := 0; i < len(s); i++ { + v := s[i]; + t += string(udigits[v>>4]); + t += string(udigits[v&0xF]); + } + f.pad(t); + f.clearflags(); + return f; +} + +// Fmt_q formats a string as a double-quoted, escaped Go string constant. +func (f *Fmt) Fmt_q(s string) *Fmt { + var quoted string; + if f.sharp && strconv.CanBackquote(s) { + quoted = "`"+s+"`"; + } else { + quoted = strconv.Quote(s); + } + f.pad(quoted); + f.clearflags(); + return f; +} + +// floating-point + +func doPrec(f *Fmt, def int) int { + if f.prec_present { + return f.prec; + } + return def; +} + +func fmtString(f *Fmt, s string) *Fmt { + f.pad(s); + f.clearflags(); + return f; +} + +// Fmt_e64 formats a float64 in the form -1.23e+12. +func (f *Fmt) Fmt_e64(v float64) *Fmt { + return fmtString(f, strconv.Ftoa64(v, 'e', doPrec(f, 6))); +} + +// Fmt_f64 formats a float64 in the form -1.23. +func (f *Fmt) Fmt_f64(v float64) *Fmt { + return fmtString(f, strconv.Ftoa64(v, 'f', doPrec(f, 6))); +} + +// Fmt_g64 formats a float64 in the 'f' or 'e' form according to size. +func (f *Fmt) Fmt_g64(v float64) *Fmt { + return fmtString(f, strconv.Ftoa64(v, 'g', doPrec(f, -1))); +} + +// Fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2). +func (f *Fmt) Fmt_fb64(v float64) *Fmt { + return fmtString(f, strconv.Ftoa64(v, 'b', 0)); +} + +// float32 +// cannot defer to float64 versions +// because it will get rounding wrong in corner cases. + +// Fmt_e32 formats a float32 in the form -1.23e+12. +func (f *Fmt) Fmt_e32(v float32) *Fmt { + return fmtString(f, strconv.Ftoa32(v, 'e', doPrec(f, 6))); +} + +// Fmt_f32 formats a float32 in the form -1.23. +func (f *Fmt) Fmt_f32(v float32) *Fmt { + return fmtString(f, strconv.Ftoa32(v, 'f', doPrec(f, 6))); +} + +// Fmt_g32 formats a float32 in the 'f' or 'e' form according to size. +func (f *Fmt) Fmt_g32(v float32) *Fmt { + return fmtString(f, strconv.Ftoa32(v, 'g', doPrec(f, -1))); +} + +// Fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2). +func (f *Fmt) Fmt_fb32(v float32) *Fmt { + return fmtString(f, strconv.Ftoa32(v, 'b', 0)); +} + +// float +func (x *Fmt) f(a float) *Fmt { + if strconv.FloatSize == 32 { + return x.Fmt_f32(float32(a)) + } + return x.Fmt_f64(float64(a)) +} + +func (x *Fmt) e(a float) *Fmt { + if strconv.FloatSize == 32 { + return x.Fmt_e32(float32(a)) + } + return x.Fmt_e64(float64(a)) +} + +func (x *Fmt) g(a float) *Fmt { + if strconv.FloatSize == 32 { + return x.Fmt_g32(float32(a)) + } + return x.Fmt_g64(float64(a)) +} + +func (x *Fmt) fb(a float) *Fmt { + if strconv.FloatSize == 32 { + return x.Fmt_fb32(float32(a)) + } + return x.Fmt_fb64(float64(a)) +} diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go new file mode 100644 index 000000000..66174c74b --- /dev/null +++ b/src/pkg/fmt/print.go @@ -0,0 +1,705 @@ +// Copyright 2009 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 fmt implements formatted I/O with functions analogous +// to C's printf. Because of reflection knowledge it does not need +// to be told about sizes and signedness (no %llud etc. - just %d). +// Still to do: document the formats properly. For now, like C but: +// - don't need l or u flags - type of integer tells that. +// - %v prints any value using its native format. +// - for each Printf-like fn, there is also a Print fn that takes no format +// and is equivalent to saying %v for every operand. +// - another variant Println inserts blanks and appends a newline. +// - if an operand implements method String() that method will +// be used for %v, %s, or Print etc. +// - if an operand implements interface Formatter, that interface can +// be used for fine control of formatting. +package fmt + + +import ( + "fmt"; + "io"; + "os"; + "reflect"; + "utf8"; +) + +// Formatter represents the printer state passed to custom formatters. +// It provides access to the io.Writer interface plus information about +// the flags and options for the operand's format specifier. +type Formatter interface { + // Write is the function to call to emit formatted output to be printed. + Write(b []byte) (ret int, err os.Error); + // Width returns the value of the width option and whether it has been set. + Width() (wid int, ok bool); + // Precision returns the value of the precision option and whether it has been set. + Precision() (prec int, ok bool); + + // Flag returns whether the flag c, a character, has been set. + Flag(int) bool; +} + +// Format is the interface implemented by objects with a custom formatter. +// The implementation of Format may call Sprintf or Fprintf(f) etc. +// to generate its output. +type Format interface { + Format(f Formatter, c int); +} + +// String represents any object being printed that has a String() method that +// returns a string, which defines the ``native'' format for that object. +// Any such object will be printed using that method if passed +// as operand to a %s or %v format or to an unformatted printer such as Print. +type Stringer interface { + String() string +} + +const runeSelf = utf8.RuneSelf +const allocSize = 32 + +type pp struct { + n int; + buf []byte; + fmt *Fmt; +} + +func newPrinter() *pp { + p := new(pp); + p.fmt = fmt.New(); + return p; +} + +func (p *pp) Width() (wid int, ok bool) { + return p.fmt.wid, p.fmt.wid_present +} + +func (p *pp) Precision() (prec int, ok bool) { + return p.fmt.prec, p.fmt.prec_present +} + +func (p *pp) Flag(b int) bool { + switch b { + case '-': + return p.fmt.minus; + case '+': + return p.fmt.plus; + case '#': + return p.fmt.sharp; + case ' ': + return p.fmt.space; + case '0': + return p.fmt.zero; + } + return false +} + +func (p *pp) ensure(n int) { + if len(p.buf) < n { + newn := allocSize + len(p.buf); + if newn < n { + newn = n + allocSize + } + b := make([]byte, newn); + for i := 0; i < p.n; i++ { + b[i] = p.buf[i]; + } + p.buf = b; + } +} + +func (p *pp) addstr(s string) { + n := len(s); + p.ensure(p.n + n); + for i := 0; i < n; i++ { + p.buf[p.n] = s[i]; + p.n++; + } +} + +func (p *pp) addbytes(b []byte, start, end int) { + p.ensure(p.n + end-start); + for i := start; i < end; i++ { + p.buf[p.n] = b[i]; + p.n++; + } +} + +func (p *pp) add(c int) { + p.ensure(p.n + 1); + if c < runeSelf { + p.buf[p.n] = byte(c); + p.n++; + } else { + p.addstr(string(c)); + } +} + +// Implement Write so we can call fprintf on a P, for +// recursive use in custom verbs. +func (p *pp) Write(b []byte) (ret int, err os.Error) { + p.addbytes(b, 0, len(b)); + return len(b), nil; +} + +func (p *pp) doprintf(format string, v reflect.StructValue); +func (p *pp) doprint(v reflect.StructValue, addspace, addnewline bool); + +// These routines end in 'f' and take a format string. + +// Fprintf formats according to a format specifier and writes to w. +func Fprintf(w io.Writer, format string, a ...) (n int, error os.Error) { + v := reflect.NewValue(a).(reflect.StructValue); + p := newPrinter(); + p.doprintf(format, v); + n, error = w.Write(p.buf[0:p.n]); + return n, error; +} + +// Printf formats according to a format specifier and writes to standard output. +func Printf(format string, v ...) (n int, errno os.Error) { + n, errno = Fprintf(os.Stdout, format, v); + return n, errno; +} + +// Sprintf formats according to a format specifier and returns the resulting string. +func Sprintf(format string, a ...) string { + v := reflect.NewValue(a).(reflect.StructValue); + p := newPrinter(); + p.doprintf(format, v); + s := string(p.buf)[0 : p.n]; + return s; +} + +// These routines do not take a format string + +// Fprint formats using the default formats for its operands and writes to w. +// Spaces are added between operands when neither is a string. +func Fprint(w io.Writer, a ...) (n int, error os.Error) { + v := reflect.NewValue(a).(reflect.StructValue); + p := newPrinter(); + p.doprint(v, false, false); + n, error = w.Write(p.buf[0:p.n]); + return n, error; +} + +// Print formats using the default formats for its operands and writes to standard output. +// Spaces are added between operands when neither is a string. +func Print(v ...) (n int, errno os.Error) { + n, errno = Fprint(os.Stdout, v); + return n, errno; +} + +// Sprint formats using the default formats for its operands and returns the resulting string. +// Spaces are added between operands when neither is a string. +func Sprint(a ...) string { + v := reflect.NewValue(a).(reflect.StructValue); + p := newPrinter(); + p.doprint(v, false, false); + s := string(p.buf)[0 : p.n]; + return s; +} + +// These routines end in 'ln', do not take a format string, +// always add spaces between operands, and add a newline +// after the last operand. + +// Fprintln formats using the default formats for its operands and writes to w. +// Spaces are always added between operands and a newline is appended. +func Fprintln(w io.Writer, a ...) (n int, error os.Error) { + v := reflect.NewValue(a).(reflect.StructValue); + p := newPrinter(); + p.doprint(v, true, true); + n, error = w.Write(p.buf[0:p.n]); + return n, error; +} + +// Println formats using the default formats for its operands and writes to standard output. +// Spaces are always added between operands and a newline is appended. +func Println(v ...) (n int, errno os.Error) { + n, errno = Fprintln(os.Stdout, v); + return n, errno; +} + +// Sprintln formats using the default formats for its operands and returns the resulting string. +// Spaces are always added between operands and a newline is appended. +func Sprintln(a ...) string { + v := reflect.NewValue(a).(reflect.StructValue); + p := newPrinter(); + p.doprint(v, true, true); + s := string(p.buf)[0 : p.n]; + return s; +} + + +// Get the i'th arg of the struct value. +// If the arg itself is an interface, return a value for +// the thing inside the interface, not the interface itself. +func getField(v reflect.StructValue, i int) reflect.Value { + val := v.Field(i); + if val.Kind() == reflect.InterfaceKind { + inter := val.(reflect.InterfaceValue).Get(); + return reflect.NewValue(inter); + } + return val; +} + +// Getters for the fields of the argument structure. + +func getBool(v reflect.Value) (val bool, ok bool) { + switch v.Kind() { + case reflect.BoolKind: + return v.(reflect.BoolValue).Get(), true; + } + return false, false +} + +func getInt(v reflect.Value) (val int64, signed, ok bool) { + switch v.Kind() { + case reflect.IntKind: + return int64(v.(reflect.IntValue).Get()), true, true; + case reflect.Int8Kind: + return int64(v.(reflect.Int8Value).Get()), true, true; + case reflect.Int16Kind: + return int64(v.(reflect.Int16Value).Get()), true, true; + case reflect.Int32Kind: + return int64(v.(reflect.Int32Value).Get()), true, true; + case reflect.Int64Kind: + return int64(v.(reflect.Int64Value).Get()), true, true; + case reflect.UintKind: + return int64(v.(reflect.UintValue).Get()), false, true; + case reflect.Uint8Kind: + return int64(v.(reflect.Uint8Value).Get()), false, true; + case reflect.Uint16Kind: + return int64(v.(reflect.Uint16Value).Get()), false, true; + case reflect.Uint32Kind: + return int64(v.(reflect.Uint32Value).Get()), false, true; + case reflect.Uint64Kind: + return int64(v.(reflect.Uint64Value).Get()), false, true; + case reflect.UintptrKind: + return int64(v.(reflect.UintptrValue).Get()), false, true; + } + return 0, false, false; +} + +func getString(v reflect.Value) (val string, ok bool) { + switch v.Kind() { + case reflect.StringKind: + return v.(reflect.StringValue).Get(), true; + case reflect.ArrayKind: + if val, ok := v.Interface().([]byte); ok { + return string(val), true; + } + } + return "", false; +} + +func getFloat32(v reflect.Value) (val float32, ok bool) { + switch v.Kind() { + case reflect.Float32Kind: + return float32(v.(reflect.Float32Value).Get()), true; + case reflect.FloatKind: + if v.Type().Size()*8 == 32 { + return float32(v.(reflect.FloatValue).Get()), true; + } + } + return 0.0, false; +} + +func getFloat64(v reflect.Value) (val float64, ok bool) { + switch v.Kind() { + case reflect.FloatKind: + if v.Type().Size()*8 == 64 { + return float64(v.(reflect.FloatValue).Get()), true; + } + case reflect.Float64Kind: + return float64(v.(reflect.Float64Value).Get()), true; + } + return 0.0, false; +} + +func getPtr(v reflect.Value) (val uintptr, ok bool) { + switch v.Kind() { + case reflect.PtrKind: + return uintptr(v.(reflect.PtrValue).Get()), true; + } + return 0, false; +} + +func getArrayPtr(v reflect.Value) (val reflect.ArrayValue, ok bool) { + if v.Kind() == reflect.PtrKind { + v = v.(reflect.PtrValue).Sub(); + if v.Kind() == reflect.ArrayKind { + return v.(reflect.ArrayValue), true; + } + } + return nil, false; +} + +func getArray(v reflect.Value) (val reflect.ArrayValue, ok bool) { + switch v.Kind() { + case reflect.ArrayKind: + return v.(reflect.ArrayValue), true; + } + return nil, false; +} + +// Convert ASCII to integer. n is 0 (and got is false) if no number present. + +func parsenum(s string, start, end int) (n int, got bool, newi int) { + if start >= end { + return 0, false, end + } + isnum := false; + num := 0; + for '0' <= s[start] && s[start] <= '9' { + num = num*10 + int(s[start] - '0'); + start++; + isnum = true; + } + return num, isnum, start; +} + +func (p *pp) printField(field reflect.Value) (was_string bool) { + inter := field.Interface(); + if inter != nil { + if stringer, ok := inter.(Stringer); ok { + p.addstr(stringer.String()); + return false; // this value is not a string + } + } + s := ""; + switch field.Kind() { + case reflect.BoolKind: + s = p.fmt.Fmt_boolean(field.(reflect.BoolValue).Get()).Str(); + case reflect.IntKind, reflect.Int8Kind, reflect.Int16Kind, reflect.Int32Kind, reflect.Int64Kind: + v, signed, ok := getInt(field); + s = p.fmt.Fmt_d64(v).Str(); + case reflect.UintKind, reflect.Uint8Kind, reflect.Uint16Kind, reflect.Uint32Kind, reflect.Uint64Kind: + v, signed, ok := getInt(field); + s = p.fmt.Fmt_ud64(uint64(v)).Str(); + case reflect.UintptrKind: + v, signed, ok := getInt(field); + p.fmt.sharp = !p.fmt.sharp; // turn 0x on by default + s = p.fmt.Fmt_ux64(uint64(v)).Str(); + case reflect.Float32Kind: + v, ok := getFloat32(field); + s = p.fmt.Fmt_g32(v).Str(); + case reflect.Float64Kind: + v, ok := getFloat64(field); + s = p.fmt.Fmt_g64(v).Str(); + case reflect.FloatKind: + if field.Type().Size()*8 == 32 { + v, ok := getFloat32(field); + s = p.fmt.Fmt_g32(v).Str(); + } else { + v, ok := getFloat64(field); + s = p.fmt.Fmt_g64(v).Str(); + } + case reflect.StringKind: + v, ok := getString(field); + s = p.fmt.Fmt_s(v).Str(); + was_string = true; + case reflect.PtrKind: + if v, ok := getPtr(field); v == 0 { + s = "<nil>" + } else { + // pointer to array? (TODO(r): holdover; delete?) + if a, ok := getArrayPtr(field); ok { + p.addstr("&["); + for i := 0; i < a.Len(); i++ { + if i > 0 { + p.addstr(" "); + } + p.printField(a.Elem(i)); + } + p.addstr("]"); + } else { + p.fmt.sharp = !p.fmt.sharp; // turn 0x on by default + s = p.fmt.Fmt_uX64(uint64(v)).Str(); + } + } + case reflect.ArrayKind: + if a, ok := getArray(field); ok { + p.addstr("["); + for i := 0; i < a.Len(); i++ { + if i > 0 { + p.addstr(" "); + } + p.printField(a.Elem(i)); + } + p.addstr("]"); + } + case reflect.StructKind: + p.add('{'); + v := field.(reflect.StructValue); + t := v.Type().(reflect.StructType); + donames := p.fmt.plus; + p.fmt.clearflags(); // clear flags for p.printField + for i := 0; i < v.Len(); i++ { + if i > 0 { + p.add(' ') + } + if donames { + if name, typ, tag, off := t.Field(i); name != "" { + p.addstr(name); + p.add('='); + } + } + p.printField(getField(v, i)); + } + p.add('}'); + case reflect.InterfaceKind: + value := field.(reflect.InterfaceValue).Value(); + if value == nil { + s = "<nil>" + } else { + return p.printField(value); + } + default: + s = "?" + field.Type().String() + "?"; + } + p.addstr(s); + return was_string; +} + +func (p *pp) doprintf(format string, v reflect.StructValue) { + p.ensure(len(format)); // a good starting size + end := len(format) - 1; + fieldnum := 0; // we process one field per non-trivial format + for i := 0; i <= end; { + c, w := utf8.DecodeRuneInString(format[i:len(format)]); + if c != '%' || i == end { + p.add(c); + i += w; + continue; + } + i++; + // flags and widths + p.fmt.clearflags(); + F: for ; i < end; i++ { + switch format[i] { + case '#': + p.fmt.sharp = true; + case '0': + p.fmt.zero = true; + case '+': + p.fmt.plus = true; + case '-': + p.fmt.minus = true; + case ' ': + p.fmt.space = true; + default: + break F; + } + } + // do we have 20 (width)? + p.fmt.wid, p.fmt.wid_present, i = parsenum(format, i, end); + // do we have .20 (precision)? + if i < end && format[i] == '.' { + p.fmt.prec, p.fmt.prec_present, i = parsenum(format, i+1, end); + } + c, w = utf8.DecodeRuneInString(format[i:len(format)]); + i += w; + // percent is special - absorbs no operand + if c == '%' { + p.add('%'); // TODO: should we bother with width & prec? + continue; + } + if fieldnum >= v.Len() { // out of operands + p.add('%'); + p.add(c); + p.addstr("(missing)"); + continue; + } + field := getField(v, fieldnum); + fieldnum++; + inter := field.Interface(); + if inter != nil && c != 'T' { // don't want thing to describe itself if we're asking for its type + if formatter, ok := inter.(Format); ok { + formatter.Format(p, c); + continue; + } + } + s := ""; + switch c { + // bool + case 't': + if v, ok := getBool(field); ok { + if v { + s = "true"; + } else { + s = "false"; + } + } else { + goto badtype; + } + + // int + case 'b': + if v, signed, ok := getInt(field); ok { + s = p.fmt.Fmt_b64(uint64(v)).Str() // always unsigned + } else if v, ok := getFloat32(field); ok { + s = p.fmt.Fmt_fb32(v).Str() + } else if v, ok := getFloat64(field); ok { + s = p.fmt.Fmt_fb64(v).Str() + } else { + goto badtype + } + case 'c': + if v, signed, ok := getInt(field); ok { + s = p.fmt.Fmt_c(int(v)).Str() + } else { + goto badtype + } + case 'd': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.Fmt_d64(v).Str() + } else { + s = p.fmt.Fmt_ud64(uint64(v)).Str() + } + } else { + goto badtype + } + case 'o': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.Fmt_o64(v).Str() + } else { + s = p.fmt.Fmt_uo64(uint64(v)).Str() + } + } else { + goto badtype + } + case 'x': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.Fmt_x64(v).Str() + } else { + s = p.fmt.Fmt_ux64(uint64(v)).Str() + } + } else if v, ok := getString(field); ok { + s = p.fmt.Fmt_sx(v).Str(); + } else { + goto badtype + } + case 'X': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.Fmt_X64(v).Str() + } else { + s = p.fmt.Fmt_uX64(uint64(v)).Str() + } + } else if v, ok := getString(field); ok { + s = p.fmt.Fmt_sX(v).Str(); + } else { + goto badtype + } + + // float + case 'e': + if v, ok := getFloat32(field); ok { + s = p.fmt.Fmt_e32(v).Str() + } else if v, ok := getFloat64(field); ok { + s = p.fmt.Fmt_e64(v).Str() + } else { + goto badtype + } + case 'f': + if v, ok := getFloat32(field); ok { + s = p.fmt.Fmt_f32(v).Str() + } else if v, ok := getFloat64(field); ok { + s = p.fmt.Fmt_f64(v).Str() + } else { + goto badtype + } + case 'g': + if v, ok := getFloat32(field); ok { + s = p.fmt.Fmt_g32(v).Str() + } else if v, ok := getFloat64(field); ok { + s = p.fmt.Fmt_g64(v).Str() + } else { + goto badtype + } + + // string + case 's': + if inter != nil { + // if object implements String, use the result. + if stringer, ok := inter.(Stringer); ok { + s = p.fmt.Fmt_s(stringer.String()).Str(); + break; + } + } + if v, ok := getString(field); ok { + s = p.fmt.Fmt_s(v).Str() + } else { + goto badtype + } + case 'q': + if v, ok := getString(field); ok { + s = p.fmt.Fmt_q(v).Str() + } else { + goto badtype + } + + // pointer + case 'p': + if v, ok := getPtr(field); ok { + if v == 0 { + s = "<nil>" + } else { + s = "0x" + p.fmt.Fmt_uX64(uint64(v)).Str() + } + } else { + goto badtype + } + + // arbitrary value; do your best + case 'v': + p.printField(field); + + // the value's type + case 'T': + s = field.Type().String(); + + default: + badtype: + s = "%" + string(c) + "(" + field.Type().String() + ")%"; + } + p.addstr(s); + } + if fieldnum < v.Len() { + p.addstr("?(extra "); + for ; fieldnum < v.Len(); fieldnum++ { + p.addstr(getField(v, fieldnum).Type().String()); + if fieldnum + 1 < v.Len() { + p.addstr(", "); + } + } + p.addstr(")"); + } +} + +func (p *pp) doprint(v reflect.StructValue, addspace, addnewline bool) { + prev_string := false; + for fieldnum := 0; fieldnum < v.Len(); fieldnum++ { + // always add spaces if we're doing println + field := getField(v, fieldnum); + if fieldnum > 0 { + if addspace { + p.add(' ') + } else if field.Kind() != reflect.StringKind && !prev_string{ + // if not doing println, add spaces if neither side is a string + p.add(' ') + } + } + was_string := p.printField(field); + prev_string = was_string; + } + if addnewline { + p.add('\n') + } +} diff --git a/src/pkg/go/ast/Makefile b/src/pkg/go/ast/Makefile new file mode 100644 index 000000000..1fd22ae71 --- /dev/null +++ b/src/pkg/go/ast/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/go/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + ast.$O\ + +O2=\ + format.$O\ + + +phases: a1 a2 +_obj$D/ast.a: phases + +a1: $(O1) + $(AR) grc _obj$D/ast.a ast.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/ast.a format.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/ast.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ast.a + +packages: _obj$D/ast.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/ast.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ast.a diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go new file mode 100644 index 000000000..6cac8ea1a --- /dev/null +++ b/src/pkg/go/ast/ast.go @@ -0,0 +1,772 @@ +// Copyright 2009 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. + +// The AST package declares the types used to represent +// syntax trees for Go source files. +// +package ast + +import ( + "go/token"; + "unicode"; + "utf8"; +) + + +// ---------------------------------------------------------------------------- +// Interfaces +// +// There are 3 main classes of nodes: Expressions and type nodes, +// statement nodes, and declaration nodes. The node names usually +// match the corresponding Go spec production names to which they +// correspond. The node fields correspond to the individual parts +// of the respective productions. +// +// All nodes contain position information marking the beginning of +// the corresponding source text segment; it is accessible via the +// Pos accessor method. Nodes may contain additional position info +// for language constructs where comments may be found between parts +// of the construct (typically any larger, parenthesized subpart). +// That position information is needed to properly position comments +// when printing the construct. + +// TODO: For comment positioning only the byte position and not +// a complete token.Position field is needed. May be able to trim +// node sizes a bit. + + +type ( + ExprVisitor interface; + StmtVisitor interface; + DeclVisitor interface; +) + + +// All expression nodes implement the Expr interface. +type Expr interface { + // For a (dynamic) node type X, calling Visit with an expression + // visitor v invokes the node-specific DoX function of the visitor. + // + Visit(v ExprVisitor); + + // Pos returns the (beginning) position of the expression. + Pos() token.Position; +} + + +// All statement nodes implement the Stmt interface. +type Stmt interface { + // For a (dynamic) node type X, calling Visit with a statement + // visitor v invokes the node-specific DoX function of the visitor. + // + Visit(v StmtVisitor); + + // Pos returns the (beginning) position of the statement. + Pos() token.Position; +} + + +// All declaration nodes implement the Decl interface. +type Decl interface { + // For a (dynamic) node type X, calling Visit with a declaration + // visitor v invokes the node-specific DoX function of the visitor. + // + Visit(v DeclVisitor); + + // Pos returns the (beginning) position of the declaration. + Pos() token.Position; +} + + +// ---------------------------------------------------------------------------- +// Comments + +// A Comment node represents a single //-style or /*-style comment. +type Comment struct { + token.Position; // beginning position of the comment + Text []byte; // the comment text (without '\n' for //-style comments) + EndLine int; // the line where the comment ends +} + + +// A Comments node represents a sequence of single comments +// with no other tokens and no empty lines between. +// +type Comments []*Comment + + +// ---------------------------------------------------------------------------- +// Expressions and types + +// Support types. +type ( + Ident struct; + StringLit struct; + FuncType struct; + BlockStmt struct; + + // A Field represents a Field declaration list in a struct type, + // a method in an interface type, or a parameter/result declaration + // in a signature. + Field struct { + Doc Comments; // associated documentation; or nil + Names []*Ident; // field/method/parameter names; nil if anonymous field + Type Expr; // field/method/parameter type + Tag []*StringLit; // field tag; nil if no tag + }; +) + + +// An expression is represented by a tree consisting of one +// or more of the following concrete expression nodes. +// +type ( + // A BadExpr node is a placeholder for expressions containing + // syntax errors for which no correct expression nodes can be + // created. + // + BadExpr struct { + token.Position; // beginning position of bad expression + }; + + // An Ident node represents an identifier. + Ident struct { + token.Position; // identifier position + Value string; // identifier string (e.g. foobar) + }; + + // An Ellipsis node stands for the "..." type in a + // parameter list or the "..." length in an array type. + // + Ellipsis struct { + token.Position; // position of "..." + }; + + // An IntLit node represents an integer literal. + IntLit struct { + token.Position; // int literal position + Value []byte; // literal string; e.g. 42 or 0x7f + }; + + // A FloatLit node represents a floating-point literal. + FloatLit struct { + token.Position; // float literal position + Value []byte; // literal string; e.g. 3.14 or 1e-9 + }; + + // A CharLit node represents a character literal. + CharLit struct { + token.Position; // char literal position + Value []byte; // literal string, including quotes; e.g. 'a' or '\x7f' + }; + + // A StringLit node represents a string literal. + StringLit struct { + token.Position; // string literal position + Value []byte; // literal string, including quotes; e.g. "foo" or `\m\n\o` + }; + + // A StringList node represents a sequence of adjacent string literals. + // A single string literal (common case) is represented by a StringLit + // node; StringList nodes are used only if there are two or more string + // literals in a sequence. + // + StringList struct { + Strings []*StringLit; // list of strings, len(Strings) > 1 + }; + + // A FuncLit node represents a function literal. + FuncLit struct { + Type *FuncType; // function type + Body *BlockStmt; // function body + }; + + // A CompositeLit node represents a composite literal. + // + CompositeLit struct { + Type Expr; // literal type + Lbrace token.Position; // position of "{" + Elts []Expr; // list of composite elements + Rbrace token.Position; // position of "}" + }; + + // A ParenExpr node represents a parenthesized expression. + ParenExpr struct { + token.Position; // position of "(" + X Expr; // parenthesized expression + Rparen token.Position; // position of ")" + }; + + // A SelectorExpr node represents an expression followed by a selector. + SelectorExpr struct { + X Expr; // expression + Sel *Ident; // field selector + }; + + // An IndexExpr node represents an expression followed by an index or slice. + IndexExpr struct { + X Expr; // expression + Index Expr; // index expression or beginning of slice range + End Expr; // end of slice range; or nil + }; + + // A TypeAssertExpr node represents an expression followed by a + // type assertion. + // + TypeAssertExpr struct { + X Expr; // expression + Type Expr; // asserted type + }; + + // A CallExpr node represents an expression followed by an argument list. + CallExpr struct { + Fun Expr; // function expression + Lparen token.Position; // position of "(" + Args []Expr; // function arguments + Rparen token.Position; // positions of ")" + }; + + // A StarExpr node represents an expression of the form "*" Expression. + // Semantically it could be a unary "*" expression, or a pointer type. + StarExpr struct { + token.Position; // position of "*" + X Expr; // operand + }; + + // A UnaryExpr node represents a unary expression. + // Unary "*" expressions are represented via StarExpr nodes. + // + UnaryExpr struct { + token.Position; // position of Op + Op token.Token; // operator + X Expr; // operand + }; + + // A BinaryExpr node represents a binary expression. + // + BinaryExpr struct { + X Expr; // left operand + OpPos token.Position; // position of Op + Op token.Token; // operator + Y Expr; // right operand + }; + + // A KeyValueExpr node represents (key : value) pairs + // in composite literals. + // + KeyValueExpr struct { + Key Expr; + Colon token.Position; // position of ":" + Value Expr; + }; +) + + +// The direction of a channel type is indicated by one +// of the following constants. +// +type ChanDir int +const ( + SEND ChanDir = 1 << iota; + RECV; +) + + +// A type is represented by a tree consisting of one +// or more of the following type-specific expression +// nodes. +// +type ( + // An ArrayType node represents an array or slice type. + ArrayType struct { + token.Position; // position of "[" + Len Expr; // Ellipsis node for [...]T array types, nil for slice types + Elt Expr; // element type + }; + + // A StructType node represents a struct type. + StructType struct { + token.Position; // position of "struct" keyword + Lbrace token.Position; // position of "{" + Fields []*Field; // list of field declarations; nil if forward declaration + Rbrace token.Position; // position of "}" + }; + + // Pointer types are represented via StarExpr nodes. + + // A FuncType node represents a function type. + FuncType struct { + token.Position; // position of "func" keyword + Params []*Field; // (incoming) parameters + Results []*Field; // (outgoing) results + }; + + // An InterfaceType node represents an interface type. + InterfaceType struct { + token.Position; // position of "interface" keyword + Lbrace token.Position; // position of "{" + Methods []*Field; // list of methods; nil if forward declaration + Rbrace token.Position; // position of "}" + }; + + // A MapType node represents a map type. + MapType struct { + token.Position; // position of "map" keyword + Key Expr; + Value Expr; + }; + + // A ChanType node represents a channel type. + ChanType struct { + token.Position; // position of "chan" keyword or "<-" (whichever comes first) + Dir ChanDir; // channel direction + Value Expr; // value type + }; +) + + +// Pos() implementations for expression/type where the position +// corresponds to the position of a sub-node. +// +func (x *StringList) Pos() token.Position { return x.Strings[0].Pos(); } +func (x *FuncLit) Pos() token.Position { return x.Type.Pos(); } +func (x *CompositeLit) Pos() token.Position { return x.Type.Pos(); } +func (x *SelectorExpr) Pos() token.Position { return x.X.Pos(); } +func (x *IndexExpr) Pos() token.Position { return x.X.Pos(); } +func (x *TypeAssertExpr) Pos() token.Position { return x.X.Pos(); } +func (x *CallExpr) Pos() token.Position { return x.Fun.Pos(); } +func (x *BinaryExpr) Pos() token.Position { return x.X.Pos(); } +func (x *KeyValueExpr) Pos() token.Position { return x.Key.Pos(); } + + +// All expression/type nodes implement a Visit method which takes +// an ExprVisitor as argument. For a given node x of type X, and +// an implementation v of an ExprVisitor, calling x.Visit(v) will +// result in a call of v.DoX(x) (through a double-dispatch). +// +type ExprVisitor interface { + // Expressions + DoBadExpr(x *BadExpr); + DoIdent(x *Ident); + DoIntLit(x *IntLit); + DoFloatLit(x *FloatLit); + DoCharLit(x *CharLit); + DoStringLit(x *StringLit); + DoStringList(x *StringList); + DoFuncLit(x *FuncLit); + DoCompositeLit(x *CompositeLit); + DoParenExpr(x *ParenExpr); + DoSelectorExpr(x *SelectorExpr); + DoIndexExpr(x *IndexExpr); + DoTypeAssertExpr(x *TypeAssertExpr); + DoCallExpr(x *CallExpr); + DoStarExpr(x *StarExpr); + DoUnaryExpr(x *UnaryExpr); + DoBinaryExpr(x *BinaryExpr); + DoKeyValueExpr(x *KeyValueExpr); + + // Type expressions + DoEllipsis(x *Ellipsis); + DoArrayType(x *ArrayType); + DoStructType(x *StructType); + DoFuncType(x *FuncType); + DoInterfaceType(x *InterfaceType); + DoMapType(x *MapType); + DoChanType(x *ChanType); +} + + +// Visit() implementations for all expression/type nodes. +// +func (x *BadExpr) Visit(v ExprVisitor) { v.DoBadExpr(x); } +func (x *Ident) Visit(v ExprVisitor) { v.DoIdent(x); } +func (x *Ellipsis) Visit(v ExprVisitor) { v.DoEllipsis(x); } +func (x *IntLit) Visit(v ExprVisitor) { v.DoIntLit(x); } +func (x *FloatLit) Visit(v ExprVisitor) { v.DoFloatLit(x); } +func (x *CharLit) Visit(v ExprVisitor) { v.DoCharLit(x); } +func (x *StringLit) Visit(v ExprVisitor) { v.DoStringLit(x); } +func (x *StringList) Visit(v ExprVisitor) { v.DoStringList(x); } +func (x *FuncLit) Visit(v ExprVisitor) { v.DoFuncLit(x); } +func (x *CompositeLit) Visit(v ExprVisitor) { v.DoCompositeLit(x); } +func (x *ParenExpr) Visit(v ExprVisitor) { v.DoParenExpr(x); } +func (x *SelectorExpr) Visit(v ExprVisitor) { v.DoSelectorExpr(x); } +func (x *IndexExpr) Visit(v ExprVisitor) { v.DoIndexExpr(x); } +func (x *TypeAssertExpr) Visit(v ExprVisitor) { v.DoTypeAssertExpr(x); } +func (x *CallExpr) Visit(v ExprVisitor) { v.DoCallExpr(x); } +func (x *StarExpr) Visit(v ExprVisitor) { v.DoStarExpr(x); } +func (x *UnaryExpr) Visit(v ExprVisitor) { v.DoUnaryExpr(x); } +func (x *BinaryExpr) Visit(v ExprVisitor) { v.DoBinaryExpr(x); } +func (x *KeyValueExpr) Visit(v ExprVisitor) { v.DoKeyValueExpr(x); } + +func (x *ArrayType) Visit(v ExprVisitor) { v.DoArrayType(x); } +func (x *StructType) Visit(v ExprVisitor) { v.DoStructType(x); } +func (x *FuncType) Visit(v ExprVisitor) { v.DoFuncType(x); } +func (x *InterfaceType) Visit(v ExprVisitor) { v.DoInterfaceType(x); } +func (x *MapType) Visit(v ExprVisitor) { v.DoMapType(x); } +func (x *ChanType) Visit(v ExprVisitor) { v.DoChanType(x); } + + +// IsExported returns whether name is an exported Go symbol +// (i.e., whether it begins with an uppercase letter). +func IsExported(name string) bool { + ch, len := utf8.DecodeRuneInString(name); + return unicode.IsUpper(ch); +} + +// IsExported returns whether name is an exported Go symbol +// (i.e., whether it begins with an uppercase letter). +func (name *ast.Ident) IsExported() bool { + return IsExported(name.Value); +} + +func (name *ast.Ident) String() string { + return name.Value; +} + + +// ---------------------------------------------------------------------------- +// Statements + +// A statement is represented by a tree consisting of one +// or more of the following concrete statement nodes. +// +type ( + // A BadStmt node is a placeholder for statements containing + // syntax errors for which no correct statement nodes can be + // created. + // + BadStmt struct { + token.Position; // beginning position of bad statement + }; + + // A DeclStmt node represents a declaration in a statement list. + DeclStmt struct { + Decl Decl; + }; + + // An EmptyStmt node represents an empty statement. + // The "position" of the empty statement is the position + // of the immediately preceeding semicolon. + // + EmptyStmt struct { + token.Position; // position of preceeding ";" + }; + + // A LabeledStmt node represents a labeled statement. + LabeledStmt struct { + Label *Ident; + Stmt Stmt; + }; + + // An ExprStmt node represents a (stand-alone) expression + // in a statement list. + // + ExprStmt struct { + X Expr; // expression + }; + + // An IncDecStmt node represents an increment or decrement statement. + IncDecStmt struct { + X Expr; + Tok token.Token; // INC or DEC + }; + + // An AssignStmt node represents an assignment or + // a short variable declaration. + AssignStmt struct { + Lhs []Expr; + TokPos token.Position; // position of Tok + Tok token.Token; // assignment token, DEFINE + Rhs []Expr; + }; + + // A GoStmt node represents a go statement. + GoStmt struct { + token.Position; // position of "go" keyword + Call *CallExpr; + }; + + // A DeferStmt node represents a defer statement. + DeferStmt struct { + token.Position; // position of "defer" keyword + Call *CallExpr; + }; + + // A ReturnStmt node represents a return statement. + ReturnStmt struct { + token.Position; // position of "return" keyword + Results []Expr; + }; + + // A BranchStmt node represents a break, continue, goto, + // or fallthrough statement. + // + BranchStmt struct { + token.Position; // position of Tok + Tok token.Token; // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH) + Label *Ident; + }; + + // A BlockStmt node represents a braced statement list. + BlockStmt struct { + token.Position; // position of "{" + List []Stmt; + Rbrace token.Position; // position of "}" + }; + + // An IfStmt node represents an if statement. + IfStmt struct { + token.Position; // position of "if" keyword + Init Stmt; + Cond Expr; + Body *BlockStmt; + Else Stmt; + }; + + // A CaseClause represents a case of an expression switch statement. + CaseClause struct { + token.Position; // position of "case" or "default" keyword + Values []Expr; // nil means default case + Colon token.Position; // position of ":" + Body []Stmt; // statement list; or nil + }; + + // A SwitchStmt node represents an expression switch statement. + SwitchStmt struct { + token.Position; // position of "switch" keyword + Init Stmt; + Tag Expr; + Body *BlockStmt; // CaseClauses only + }; + + // A TypeCaseClause represents a case of a type switch statement. + TypeCaseClause struct { + token.Position; // position of "case" or "default" keyword + Type Expr; // nil means default case + Colon token.Position; // position of ":" + Body []Stmt; // statement list; or nil + }; + + // An TypeSwitchStmt node represents a type switch statement. + TypeSwitchStmt struct { + token.Position; // position of "switch" keyword + Init Stmt; + Assign Stmt; // x := y.(type) + Body *BlockStmt; // TypeCaseClauses only + }; + + // A CommClause node represents a case of a select statement. + CommClause struct { + token.Position; // position of "case" or "default" keyword + Tok token.Token; // ASSIGN or DEFINE (valid only if Lhs != nil) + Lhs, Rhs Expr; // Rhs == nil means default case + Colon token.Position; // position of ":" + Body []Stmt; // statement list; or nil + }; + + // An SelectStmt node represents a select statement. + SelectStmt struct { + token.Position; // position of "select" keyword + Body *BlockStmt; // CommClauses only + }; + + // A ForStmt represents a for statement. + ForStmt struct { + token.Position; // position of "for" keyword + Init Stmt; + Cond Expr; + Post Stmt; + Body *BlockStmt; + }; + + // A RangeStmt represents a for statement with a range clause. + RangeStmt struct { + token.Position; // position of "for" keyword + Key, Value Expr; // Value may be nil + TokPos token.Position; // position of Tok + Tok token.Token; // ASSIGN, DEFINE + X Expr; // value to range over + Body *BlockStmt; + }; +) + + +// Pos() implementations for statement nodes where the position +// corresponds to the position of a sub-node. +// +func (s *DeclStmt) Pos() token.Position { return s.Decl.Pos(); } +func (s *LabeledStmt) Pos() token.Position { return s.Label.Pos(); } +func (s *ExprStmt) Pos() token.Position { return s.X.Pos(); } +func (s *IncDecStmt) Pos() token.Position { return s.X.Pos(); } +func (s *AssignStmt) Pos() token.Position { return s.Lhs[0].Pos(); } + + +// All statement nodes implement a Visit method which takes +// a StmtVisitor as argument. For a given node x of type X, and +// an implementation v of a StmtVisitor, calling x.Visit(v) will +// result in a call of v.DoX(x) (through a double-dispatch). +// +type StmtVisitor interface { + DoBadStmt(s *BadStmt); + DoDeclStmt(s *DeclStmt); + DoEmptyStmt(s *EmptyStmt); + DoLabeledStmt(s *LabeledStmt); + DoExprStmt(s *ExprStmt); + DoIncDecStmt(s *IncDecStmt); + DoAssignStmt(s *AssignStmt); + DoGoStmt(s *GoStmt); + DoDeferStmt(s *DeferStmt); + DoReturnStmt(s *ReturnStmt); + DoBranchStmt(s *BranchStmt); + DoBlockStmt(s *BlockStmt); + DoIfStmt(s *IfStmt); + DoCaseClause(s *CaseClause); + DoSwitchStmt(s *SwitchStmt); + DoTypeCaseClause(s *TypeCaseClause); + DoTypeSwitchStmt(s *TypeSwitchStmt); + DoCommClause(s *CommClause); + DoSelectStmt(s *SelectStmt); + DoForStmt(s *ForStmt); + DoRangeStmt(s *RangeStmt); +} + + +// Visit() implementations for all statement nodes. +// +func (s *BadStmt) Visit(v StmtVisitor) { v.DoBadStmt(s); } +func (s *DeclStmt) Visit(v StmtVisitor) { v.DoDeclStmt(s); } +func (s *EmptyStmt) Visit(v StmtVisitor) { v.DoEmptyStmt(s); } +func (s *LabeledStmt) Visit(v StmtVisitor) { v.DoLabeledStmt(s); } +func (s *ExprStmt) Visit(v StmtVisitor) { v.DoExprStmt(s); } +func (s *IncDecStmt) Visit(v StmtVisitor) { v.DoIncDecStmt(s); } +func (s *AssignStmt) Visit(v StmtVisitor) { v.DoAssignStmt(s); } +func (s *GoStmt) Visit(v StmtVisitor) { v.DoGoStmt(s); } +func (s *DeferStmt) Visit(v StmtVisitor) { v.DoDeferStmt(s); } +func (s *ReturnStmt) Visit(v StmtVisitor) { v.DoReturnStmt(s); } +func (s *BranchStmt) Visit(v StmtVisitor) { v.DoBranchStmt(s); } +func (s *BlockStmt) Visit(v StmtVisitor) { v.DoBlockStmt(s); } +func (s *IfStmt) Visit(v StmtVisitor) { v.DoIfStmt(s); } +func (s *CaseClause) Visit(v StmtVisitor) { v.DoCaseClause(s); } +func (s *SwitchStmt) Visit(v StmtVisitor) { v.DoSwitchStmt(s); } +func (s *TypeCaseClause) Visit(v StmtVisitor) { v.DoTypeCaseClause(s); } +func (s *TypeSwitchStmt) Visit(v StmtVisitor) { v.DoTypeSwitchStmt(s); } +func (s *CommClause) Visit(v StmtVisitor) { v.DoCommClause(s); } +func (s *SelectStmt) Visit(v StmtVisitor) { v.DoSelectStmt(s); } +func (s *ForStmt) Visit(v StmtVisitor) { v.DoForStmt(s); } +func (s *RangeStmt) Visit(v StmtVisitor) { v.DoRangeStmt(s); } + + +// ---------------------------------------------------------------------------- +// Declarations + +// A Spec node represents a single (non-parenthesized) import, +// constant, type, or variable declaration. +// +type ( + // The Spec type stands for any of *ImportSpec, *ValueSpec, and *TypeSpec. + Spec interface {}; + + // An ImportSpec node represents a single package import. + ImportSpec struct { + Doc Comments; // associated documentation; or nil + Name *Ident; // local package name (including "."); or nil + Path []*StringLit; // package path + }; + + // A ValueSpec node represents a constant or variable declaration + // (ConstSpec or VarSpec production). + ValueSpec struct { + Doc Comments; // associated documentation; or nil + Names []*Ident; + Type Expr; // value type; or nil + Values []Expr; + }; + + // A TypeSpec node represents a type declaration (TypeSpec production). + TypeSpec struct { + Doc Comments; // associated documentation; or nil + Name *Ident; // type name + Type Expr; + }; +) + + +// A declaration is represented by one of the following declaration nodes. +// +type ( + // A BadDecl node is a placeholder for declarations containing + // syntax errors for which no correct declaration nodes can be + // created. + // + BadDecl struct { + token.Position; // beginning position of bad declaration + }; + + // A GenDecl node (generic declaration node) represents an import, + // constant, type or variable declaration. A valid Lparen position + // (Lparen.Line > 0) indicates a parenthesized declaration. + // + // Relationship between Tok value and Specs element type: + // + // token.IMPORT *ImportSpec + // token.CONST *ValueSpec + // token.TYPE *TypeSpec + // token.VAR *ValueSpec + // + GenDecl struct { + Doc Comments; // associated documentation; or nil + token.Position; // position of Tok + Tok token.Token; // IMPORT, CONST, TYPE, VAR + Lparen token.Position; // position of '(', if any + Specs []Spec; + Rparen token.Position; // position of ')', if any + }; + + // A FuncDecl node represents a function declaration. + FuncDecl struct { + Doc Comments; // associated documentation; or nil + Recv *Field; // receiver (methods); or nil (functions) + Name *Ident; // function/method name + Type *FuncType; // position of Func keyword, parameters and results + Body *BlockStmt; // function body; or nil (forward declaration) + }; +) + + +// The position of a FuncDecl node is the position of its function type. +func (d *FuncDecl) Pos() token.Position { return d.Type.Pos(); } + + +// All declaration nodes implement a Visit method which takes +// a DeclVisitor as argument. For a given node x of type X, and +// an implementation v of a DeclVisitor, calling x.Visit(v) will +// result in a call of v.DoX(x) (through a double-dispatch). +// +type DeclVisitor interface { + DoBadDecl(d *BadDecl); + DoGenDecl(d *GenDecl); + DoFuncDecl(d *FuncDecl); +} + + +// Visit() implementations for all declaration nodes. +// +func (d *BadDecl) Visit(v DeclVisitor) { v.DoBadDecl(d); } +func (d *GenDecl) Visit(v DeclVisitor) { v.DoGenDecl(d); } +func (d *FuncDecl) Visit(v DeclVisitor) { v.DoFuncDecl(d); } + + +// ---------------------------------------------------------------------------- +// Programs + +// A Program node represents the root node of an AST +// for an entire source file. +// +type Program struct { + Doc Comments; // associated documentation; or nil + token.Position; // position of "package" keyword + Name *Ident; // package name + Decls []Decl; // top-level declarations + Comments []*Comment; // list of unassociated comments +} diff --git a/src/pkg/go/ast/format.go b/src/pkg/go/ast/format.go new file mode 100644 index 000000000..caeca19aa --- /dev/null +++ b/src/pkg/go/ast/format.go @@ -0,0 +1,123 @@ +// Copyright 2009 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 ast + +import ( + "datafmt"; + "go/ast"; + "go/token"; + "io"; + "os"; +) + + +// Format is a customized datafmt.Format for printing of ASTs. +type Format datafmt.Format; + + +// ---------------------------------------------------------------------------- +// Custom formatters + +// The AST-specific formatting state is maintained by a state variable. +type state struct { + // for now we have very little state + // TODO maintain list of unassociated comments + optSemi bool +} + + +func (s *state) Copy() datafmt.Environment { + copy := *s; + return © +} + + +func isValidPos(s *datafmt.State, value interface{}, ruleName string) bool { + pos := value.(token.Position); + return pos.IsValid(); +} + + +func isSend(s *datafmt.State, value interface{}, ruleName string) bool { + return value.(ast.ChanDir) & ast.SEND != 0; +} + + +func isRecv(s *datafmt.State, value interface{}, ruleName string) bool { + return value.(ast.ChanDir) & ast.RECV != 0; +} + + +func isMultiLineComment(s *datafmt.State, value interface{}, ruleName string) bool { + return value.([]byte)[1] == '*'; +} + + +func clearOptSemi(s *datafmt.State, value interface{}, ruleName string) bool { + s.Env().(*state).optSemi = false; + return true; +} + + +func setOptSemi(s *datafmt.State, value interface{}, ruleName string) bool { + s.Env().(*state).optSemi = true; + return true; +} + + +func optSemi(s *datafmt.State, value interface{}, ruleName string) bool { + if !s.Env().(*state).optSemi { + s.Write([]byte{';'}); + } + return true; +} + + +var fmap = datafmt.FormatterMap { + "isValidPos": isValidPos, + "isSend": isSend, + "isRecv": isRecv, + "isMultiLineComment": isMultiLineComment, + "/": clearOptSemi, + "clearOptSemi": clearOptSemi, + "setOptSemi": setOptSemi, + "optSemi": optSemi, +} + + +// ---------------------------------------------------------------------------- +// Printing + +// NewFormat parses a datafmt format specification from a file +// and adds AST-specific custom formatter rules. The result is +// the customized format or an os.Error, if any. +// +func NewFormat(filename string) (Format, os.Error) { + src, err := io.ReadFile(filename); + if err != nil { + return nil, err; + } + f, err := datafmt.Parse(src, fmap); + return Format(f), err; +} + + +// Fprint formats each AST node provided as argument according to the +// format f and writes to standard output. The result is the total number +// of bytes written and an os.Error, if any. +// +func (f Format) Fprint(w io.Writer, nodes ...) (int, os.Error) { + var s state; + return datafmt.Format(f).Fprint(w, &s, nodes); +} + + +// Fprint formats each AST node provided as argument according to the +// format f and writes to w. The result is the total number of bytes +// written and an os.Error, if any. +// +func (f Format) Print(nodes ...) (int, os.Error) { + return f.Fprint(os.Stdout, nodes); +} diff --git a/src/pkg/go/doc/Makefile b/src/pkg/go/doc/Makefile new file mode 100644 index 000000000..d7c6acaac --- /dev/null +++ b/src/pkg/go/doc/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/go/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + comment.$O\ + +O2=\ + doc.$O\ + + +phases: a1 a2 +_obj$D/doc.a: phases + +a1: $(O1) + $(AR) grc _obj$D/doc.a comment.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/doc.a doc.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/doc.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/doc.a + +packages: _obj$D/doc.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/doc.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/doc.a diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go new file mode 100644 index 000000000..19a65a227 --- /dev/null +++ b/src/pkg/go/doc/comment.go @@ -0,0 +1,310 @@ +// Copyright 2009 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. + +// Godoc comment extraction and comment -> HTML formatting. + +package doc + +import ( + "fmt"; + "io"; + "once"; + "regexp"; + "strings"; + "template"; // for htmlEscape +) + +// Comment extraction + +var ( + comment_markers *regexp.Regexp; + trailing_whitespace *regexp.Regexp; + comment_junk *regexp.Regexp; +) + +func makeRex(s string) *regexp.Regexp { + re, err := regexp.Compile(s); + if err != nil { + panic("MakeRegexp ", s, " ", err.String()); + } + return re; +} + +// TODO(rsc): Cannot use var initialization for regexps, +// because Regexp constructor needs threads. +func setupRegexps() { + comment_markers = makeRex("^/(/|\\*) ?"); + trailing_whitespace = makeRex("[ \t\r]+$"); + comment_junk = makeRex("^[ \t]*(/\\*|\\*/)[ \t]*$"); +} + +// Aggregate comment text, without comment markers. +func commentText(comments []string) string { + once.Do(setupRegexps); + lines := make([]string, 0, 20); + for i, c := range comments { + // split on newlines + cl := strings.Split(c, "\n"); + + // walk lines, stripping comment markers + w := 0; + for j, l := range cl { + // remove /* and */ lines + if comment_junk.Match(l) { + continue; + } + + // strip trailing white space + m := trailing_whitespace.Execute(l); + if len(m) > 0 { + l = l[0 : m[1]]; + } + + // strip leading comment markers + m = comment_markers.Execute(l); + if len(m) > 0 { + l = l[m[1] : len(l)]; + } + + // throw away leading blank lines + if w == 0 && l == "" { + continue; + } + + cl[w] = l; + w++; + } + + // throw away trailing blank lines + for w > 0 && cl[w-1] == "" { + w--; + } + cl = cl[0 : w]; + + // add this comment to total list + // TODO: maybe separate with a single blank line + // if there is already a comment and len(cl) > 0? + for j, l := range cl { + n := len(lines); + if n+1 >= cap(lines) { + newlines := make([]string, n, 2*cap(lines)); + for k := range newlines { + newlines[k] = lines[k]; + } + lines = newlines; + } + lines = lines[0 : n+1]; + lines[n] = l; + } + } + + // add final "" entry to get trailing newline. + // loop always leaves room for one more. + n := len(lines); + lines = lines[0 : n+1]; + + return strings.Join(lines, "\n"); +} + +// Split bytes into lines. +func split(text []byte) [][]byte { + // count lines + n := 0; + last := 0; + for i, c := range text { + if c == '\n' { + last = i+1; + n++; + } + } + if last < len(text) { + n++; + } + + // split + out := make([][]byte, n); + last = 0; + n = 0; + for i, c := range text { + if c == '\n' { + out[n] = text[last : i+1]; + last = i+1; + n++; + } + } + if last < len(text) { + out[n] = text[last : len(text)]; + } + + return out; +} + + +var ( + ldquo = io.StringBytes("“"); + rdquo = io.StringBytes("”"); +) + +// Escape comment text for HTML. +// Also, turn `` into “ and '' into ”. +func commentEscape(w io.Writer, s []byte) { + last := 0; + for i := 0; i < len(s)-1; i++ { + if s[i] == s[i+1] && (s[i] == '`' || s[i] == '\'') { + template.HtmlEscape(w, s[last : i]); + last = i+2; + switch s[i] { + case '`': + w.Write(ldquo); + case '\'': + w.Write(rdquo); + } + i++; // loop will add one more + } + } + template.HtmlEscape(w, s[last : len(s)]); +} + + +var ( + html_p = io.StringBytes("<p>\n"); + html_endp = io.StringBytes("</p>\n"); + html_pre = io.StringBytes("<pre>"); + html_endpre = io.StringBytes("</pre>\n"); +) + + +func indentLen(s []byte) int { + i := 0; + for i < len(s) && (s[i] == ' ' || s[i] == '\t') { + i++; + } + return i; +} + + +func isBlank(s []byte) bool { + return len(s) == 0 || (len(s) == 1 && s[0] == '\n') +} + + +func commonPrefix(a, b []byte) []byte { + i := 0; + for i < len(a) && i < len(b) && a[i] == b[i] { + i++; + } + return a[0 : i]; +} + + +func unindent(block [][]byte) { + if len(block) == 0 { + return; + } + + // compute maximum common white prefix + prefix := block[0][0 : indentLen(block[0])]; + for i, line := range block { + if !isBlank(line) { + prefix = commonPrefix(prefix, line[0 : indentLen(line)]); + } + } + n := len(prefix); + + // remove + for i, line := range block { + if !isBlank(line) { + block[i] = line[n : len(line)]; + } + } +} + + +// Convert comment text to formatted HTML. +// The comment was prepared by DocReader, +// so it is known not to have leading, trailing blank lines +// nor to have trailing spaces at the end of lines. +// The comment markers have already been removed. +// +// Turn each run of multiple \n into </p><p> +// Turn each run of indented lines into <pre> without indent. +// +// TODO(rsc): I'd like to pass in an array of variable names []string +// and then italicize those strings when they appear as words. +func ToHtml(w io.Writer, s []byte) { + inpara := false; + + /* TODO(rsc): 6g cant generate code for these + close := func() { + if inpara { + w.Write(html_endp); + inpara = false; + } + }; + open := func() { + if !inpara { + w.Write(html_p); + inpara = true; + } + }; + */ + + lines := split(s); + unindent(lines); + for i := 0; i < len(lines); { + line := lines[i]; + if isBlank(line) { + // close paragraph + if inpara { + w.Write(html_endp); + inpara = false; + } + i++; + continue; + } + if indentLen(line) > 0 { + // close paragraph + if inpara { + w.Write(html_endp); + inpara = false; + } + + // count indented or blank lines + j := i+1; + for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) { + j++; + } + // but not trailing blank lines + for j > i && isBlank(lines[j-1]) { + j--; + } + block := lines[i : j]; + i = j; + + unindent(block); + + // put those lines in a pre block. + // they don't get the nice text formatting, + // just html escaping + w.Write(html_pre); + for k, line := range block { + template.HtmlEscape(w, line); + } + w.Write(html_endpre); + continue; + } + // open paragraph + if !inpara { + w.Write(html_p); + inpara = true; + } + commentEscape(w, lines[i]); + i++; + } + if inpara { + w.Write(html_endp); + inpara = false; + } +} + diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go new file mode 100644 index 000000000..03872fd14 --- /dev/null +++ b/src/pkg/go/doc/doc.go @@ -0,0 +1,486 @@ +// Copyright 2009 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 doc + +import ( + "container/vector"; + "fmt"; + "go/ast"; + "go/doc"; + "go/token"; + "io"; + "regexp"; + "sort"; + "strings"; +) + + +// ---------------------------------------------------------------------------- +// Elementary support + +func hasExportedNames(names []*ast.Ident) bool { + for i, name := range names { + if name.IsExported() { + return true; + } + } + return false; +} + + +func hasExportedSpecs(specs []ast.Spec) bool { + for i, s := range specs { + // only called for []astSpec lists of *ast.ValueSpec + return hasExportedNames(s.(*ast.ValueSpec).Names); + } + return false; +} + + +// ---------------------------------------------------------------------------- + +type typeDoc struct { + decl *ast.GenDecl; // len(decl.Specs) == 1, and the element type is *ast.TypeSpec + factories map[string] *ast.FuncDecl; + methods map[string] *ast.FuncDecl; +} + + +// DocReader accumulates documentation for a single package. +// +type DocReader struct { + name string; // package name + path string; // import path + doc ast.Comments; // package documentation, if any + consts *vector.Vector; // list of *ast.GenDecl + types map[string] *typeDoc; + vars *vector.Vector; // list of *ast.GenDecl + funcs map[string] *ast.FuncDecl; +} + + +// Init initializes a DocReader to collect package documentation +// for the package with the given package name and import path. +// +func (doc *DocReader) Init(pkg, imp string) { + doc.name = pkg; + doc.path = imp; + doc.consts = vector.New(0); + doc.types = make(map[string] *typeDoc); + doc.vars = vector.New(0); + doc.funcs = make(map[string] *ast.FuncDecl); +} + + +func baseTypeName(typ ast.Expr) string { + switch t := typ.(type) { + case *ast.Ident: + return string(t.Value); + case *ast.StarExpr: + return baseTypeName(t.X); + } + return ""; +} + + +func (doc *DocReader) lookupTypeDoc(typ ast.Expr) *typeDoc { + tdoc, found := doc.types[baseTypeName(typ)]; + if found { + return tdoc; + } + return nil; +} + + +func (doc *DocReader) addType(decl *ast.GenDecl) { + typ := decl.Specs[0].(*ast.TypeSpec); + name := typ.Name.Value; + tdoc := &typeDoc{decl, make(map[string] *ast.FuncDecl), make(map[string] *ast.FuncDecl)}; + doc.types[name] = tdoc; +} + + +func (doc *DocReader) addFunc(fun *ast.FuncDecl) { + name := fun.Name.Value; + + // determine if it should be associated with a type + var typ *typeDoc; + if fun.Recv != nil { + // method + // (all receiver types must be declared before they are used) + typ = doc.lookupTypeDoc(fun.Recv.Type); + if typ != nil { + // type found (i.e., exported) + typ.methods[name] = fun; + } + // if the type wasn't found, it wasn't exported + // TODO(gri): a non-exported type may still have exported functions + // determine what to do in that case + return; + } + + // perhaps a factory function + // determine result type, if any + if len(fun.Type.Results) >= 1 { + res := fun.Type.Results[0]; + if len(res.Names) <= 1 { + // exactly one (named or anonymous) result type + typ = doc.lookupTypeDoc(res.Type); + if typ != nil { + typ.factories[name] = fun; + return; + } + } + } + + // ordinary function + doc.funcs[name] = fun; +} + + +func (doc *DocReader) addDecl(decl ast.Decl) { + switch d := decl.(type) { + case *ast.GenDecl: + if len(d.Specs) > 0 { + switch d.Tok { + case token.IMPORT: + // ignore + case token.CONST: + // constants are always handled as a group + if hasExportedSpecs(d.Specs) { + doc.consts.Push(d); + } + case token.TYPE: + // types are handled individually + for i, spec := range d.Specs { + s := spec.(*ast.TypeSpec); + if s.Name.IsExported() { + // make a (fake) GenDecl node for this TypeSpec + // (we need to do this here - as opposed to just + // for printing - so we don't loose the GenDecl + // documentation) + var noPos token.Position; + doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos}); + } + } + case token.VAR: + // variables are always handled as a group + if hasExportedSpecs(d.Specs) { + doc.vars.Push(d); + } + } + } + case *ast.FuncDecl: + if d.Name.IsExported() { + doc.addFunc(d); + } + } +} + + +// AddProgram adds the AST for a source file to the DocReader. +// Adding the same AST multiple times is a no-op. +// +func (doc *DocReader) AddProgram(prog *ast.Program) { + if doc.name != prog.Name.Value { + panic("package names don't match"); + } + + // add package documentation + // TODO(gri) what to do if there are multiple files? + if prog.Doc != nil { + doc.doc = prog.Doc + } + + // add all exported declarations + for i, decl := range prog.Decls { + doc.addDecl(decl); + } +} + +// ---------------------------------------------------------------------------- +// Conversion to external representation + +func astComment(comments ast.Comments) string { + text := make([]string, len(comments)); + for i, c := range comments { + text[i] = string(c.Text); + } + return commentText(text); +} + +// ValueDoc is the documentation for a group of declared +// values, either vars or consts. +// +type ValueDoc struct { + Doc string; + Decl *ast.GenDecl; + order int; +} + +type sortValueDoc []*ValueDoc +func (p sortValueDoc) Len() int { return len(p); } +func (p sortValueDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } + + +func declName(d *ast.GenDecl) string { + if len(d.Specs) != 1 { + return "" + } + + switch v := d.Specs[0].(type) { + case *ast.ValueSpec: + return v.Names[0].Value; + case *ast.TypeSpec: + return v.Name.Value; + } + + return ""; +} + + +func (p sortValueDoc) Less(i, j int) bool { + // sort by name + // pull blocks (name = "") up to top + // in original order + if ni, nj := declName(p[i].Decl), declName(p[j].Decl); ni != nj { + return ni < nj; + } + return p[i].order < p[j].order; +} + + +func makeValueDocs(v *vector.Vector) []*ValueDoc { + d := make([]*ValueDoc, v.Len()); + for i := range d { + decl := v.At(i).(*ast.GenDecl); + d[i] = &ValueDoc{astComment(decl.Doc), decl, i}; + } + sort.Sort(sortValueDoc(d)); + return d; +} + + +// FuncDoc is the documentation for a func declaration, +// either a top-level function or a method function. +// +type FuncDoc struct { + Doc string; + Recv ast.Expr; // TODO(rsc): Would like string here + Name string; + Decl *ast.FuncDecl; +} + +type sortFuncDoc []*FuncDoc +func (p sortFuncDoc) Len() int { return len(p); } +func (p sortFuncDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } +func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name; } + + +func makeFuncDocs(m map[string] *ast.FuncDecl) []*FuncDoc { + d := make([]*FuncDoc, len(m)); + i := 0; + for name, f := range m { + doc := new(FuncDoc); + doc.Doc = astComment(f.Doc); + if f.Recv != nil { + doc.Recv = f.Recv.Type; + } + doc.Name = f.Name.Value; + doc.Decl = f; + d[i] = doc; + i++; + } + sort.Sort(sortFuncDoc(d)); + return d; +} + + +// TypeDoc is the documentation for a declared type. +// Factories is a sorted list of factory functions that return that type. +// Methods is a sorted list of method functions on that type. +type TypeDoc struct { + Doc string; + Type *ast.TypeSpec; + Factories []*FuncDoc; + Methods []*FuncDoc; + Decl *ast.GenDecl; + order int; +} + +type sortTypeDoc []*TypeDoc +func (p sortTypeDoc) Len() int { return len(p); } +func (p sortTypeDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } +func (p sortTypeDoc) Less(i, j int) bool { + // sort by name + // pull blocks (name = "") up to top + // in original order + if ni, nj := p[i].Type.Name.Value, p[j].Type.Name.Value; ni != nj { + return ni < nj; + } + return p[i].order < p[j].order; +} + + +// NOTE(rsc): This would appear not to be correct for type ( ) +// blocks, but the doc extractor above has split them into +// individual statements. +func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc { + d := make([]*TypeDoc, len(m)); + i := 0; + for name, old := range m { + typespec := old.decl.Specs[0].(*ast.TypeSpec); + t := new(TypeDoc); + t.Doc = astComment(typespec.Doc); + t.Type = typespec; + t.Factories = makeFuncDocs(old.factories); + t.Methods = makeFuncDocs(old.methods); + t.Decl = old.decl; + t.order = i; + d[i] = t; + i++; + } + sort.Sort(sortTypeDoc(d)); + return d; +} + + +// PackageDoc is the documentation for an entire package. +// +type PackageDoc struct { + PackageName string; + ImportPath string; + Doc string; + Consts []*ValueDoc; + Types []*TypeDoc; + Vars []*ValueDoc; + Funcs []*FuncDoc; +} + + +// Doc returns the accumulated documentation for the package. +// +func (doc *DocReader) Doc() *PackageDoc { + p := new(PackageDoc); + p.PackageName = doc.name; + p.ImportPath = doc.path; + p.Doc = astComment(doc.doc); + p.Consts = makeValueDocs(doc.consts); + p.Vars = makeValueDocs(doc.vars); + p.Types = makeTypeDocs(doc.types); + p.Funcs = makeFuncDocs(doc.funcs); + return p; +} + + +// ---------------------------------------------------------------------------- +// Filtering by name + +// Does s look like a regular expression? +func isRegexp(s string) bool { + metachars := ".(|)*+?^$[]"; + for i, c := range s { + for j, m := range metachars { + if c == m { + return true + } + } + } + return false +} + + +func match(s string, a []string) bool { + for i, t := range a { + if isRegexp(t) { + if matched, err := regexp.Match(t, s); matched { + return true; + } + } + if s == t { + return true; + } + } + return false; +} + + +func matchDecl(d *ast.GenDecl, names []string) bool { + for i, d := range d.Specs { + switch v := d.(type) { + case *ast.ValueSpec: + for j, name := range v.Names { + if match(name.Value, names) { + return true; + } + } + case *ast.TypeSpec: + if match(v.Name.Value, names) { + return true; + } + } + } + return false; +} + + +func filterValueDocs(a []*ValueDoc, names []string) []*ValueDoc { + w := 0; + for i, vd := range a { + if matchDecl(vd.Decl, names) { + a[w] = vd; + w++; + } + } + return a[0 : w]; +} + + +func filterFuncDocs(a []*FuncDoc, names []string) []*FuncDoc { + w := 0; + for i, fd := range a { + if match(fd.Name, names) { + a[w] = fd; + w++; + } + } + return a[0 : w]; +} + + +func filterTypeDocs(a []*TypeDoc, names []string) []*TypeDoc { + w := 0; + for i, td := range a { + match := false; + if matchDecl(td.Decl, names) { + match = true; + } else { + // type name doesn't match, but we may have matching factories or methods + td.Factories = filterFuncDocs(td.Factories, names); + td.Methods = filterFuncDocs(td.Methods, names); + match = len(td.Factories) > 0 || len(td.Methods) > 0; + } + if match { + a[w] = td; + w++; + } + } + return a[0 : w]; +} + + +// Filter eliminates information from d that is not +// about one of the given names. +// TODO: Recognize "Type.Method" as a name. +// TODO(r): maybe precompile the regexps. +// +func (p *PackageDoc) Filter(names []string) { + p.Consts = filterValueDocs(p.Consts, names); + p.Vars = filterValueDocs(p.Vars, names); + p.Types = filterTypeDocs(p.Types, names); + p.Funcs = filterFuncDocs(p.Funcs, names); + p.Doc = ""; // don't show top-level package doc +} + diff --git a/src/pkg/go/parser/Makefile b/src/pkg/go/parser/Makefile new file mode 100644 index 000000000..08d83646f --- /dev/null +++ b/src/pkg/go/parser/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/go/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + parser.$O\ + + +phases: a1 +_obj$D/parser.a: phases + +a1: $(O1) + $(AR) grc _obj$D/parser.a parser.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/parser.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/parser.a + +packages: _obj$D/parser.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/parser.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/parser.a diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go new file mode 100644 index 000000000..056868695 --- /dev/null +++ b/src/pkg/go/parser/parser.go @@ -0,0 +1,1975 @@ +// Copyright 2009 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. + +// A parser for Go source text. The input is a stream of lexical tokens +// provided via the Scanner interface. The output is an abstract syntax +// tree (AST) representing the Go source. The parser is invoked by calling +// Parse. +// +package parser + +import ( + "container/vector"; + "fmt"; + "go/ast"; + "go/scanner"; + "go/token"; + "io"; + "os"; +) + + +// A parser error is represented by an Error node. The position Pos, if +// valid, points to the beginning of the offending token, and the error +// condition is described by Msg. +// +type Error struct { + Pos token.Position; + Msg string; +} + + +func (e *Error) String() string { + pos := ""; + if e.Pos.IsValid() { + pos = fmt.Sprintf("%d:%d: ", e.Pos.Line, e.Pos.Column); + } + return pos + e.Msg; +} + + +// Parser errors are returned as an ErrorList. +type ErrorList []*Error + + +// ErrorList implements the SortInterface. +func (p ErrorList) Len() int { return len(p); } +func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } +func (p ErrorList) Less(i, j int) bool { return p[i].Pos.Offset < p[j].Pos.Offset; } + + +func (p ErrorList) String() string { + switch len(p) { + case 0: return "unspecified error"; + case 1: return p[0].String(); + } + return fmt.Sprintf("%s (and %d more errors)", p[0].String(), len(p) - 1); +} + + +type interval struct { + beg, end int; +} + + +// The parser structure holds the parser's internal state. +type parser struct { + errors vector.Vector; + scanner scanner.Scanner; + + // Tracing/debugging + mode uint; // parsing mode + trace bool; // == (mode & Trace != 0) + indent uint; // indentation used for tracing output + + // Comments + comments vector.Vector; // list of collected, unassociated comments + last_doc interval; // last comments interval of consecutive comments + + // The next token + pos token.Position; // token position + tok token.Token; // one token look-ahead + lit []byte; // token literal + + // Non-syntactic parser control + opt_semi bool; // true if semicolon separator is optional in statement list + expr_lev int; // < 0: in control clause, >= 0: in expression +}; + + +// noPos is used when there is no corresponding source position for a token +var noPos token.Position; + + +// ---------------------------------------------------------------------------- +// Parsing support + +func (p *parser) printTrace(a ...) { + const dots = + ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "; + const n = uint(len(dots)); + fmt.Printf("%5d:%3d: ", p.pos.Line, p.pos.Column); + i := 2*p.indent; + for ; i > n; i -= n { + fmt.Print(dots); + } + fmt.Print(dots[0 : i]); + fmt.Println(a); +} + + +func trace(p *parser, msg string) *parser { + p.printTrace(msg, "("); + p.indent++; + return p; +} + + +func un/*trace*/(p *parser) { + p.indent--; + p.printTrace(")"); +} + + +func (p *parser) next0() { + // Because of one-token look-ahead, print the previous token + // when tracing as it provides a more readable output. The + // very first token (p.pos.Line == 0) is not initialized (it + // is token.ILLEGAL), so don't print it . + if p.trace && p.pos.Line > 0 { + s := p.tok.String(); + switch { + case p.tok.IsLiteral(): + p.printTrace(s, string(p.lit)); + case p.tok.IsOperator(), p.tok.IsKeyword(): + p.printTrace("\"" + s + "\""); + default: + p.printTrace(s); + } + } + + p.pos, p.tok, p.lit = p.scanner.Scan(); + p.opt_semi = false; +} + + +// Collect a comment in the parser's comment list and return the line +// on which the comment ends. +// +func (p *parser) collectComment() int { + // For /*-style comments, the comment may end on a different line. + // Scan the comment for '\n' chars and adjust the end line accordingly. + // (Note that the position of the next token may be even further down + // as there may be more whitespace lines after the comment.) + endline := p.pos.Line; + if p.lit[1] == '*' { + for _, b := range p.lit { + if b == '\n' { + endline++; + } + } + } + p.comments.Push(&ast.Comment{p.pos, p.lit, endline}); + p.next0(); + + return endline; +} + + +func (p *parser) getComments() interval { + // group adjacent comments, an empty line terminates a group + beg := p.comments.Len(); + endline := p.pos.Line; + for p.tok == token.COMMENT && endline+1 >= p.pos.Line { + endline = p.collectComment(); + } + end := p.comments.Len(); + return interval {beg, end}; +} + + +func (p *parser) getDoc() ast.Comments { + doc := p.last_doc; + n := doc.end - doc.beg; + + if n <= 0 || p.comments.At(doc.end - 1).(*ast.Comment).EndLine + 1 < p.pos.Line { + // no comments or empty line between last comment and current token; + // do not use as documentation + return nil; + } + + // found immediately adjacent comment interval; + // use as documentation + c := make(ast.Comments, n); + for i := 0; i < n; i++ { + c[i] = p.comments.At(doc.beg + i).(*ast.Comment); + } + + // remove comments from the general list + p.comments.Cut(doc.beg, doc.end); + + return c; +} + + +func (p *parser) next() { + p.next0(); + p.last_doc = interval{0, 0}; + for p.tok == token.COMMENT { + p.last_doc = p.getComments(); + } +} + + +// The parser implements scanner.Error. +func (p *parser) Error(pos token.Position, msg string) { + // Don't collect errors that are on the same line as the previous error + // in the hope to reduce the number of spurious errors due to incorrect + // parser synchronization. + if p.errors.Len() == 0 || p.errors.Last().(*Error).Pos.Line != pos.Line { + p.errors.Push(&Error{pos, msg}); + } +} + + +func (p *parser) error_expected(pos token.Position, msg string) { + msg = "expected " + msg; + if pos.Offset == p.pos.Offset { + // the error happened at the current position; + // make the error message more specific + msg += ", found '" + p.tok.String() + "'"; + if p.tok.IsLiteral() { + msg += " " + string(p.lit); + } + } + p.Error(pos, msg); +} + + +func (p *parser) expect(tok token.Token) token.Position { + pos := p.pos; + if p.tok != tok { + p.error_expected(pos, "'" + tok.String() + "'"); + } + p.next(); // make progress in any case + return pos; +} + + +// ---------------------------------------------------------------------------- +// Common productions + +func (p *parser) tryType() ast.Expr; +func (p *parser) parseStringList(x *ast.StringLit) []*ast.StringLit +func (p *parser) parseExpression() ast.Expr; +func (p *parser) parseStatement() ast.Stmt; +func (p *parser) parseDeclaration() ast.Decl; + + +func (p *parser) parseIdent() *ast.Ident { + if p.tok == token.IDENT { + x := &ast.Ident{p.pos, string(p.lit)}; + p.next(); + return x; + } + p.expect(token.IDENT); // use expect() error handling + return &ast.Ident{p.pos, ""}; +} + + +func (p *parser) parseIdentList(x ast.Expr) []*ast.Ident { + if p.trace { + defer un(trace(p, "IdentList")); + } + + list := vector.New(0); + if x == nil { + x = p.parseIdent(); + } + list.Push(x); + for p.tok == token.COMMA { + p.next(); + list.Push(p.parseIdent()); + } + + // convert vector + idents := make([]*ast.Ident, list.Len()); + for i := 0; i < list.Len(); i++ { + idents[i] = list.At(i).(*ast.Ident); + } + + return idents; +} + + +func (p *parser) parseExpressionList() []ast.Expr { + if p.trace { + defer un(trace(p, "ExpressionList")); + } + + list := vector.New(0); + list.Push(p.parseExpression()); + for p.tok == token.COMMA { + p.next(); + list.Push(p.parseExpression()); + } + + // convert list + exprs := make([]ast.Expr, list.Len()); + for i := 0; i < list.Len(); i++ { + exprs[i] = list.At(i).(ast.Expr); + } + + return exprs; +} + + +// ---------------------------------------------------------------------------- +// Types + +func (p *parser) parseType() ast.Expr { + if p.trace { + defer un(trace(p, "Type")); + } + + typ := p.tryType(); + + if typ == nil { + p.error_expected(p.pos, "type"); + p.next(); // make progress + return &ast.BadExpr{p.pos}; + } + + return typ; +} + + +func (p *parser) parseQualifiedIdent() ast.Expr { + if p.trace { + defer un(trace(p, "QualifiedIdent")); + } + + var x ast.Expr = p.parseIdent(); + if p.tok == token.PERIOD { + // first identifier is a package identifier + p.next(); + sel := p.parseIdent(); + x = &ast.SelectorExpr{x, sel}; + } + return x; +} + + +func (p *parser) parseTypeName() ast.Expr { + if p.trace { + defer un(trace(p, "TypeName")); + } + + return p.parseQualifiedIdent(); +} + + +func (p *parser) parseArrayType(ellipsis_ok bool) ast.Expr { + if p.trace { + defer un(trace(p, "ArrayType")); + } + + lbrack := p.expect(token.LBRACK); + var len ast.Expr; + if ellipsis_ok && p.tok == token.ELLIPSIS { + len = &ast.Ellipsis{p.pos}; + p.next(); + } else if p.tok != token.RBRACK { + len = p.parseExpression(); + } + p.expect(token.RBRACK); + elt := p.parseType(); + + return &ast.ArrayType{lbrack, len, elt}; +} + + +func (p *parser) makeIdentList(list *vector.Vector) []*ast.Ident { + idents := make([]*ast.Ident, list.Len()); + for i := 0; i < list.Len(); i++ { + ident, is_ident := list.At(i).(*ast.Ident); + if !is_ident { + pos := list.At(i).(ast.Expr).Pos(); + p.error_expected(pos, "identifier"); + idents[i] = &ast.Ident{pos, ""}; + } + idents[i] = ident; + } + return idents; +} + + +func (p *parser) parseFieldDecl() *ast.Field { + if p.trace { + defer un(trace(p, "FieldDecl")); + } + + doc := p.getDoc(); + + // a list of identifiers looks like a list of type names + list := vector.New(0); + for { + // TODO do not allow ()'s here + list.Push(p.parseType()); + if p.tok == token.COMMA { + p.next(); + } else { + break; + } + } + + // if we had a list of identifiers, it must be followed by a type + typ := p.tryType(); + + // optional tag + var tag []*ast.StringLit; + if p.tok == token.STRING { + tag = p.parseStringList(nil); + } + + // analyze case + var idents []*ast.Ident; + if typ != nil { + // IdentifierList Type + idents = p.makeIdentList(list); + } else { + // Type (anonymous field) + if list.Len() == 1 { + // TODO check that this looks like a type + typ = list.At(0).(ast.Expr); + } else { + p.error_expected(p.pos, "anonymous field"); + typ = &ast.BadExpr{p.pos}; + } + } + + return &ast.Field{doc, idents, typ, tag}; +} + + +func (p *parser) parseStructType() *ast.StructType { + if p.trace { + defer un(trace(p, "StructType")); + } + + pos := p.expect(token.STRUCT); + var lbrace, rbrace token.Position; + var fields []*ast.Field; + if p.tok == token.LBRACE { + lbrace = p.pos; + p.next(); + + list := vector.New(0); + for p.tok != token.RBRACE && p.tok != token.EOF { + list.Push(p.parseFieldDecl()); + if p.tok == token.SEMICOLON { + p.next(); + } else { + break; + } + } + if p.tok == token.SEMICOLON { + p.next(); + } + + rbrace = p.expect(token.RBRACE); + p.opt_semi = true; + + // convert vector + fields = make([]*ast.Field, list.Len()); + for i := list.Len() - 1; i >= 0; i-- { + fields[i] = list.At(i).(*ast.Field); + } + } + + return &ast.StructType{pos, lbrace, fields, rbrace}; +} + + +func (p *parser) parsePointerType() *ast.StarExpr { + if p.trace { + defer un(trace(p, "PointerType")); + } + + star := p.expect(token.MUL); + base := p.parseType(); + + return &ast.StarExpr{star, base}; +} + + +func (p *parser) tryParameterType(ellipsis_ok bool) ast.Expr { + if ellipsis_ok && p.tok == token.ELLIPSIS { + pos := p.pos; + p.next(); + if p.tok != token.RPAREN { + // "..." always must be at the very end of a parameter list + p.Error(pos, "expected type, found '...'"); + } + return &ast.Ellipsis{pos}; + } + return p.tryType(); +} + + +func (p *parser) parseParameterType(ellipsis_ok bool) ast.Expr { + typ := p.tryParameterType(ellipsis_ok); + if typ == nil { + p.error_expected(p.pos, "type"); + p.next(); // make progress + typ = &ast.BadExpr{p.pos}; + } + return typ; +} + + +func (p *parser) parseParameterDecl(ellipsis_ok bool) (*vector.Vector, ast.Expr) { + if p.trace { + defer un(trace(p, "ParameterDecl")); + } + + // a list of identifiers looks like a list of type names + list := vector.New(0); + for { + // TODO do not allow ()'s here + list.Push(p.parseParameterType(ellipsis_ok)); + if p.tok == token.COMMA { + p.next(); + } else { + break; + } + } + + // if we had a list of identifiers, it must be followed by a type + typ := p.tryParameterType(ellipsis_ok); + + return list, typ; +} + + +func (p *parser) parseParameterList(ellipsis_ok bool) []*ast.Field { + if p.trace { + defer un(trace(p, "ParameterList")); + } + + list, typ := p.parseParameterDecl(ellipsis_ok); + if typ != nil { + // IdentifierList Type + idents := p.makeIdentList(list); + list.Init(0); + list.Push(&ast.Field{nil, idents, typ, nil}); + + for p.tok == token.COMMA { + p.next(); + idents := p.parseIdentList(nil); + typ := p.parseParameterType(ellipsis_ok); + list.Push(&ast.Field{nil, idents, typ, nil}); + } + + } else { + // Type { "," Type } (anonymous parameters) + // convert list of types into list of *Param + for i := 0; i < list.Len(); i++ { + list.Set(i, &ast.Field{nil, nil, list.At(i).(ast.Expr), nil}); + } + } + + // convert list + params := make([]*ast.Field, list.Len()); + for i := 0; i < list.Len(); i++ { + params[i] = list.At(i).(*ast.Field); + } + + return params; +} + + +func (p *parser) parseParameters(ellipsis_ok bool) []*ast.Field { + if p.trace { + defer un(trace(p, "Parameters")); + } + + var params []*ast.Field; + p.expect(token.LPAREN); + if p.tok != token.RPAREN { + params = p.parseParameterList(ellipsis_ok); + } + p.expect(token.RPAREN); + + return params; +} + + +func (p *parser) parseResult() []*ast.Field { + if p.trace { + defer un(trace(p, "Result")); + } + + var results []*ast.Field; + if p.tok == token.LPAREN { + results = p.parseParameters(false); + } else if p.tok != token.FUNC { + typ := p.tryType(); + if typ != nil { + results = make([]*ast.Field, 1); + results[0] = &ast.Field{nil, nil, typ, nil}; + } + } + + return results; +} + + +func (p *parser) parseSignature() (params []*ast.Field, results []*ast.Field) { + if p.trace { + defer un(trace(p, "Signature")); + } + + params = p.parseParameters(true); + results = p.parseResult(); + + return params, results; +} + + +func (p *parser) parseFuncType() *ast.FuncType { + if p.trace { + defer un(trace(p, "FuncType")); + } + + pos := p.expect(token.FUNC); + params, results := p.parseSignature(); + + return &ast.FuncType{pos, params, results}; +} + + +func (p *parser) parseMethodSpec() *ast.Field { + if p.trace { + defer un(trace(p, "MethodSpec")); + } + + doc := p.getDoc(); + var idents []*ast.Ident; + var typ ast.Expr; + x := p.parseQualifiedIdent(); + if tmp, is_ident := x.(*ast.Ident); is_ident && (p.tok == token.COMMA || p.tok == token.LPAREN) { + // methods + idents = p.parseIdentList(x); + params, results := p.parseSignature(); + typ = &ast.FuncType{noPos, params, results}; + } else { + // embedded interface + typ = x; + } + + return &ast.Field{doc, idents, typ, nil}; +} + + +func (p *parser) parseInterfaceType() *ast.InterfaceType { + if p.trace { + defer un(trace(p, "InterfaceType")); + } + + pos := p.expect(token.INTERFACE); + var lbrace, rbrace token.Position; + var methods []*ast.Field; + if p.tok == token.LBRACE { + lbrace = p.pos; + p.next(); + + list := vector.New(0); + for p.tok == token.IDENT { + list.Push(p.parseMethodSpec()); + if p.tok != token.RBRACE { + p.expect(token.SEMICOLON); + } + } + + rbrace = p.expect(token.RBRACE); + p.opt_semi = true; + + // convert vector + methods = make([]*ast.Field, list.Len()); + for i := list.Len() - 1; i >= 0; i-- { + methods[i] = list.At(i).(*ast.Field); + } + } + + return &ast.InterfaceType{pos, lbrace, methods, rbrace}; +} + + +func (p *parser) parseMapType() *ast.MapType { + if p.trace { + defer un(trace(p, "MapType")); + } + + pos := p.expect(token.MAP); + p.expect(token.LBRACK); + key := p.parseType(); + p.expect(token.RBRACK); + value := p.parseType(); + + return &ast.MapType{pos, key, value}; +} + + +func (p *parser) parseChanType() *ast.ChanType { + if p.trace { + defer un(trace(p, "ChanType")); + } + + pos := p.pos; + dir := ast.SEND | ast.RECV; + if p.tok == token.CHAN { + p.next(); + if p.tok == token.ARROW { + p.next(); + dir = ast.SEND; + } + } else { + p.expect(token.ARROW); + p.expect(token.CHAN); + dir = ast.RECV; + } + value := p.parseType(); + + return &ast.ChanType{pos, dir, value}; +} + + +func (p *parser) tryRawType(ellipsis_ok bool) ast.Expr { + switch p.tok { + case token.IDENT: return p.parseTypeName(); + case token.LBRACK: return p.parseArrayType(ellipsis_ok); + case token.STRUCT: return p.parseStructType(); + case token.MUL: return p.parsePointerType(); + case token.FUNC: return p.parseFuncType(); + case token.INTERFACE: return p.parseInterfaceType(); + case token.MAP: return p.parseMapType(); + case token.CHAN, token.ARROW: return p.parseChanType(); + case token.LPAREN: + lparen := p.pos; + p.next(); + typ := p.parseType(); + rparen := p.expect(token.RPAREN); + return &ast.ParenExpr{lparen, typ, rparen}; + } + + // no type found + return nil; +} + + +func (p *parser) tryType() ast.Expr { + return p.tryRawType(false); +} + + +// ---------------------------------------------------------------------------- +// Blocks + +func makeStmtList(list *vector.Vector) []ast.Stmt { + stats := make([]ast.Stmt, list.Len()); + for i := 0; i < list.Len(); i++ { + stats[i] = list.At(i).(ast.Stmt); + } + return stats; +} + + +func (p *parser) parseStatementList() []ast.Stmt { + if p.trace { + defer un(trace(p, "StatementList")); + } + + list := vector.New(0); + expect_semi := false; + for p.tok != token.CASE && p.tok != token.DEFAULT && p.tok != token.RBRACE && p.tok != token.EOF { + if expect_semi { + p.expect(token.SEMICOLON); + expect_semi = false; + } + list.Push(p.parseStatement()); + if p.tok == token.SEMICOLON { + p.next(); + } else if p.opt_semi { + p.opt_semi = false; // "consume" optional semicolon + } else { + expect_semi = true; + } + } + + return makeStmtList(list); +} + + +func (p *parser) parseBlockStmt() *ast.BlockStmt { + if p.trace { + defer un(trace(p, "BlockStmt")); + } + + lbrace := p.expect(token.LBRACE); + list := p.parseStatementList(); + rbrace := p.expect(token.RBRACE); + p.opt_semi = true; + + return &ast.BlockStmt{lbrace, list, rbrace}; +} + + +// ---------------------------------------------------------------------------- +// Expressions + +func (p *parser) parseStringList(x *ast.StringLit) []*ast.StringLit { + if p.trace { + defer un(trace(p, "StringList")); + } + + list := vector.New(0); + if x != nil { + list.Push(x); + } + + for p.tok == token.STRING { + list.Push(&ast.StringLit{p.pos, p.lit}); + p.next(); + } + + // convert list + strings := make([]*ast.StringLit, list.Len()); + for i := 0; i < list.Len(); i++ { + strings[i] = list.At(i).(*ast.StringLit); + } + + return strings; +} + + +func (p *parser) parseFuncLit() ast.Expr { + if p.trace { + defer un(trace(p, "FuncLit")); + } + + typ := p.parseFuncType(); + p.expr_lev++; + body := p.parseBlockStmt(); + p.opt_semi = false; // function body requires separating ";" + p.expr_lev--; + + return &ast.FuncLit{typ, body}; +} + + +// parseOperand may return an expression or a raw type (incl. array +// types of the form [...]T. Callers must verify the result. +// +func (p *parser) parseOperand() ast.Expr { + if p.trace { + defer un(trace(p, "Operand")); + } + + switch p.tok { + case token.IDENT: + return p.parseIdent(); + + case token.INT: + x := &ast.IntLit{p.pos, p.lit}; + p.next(); + return x; + + case token.FLOAT: + x := &ast.FloatLit{p.pos, p.lit}; + p.next(); + return x; + + case token.CHAR: + x := &ast.CharLit{p.pos, p.lit}; + p.next(); + return x; + + case token.STRING: + x := &ast.StringLit{p.pos, p.lit}; + p.next(); + if p.tok == token.STRING { + return &ast.StringList{p.parseStringList(x)}; + } + return x; + + case token.LPAREN: + lparen := p.pos; + p.next(); + p.expr_lev++; + x := p.parseExpression(); + p.expr_lev--; + rparen := p.expect(token.RPAREN); + return &ast.ParenExpr{lparen, x, rparen}; + + case token.FUNC: + return p.parseFuncLit(); + + default: + t := p.tryRawType(true); // could be type for composite literal + if t != nil { + return t; + } + } + + p.error_expected(p.pos, "operand"); + p.next(); // make progress + return &ast.BadExpr{p.pos}; +} + + +func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr { + if p.trace { + defer un(trace(p, "SelectorOrTypeAssertion")); + } + + p.expect(token.PERIOD); + if p.tok == token.IDENT { + // selector + sel := p.parseIdent(); + return &ast.SelectorExpr{x, sel}; + } + + // type assertion + p.expect(token.LPAREN); + var typ ast.Expr; + if p.tok == token.TYPE { + // special case for type switch + typ = &ast.Ident{p.pos, "type"}; + p.next(); + } else { + typ = p.parseType(); + } + p.expect(token.RPAREN); + + return &ast.TypeAssertExpr{x, typ}; +} + + +func (p *parser) parseIndex(x ast.Expr) ast.Expr { + if p.trace { + defer un(trace(p, "Index")); + } + + p.expect(token.LBRACK); + p.expr_lev++; + begin := p.parseExpression(); + var end ast.Expr; + if p.tok == token.COLON { + p.next(); + end = p.parseExpression(); + } + p.expr_lev--; + p.expect(token.RBRACK); + + return &ast.IndexExpr{x, begin, end}; +} + + +func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { + if p.trace { + defer un(trace(p, "CallOrConversion")); + } + + lparen := p.expect(token.LPAREN); + var args []ast.Expr; + if p.tok != token.RPAREN { + args = p.parseExpressionList(); + } + rparen := p.expect(token.RPAREN); + + return &ast.CallExpr{fun, lparen, args, rparen}; +} + + +func (p *parser) parseElement() ast.Expr { + if p.trace { + defer un(trace(p, "Element")); + } + + x := p.parseExpression(); + if p.tok == token.COLON { + colon := p.pos; + p.next(); + x = &ast.KeyValueExpr{x, colon, p.parseExpression()}; + } + + return x; +} + + +func (p *parser) parseElementList() []ast.Expr { + if p.trace { + defer un(trace(p, "ElementList")); + } + + list := vector.New(0); + for p.tok != token.RBRACE && p.tok != token.EOF { + list.Push(p.parseElement()); + if p.tok == token.COMMA { + p.next(); + } else { + break; + } + } + + // convert list + elts := make([]ast.Expr, list.Len()); + for i := 0; i < list.Len(); i++ { + elts[i] = list.At(i).(ast.Expr); + } + + return elts; +} + + +func (p *parser) parseCompositeLit(typ ast.Expr) ast.Expr { + if p.trace { + defer un(trace(p, "CompositeLit")); + } + + lbrace := p.expect(token.LBRACE); + var elts []ast.Expr; + if p.tok != token.RBRACE { + elts = p.parseElementList(); + } + rbrace := p.expect(token.RBRACE); + return &ast.CompositeLit{typ, lbrace, elts, rbrace}; +} + + +// TODO Consider different approach to checking syntax after parsing: +// Provide a arguments (set of flags) to parsing functions +// restricting what they are syupposed to accept depending +// on context. + +// checkExpr checks that x is an expression (and not a type). +func (p *parser) checkExpr(x ast.Expr) ast.Expr { + // TODO should provide predicate in AST nodes + switch t := x.(type) { + case *ast.BadExpr: + case *ast.Ident: + case *ast.IntLit: + case *ast.FloatLit: + case *ast.CharLit: + case *ast.StringLit: + case *ast.StringList: + case *ast.FuncLit: + case *ast.CompositeLit: + case *ast.ParenExpr: + case *ast.SelectorExpr: + case *ast.IndexExpr: + case *ast.TypeAssertExpr: + case *ast.CallExpr: + case *ast.StarExpr: + case *ast.UnaryExpr: + if t.Op == token.RANGE { + // the range operator is only allowed at the top of a for statement + p.error_expected(x.Pos(), "expression"); + x = &ast.BadExpr{x.Pos()}; + } + case *ast.BinaryExpr: + default: + // all other nodes are not proper expressions + p.error_expected(x.Pos(), "expression"); + x = &ast.BadExpr{x.Pos()}; + } + return x; +} + + +// isTypeName returns true iff x is type name. +func isTypeName(x ast.Expr) bool { + // TODO should provide predicate in AST nodes + switch t := x.(type) { + case *ast.BadExpr: + case *ast.Ident: + case *ast.ParenExpr: return isTypeName(t.X); // TODO should (TypeName) be illegal? + case *ast.SelectorExpr: return isTypeName(t.X); + default: return false; // all other nodes are not type names + } + return true; +} + + +// isCompositeLitType returns true iff x is a legal composite literal type. +func isCompositeLitType(x ast.Expr) bool { + // TODO should provide predicate in AST nodes + switch t := x.(type) { + case *ast.BadExpr: + case *ast.Ident: + case *ast.ParenExpr: return isCompositeLitType(t.X); + case *ast.SelectorExpr: return isTypeName(t.X); + case *ast.ArrayType: + case *ast.StructType: + case *ast.MapType: + default: return false; // all other nodes are not legal composite literal types + } + return true; +} + + +// checkExprOrType checks that x is an expression or a type +// (and not a raw type such as [...]T). +// +func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { + // TODO should provide predicate in AST nodes + switch t := x.(type) { + case *ast.UnaryExpr: + if t.Op == token.RANGE { + // the range operator is only allowed at the top of a for statement + p.error_expected(x.Pos(), "expression"); + x = &ast.BadExpr{x.Pos()}; + } + case *ast.ArrayType: + if len, is_ellipsis := t.Len.(*ast.Ellipsis); is_ellipsis { + p.Error(len.Pos(), "expected array length, found '...'"); + x = &ast.BadExpr{x.Pos()}; + } + } + + // all other nodes are expressions or types + return x; +} + + +func (p *parser) parsePrimaryExpr() ast.Expr { + if p.trace { + defer un(trace(p, "PrimaryExpr")); + } + + x := p.parseOperand(); +L: for { + switch p.tok { + case token.PERIOD: x = p.parseSelectorOrTypeAssertion(p.checkExpr(x)); + case token.LBRACK: x = p.parseIndex(p.checkExpr(x)); + case token.LPAREN: x = p.parseCallOrConversion(p.checkExprOrType(x)); + case token.LBRACE: + if isCompositeLitType(x) && (p.expr_lev >= 0 || !isTypeName(x)) { + x = p.parseCompositeLit(x); + } else { + break L; + } + default: + break L; + } + } + + return p.checkExprOrType(x); +} + + +func (p *parser) parseUnaryExpr() ast.Expr { + if p.trace { + defer un(trace(p, "UnaryExpr")); + } + + switch p.tok { + case token.ADD, token.SUB, token.NOT, token.XOR, token.ARROW, token.AND, token.RANGE: + pos, op := p.pos, p.tok; + p.next(); + x := p.parseUnaryExpr(); + return &ast.UnaryExpr{pos, op, p.checkExpr(x)}; + + case token.MUL: + // unary "*" expression or pointer type + pos := p.pos; + p.next(); + x := p.parseUnaryExpr(); + return &ast.StarExpr{pos, p.checkExprOrType(x)}; + } + + return p.parsePrimaryExpr(); +} + + +func (p *parser) parseBinaryExpr(prec1 int) ast.Expr { + if p.trace { + defer un(trace(p, "BinaryExpr")); + } + + x := p.parseUnaryExpr(); + for prec := p.tok.Precedence(); prec >= prec1; prec-- { + for p.tok.Precedence() == prec { + pos, op := p.pos, p.tok; + p.next(); + y := p.parseBinaryExpr(prec + 1); + x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}; + } + } + + return x; +} + + +func (p *parser) parseExpression() ast.Expr { + if p.trace { + defer un(trace(p, "Expression")); + } + + return p.parseBinaryExpr(token.LowestPrec + 1); +} + + +// ---------------------------------------------------------------------------- +// Statements + + +func (p *parser) parseSimpleStmt(label_ok bool) ast.Stmt { + if p.trace { + defer un(trace(p, "SimpleStmt")); + } + + x := p.parseExpressionList(); + + switch p.tok { + case token.COLON: + // labeled statement + p.next(); + if label_ok && len(x) == 1 { + if label, is_ident := x[0].(*ast.Ident); is_ident { + return &ast.LabeledStmt{label, p.parseStatement()}; + } + } + p.Error(x[0].Pos(), "illegal label declaration"); + return &ast.BadStmt{x[0].Pos()}; + + case + token.DEFINE, token.ASSIGN, token.ADD_ASSIGN, + token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN, + token.REM_ASSIGN, token.AND_ASSIGN, token.OR_ASSIGN, + token.XOR_ASSIGN, token.SHL_ASSIGN, token.SHR_ASSIGN, token.AND_NOT_ASSIGN: + // assignment statement + pos, tok := p.pos, p.tok; + p.next(); + y := p.parseExpressionList(); + if len(x) > 1 && len(y) > 1 && len(x) != len(y) { + p.Error(x[0].Pos(), "arity of lhs doesn't match rhs"); + } + return &ast.AssignStmt{x, pos, tok, y}; + } + + if len(x) > 1 { + p.Error(x[0].Pos(), "only one expression allowed"); + // continue with first expression + } + + if p.tok == token.INC || p.tok == token.DEC { + // increment or decrement + s := &ast.IncDecStmt{x[0], p.tok}; + p.next(); // consume "++" or "--" + return s; + } + + // expression + return &ast.ExprStmt{x[0]}; +} + + +func (p *parser) parseCallExpr() *ast.CallExpr { + x := p.parseExpression(); + if call, is_call := x.(*ast.CallExpr); is_call { + return call; + } + p.error_expected(x.Pos(), "function/method call"); + return nil; +} + + +func (p *parser) parseGoStmt() ast.Stmt { + if p.trace { + defer un(trace(p, "GoStmt")); + } + + pos := p.expect(token.GO); + call := p.parseCallExpr(); + if call != nil { + return &ast.GoStmt{pos, call}; + } + return &ast.BadStmt{pos}; +} + + +func (p *parser) parseDeferStmt() ast.Stmt { + if p.trace { + defer un(trace(p, "DeferStmt")); + } + + pos := p.expect(token.DEFER); + call := p.parseCallExpr(); + if call != nil { + return &ast.DeferStmt{pos, call}; + } + return &ast.BadStmt{pos}; +} + + +func (p *parser) parseReturnStmt() *ast.ReturnStmt { + if p.trace { + defer un(trace(p, "ReturnStmt")); + } + + pos := p.pos; + p.expect(token.RETURN); + var x []ast.Expr; + if p.tok != token.SEMICOLON && p.tok != token.RBRACE { + x = p.parseExpressionList(); + } + + return &ast.ReturnStmt{pos, x}; +} + + +func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt { + if p.trace { + defer un(trace(p, "BranchStmt")); + } + + s := &ast.BranchStmt{p.pos, tok, nil}; + p.expect(tok); + if tok != token.FALLTHROUGH && p.tok == token.IDENT { + s.Label = p.parseIdent(); + } + + return s; +} + + +func (p *parser) isExpr(s ast.Stmt) bool { + if s == nil { + return true; + } + dummy, is_expr := s.(*ast.ExprStmt); + return is_expr; +} + + +func (p *parser) makeExpr(s ast.Stmt) ast.Expr { + if s == nil { + return nil; + } + if es, is_expr := s.(*ast.ExprStmt); is_expr { + return p.checkExpr(es.X); + } + p.Error(s.Pos(), "expected condition, found simple statement"); + return &ast.BadExpr{s.Pos()}; +} + + +func (p *parser) parseControlClause(isForStmt bool) (s1, s2, s3 ast.Stmt) { + if p.tok != token.LBRACE { + prev_lev := p.expr_lev; + p.expr_lev = -1; + + if p.tok != token.SEMICOLON { + s1 = p.parseSimpleStmt(false); + } + if p.tok == token.SEMICOLON { + p.next(); + if p.tok != token.LBRACE && p.tok != token.SEMICOLON { + s2 = p.parseSimpleStmt(false); + } + if isForStmt { + // for statements have a 3rd section + p.expect(token.SEMICOLON); + if p.tok != token.LBRACE { + s3 = p.parseSimpleStmt(false); + } + } + } else { + s1, s2 = nil, s1; + } + + p.expr_lev = prev_lev; + } + + return s1, s2, s3; +} + + +func (p *parser) parseIfStmt() *ast.IfStmt { + if p.trace { + defer un(trace(p, "IfStmt")); + } + + pos := p.expect(token.IF); + s1, s2, dummy := p.parseControlClause(false); + body := p.parseBlockStmt(); + var else_ ast.Stmt; + if p.tok == token.ELSE { + p.next(); + else_ = p.parseStatement(); + } + + return &ast.IfStmt{pos, s1, p.makeExpr(s2), body, else_}; +} + + +func (p *parser) parseCaseClause() *ast.CaseClause { + if p.trace { + defer un(trace(p, "CaseClause")); + } + + // SwitchCase + pos := p.pos; + var x []ast.Expr; + if p.tok == token.CASE { + p.next(); + x = p.parseExpressionList(); + } else { + p.expect(token.DEFAULT); + } + + colon := p.expect(token.COLON); + body := p.parseStatementList(); + + return &ast.CaseClause{pos, x, colon, body}; +} + + +func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause { + if p.trace { + defer un(trace(p, "CaseClause")); + } + + // TypeSwitchCase + pos := p.pos; + var typ ast.Expr; + if p.tok == token.CASE { + p.next(); + typ = p.parseType(); + } else { + p.expect(token.DEFAULT); + } + + colon := p.expect(token.COLON); + body := p.parseStatementList(); + + return &ast.TypeCaseClause{pos, typ, colon, body}; +} + + +func (p *parser) parseSwitchStmt() ast.Stmt { + if p.trace { + defer un(trace(p, "SwitchStmt")); + } + + pos := p.expect(token.SWITCH); + s1, s2, dummy := p.parseControlClause(false); + + if p.isExpr(s2) { + // expression switch + lbrace := p.expect(token.LBRACE); + cases := vector.New(0); + for p.tok == token.CASE || p.tok == token.DEFAULT { + cases.Push(p.parseCaseClause()); + } + rbrace := p.expect(token.RBRACE); + p.opt_semi = true; + body := &ast.BlockStmt{lbrace, makeStmtList(cases), rbrace}; + return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}; + } + + // type switch + // TODO do all the checks! + lbrace := p.expect(token.LBRACE); + cases := vector.New(0); + for p.tok == token.CASE || p.tok == token.DEFAULT { + cases.Push(p.parseTypeCaseClause()); + } + rbrace := p.expect(token.RBRACE); + p.opt_semi = true; + body := &ast.BlockStmt{lbrace, makeStmtList(cases), rbrace}; + return &ast.TypeSwitchStmt{pos, s1, s2, body}; +} + + +func (p *parser) parseCommClause() *ast.CommClause { + if p.trace { + defer un(trace(p, "CommClause")); + } + + // CommCase + pos := p.pos; + var tok token.Token; + var lhs, rhs ast.Expr; + if p.tok == token.CASE { + p.next(); + if p.tok == token.ARROW { + // RecvExpr without assignment + rhs = p.parseExpression(); + } else { + // SendExpr or RecvExpr + rhs = p.parseExpression(); + if p.tok == token.ASSIGN || p.tok == token.DEFINE { + // RecvExpr with assignment + tok = p.tok; + p.next(); + lhs = rhs; + if p.tok == token.ARROW { + rhs = p.parseExpression(); + } else { + p.expect(token.ARROW); // use expect() error handling + } + } + // else SendExpr + } + } else { + p.expect(token.DEFAULT); + } + + colon := p.expect(token.COLON); + body := p.parseStatementList(); + + return &ast.CommClause{pos, tok, lhs, rhs, colon, body}; +} + + +func (p *parser) parseSelectStmt() *ast.SelectStmt { + if p.trace { + defer un(trace(p, "SelectStmt")); + } + + pos := p.expect(token.SELECT); + lbrace := p.expect(token.LBRACE); + cases := vector.New(0); + for p.tok == token.CASE || p.tok == token.DEFAULT { + cases.Push(p.parseCommClause()); + } + rbrace := p.expect(token.RBRACE); + p.opt_semi = true; + body := &ast.BlockStmt{lbrace, makeStmtList(cases), rbrace}; + + return &ast.SelectStmt{pos, body}; +} + + +func (p *parser) parseForStmt() ast.Stmt { + if p.trace { + defer un(trace(p, "ForStmt")); + } + + pos := p.expect(token.FOR); + s1, s2, s3 := p.parseControlClause(true); + body := p.parseBlockStmt(); + + if as, is_as := s2.(*ast.AssignStmt); is_as { + // possibly a for statement with a range clause; check assignment operator + if as.Tok != token.ASSIGN && as.Tok != token.DEFINE { + p.error_expected(as.TokPos, "'=' or ':='"); + return &ast.BadStmt{pos}; + } + // check lhs + var key, value ast.Expr; + switch len(as.Lhs) { + case 2: + value = as.Lhs[1]; + fallthrough; + case 1: + key = as.Lhs[0]; + default: + p.error_expected(as.Lhs[0].Pos(), "1 or 2 expressions"); + return &ast.BadStmt{pos}; + } + // check rhs + if len(as.Rhs) != 1 { + p.error_expected(as.Rhs[0].Pos(), "1 expressions"); + return &ast.BadStmt{pos}; + } + if rhs, is_unary := as.Rhs[0].(*ast.UnaryExpr); is_unary && rhs.Op == token.RANGE { + // rhs is range expression; check lhs + return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body} + } else { + p.error_expected(s2.Pos(), "range clause"); + return &ast.BadStmt{pos}; + } + } else { + // regular for statement + return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}; + } + + panic(); // unreachable + return nil; +} + + +func (p *parser) parseStatement() ast.Stmt { + if p.trace { + defer un(trace(p, "Statement")); + } + + switch p.tok { + case token.CONST, token.TYPE, token.VAR: + return &ast.DeclStmt{p.parseDeclaration()}; + case + // tokens that may start a top-level expression + token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand + token.LBRACK, token.STRUCT, // composite type + token.MUL, token.AND, token.ARROW: // unary operators + return p.parseSimpleStmt(true); + case token.GO: + return p.parseGoStmt(); + case token.DEFER: + return p.parseDeferStmt(); + case token.RETURN: + return p.parseReturnStmt(); + case token.BREAK, token.CONTINUE, token.GOTO, token.FALLTHROUGH: + return p.parseBranchStmt(p.tok); + case token.LBRACE: + return p.parseBlockStmt(); + case token.IF: + return p.parseIfStmt(); + case token.SWITCH: + return p.parseSwitchStmt(); + case token.SELECT: + return p.parseSelectStmt(); + case token.FOR: + return p.parseForStmt(); + case token.SEMICOLON, token.RBRACE: + // don't consume the ";", it is the separator following the empty statement + return &ast.EmptyStmt{p.pos}; + } + + // no statement found + p.error_expected(p.pos, "statement"); + p.next(); // make progress + return &ast.BadStmt{p.pos}; +} + + +// ---------------------------------------------------------------------------- +// Declarations + +type parseSpecFunction func(p *parser, doc ast.Comments) ast.Spec + +func parseImportSpec(p *parser, doc ast.Comments) ast.Spec { + if p.trace { + defer un(trace(p, "ImportSpec")); + } + + var ident *ast.Ident; + if p.tok == token.PERIOD { + ident = &ast.Ident{p.pos, "."}; + p.next(); + } else if p.tok == token.IDENT { + ident = p.parseIdent(); + } + + var path []*ast.StringLit; + if p.tok == token.STRING { + path = p.parseStringList(nil); + } else { + p.expect(token.STRING); // use expect() error handling + } + + return &ast.ImportSpec{doc, ident, path}; +} + + +func parseConstSpec(p *parser, doc ast.Comments) ast.Spec { + if p.trace { + defer un(trace(p, "ConstSpec")); + } + + idents := p.parseIdentList(nil); + typ := p.tryType(); + var values []ast.Expr; + if typ != nil || p.tok == token.ASSIGN { + p.expect(token.ASSIGN); + values = p.parseExpressionList(); + } + + return &ast.ValueSpec{doc, idents, typ, values}; +} + + +func parseTypeSpec(p *parser, doc ast.Comments) ast.Spec { + if p.trace { + defer un(trace(p, "TypeSpec")); + } + + ident := p.parseIdent(); + typ := p.parseType(); + + return &ast.TypeSpec{doc, ident, typ}; +} + + +func parseVarSpec(p *parser, doc ast.Comments) ast.Spec { + if p.trace { + defer un(trace(p, "VarSpec")); + } + + idents := p.parseIdentList(nil); + typ := p.tryType(); + var values []ast.Expr; + if typ == nil || p.tok == token.ASSIGN { + p.expect(token.ASSIGN); + values = p.parseExpressionList(); + } + + return &ast.ValueSpec{doc, idents, typ, values}; +} + + +func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl { + if p.trace { + defer un(trace(p, keyword.String() + "Decl")); + } + + doc := p.getDoc(); + pos := p.expect(keyword); + var lparen, rparen token.Position; + list := vector.New(0); + if p.tok == token.LPAREN { + lparen = p.pos; + p.next(); + for p.tok != token.RPAREN && p.tok != token.EOF { + doc := p.getDoc(); + list.Push(f(p, doc)); + if p.tok == token.SEMICOLON { + p.next(); + } else { + break; + } + } + rparen = p.expect(token.RPAREN); + p.opt_semi = true; + } else { + list.Push(f(p, doc)); + } + + // convert vector + specs := make([]ast.Spec, list.Len()); + for i := 0; i < list.Len(); i++ { + specs[i] = list.At(i); + } + return &ast.GenDecl{doc, pos, keyword, lparen, specs, rparen}; +} + + +func (p *parser) parseReceiver() *ast.Field { + if p.trace { + defer un(trace(p, "Receiver")); + } + + pos := p.pos; + par := p.parseParameters(false); + + // must have exactly one receiver + if len(par) != 1 || len(par) == 1 && len(par[0].Names) > 1 { + p.error_expected(pos, "exactly one receiver"); + return &ast.Field{nil, nil, &ast.BadExpr{noPos}, nil}; + } + + recv := par[0]; + + // recv type must be TypeName or *TypeName + base := recv.Type; + if ptr, is_ptr := base.(*ast.StarExpr); is_ptr { + base = ptr.X; + } + if !isTypeName(base) { + p.error_expected(base.Pos(), "type name"); + } + + return recv; +} + + +func (p *parser) parseFunctionDecl() *ast.FuncDecl { + if p.trace { + defer un(trace(p, "FunctionDecl")); + } + + doc := p.getDoc(); + pos := p.expect(token.FUNC); + + var recv *ast.Field; + if p.tok == token.LPAREN { + recv = p.parseReceiver(); + } + + ident := p.parseIdent(); + params, results := p.parseSignature(); + + var body *ast.BlockStmt; + if p.tok == token.LBRACE { + body = p.parseBlockStmt(); + } + + return &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}; +} + + +func (p *parser) parseDeclaration() ast.Decl { + if p.trace { + defer un(trace(p, "Declaration")); + } + + var f parseSpecFunction; + switch p.tok { + case token.CONST: f = parseConstSpec; + case token.TYPE: f = parseTypeSpec; + case token.VAR: f = parseVarSpec; + case token.FUNC: + return p.parseFunctionDecl(); + default: + pos := p.pos; + p.error_expected(pos, "declaration"); + p.next(); // make progress + return &ast.BadDecl{pos}; + } + + return p.parseGenDecl(p.tok, f); +} + + +// ---------------------------------------------------------------------------- +// Packages + +// The mode parameter to the Parse function is a set of flags (or 0). +// They control the amount of source code parsed and other optional +// parser functionality. +// +const ( + PackageClauseOnly uint = 1 << iota; // parsing stops after package clause + ImportsOnly; // parsing stops after import declarations + ParseComments; // parse comments and add them to AST + Trace; // print a trace of parsed productions +) + + +func (p *parser) parsePackage() *ast.Program { + if p.trace { + defer un(trace(p, "Program")); + } + + // package clause + comment := p.getDoc(); + pos := p.expect(token.PACKAGE); + ident := p.parseIdent(); + var decls []ast.Decl; + + // Don't bother parsing the rest if we had errors already. + // Likely not a Go source file at all. + + if p.errors.Len() == 0 && p.mode & PackageClauseOnly == 0 { + // import decls + list := vector.New(0); + for p.tok == token.IMPORT { + list.Push(p.parseGenDecl(token.IMPORT, parseImportSpec)); + if p.tok == token.SEMICOLON { + p.next(); + } + } + + if p.mode & ImportsOnly == 0 { + // rest of package body + for p.tok != token.EOF { + list.Push(p.parseDeclaration()); + if p.tok == token.SEMICOLON { + p.next(); + } + } + } + + // convert declaration list + decls = make([]ast.Decl, list.Len()); + for i := 0; i < list.Len(); i++ { + decls[i] = list.At(i).(ast.Decl); + } + } + + // convert comments list + comments := make([]*ast.Comment, p.comments.Len()); + for i := 0; i < p.comments.Len(); i++ { + comments[i] = p.comments.At(i).(*ast.Comment); + } + + return &ast.Program{comment, pos, ident, decls, comments}; +} + + +// ---------------------------------------------------------------------------- +// Parsing of entire programs. + +func readSource(src interface{}) ([]byte, os.Error) { + if src != nil { + switch s := src.(type) { + case string: + return io.StringBytes(s), nil; + case []byte: + return s, nil; + case *io.ByteBuffer: + // is io.Read, but src is already available in []byte form + if s != nil { + return s.Data(), nil; + } + case io.Reader: + var buf io.ByteBuffer; + n, err := io.Copy(s, &buf); + if err != nil { + return nil, err; + } + return buf.Data(), nil; + } + } + return nil, os.ErrorString("invalid source"); +} + + +// scannerMode returns the scanner mode bits given the parser's mode bits. +func scannerMode(mode uint) uint { + if mode & ParseComments != 0 { + return scanner.ScanComments; + } + return 0; +} + + +// Parse parses a Go program. +// +// The program source src may be provided in a variety of formats. At the +// moment the following types are supported: string, []byte, and io.Read. +// The mode parameter controls the amount of source text parsed and other +// optional parser functionality. +// +// Parse returns a complete AST if no error occured. Otherwise, if the +// source couldn't be read, the returned program is nil and the error +// indicates the specific failure. If the source was read but syntax +// errors were found, the result is a partial AST (with ast.BadX nodes +// representing the fragments of erroneous source code) and an ErrorList +// describing the syntax errors. +// +func Parse(src interface{}, mode uint) (*ast.Program, os.Error) { + data, err := readSource(src); + if err != nil { + return nil, err; + } + + // initialize parser state + var p parser; + p.errors.Init(0); + p.scanner.Init(data, &p, scannerMode(mode)); + p.mode = mode; + p.trace = mode & Trace != 0; // for convenience (p.trace is used frequently) + p.comments.Init(0); + p.next(); + + // parse program + prog := p.parsePackage(); + + // convert errors list, if any + if p.errors.Len() > 0 { + errors := make(ErrorList, p.errors.Len()); + for i := 0; i < p.errors.Len(); i++ { + errors[i] = p.errors.At(i).(*Error); + } + return prog, errors; + } + + return prog, nil; +} diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go new file mode 100644 index 000000000..887fcf80f --- /dev/null +++ b/src/pkg/go/parser/parser_test.go @@ -0,0 +1,68 @@ +// Copyright 2009 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 parser + +import ( + "go/ast"; + "go/parser"; + "os"; + "testing"; +) + + +var illegalInputs = []interface{} { + nil, + 3.14, + []byte(nil), + "foo!", +} + + +func TestParseIllegalInputs(t *testing.T) { + for _, src := range illegalInputs { + prog, err := Parse(src, 0); + if err == nil { + t.Errorf("Parse(%v) should have failed", src); + } + } +} + + +var validPrograms = []interface{} { + `package main`, + `package main import "fmt" func main() { fmt.Println("Hello, World!") }`, +} + + +func TestParseValidPrograms(t *testing.T) { + for _, src := range validPrograms { + prog, err := Parse(src, 0); + if err != nil { + t.Errorf("Parse(%q) failed: %v", src, err); + } + } +} + + +var validFiles = []string { + "parser.go", + "parser_test.go", +} + + +func TestParse3(t *testing.T) { + for _, filename := range validFiles { + src, err := os.Open(filename, os.O_RDONLY, 0); + defer src.Close(); + if err != nil { + t.Fatalf("os.Open(%s): %v\n", filename, err); + } + + prog, err := Parse(src, 0); + if err != nil { + t.Errorf("Parse(%q): %v", src, err); + } + } +} diff --git a/src/pkg/go/scanner/Makefile b/src/pkg/go/scanner/Makefile new file mode 100644 index 000000000..d47fecb7c --- /dev/null +++ b/src/pkg/go/scanner/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/go/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + scanner.$O\ + + +phases: a1 +_obj$D/scanner.a: phases + +a1: $(O1) + $(AR) grc _obj$D/scanner.a scanner.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/scanner.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/scanner.a + +packages: _obj$D/scanner.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/scanner.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/scanner.a diff --git a/src/pkg/go/scanner/scanner.go b/src/pkg/go/scanner/scanner.go new file mode 100644 index 000000000..a90e6f259 --- /dev/null +++ b/src/pkg/go/scanner/scanner.go @@ -0,0 +1,501 @@ +// Copyright 2009 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. + +// A scanner for Go source text. Takes a []byte as source which can +// then be tokenized through repeated calls to the Scan function. +// For a sample use of a scanner, see the implementation of Tokenize. +// +package scanner + +import ( + "go/token"; + "strconv"; + "unicode"; + "utf8"; +) + + +// An implementation of an ErrorHandler may be provided to the Scanner. +// If a syntax error is encountered and a handler was installed, Error +// is called with a position and an error message. The position points +// to the beginning of the offending token. +// +type ErrorHandler interface { + Error(pos token.Position, msg string); +} + + +// A Scanner holds the scanner's internal state while processing +// a given text. It can be allocated as part of another data +// structure but must be initialized via Init before use. For +// a sample use, see the implementation of Tokenize. +// +type Scanner struct { + // immutable state + src []byte; // source + err ErrorHandler; // error reporting; or nil + mode uint; // scanning mode + + // scanning state + pos token.Position; // previous reading position (position before ch) + offset int; // current reading offset (position after ch) + ch int; // one char look-ahead + + // public state - ok to modify + ErrorCount int; // number of errors encountered +} + + +// Read the next Unicode char into S.ch. +// S.ch < 0 means end-of-file. +// +func (S *Scanner) next() { + if S.offset < len(S.src) { + S.pos.Offset = S.offset; + S.pos.Column++; + r, w := int(S.src[S.offset]), 1; + switch { + case r == '\n': + S.pos.Line++; + S.pos.Column = 0; + case r >= 0x80: + // not ASCII + r, w = utf8.DecodeRune(S.src[S.offset : len(S.src)]); + } + S.offset += w; + S.ch = r; + } else { + S.pos.Offset = len(S.src); + S.ch = -1; // eof + } +} + + +// The mode parameter to the Init function is a set of flags (or 0). +// They control scanner behavior. +// +const ( + ScanComments = 1 << iota; // return comments as COMMENT tokens + AllowIllegalChars; // do not report an error for illegal chars +) + + +// Init prepares the scanner S to tokenize the text src. Calls to Scan +// will use the error handler err if they encounter a syntax error and +// err is not nil. Also, for each error encountered, the Scanner field +// ErrorCount is incremented by one. The mode parameter determines how +// comments and illegal characters are handled. +// +func (S *Scanner) Init(src []byte, err ErrorHandler, mode uint) { + // Explicitly initialize all fields since a scanner may be reused. + S.src = src; + S.err = err; + S.mode = mode; + S.pos = token.Position{0, 1, 0}; + S.offset = 0; + S.ErrorCount = 0; + S.next(); +} + + +func charString(ch int) string { + var s string; + switch ch { + case '\a': s = `\a`; + case '\b': s = `\b`; + case '\f': s = `\f`; + case '\n': s = `\n`; + case '\r': s = `\r`; + case '\t': s = `\t`; + case '\v': s = `\v`; + case '\\': s = `\\`; + case '\'': s = `\'`; + default : s = string(ch); + } + return "'" + s + "' (U+" + strconv.Itob(ch, 16) + ")"; +} + + +func (S *Scanner) error(pos token.Position, msg string) { + if S.err != nil { + S.err.Error(pos, msg); + } + S.ErrorCount++; +} + + +func (S *Scanner) expect(ch int) { + if S.ch != ch { + S.error(S.pos, "expected " + charString(ch) + ", found " + charString(S.ch)); + } + S.next(); // always make progress +} + + +func (S *Scanner) scanComment(pos token.Position) { + // first '/' already consumed + + if S.ch == '/' { + //-style comment + for S.ch >= 0 { + S.next(); + if S.ch == '\n' { + S.next(); // '\n' belongs to the comment + return; + } + } + + } else { + /*-style comment */ + S.expect('*'); + for S.ch >= 0 { + ch := S.ch; + S.next(); + if ch == '*' && S.ch == '/' { + S.next(); + return; + } + } + } + + S.error(pos, "comment not terminated"); +} + + +func isLetter(ch int) bool { + return + 'a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + ch == '_' || + ch >= 0x80 && unicode.IsLetter(ch); +} + + +func isDigit(ch int) bool { + return + '0' <= ch && ch <= '9' || + ch >= 0x80 && unicode.IsDecimalDigit(ch); +} + + +func (S *Scanner) scanIdentifier() token.Token { + pos := S.pos.Offset; + for isLetter(S.ch) || isDigit(S.ch) { + S.next(); + } + return token.Lookup(S.src[pos : S.pos.Offset]); +} + + +func digitVal(ch int) int { + switch { + case '0' <= ch && ch <= '9': return ch - '0'; + case 'a' <= ch && ch <= 'f': return ch - 'a' + 10; + case 'A' <= ch && ch <= 'F': return ch - 'A' + 10; + } + return 16; // larger than any legal digit val +} + + +func (S *Scanner) scanMantissa(base int) { + for digitVal(S.ch) < base { + S.next(); + } +} + + +func (S *Scanner) scanNumber(seen_decimal_point bool) token.Token { + tok := token.INT; + + if seen_decimal_point { + tok = token.FLOAT; + S.scanMantissa(10); + goto exponent; + } + + if S.ch == '0' { + // int or float + S.next(); + if S.ch == 'x' || S.ch == 'X' { + // hexadecimal int + S.next(); + S.scanMantissa(16); + } else { + // octal int or float + S.scanMantissa(8); + if digitVal(S.ch) < 10 || S.ch == '.' || S.ch == 'e' || S.ch == 'E' { + // float + tok = token.FLOAT; + goto mantissa; + } + // octal int + } + goto exit; + } + +mantissa: + // decimal int or float + S.scanMantissa(10); + + if S.ch == '.' { + // float + tok = token.FLOAT; + S.next(); + S.scanMantissa(10) + } + +exponent: + if S.ch == 'e' || S.ch == 'E' { + // float + tok = token.FLOAT; + S.next(); + if S.ch == '-' || S.ch == '+' { + S.next(); + } + S.scanMantissa(10); + } + +exit: + return tok; +} + + +func (S *Scanner) scanDigits(base, length int) { + for length > 0 && digitVal(S.ch) < base { + S.next(); + length--; + } + if length > 0 { + S.error(S.pos, "illegal char escape"); + } +} + + +func (S *Scanner) scanEscape(quote int) { + pos := S.pos; + ch := S.ch; + S.next(); + switch ch { + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: + // nothing to do + case '0', '1', '2', '3', '4', '5', '6', '7': + S.scanDigits(8, 3 - 1); // 1 char read already + case 'x': + S.scanDigits(16, 2); + case 'u': + S.scanDigits(16, 4); + case 'U': + S.scanDigits(16, 8); + default: + S.error(pos, "illegal char escape"); + } +} + + +func (S *Scanner) scanChar() { + // '\'' already consumed + + ch := S.ch; + S.next(); + if ch == '\\' { + S.scanEscape('\''); + } + + S.expect('\''); +} + + +func (S *Scanner) scanString(pos token.Position) { + // '"' already consumed + + for S.ch != '"' { + ch := S.ch; + S.next(); + if ch == '\n' || ch < 0 { + S.error(pos, "string not terminated"); + break; + } + if ch == '\\' { + S.scanEscape('"'); + } + } + + S.next(); +} + + +func (S *Scanner) scanRawString(pos token.Position) { + // '`' already consumed + + for S.ch != '`' { + ch := S.ch; + S.next(); + if ch == '\n' || ch < 0 { + S.error(pos, "string not terminated"); + break; + } + } + + S.next(); +} + + +// Helper functions for scanning multi-byte tokens such as >> += >>= . +// Different routines recognize different length tok_i based on matches +// of ch_i. If a token ends in '=', the result is tok1 or tok3 +// respectively. Otherwise, the result is tok0 if there was no other +// matching character, or tok2 if the matching character was ch2. + +func (S *Scanner) switch2(tok0, tok1 token.Token) token.Token { + if S.ch == '=' { + S.next(); + return tok1; + } + return tok0; +} + + +func (S *Scanner) switch3(tok0, tok1 token.Token, ch2 int, tok2 token.Token) token.Token { + if S.ch == '=' { + S.next(); + return tok1; + } + if S.ch == ch2 { + S.next(); + return tok2; + } + return tok0; +} + + +func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Token) token.Token { + if S.ch == '=' { + S.next(); + return tok1; + } + if S.ch == ch2 { + S.next(); + if S.ch == '=' { + S.next(); + return tok3; + } + return tok2; + } + return tok0; +} + + +// Scan scans the next token and returns the token position pos, +// the token tok, and the literal text lit corresponding to the +// token. The source end is indicated by token.EOF. +// +// For more tolerant parsing, Scan will return a valid token if +// possible even if a syntax error was encountered. Thus, even +// if the resulting token sequence contains no illegal tokens, +// a client may not assume that no error occurred. Instead it +// must check the scanner's ErrorCount or the number of calls +// of the error handler, if there was one installed. +// +func (S *Scanner) Scan() (pos token.Position, tok token.Token, lit []byte) { +scan_again: + // skip white space + for S.ch == ' ' || S.ch == '\t' || S.ch == '\n' || S.ch == '\r' { + S.next(); + } + + // current token start + pos, tok = S.pos, token.ILLEGAL; + + // determine token value + switch ch := S.ch; { + case isLetter(ch): + tok = S.scanIdentifier(); + case digitVal(ch) < 10: + tok = S.scanNumber(false); + default: + S.next(); // always make progress + switch ch { + case -1 : tok = token.EOF; + case '"' : tok = token.STRING; S.scanString(pos); + case '\'': tok = token.CHAR; S.scanChar(); + case '`' : tok = token.STRING; S.scanRawString(pos); + case ':' : tok = S.switch2(token.COLON, token.DEFINE); + case '.' : + if digitVal(S.ch) < 10 { + tok = S.scanNumber(true); + } else if S.ch == '.' { + S.next(); + if S.ch == '.' { + S.next(); + tok = token.ELLIPSIS; + } + } else { + tok = token.PERIOD; + } + case ',': tok = token.COMMA; + case ';': tok = token.SEMICOLON; + case '(': tok = token.LPAREN; + case ')': tok = token.RPAREN; + case '[': tok = token.LBRACK; + case ']': tok = token.RBRACK; + case '{': tok = token.LBRACE; + case '}': tok = token.RBRACE; + case '+': tok = S.switch3(token.ADD, token.ADD_ASSIGN, '+', token.INC); + case '-': tok = S.switch3(token.SUB, token.SUB_ASSIGN, '-', token.DEC); + case '*': tok = S.switch2(token.MUL, token.MUL_ASSIGN); + case '/': + if S.ch == '/' || S.ch == '*' { + S.scanComment(pos); + tok = token.COMMENT; + if S.mode & ScanComments == 0 { + goto scan_again; + } + } else { + tok = S.switch2(token.QUO, token.QUO_ASSIGN); + } + case '%': tok = S.switch2(token.REM, token.REM_ASSIGN); + case '^': tok = S.switch2(token.XOR, token.XOR_ASSIGN); + case '<': + if S.ch == '-' { + S.next(); + tok = token.ARROW; + } else { + tok = S.switch4(token.LSS, token.LEQ, '<', token.SHL, token.SHL_ASSIGN); + } + case '>': tok = S.switch4(token.GTR, token.GEQ, '>', token.SHR, token.SHR_ASSIGN); + case '=': tok = S.switch2(token.ASSIGN, token.EQL); + case '!': tok = S.switch2(token.NOT, token.NEQ); + case '&': + if S.ch == '^' { + S.next(); + tok = S.switch2(token.AND_NOT, token.AND_NOT_ASSIGN); + } else { + tok = S.switch3(token.AND, token.AND_ASSIGN, '&', token.LAND); + } + case '|': tok = S.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR); + default: + if S.mode & AllowIllegalChars == 0 { + S.error(pos, "illegal character " + charString(ch)); + } + } + } + + return pos, tok, S.src[pos.Offset : S.pos.Offset]; +} + + +// Tokenize calls a function f with the token position, token value, and token +// text for each token in the source src. The other parameters have the same +// meaning as for the Init function. Tokenize keeps scanning until f returns +// false (usually when the token value is token.EOF). The result is the number +// of errors encountered. +// +func Tokenize(src []byte, err ErrorHandler, mode uint, f func (pos token.Position, tok token.Token, lit []byte) bool) int { + var s Scanner; + s.Init(src, err, mode); + for f(s.Scan()) { + // action happens in f + } + return s.ErrorCount; +} diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go new file mode 100644 index 000000000..0defece8b --- /dev/null +++ b/src/pkg/go/scanner/scanner_test.go @@ -0,0 +1,276 @@ +// Copyright 2009 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 scanner + +import ( + "go/scanner"; + "go/token"; + "io"; + "testing"; +) + + +const /* class */ ( + special = iota; + literal; + operator; + keyword; +) + + +func tokenclass(tok token.Token) int { + switch { + case tok.IsLiteral(): return literal; + case tok.IsOperator(): return operator; + case tok.IsKeyword(): return keyword; + } + return special; +} + + +type elt struct { + tok token.Token; + lit string; + class int; +} + + +var tokens = [...]elt{ + // Special tokens + elt{ token.COMMENT, "/* a comment */", special }, + elt{ token.COMMENT, "// a comment \n", special }, + + // Identifiers and basic type literals + elt{ token.IDENT, "foobar", literal }, + elt{ token.IDENT, "a۰۱۸", literal }, + elt{ token.IDENT, "foo६४", literal }, + elt{ token.IDENT, "bar9876", literal }, + elt{ token.INT, "0", literal }, + elt{ token.INT, "01234567", literal }, + elt{ token.INT, "0xcafebabe", literal }, + elt{ token.FLOAT, "0.", literal }, + elt{ token.FLOAT, ".0", literal }, + elt{ token.FLOAT, "3.14159265", literal }, + elt{ token.FLOAT, "1e0", literal }, + elt{ token.FLOAT, "1e+100", literal }, + elt{ token.FLOAT, "1e-100", literal }, + elt{ token.FLOAT, "2.71828e-1000", literal }, + elt{ token.CHAR, "'a'", literal }, + elt{ token.CHAR, "'\\000'", literal }, + elt{ token.CHAR, "'\\xFF'", literal }, + elt{ token.CHAR, "'\\uff16'", literal }, + elt{ token.CHAR, "'\\U0000ff16'", literal }, + elt{ token.STRING, "`foobar`", literal }, + + // Operators and delimitors + elt{ token.ADD, "+", operator }, + elt{ token.SUB, "-", operator }, + elt{ token.MUL, "*", operator }, + elt{ token.QUO, "/", operator }, + elt{ token.REM, "%", operator }, + + elt{ token.AND, "&", operator }, + elt{ token.OR, "|", operator }, + elt{ token.XOR, "^", operator }, + elt{ token.SHL, "<<", operator }, + elt{ token.SHR, ">>", operator }, + elt{ token.AND_NOT, "&^", operator }, + + elt{ token.ADD_ASSIGN, "+=", operator }, + elt{ token.SUB_ASSIGN, "-=", operator }, + elt{ token.MUL_ASSIGN, "*=", operator }, + elt{ token.QUO_ASSIGN, "/=", operator }, + elt{ token.REM_ASSIGN, "%=", operator }, + + elt{ token.AND_ASSIGN, "&=", operator }, + elt{ token.OR_ASSIGN, "|=", operator }, + elt{ token.XOR_ASSIGN, "^=", operator }, + elt{ token.SHL_ASSIGN, "<<=", operator }, + elt{ token.SHR_ASSIGN, ">>=", operator }, + elt{ token.AND_NOT_ASSIGN, "&^=", operator }, + + elt{ token.LAND, "&&", operator }, + elt{ token.LOR, "||", operator }, + elt{ token.ARROW, "<-", operator }, + elt{ token.INC, "++", operator }, + elt{ token.DEC, "--", operator }, + + elt{ token.EQL, "==", operator }, + elt{ token.LSS, "<", operator }, + elt{ token.GTR, ">", operator }, + elt{ token.ASSIGN, "=", operator }, + elt{ token.NOT, "!", operator }, + + elt{ token.NEQ, "!=", operator }, + elt{ token.LEQ, "<=", operator }, + elt{ token.GEQ, ">=", operator }, + elt{ token.DEFINE, ":=", operator }, + elt{ token.ELLIPSIS, "...", operator }, + + elt{ token.LPAREN, "(", operator }, + elt{ token.LBRACK, "[", operator }, + elt{ token.LBRACE, "{", operator }, + elt{ token.COMMA, ",", operator }, + elt{ token.PERIOD, ".", operator }, + + elt{ token.RPAREN, ")", operator }, + elt{ token.RBRACK, "]", operator }, + elt{ token.RBRACE, "}", operator }, + elt{ token.SEMICOLON, ";", operator }, + elt{ token.COLON, ":", operator }, + + // Keywords + elt{ token.BREAK, "break", keyword }, + elt{ token.CASE, "case", keyword }, + elt{ token.CHAN, "chan", keyword }, + elt{ token.CONST, "const", keyword }, + elt{ token.CONTINUE, "continue", keyword }, + + elt{ token.DEFAULT, "default", keyword }, + elt{ token.DEFER, "defer", keyword }, + elt{ token.ELSE, "else", keyword }, + elt{ token.FALLTHROUGH, "fallthrough", keyword }, + elt{ token.FOR, "for", keyword }, + + elt{ token.FUNC, "func", keyword }, + elt{ token.GO, "go", keyword }, + elt{ token.GOTO, "goto", keyword }, + elt{ token.IF, "if", keyword }, + elt{ token.IMPORT, "import", keyword }, + + elt{ token.INTERFACE, "interface", keyword }, + elt{ token.MAP, "map", keyword }, + elt{ token.PACKAGE, "package", keyword }, + elt{ token.RANGE, "range", keyword }, + elt{ token.RETURN, "return", keyword }, + + elt{ token.SELECT, "select", keyword }, + elt{ token.STRUCT, "struct", keyword }, + elt{ token.SWITCH, "switch", keyword }, + elt{ token.TYPE, "type", keyword }, + elt{ token.VAR, "var", keyword }, +} + + +const whitespace = " \t \n\n\n"; // to separate tokens + +type TestErrorHandler struct { + t *testing.T +} + +func (h *TestErrorHandler) Error(pos token.Position, msg string) { + h.t.Errorf("Error() called (msg = %s)", msg); +} + + +func NewlineCount(s string) int { + n := 0; + for i := 0; i < len(s); i++ { + if s[i] == '\n' { + n++; + } + } + return n; +} + + +// Verify that calling Scan() provides the correct results. +func TestScan(t *testing.T) { + // make source + var src string; + for i, e := range tokens { + src += e.lit + whitespace; + } + whitespace_linecount := NewlineCount(whitespace); + + // verify scan + index := 0; + eloc := token.Position{0, 1, 1}; + nerrors := scanner.Tokenize(io.StringBytes(src), &TestErrorHandler{t}, scanner.ScanComments, + func (pos token.Position, tok token.Token, litb []byte) bool { + e := elt{token.EOF, "", special}; + if index < len(tokens) { + e = tokens[index]; + } + lit := string(litb); + if tok == token.EOF { + lit = "<EOF>"; + eloc.Column = 0; + } + if pos.Offset != eloc.Offset { + t.Errorf("bad position for %s: got %d, expected %d", lit, pos.Offset, eloc.Offset); + } + if pos.Line != eloc.Line { + t.Errorf("bad line for %s: got %d, expected %d", lit, pos.Line, eloc.Line); + } + if pos.Column!= eloc.Column { + t.Errorf("bad column for %s: got %d, expected %d", lit, pos.Column, eloc.Column); + } + if tok != e.tok { + t.Errorf("bad token for %s: got %s, expected %s", lit, tok.String(), e.tok.String()); + } + if e.tok.IsLiteral() && lit != e.lit { + t.Errorf("bad literal for %s: got %s, expected %s", lit, lit, e.lit); + } + if tokenclass(tok) != e.class { + t.Errorf("bad class for %s: got %d, expected %d", lit, tokenclass(tok), e.class); + } + eloc.Offset += len(lit) + len(whitespace); + eloc.Line += NewlineCount(lit) + whitespace_linecount; + index++; + return tok != token.EOF; + } + ); + if nerrors != 0 { + t.Errorf("found %d errors", nerrors); + } +} + + +// Verify that initializing the same scanner more then once works correctly. +func TestInit(t *testing.T) { + var s scanner.Scanner; + + // 1st init + s.Init(io.StringBytes("if true { }"), nil, 0); + s.Scan(); // if + s.Scan(); // true + pos, tok, lit := s.Scan(); // { + if tok != token.LBRACE { + t.Errorf("bad token: got %s, expected %s", tok.String(), token.LBRACE); + } + + // 2nd init + s.Init(io.StringBytes("go true { ]"), nil, 0); + pos, tok, lit = s.Scan(); // go + if tok != token.GO { + t.Errorf("bad token: got %s, expected %s", tok.String(), token.GO); + } + + if s.ErrorCount != 0 { + t.Errorf("found %d errors", s.ErrorCount); + } +} + + +func TestIllegalChars(t *testing.T) { + var s scanner.Scanner; + + const src = "*?*$*@*"; + s.Init(io.StringBytes(src), &TestErrorHandler{t}, scanner.AllowIllegalChars); + for offs, ch := range src { + pos, tok, lit := s.Scan(); + if pos.Offset != offs { + t.Errorf("bad position for %s: got %d, expected %d", string(lit), pos.Offset, offs); + } + if tok == token.ILLEGAL && string(lit) != string(ch) { + t.Errorf("bad token: got %s, expected %s", string(lit), string(ch)); + } + } + + if s.ErrorCount != 0 { + t.Errorf("found %d errors", s.ErrorCount); + } +} diff --git a/src/pkg/go/token/Makefile b/src/pkg/go/token/Makefile new file mode 100644 index 000000000..12ef2a4aa --- /dev/null +++ b/src/pkg/go/token/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/go/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + token.$O\ + + +phases: a1 +_obj$D/token.a: phases + +a1: $(O1) + $(AR) grc _obj$D/token.a token.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/token.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/token.a + +packages: _obj$D/token.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/token.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/token.a diff --git a/src/pkg/go/token/token.go b/src/pkg/go/token/token.go new file mode 100644 index 000000000..a70a75a54 --- /dev/null +++ b/src/pkg/go/token/token.go @@ -0,0 +1,347 @@ +// Copyright 2009 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. + +// This package defines constants representing the lexical +// tokens of the Go programming language and basic operations +// on tokens (printing, predicates). +// +package token + +import "strconv" + +// Token is the set of lexical tokens of the Go programming language. +type Token int + +// The list of tokens. +const ( + // Special tokens + ILLEGAL Token = iota; + EOF; + COMMENT; + + // Identifiers and basic type literals + // (these tokens stand for classes of literals) + literal_beg; + IDENT; + INT; + FLOAT; + CHAR; + STRING; + literal_end; + + // Operators and delimiters + operator_beg; + ADD; + SUB; + MUL; + QUO; + REM; + + AND; + OR; + XOR; + SHL; + SHR; + AND_NOT; + + ADD_ASSIGN; + SUB_ASSIGN; + MUL_ASSIGN; + QUO_ASSIGN; + REM_ASSIGN; + + AND_ASSIGN; + OR_ASSIGN; + XOR_ASSIGN; + SHL_ASSIGN; + SHR_ASSIGN; + AND_NOT_ASSIGN; + + LAND; + LOR; + ARROW; + INC; + DEC; + + EQL; + LSS; + GTR; + ASSIGN; + NOT; + + NEQ; + LEQ; + GEQ; + DEFINE; + ELLIPSIS; + + LPAREN; + LBRACK; + LBRACE; + COMMA; + PERIOD; + + RPAREN; + RBRACK; + RBRACE; + SEMICOLON; + COLON; + operator_end; + + // Keywords + keyword_beg; + BREAK; + CASE; + CHAN; + CONST; + CONTINUE; + + DEFAULT; + DEFER; + ELSE; + FALLTHROUGH; + FOR; + + FUNC; + GO; + GOTO; + IF; + IMPORT; + + INTERFACE; + MAP; + PACKAGE; + RANGE; + RETURN; + + SELECT; + STRUCT; + SWITCH; + TYPE; + VAR; + keyword_end; +) + + +// At the moment we have no array literal syntax that lets us describe +// the index for each element - use a map for now to make sure they are +// in sync. +var tokens = map [Token] string { + ILLEGAL : "ILLEGAL", + + EOF : "EOF", + COMMENT : "COMMENT", + + IDENT : "IDENT", + INT : "INT", + FLOAT : "FLOAT", + CHAR : "CHAR", + STRING : "STRING", + + ADD : "+", + SUB : "-", + MUL : "*", + QUO : "/", + REM : "%", + + AND : "&", + OR : "|", + XOR : "^", + SHL : "<<", + SHR : ">>", + AND_NOT : "&^", + + ADD_ASSIGN : "+=", + SUB_ASSIGN : "-=", + MUL_ASSIGN : "+=", + QUO_ASSIGN : "/=", + REM_ASSIGN : "%=", + + AND_ASSIGN : "&=", + OR_ASSIGN : "|=", + XOR_ASSIGN : "^=", + SHL_ASSIGN : "<<=", + SHR_ASSIGN : ">>=", + AND_NOT_ASSIGN : "&^=", + + LAND : "&&", + LOR : "||", + ARROW : "<-", + INC : "++", + DEC : "--", + + EQL : "==", + LSS : "<", + GTR : ">", + ASSIGN : "=", + NOT : "!", + + NEQ : "!=", + LEQ : "<=", + GEQ : ">=", + DEFINE : ":=", + ELLIPSIS : "...", + + LPAREN : "(", + LBRACK : "[", + LBRACE : "{", + COMMA : ",", + PERIOD : ".", + + RPAREN : ")", + RBRACK : "]", + RBRACE : "}", + SEMICOLON : ";", + COLON : ":", + + BREAK : "break", + CASE : "case", + CHAN : "chan", + CONST : "const", + CONTINUE : "continue", + + DEFAULT : "default", + DEFER : "defer", + ELSE : "else", + FALLTHROUGH : "fallthrough", + FOR : "for", + + FUNC : "func", + GO : "go", + GOTO : "goto", + IF : "if", + IMPORT : "import", + + INTERFACE : "interface", + MAP : "map", + PACKAGE : "package", + RANGE : "range", + RETURN : "return", + + SELECT : "select", + STRUCT : "struct", + SWITCH : "switch", + TYPE : "type", + VAR : "var", +} + + +// String returns the string corresponding to the token tok. +// For operators, delimiters, and keywords the string is the actual +// token character sequence (e.g., for the token ADD, the string is +// "+"). For all other tokens the string corresponds to the token +// constant name (e.g. for the token IDENT, the string is "IDENT"). +// +func (tok Token) String() string { + if str, exists := tokens[tok]; exists { + return str; + } + return "token(" + strconv.Itoa(int(tok)) + ")"; +} + + +// A set of constants for precedence-based expression parsing. +// Non-operators have lowest precedence, followed by operators +// starting with precedence 1 up to unary operators. The highest +// precedence corresponds serves as "catch-all" precedence for +// selector, indexing, and other operator and delimiter tokens. +// +const ( + LowestPrec = 0; // non-operators + UnaryPrec = 7; + HighestPrec = 8; +) + + +// Precedence returns the operator precedence of the binary +// operator op. If op is not a binary operator, the result +// is LowestPrecedence. +// +func (op Token) Precedence() int { + switch op { + case LOR: + return 1; + case LAND: + return 2; + case ARROW: + return 3; + case EQL, NEQ, LSS, LEQ, GTR, GEQ: + return 4; + case ADD, SUB, OR, XOR: + return 5; + case MUL, QUO, REM, SHL, SHR, AND, AND_NOT: + return 6; + } + return LowestPrec; +} + + +var keywords map [string] Token; + +func init() { + keywords = make(map [string] Token); + for i := keyword_beg + 1; i < keyword_end; i++ { + keywords[tokens[i]] = i; + } +} + + +// Lookup maps an identifier to its keyword token or IDENT (if not a keyword). +// +func Lookup(ident []byte) Token { + // TODO Maps with []byte key are illegal because []byte does not + // support == . Should find a more efficient solution eventually. + if tok, is_keyword := keywords[string(ident)]; is_keyword { + return tok; + } + return IDENT; +} + + +// Predicates + +// IsLiteral returns true for tokens corresponding to identifiers +// and basic type literals; returns false otherwise. +// +func (tok Token) IsLiteral() bool { + return literal_beg < tok && tok < literal_end; +} + +// IsOperator returns true for tokens corresponding to operators and +// delimiters; returns false otherwise. +// +func (tok Token) IsOperator() bool { + return operator_beg < tok && tok < operator_end; +} + +// IsKeyword returns true for tokens corresponding to keywords; +// returns false otherwise. +// +func (tok Token) IsKeyword() bool { + return keyword_beg < tok && tok < keyword_end; +} + + +// Token source positions are represented by a Position value. +// A Position is valid if the line number is > 0. +// +type Position struct { + Offset int; // byte offset, starting at 0 + Line int; // line number, starting at 1 + Column int; // column number, starting at 1 (character count) +} + + +// Pos is an accessor method for anonymous Position fields. +// It returns its receiver. +// +func (pos *Position) Pos() Position { + return *pos; +} + + +// IsValid returns true if the position is valid. +func (pos *Position) IsValid() bool { + return pos.Line > 0 +} diff --git a/src/pkg/hash/Makefile b/src/pkg/hash/Makefile new file mode 100644 index 000000000..bdbb6f347 --- /dev/null +++ b/src/pkg/hash/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + hash.$O\ + + +phases: a1 +_obj$D/hash.a: phases + +a1: $(O1) + $(AR) grc _obj$D/hash.a hash.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/hash.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/hash.a + +packages: _obj$D/hash.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/hash.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/hash.a diff --git a/src/pkg/hash/adler32/Makefile b/src/pkg/hash/adler32/Makefile new file mode 100644 index 000000000..134131259 --- /dev/null +++ b/src/pkg/hash/adler32/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/hash/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + adler32.$O\ + + +phases: a1 +_obj$D/adler32.a: phases + +a1: $(O1) + $(AR) grc _obj$D/adler32.a adler32.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/adler32.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/adler32.a + +packages: _obj$D/adler32.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/adler32.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/adler32.a diff --git a/src/pkg/hash/adler32/adler32.go b/src/pkg/hash/adler32/adler32.go new file mode 100644 index 000000000..fbf9177f8 --- /dev/null +++ b/src/pkg/hash/adler32/adler32.go @@ -0,0 +1,96 @@ +// Copyright 2009 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. + +// This package implements the Adler-32 checksum. +// Defined in RFC 1950: +// Adler-32 is composed of two sums accumulated per byte: s1 is +// the sum of all bytes, s2 is the sum of all s1 values. Both sums +// are done modulo 65521. s1 is initialized to 1, s2 to zero. The +// Adler-32 checksum is stored as s2*65536 + s1 in most- +// significant-byte first (network) order. +package adler32 + +import ( + "hash"; + "os"; +) + +const ( + mod = 65521; +) + +// The size of an Adler-32 checksum in bytes. +const Size = 4; + +// digest represents the partial evaluation of a checksum. +type digest struct { + // invariant: (a < mod && b < mod) || a <= b + // invariant: a + b + 255 <= 0xffffffff + a, b uint32; +} + +func (d *digest) Reset() { + d.a, d.b = 1, 0; +} + +// New returns a new Hash32 computing the Adler-32 checksum. +func New() hash.Hash32 { + d := new(digest); + d.Reset(); + return d; +} + +func (d *digest) Size() int { + return Size; +} + +// Add p to the running checksum a, b. +func update(a, b uint32, p []byte) (aa, bb uint32) { + for i := 0; i < len(p); i++ { + a += uint32(p[i]); + b += a; + // invariant: a <= b + if b > (0xffffffff - 255) / 2 { + a %= mod; + b %= mod; + // invariant: a < mod && b < mod + } else { + // invariant: a + b + 255 <= 2 * b + 255 <= 0xffffffff + } + } + return a, b; +} + +// Return the 32-bit checksum corresponding to a, b. +func finish(a, b uint32) uint32 { + if b >= mod { + a %= mod; + b %= mod; + } + return b<<16 | a; +} + +func (d *digest) Write(p []byte) (nn int, err os.Error) { + d.a, d.b = update(d.a, d.b, p); + return len(p), nil; +} + +func (d *digest) Sum32() uint32 { + return finish(d.a, d.b); +} + +func (d *digest) Sum() []byte { + p := make([]byte, 4); + s := d.Sum32(); + p[0] = byte(s>>24); + p[1] = byte(s>>16); + p[2] = byte(s>>8); + p[3] = byte(s); + return p; +} + +// Checksum returns the Adler-32 checksum of data. +func Checksum(data []byte) uint32 { + return finish(update(1, 0, data)); +} diff --git a/src/pkg/hash/adler32/adler32_test.go b/src/pkg/hash/adler32/adler32_test.go new file mode 100644 index 000000000..ce49a110b --- /dev/null +++ b/src/pkg/hash/adler32/adler32_test.go @@ -0,0 +1,65 @@ +// Copyright 2009 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 adler32 + +import ( + "hash/adler32"; + "io"; + "testing"; +) + +type _Adler32Test struct { + out uint32; + in string; +} + +var golden = []_Adler32Test { + _Adler32Test{ 0x1, "" }, + _Adler32Test{ 0x620062, "a" }, + _Adler32Test{ 0x12600c4, "ab" }, + _Adler32Test{ 0x24d0127, "abc" }, + _Adler32Test{ 0x3d8018b, "abcd" }, + _Adler32Test{ 0x5c801f0, "abcde" }, + _Adler32Test{ 0x81e0256, "abcdef" }, + _Adler32Test{ 0xadb02bd, "abcdefg" }, + _Adler32Test{ 0xe000325, "abcdefgh" }, + _Adler32Test{ 0x118e038e, "abcdefghi" }, + _Adler32Test{ 0x158603f8, "abcdefghij" }, + _Adler32Test{ 0x3f090f02, "Discard medicine more than two years old." }, + _Adler32Test{ 0x46d81477, "He who has a shady past knows that nice guys finish last." }, + _Adler32Test{ 0x40ee0ee1, "I wouldn't marry him with a ten foot pole." }, + _Adler32Test{ 0x16661315, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave" }, + _Adler32Test{ 0x5b2e1480, "The days of the digital watch are numbered. -Tom Stoppard" }, + _Adler32Test{ 0x8c3c09ea, "Nepal premier won't resign." }, + _Adler32Test{ 0x45ac18fd, "For every action there is an equal and opposite government program." }, + _Adler32Test{ 0x53c61462, "His money is twice tainted: 'taint yours and 'taint mine." }, + _Adler32Test{ 0x7e511e63, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977" }, + _Adler32Test{ 0xe4801a6a, "It's a tiny change to the code and not completely disgusting. - Bob Manchek" }, + _Adler32Test{ 0x61b507df, "size: a.out: bad magic" }, + _Adler32Test{ 0xb8631171, "The major problem is with sendmail. -Mark Horton" }, + _Adler32Test{ 0x8b5e1904, "Give me a rock, paper and scissors and I will move the world. CCFestoon" }, + _Adler32Test{ 0x7cc6102b, "If the enemy is within range, then so are you." }, + _Adler32Test{ 0x700318e7, "It's well we cannot hear the screams/That we create in others' dreams." }, + _Adler32Test{ 0x1e601747, "You remind me of a TV show, but that's all right: I watch it anyway." }, + _Adler32Test{ 0xb55b0b09, "C is as portable as Stonehedge!!" }, + _Adler32Test{ 0x39111dd0, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley" }, + _Adler32Test{ 0x91dd304f, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule" }, + _Adler32Test{ 0x2e5d1316, "How can you write a big system without C++? -Paul Glick" }, + _Adler32Test{ 0xd0201df6, "'Invariant assertions' is the most elegant programming technique! -Tom Szymanski" }, +} + +func TestGolden(t *testing.T) { + for i := 0; i < len(golden); i++ { + g := golden[i]; + c := New(); + io.WriteString(c, g.in); + s := c.Sum32(); + if s != g.out { + t.Errorf("adler32(%s) = 0x%x want 0x%x", g.in, s, g.out); + t.FailNow(); + } + } +} + diff --git a/src/pkg/hash/crc32/Makefile b/src/pkg/hash/crc32/Makefile new file mode 100644 index 000000000..08d4f5e4e --- /dev/null +++ b/src/pkg/hash/crc32/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/hash/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + crc32.$O\ + + +phases: a1 +_obj$D/crc32.a: phases + +a1: $(O1) + $(AR) grc _obj$D/crc32.a crc32.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/crc32.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/crc32.a + +packages: _obj$D/crc32.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/crc32.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/crc32.a diff --git a/src/pkg/hash/crc32/crc32.go b/src/pkg/hash/crc32/crc32.go new file mode 100644 index 000000000..22a0f68f6 --- /dev/null +++ b/src/pkg/hash/crc32/crc32.go @@ -0,0 +1,120 @@ +// Copyright 2009 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. + +// This package implements the 32-bit cyclic redundancy check, or CRC-32, checksum. +// See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for information. +package crc32 + +import ( + "hash"; + "os"; +) + +// The size of a CRC-32 checksum in bytes. +const Size = 4; + +// Predefined polynomials. +const ( + // Far and away the most common CRC-32 polynomial. + // Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, mpeg-2, ... + IEEE = 0xedb88320; + + // Castagnoli's polynomial, used in iSCSI. + // Has better error detection characteristics than IEEE. + // http://dx.doi.org/10.1109/26.231911 + Castagnoli = 0x82f63b78; + + // Koopman's polynomial. + // Also has better error detection characteristics than IEEE. + // http://dx.doi.org/10.1109/DSN.2002.1028931 + Koopman = 0xeb31d82e; +) + +// Table is a 256-word table representing the polynomial for efficient processing. +type Table [256]uint32 + +// MakeTable returns the Table constructed from the specified polynomial. +func MakeTable(poly uint32) *Table { + t := new(Table); + for i := 0; i < 256; i++ { + crc := uint32(i); + for j := 0; j < 8; j++ { + if crc&1 == 1 { + crc = (crc>>1) ^ poly; + } else { + crc >>= 1; + } + } + t[i] = crc; + } + return t; +} + +// IEEETable is the table for the IEEE polynomial. +var IEEETable = MakeTable(IEEE); + +// digest represents the partial evaluation of a checksum. +type digest struct { + crc uint32; + tab *Table; +} + +// New creates a new Hash computing the CRC-32 checksum +// using the polynomial represented by the Table. +func New(tab *Table) hash.Hash32 { + return &digest{0, tab}; +} + +// NewIEEE creates a new Hash computing the CRC-32 checksum +// using the IEEE polynomial. +func NewIEEE() hash.Hash32 { + return New(IEEETable); +} + +func (d *digest) Size() int { + return Size; +} + +func (d *digest) Reset() { + d.crc = 0; +} + +func update(crc uint32, tab *Table, p []byte) uint32 { + crc = ^crc; + for i := 0; i < len(p); i++ { + crc = tab[byte(crc) ^ p[i]] ^ (crc >> 8); + } + return ^crc; +} + +func (d *digest) Write(p []byte) (n int, err os.Error) { + d.crc = update(d.crc, d.tab, p); + return len(p), nil; +} + +func (d *digest) Sum32() uint32 { + return d.crc +} + +func (d *digest) Sum() []byte { + p := make([]byte, 4); + s := d.Sum32(); + p[0] = byte(s>>24); + p[1] = byte(s>>16); + p[2] = byte(s>>8); + p[3] = byte(s); + return p; +} + +// Checksum returns the CRC-32 checksum of data +// using the polynomial represented by the Table. +func Checksum(data []byte, tab *Table) uint32 { + return update(0, tab, data); +} + +// ChecksumIEEE returns the CRC-32 checksum of data +// using the IEEE polynomial. +func ChecksumIEEE(data []byte) uint32 { + return update(0, IEEETable, data); +} diff --git a/src/pkg/hash/crc32/crc32_test.go b/src/pkg/hash/crc32/crc32_test.go new file mode 100644 index 000000000..c037da600 --- /dev/null +++ b/src/pkg/hash/crc32/crc32_test.go @@ -0,0 +1,64 @@ +// Copyright 2009 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 crc32 + +import ( + "hash/crc32"; + "io"; + "testing"; +) + +type _Crc32Test struct { + out uint32; + in string; +} + +var golden = []_Crc32Test { + _Crc32Test{ 0x0, "" }, + _Crc32Test{ 0xe8b7be43, "a" }, + _Crc32Test{ 0x9e83486d, "ab" }, + _Crc32Test{ 0x352441c2, "abc" }, + _Crc32Test{ 0xed82cd11, "abcd" }, + _Crc32Test{ 0x8587d865, "abcde" }, + _Crc32Test{ 0x4b8e39ef, "abcdef" }, + _Crc32Test{ 0x312a6aa6, "abcdefg" }, + _Crc32Test{ 0xaeef2a50, "abcdefgh" }, + _Crc32Test{ 0x8da988af, "abcdefghi" }, + _Crc32Test{ 0x3981703a, "abcdefghij" }, + _Crc32Test{ 0x6b9cdfe7, "Discard medicine more than two years old." }, + _Crc32Test{ 0xc90ef73f, "He who has a shady past knows that nice guys finish last." }, + _Crc32Test{ 0xb902341f, "I wouldn't marry him with a ten foot pole." }, + _Crc32Test{ 0x42080e8, "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave" }, + _Crc32Test{ 0x154c6d11, "The days of the digital watch are numbered. -Tom Stoppard" }, + _Crc32Test{ 0x4c418325, "Nepal premier won't resign." }, + _Crc32Test{ 0x33955150, "For every action there is an equal and opposite government program." }, + _Crc32Test{ 0x26216a4b, "His money is twice tainted: 'taint yours and 'taint mine." }, + _Crc32Test{ 0x1abbe45e, "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977" }, + _Crc32Test{ 0xc89a94f7, "It's a tiny change to the code and not completely disgusting. - Bob Manchek" }, + _Crc32Test{ 0xab3abe14, "size: a.out: bad magic" }, + _Crc32Test{ 0xbab102b6, "The major problem is with sendmail. -Mark Horton" }, + _Crc32Test{ 0x999149d7, "Give me a rock, paper and scissors and I will move the world. CCFestoon" }, + _Crc32Test{ 0x6d52a33c, "If the enemy is within range, then so are you." }, + _Crc32Test{ 0x90631e8d, "It's well we cannot hear the screams/That we create in others' dreams." }, + _Crc32Test{ 0x78309130, "You remind me of a TV show, but that's all right: I watch it anyway." }, + _Crc32Test{ 0x7d0a377f, "C is as portable as Stonehedge!!" }, + _Crc32Test{ 0x8c79fd79, "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley" }, + _Crc32Test{ 0xa20b7167, "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule" }, + _Crc32Test{ 0x8e0bb443, "How can you write a big system without C++? -Paul Glick" }, +} + +func TestGolden(t *testing.T) { + for i := 0; i < len(golden); i++ { + g := golden[i]; + c := NewIEEE(); + io.WriteString(c, g.in); + s := c.Sum32(); + if s != g.out { + t.Errorf("crc32(%s) = 0x%x want 0x%x", g.in, s, g.out); + t.FailNow(); + } + } +} + diff --git a/src/pkg/hash/hash.go b/src/pkg/hash/hash.go new file mode 100644 index 000000000..a7c08cfed --- /dev/null +++ b/src/pkg/hash/hash.go @@ -0,0 +1,24 @@ +// Copyright 2009 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 hash + +import "io"; + +// Hash is the common interface implemented by all hash functions. +// The Write method never returns an error. +// Sum returns the bytes of integer hash codes in big-endian order. +type Hash interface { + io.Writer; + Sum() []byte; + Reset(); + Size() int; // number of bytes Sum returns +} + +// Hash32 is the common interface implemented by all 32-bit hash functions. +type Hash32 interface { + Hash; + Sum32() uint32; +} + diff --git a/src/pkg/hash/test_cases.txt b/src/pkg/hash/test_cases.txt new file mode 100644 index 000000000..26d3ccc05 --- /dev/null +++ b/src/pkg/hash/test_cases.txt @@ -0,0 +1,31 @@ + +a +ab +abc +abcd +abcde +abcdef +abcdefg +abcdefgh +abcdefghi +abcdefghij +Discard medicine more than two years old. +He who has a shady past knows that nice guys finish last. +I wouldn't marry him with a ten foot pole. +Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave +The days of the digital watch are numbered. -Tom Stoppard +Nepal premier won't resign. +For every action there is an equal and opposite government program. +His money is twice tainted: 'taint yours and 'taint mine. +There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977 +It's a tiny change to the code and not completely disgusting. - Bob Manchek +size: a.out: bad magic +The major problem is with sendmail. -Mark Horton +Give me a rock, paper and scissors and I will move the world. CCFestoon +If the enemy is within range, then so are you. +It's well we cannot hear the screams/That we create in others' dreams. +You remind me of a TV show, but that's all right: I watch it anyway. +C is as portable as Stonehedge!! +Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley +The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule +How can you write a big system without C++? -Paul Glick diff --git a/src/pkg/hash/test_gen.awk b/src/pkg/hash/test_gen.awk new file mode 100644 index 000000000..804f78679 --- /dev/null +++ b/src/pkg/hash/test_gen.awk @@ -0,0 +1,14 @@ +# Copyright 2009 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. + +# awk -f test_gen.awk test_cases.txt +# generates test case table. +# edit next line to set particular reference implementation and name. +BEGIN { cmd = "echo -n `9 sha1sum`"; name = "Sha1Test" } +{ + printf("\t%s{ \"", name); + printf("%s", $0) |cmd; + close(cmd); + printf("\", \"%s\" },\n", $0); +} diff --git a/src/pkg/http/Makefile b/src/pkg/http/Makefile new file mode 100644 index 000000000..0a029497c --- /dev/null +++ b/src/pkg/http/Makefile @@ -0,0 +1,85 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + status.$O\ + url.$O\ + +O2=\ + request.$O\ + +O3=\ + server.$O\ + +O4=\ + fs.$O\ + + +phases: a1 a2 a3 a4 +_obj$D/http.a: phases + +a1: $(O1) + $(AR) grc _obj$D/http.a status.$O url.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/http.a request.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/http.a server.$O + rm -f $(O3) + +a4: $(O4) + $(AR) grc _obj$D/http.a fs.$O + rm -f $(O4) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/http.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 +$(O5): a4 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/http.a + +packages: _obj$D/http.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/http.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/http.a diff --git a/src/pkg/http/fs.go b/src/pkg/http/fs.go new file mode 100644 index 000000000..108734c47 --- /dev/null +++ b/src/pkg/http/fs.go @@ -0,0 +1,184 @@ +// Copyright 2009 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. + +// HTTP file system request handler + +package http + +import ( + "fmt"; + "http"; + "io"; + "os"; + "path"; + "strings"; + "utf8"; +) + +// TODO this should be in a mime package somewhere +var contentByExt = map[string] string { + ".css": "text/css", + ".gif": "image/gif", + ".html": "text/html; charset=utf-8", + ".jpg": "image/jpeg", + ".js": "application/x-javascript", + ".png": "image/png", +} + +// Heuristic: b is text if it is valid UTF-8 and doesn't +// contain any unprintable ASCII or Unicode characters. +func isText(b []byte) bool { + for len(b) > 0 && utf8.FullRune(b) { + rune, size := utf8.DecodeRune(b); + if size == 1 && rune == utf8.RuneError { + // decoding error + return false; + } + if 0x80 <= rune && rune <= 0x9F { + return false; + } + if rune < ' ' { + switch rune { + case '\n', '\r', '\t': + // okay + default: + // binary garbage + return false; + } + } + b = b[size:len(b)]; + } + return true; +} + +func dirList(c *Conn, f *os.File) { + fmt.Fprintf(c, "<pre>\n"); + for { + dirs, err := f.Readdir(100); + if err != nil || len(dirs) == 0 { + break + } + for i, d := range dirs { + name := d.Name; + if d.IsDirectory() { + name += "/" + } + // TODO htmlescape + fmt.Fprintf(c, "<a href=\"%s\">%s</a>\n", name, name); + } + } + fmt.Fprintf(c, "</pre>\n"); +} + + +func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { + const indexPage = "/index.html"; + + // redirect to strip off any index.html + n := len(name) - len(indexPage); + if n >= 0 && name[n:len(name)] == indexPage { + http.Redirect(c, name[0:n+1], StatusMovedPermanently); + return; + } + + f, err := os.Open(name, os.O_RDONLY, 0); + if err != nil { + // TODO expose actual error? + NotFound(c, r); + return; + } + defer f.Close(); + + d, err1 := f.Stat(); + if err1 != nil { + // TODO expose actual error? + NotFound(c, r); + return; + } + + if redirect { + // redirect to canonical path: / at end of directory url + // r.Url.Path always begins with / + url := r.Url.Path; + if d.IsDirectory() { + if url[len(url)-1] != '/' { + http.Redirect(c, url + "/", StatusMovedPermanently); + return; + } + } else { + if url[len(url)-1] == '/' { + http.Redirect(c, url[0:len(url)-1], StatusMovedPermanently); + return; + } + } + } + + // use contents of index.html for directory, if present + if d.IsDirectory() { + index := name + indexPage; + ff, err := os.Open(index, os.O_RDONLY, 0); + if err == nil { + defer ff.Close(); + dd, err := ff.Stat(); + if err == nil { + name = index; + d = dd; + f = ff; + } + } + } + + if d.IsDirectory() { + dirList(c, f); + return; + } + + // serve file + // use extension to find content type. + ext := path.Ext(name); + if ctype, ok := contentByExt[ext]; ok { + c.SetHeader("Content-Type", ctype); + } else { + // read first chunk to decide between utf-8 text and binary + var buf [1024]byte; + n, err := io.FullRead(f, &buf); + b := buf[0:n]; + if isText(b) { + c.SetHeader("Content-Type", "text-plain; charset=utf-8"); + } else { + c.SetHeader("Content-Type", "application/octet-stream"); // generic binary + } + c.Write(b); + } + io.Copy(f, c); +} + +// ServeFile replies to the request with the contents of the named file or directory. +func ServeFile(c *Conn, r *Request, name string) { + serveFileInternal(c, r, name, false); +} + +type fileHandler struct { + root string; + prefix string; +} + +// FileServer returns a handler that serves HTTP requests +// with the contents of the file system rooted at root. +// It strips prefix from the incoming requests before +// looking up the file name in the file system. +func FileServer(root, prefix string) Handler { + return &fileHandler{root, prefix}; +} + +func (f *fileHandler) ServeHTTP(c *Conn, r *Request) { + path := r.Url.Path; + if !strings.HasPrefix(path, f.prefix) { + NotFound(c, r); + return; + } + path = path[len(f.prefix):len(path)]; + serveFileInternal(c, r, f.root + "/" + path, true); +} + diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go new file mode 100644 index 000000000..76dd6f30c --- /dev/null +++ b/src/pkg/http/request.go @@ -0,0 +1,413 @@ +// Copyright 2009 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. + +// HTTP Request reading and parsing. + +// The http package implements parsing of HTTP requests and URLs +// and provides an extensible HTTP server. +// +// In the future it should also implement parsing of HTTP replies +// and provide methods to fetch URLs via HTTP. +package http + +import ( + "bufio"; + "fmt"; + "http"; + "io"; + "os"; + "strconv"; + "strings"; +) + +const ( + maxLineLength = 1024; // assumed < bufio.DefaultBufSize + maxValueLength = 1024; + maxHeaderLines = 1024; +) + +// HTTP request parsing errors. +type ProtocolError struct { + os.ErrorString +} +var ( + LineTooLong = &ProtocolError{"http header line too long"}; + ValueTooLong = &ProtocolError{"http header value too long"}; + HeaderTooLong = &ProtocolError{"http header too long"}; + BadContentLength = &ProtocolError{"invalid content length"}; + ShortEntityBody = &ProtocolError{"entity body too short"}; + BadHeader = &ProtocolError{"malformed http header"}; + BadRequest = &ProtocolError{"invalid http request"}; + BadHTTPVersion = &ProtocolError{"unsupported http version"}; +) + +// A Request represents a parsed HTTP request header. +type Request struct { + Method string; // GET, POST, PUT, etc. + RawUrl string; // The raw URL given in the request. + Url *URL; // Parsed URL. + Proto string; // "HTTP/1.0" + ProtoMajor int; // 1 + ProtoMinor int; // 0 + + // A header mapping request lines to their values. + // If the header says + // + // Accept-Language: en-us + // accept-encoding: gzip, deflate + // Connection: keep-alive + // + // then + // + // Header = map[string]string{ + // "Accept-Encoding": "en-us", + // "Accept-Language": "gzip, deflate", + // "Connection": "keep-alive" + // } + // + // HTTP defines that header names are case-insensitive. + // The request parser implements this by canonicalizing the + // name, making the first character and any characters + // following a hyphen uppercase and the rest lowercase. + Header map[string] string; + + // The message body. + Body io.Reader; + + // Whether to close the connection after replying to this request. + Close bool; + + // The host on which the URL is sought. + // Per RFC 2616, this is either the value of the Host: header + // or the host name given in the URL itself. + Host string; + + // The referring URL, if sent in the request. + // + // Referer is misspelled as in the request itself, + // a mistake from the earliest days of HTTP. + // This value can also be fetched from the Header map + // as Header["Referer"]; the benefit of making it + // available as a structure field is that the compiler + // can diagnose programs that use the alternate + // (correct English) spelling req.Referrer but cannot + // diagnose programs that use Header["Referrer"]. + Referer string; + + // The User-Agent: header string, if sent in the request. + UserAgent string; +} + +// ProtoAtLeast returns whether the HTTP protocol used +// in the request is at least major.minor. +func (r *Request) ProtoAtLeast(major, minor int) bool { + return r.ProtoMajor > major || + r.ProtoMajor == major && r.ProtoMinor >= minor +} + +// Read a line of bytes (up to \n) from b. +// Give up if the line exceeds maxLineLength. +// The returned bytes are a pointer into storage in +// the bufio, so they are only valid until the next bufio read. +func readLineBytes(b *bufio.Reader) (p []byte, err os.Error) { + if p, err = b.ReadLineSlice('\n'); err != nil { + return nil, err + } + if len(p) >= maxLineLength { + return nil, LineTooLong + } + + // Chop off trailing white space. + var i int; + for i = len(p); i > 0; i-- { + if c := p[i-1]; c != ' ' && c != '\r' && c != '\t' && c != '\n' { + break + } + } + return p[0:i], nil +} + +// readLineBytes, but convert the bytes into a string. +func readLine(b *bufio.Reader) (s string, err os.Error) { + p, e := readLineBytes(b); + if e != nil { + return "", e + } + return string(p), nil +} + +// Read a key/value pair from b. +// A key/value has the form Key: Value\r\n +// and the Value can continue on multiple lines if each continuation line +// starts with a space. +func readKeyValue(b *bufio.Reader) (key, value string, err os.Error) { + line, e := readLineBytes(b); + if e != nil { + return "", "", e + } + if len(line) == 0 { + return "", "", nil + } + + // Scan first line for colon. + for i := 0; i < len(line); i++ { + switch line[i] { + case ' ': + // Key field has space - no good. + return "", "", BadHeader; + case ':': + key = string(line[0:i]); + // Skip initial space before value. + for i++; i < len(line); i++ { + if line[i] != ' ' { + break + } + } + value = string(line[i:len(line)]); + + // Look for extension lines, which must begin with space. + for { + var c byte; + + if c, e = b.ReadByte(); e != nil { + return "", "", e + } + if c != ' ' { + // Not leading space; stop. + b.UnreadByte(); + break + } + + // Eat leading space. + for c == ' ' { + if c, e = b.ReadByte(); e != nil { + return "", "", e + } + } + b.UnreadByte(); + + // Read the rest of the line and add to value. + if line, e = readLineBytes(b); e != nil { + return "", "", e + } + value += " " + string(line); + + if len(value) >= maxValueLength { + return "", "", ValueTooLong + } + } + return key, value, nil + } + } + + // Line ended before space or colon. + return "", "", BadHeader; +} + +// Convert decimal at s[i:len(s)] to integer, +// returning value, string position where the digits stopped, +// and whether there was a valid number (digits, not too big). +func atoi(s string, i int) (n, i1 int, ok bool) { + const Big = 1000000; + if i >= len(s) || s[i] < '0' || s[i] > '9' { + return 0, 0, false + } + n = 0; + for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + n = n*10 + int(s[i]-'0'); + if n > Big { + return 0, 0, false + } + } + return n, i, true +} + +// Parse HTTP version: "HTTP/1.2" -> (1, 2, true). +func parseHTTPVersion(vers string) (int, int, bool) { + if vers[0:5] != "HTTP/" { + return 0, 0, false + } + major, i, ok := atoi(vers, 5); + if !ok || i >= len(vers) || vers[i] != '.' { + return 0, 0, false + } + var minor int; + minor, i, ok = atoi(vers, i+1); + if !ok || i != len(vers) { + return 0, 0, false + } + return major, minor, true +} + +var cmap = make(map[string]string) + +// CanonicalHeaderKey returns the canonical format of the +// HTTP header key s. The canonicalization converts the first +// letter and any letter following a hyphen to upper case; +// the rest are converted to lowercase. For example, the +// canonical key for "accept-encoding" is "Accept-Encoding". +func CanonicalHeaderKey(s string) string { + if t, ok := cmap[s]; ok { + return t; + } + + // canonicalize: first letter upper case + // and upper case after each dash. + // (Host, User-Agent, If-Modified-Since). + // HTTP headers are ASCII only, so no Unicode issues. + a := io.StringBytes(s); + upper := true; + for i,v := range a { + if upper && 'a' <= v && v <= 'z' { + a[i] = v + 'A' - 'a'; + } + if !upper && 'A' <= v && v <= 'Z' { + a[i] = v + 'a' - 'A'; + } + upper = false; + if v == '-' { + upper = true; + } + } + t := string(a); + cmap[s] = t; + return t; +} + +// ReadRequest reads and parses a request from b. +func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { + req = new(Request); + + // First line: GET /index.html HTTP/1.0 + var s string; + if s, err = readLine(b); err != nil { + return nil, err + } + + var f []string; + if f = strings.Split(s, " "); len(f) != 3 { + return nil, BadRequest + } + req.Method, req.RawUrl, req.Proto = f[0], f[1], f[2]; + var ok bool; + if req.ProtoMajor, req.ProtoMinor, ok = parseHTTPVersion(req.Proto); !ok { + return nil, BadHTTPVersion + } + + if req.Url, err = ParseURL(req.RawUrl); err != nil { + return nil, err + } + + // Subsequent lines: Key: value. + nheader := 0; + req.Header = make(map[string] string); + for { + var key, value string; + if key, value, err = readKeyValue(b); err != nil { + return nil, err + } + if key == "" { + break + } + if nheader++; nheader >= maxHeaderLines { + return nil, HeaderTooLong + } + + key = CanonicalHeaderKey(key); + + // RFC 2616 says that if you send the same header key + // multiple times, it has to be semantically equivalent + // to concatenating the values separated by commas. + oldvalue, present := req.Header[key]; + if present { + req.Header[key] = oldvalue+","+value + } else { + req.Header[key] = value + } + } + + // RFC2616: Must treat + // GET /index.html HTTP/1.1 + // Host: www.google.com + // and + // GET http://www.google.com/index.html HTTP/1.1 + // Host: doesntmatter + // the same. In the second case, any Host line is ignored. + if v, present := req.Header["Host"]; present && req.Url.Host == "" { + req.Host = v + } + + // RFC2616: Should treat + // Pragma: no-cache + // like + // Cache-Control: no-cache + if v, present := req.Header["Pragma"]; present && v == "no-cache" { + if cc, presentcc := req.Header["Cache-Control"]; !presentcc { + req.Header["Cache-Control"] = "no-cache" + } + } + + // Determine whether to hang up after sending the reply. + if req.ProtoMajor < 1 || (req.ProtoMajor == 1 && req.ProtoMinor < 1) { + req.Close = true + } else if v, present := req.Header["Connection"]; present { + // TODO: Should split on commas, toss surrounding white space, + // and check each field. + if v == "close" { + req.Close = true + } + } + + // Pull out useful fields as a convenience to clients. + if v, present := req.Header["Referer"]; present { + req.Referer = v + } + if v, present := req.Header["User-Agent"]; present { + req.UserAgent = v + } + + // TODO: Parse specific header values: + // Accept + // Accept-Encoding + // Accept-Language + // Authorization + // Cache-Control + // Connection + // Date + // Expect + // From + // If-Match + // If-Modified-Since + // If-None-Match + // If-Range + // If-Unmodified-Since + // Max-Forwards + // Proxy-Authorization + // Referer [sic] + // TE (transfer-codings) + // Trailer + // Transfer-Encoding + // Upgrade + // User-Agent + // Via + // Warning + + // A message body exists when either Content-Length or Transfer-Encoding + // headers are present. TODO: Handle Transfer-Encoding. + if v, present := req.Header["Content-Length"]; present { + length, err := strconv.Btoui64(v, 10); + if err != nil { + return nil, BadContentLength + } + // TODO: limit the Content-Length. This is an easy DoS vector. + raw := make([]byte, length); + n, err := b.Read(raw); + if err != nil || uint64(n) < length { + return nil, ShortEntityBody + } + req.Body = io.NewByteReader(raw); + } + + return req, nil +} diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go new file mode 100644 index 000000000..c1de5de78 --- /dev/null +++ b/src/pkg/http/server.go @@ -0,0 +1,575 @@ +// Copyright 2009 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. + +// HTTP server. See RFC 2616. + +// TODO(rsc): +// logging +// cgi support +// post support + +package http + +import ( + "bufio"; + "fmt"; + "http"; + "io"; + "log"; + "net"; + "os"; + "path"; + "strconv"; + "strings"; +) + +// Errors introduced by the HTTP server. +var ( + ErrWriteAfterFlush = os.NewError("Conn.Write called after Flush"); + ErrHijacked = os.NewError("Conn has been hijacked"); +) + +type Conn struct + +// Objects implemeting the Handler interface can be +// registered to serve a particular path or subtree +// in the HTTP server. +type Handler interface { + ServeHTTP(*Conn, *Request); +} + +// A Conn represents the server side of a single active HTTP connection. +type Conn struct { + RemoteAddr string; // network address of remote side + Req *Request; // current HTTP request + + rwc io.ReadWriteCloser; // i/o connection + buf *bufio.ReadWriter; // buffered rwc + handler Handler; // request handler + hijacked bool; // connection has been hijacked by handler + + // state for the current reply + closeAfterReply bool; // close connection after this reply + chunking bool; // using chunked transfer encoding for reply body + wroteHeader bool; // reply header has been written + header map[string] string; // reply header parameters + written int64; // number of bytes written in body + status int; // status code passed to WriteHeader +} + +// Create new connection from rwc. +func newConn(rwc io.ReadWriteCloser, raddr string, handler Handler) (c *Conn, err os.Error) { + c = new(Conn); + c.RemoteAddr = raddr; + c.handler = handler; + c.rwc = rwc; + br := bufio.NewReader(rwc); + bw := bufio.NewWriter(rwc); + c.buf = bufio.NewReadWriter(br, bw); + return c, nil +} + +func (c *Conn) SetHeader(hdr, val string) + +// Read next request from connection. +func (c *Conn) readRequest() (req *Request, err os.Error) { + if c.hijacked { + return nil, ErrHijacked + } + if req, err = ReadRequest(c.buf.Reader); err != nil { + return nil, err + } + + // Reset per-request connection state. + c.header = make(map[string] string); + c.wroteHeader = false; + c.Req = req; + + // Default output is HTML encoded in UTF-8. + c.SetHeader("Content-Type", "text/html; charset=utf-8"); + + if req.ProtoAtLeast(1, 1) { + // HTTP/1.1 or greater: use chunked transfer encoding + // to avoid closing the connection at EOF. + c.chunking = true; + c.SetHeader("Transfer-Encoding", "chunked"); + } else { + // HTTP version < 1.1: cannot do chunked transfer + // encoding, so signal EOF by closing connection. + // Could avoid closing the connection if there is + // a Content-Length: header in the response, + // but everyone who expects persistent connections + // does HTTP/1.1 now. + c.closeAfterReply = true; + c.chunking = false; + } + + return req, nil +} + +// SetHeader sets a header line in the eventual reply. +// For example, SetHeader("Content-Type", "text/html; charset=utf-8") +// will result in the header line +// +// Content-Type: text/html; charset=utf-8 +// +// being sent. UTF-8 encoded HTML is the default setting for +// Content-Type in this library, so users need not make that +// particular call. Calls to SetHeader after WriteHeader (or Write) +// are ignored. +func (c *Conn) SetHeader(hdr, val string) { + c.header[CanonicalHeaderKey(hdr)] = val; +} + +// WriteHeader sends an HTTP response header with status code. +// If WriteHeader is not called explicitly, the first call to Write +// will trigger an implicit WriteHeader(http.StatusOK). +// Thus explicit calls to WriteHeader are mainly used to +// send error codes. +func (c *Conn) WriteHeader(code int) { + if c.hijacked { + log.Stderr("http: Conn.WriteHeader on hijacked connection"); + return + } + if c.wroteHeader { + log.Stderr("http: multiple Conn.WriteHeader calls"); + return + } + c.wroteHeader = true; + c.status = code; + c.written = 0; + if !c.Req.ProtoAtLeast(1, 0) { + return + } + proto := "HTTP/1.0"; + if c.Req.ProtoAtLeast(1, 1) { + proto = "HTTP/1.1"; + } + codestring := strconv.Itoa(code); + text, ok := statusText[code]; + if !ok { + text = "status code " + codestring; + } + io.WriteString(c.buf, proto + " " + codestring + " " + text + "\r\n"); + for k,v := range c.header { + io.WriteString(c.buf, k + ": " + v + "\r\n"); + } + io.WriteString(c.buf, "\r\n"); +} + +// Write writes the data to the connection as part of an HTTP reply. +// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) +// before writing the data. +func (c *Conn) Write(data []byte) (n int, err os.Error) { + if c.hijacked { + log.Stderr("http: Conn.Write on hijacked connection"); + return 0, ErrHijacked + } + if !c.wroteHeader { + c.WriteHeader(StatusOK); + } + if len(data) == 0 { + return 0, nil + } + + c.written += int64(len(data)); // ignoring errors, for errorKludge + + // TODO(rsc): if chunking happened after the buffering, + // then there would be fewer chunk headers. + // On the other hand, it would make hijacking more difficult. + if c.chunking { + fmt.Fprintf(c.buf, "%x\r\n", len(data)); // TODO(rsc): use strconv not fmt + } + n, err = c.buf.Write(data); + if err == nil && c.chunking { + if n != len(data) { + err = io.ErrShortWrite; + } + if err == nil { + io.WriteString(c.buf, "\r\n"); + } + } + + return n, err; +} + +// If this is an error reply (4xx or 5xx) +// and the handler wrote some data explaining the error, +// some browsers (i.e., Chrome, Internet Explorer) +// will show their own error instead unless the error is +// long enough. The minimum lengths used in those +// browsers are in the 256-512 range. +// Pad to 1024 bytes. +func errorKludge(c *Conn, req *Request) { + const min = 1024; + + // Is this an error? + if kind := c.status/100; kind != 4 && kind != 5 { + return; + } + + // Did the handler supply any info? Enough? + if c.written == 0 || c.written >= min { + return; + } + + // Is it text? ("Content-Type" is always in the map) + if s := c.header["Content-Type"]; len(s) < 5 || s[0:5] != "text/" { + return; + } + + // Is it a broken browser? + var msg string; + switch agent := req.UserAgent; { + case strings.Index(agent, "MSIE") >= 0: + msg = "Internet Explorer"; + case strings.Index(agent, "Chrome/") >= 0: + msg = "Chrome"; + default: + return; + } + msg += " would ignore this error page if this text weren't here.\n"; + io.WriteString(c, "\n"); + for c.written < min { + io.WriteString(c, msg); + } +} + +func (c *Conn) flush() { + if !c.wroteHeader { + c.WriteHeader(StatusOK); + } + errorKludge(c, c.Req); + if c.chunking { + io.WriteString(c.buf, "0\r\n"); + // trailer key/value pairs, followed by blank line + io.WriteString(c.buf, "\r\n"); + } + c.buf.Flush(); +} + +// Close the connection. +func (c *Conn) close() { + if c.buf != nil { + c.buf.Flush(); + c.buf = nil; + } + if c.rwc != nil { + c.rwc.Close(); + c.rwc = nil; + } +} + +// Serve a new connection. +func (c *Conn) serve() { + for { + req, err := c.readRequest(); + if err != nil { + break + } + // HTTP cannot have multiple simultaneous active requests. + // Until the server replies to this request, it can't read another, + // so we might as well run the handler in this goroutine. + c.handler.ServeHTTP(c, req); + if c.hijacked { + return; + } + c.flush(); + if c.closeAfterReply { + break; + } + } + c.close(); +} + +// Hijack lets the caller take over the connection. +// After a call to c.Hijack(), the HTTP server library +// will not do anything else with the connection. +// It becomes the caller's responsibility to manage +// and close the connection. +func (c *Conn) Hijack() (rwc io.ReadWriteCloser, buf *bufio.ReadWriter, err os.Error) { + if c.hijacked { + return nil, nil, ErrHijacked; + } + c.hijacked = true; + rwc = c.rwc; + buf = c.buf; + c.rwc = nil; + c.buf = nil; + return; +} + +// The HandlerFunc type is an adapter to allow the use of +// ordinary functions as HTTP handlers. If f is a function +// with the appropriate signature, HandlerFunc(f) is a +// Handler object that calls f. +type HandlerFunc func(*Conn, *Request) + +// ServeHTTP calls f(c, req). +func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) { + f(c, req); +} + +// Helper handlers + +// NotFound replies to the request with an HTTP 404 not found error. +func NotFound(c *Conn, req *Request) { + c.SetHeader("Content-Type", "text/plain; charset=utf-8"); + c.WriteHeader(StatusNotFound); + io.WriteString(c, "404 page not found\n"); +} + +// NotFoundHandler returns a simple request handler +// that replies to each request with a ``404 page not found'' reply. +func NotFoundHandler() Handler { + return HandlerFunc(NotFound) +} + +// Redirect replies to the request with a redirect to url, +// which may be a path relative to the request path. +func Redirect(c *Conn, url string, code int) { + // RFC2616 recommends that a short note "SHOULD" be included in the + // response because older user agents may not understand 301/307. + note := "<a href=\"%v\">" + statusText[code] + "</a>.\n"; + if c.Req.Method == "POST" { + note = ""; + } + + u, err := ParseURL(url); + if err != nil { + goto finish + } + + // If url was relative, make absolute by + // combining with request path. + // The browser would probably do this for us, + // but doing it ourselves is more reliable. + + // NOTE(rsc): RFC 2616 says that the Location + // line must be an absolute URI, like + // "http://www.google.com/redirect/", + // not a path like "/redirect/". + // Unfortunately, we don't know what to + // put in the host name section to get the + // client to connect to us again, so we can't + // know the right absolute URI to send back. + // Because of this problem, no one pays attention + // to the RFC; they all send back just a new path. + // So do we. + oldpath := c.Req.Url.Path; + if oldpath == "" { // should not happen, but avoid a crash if it does + oldpath = "/" + } + if u.Scheme == "" { + // no leading http://server + if url == "" || url[0] != '/' { + // make relative path absolute + olddir, oldfile := path.Split(oldpath); + url = olddir + url; + } + + // clean up but preserve trailing slash + trailing := url[len(url) - 1] == '/'; + url = path.Clean(url); + if trailing && url[len(url) - 1] != '/' { + url += "/"; + } + } + +finish: + c.SetHeader("Location", url); + c.WriteHeader(code); + fmt.Fprintf(c, note, url); +} + +// Redirect to a fixed URL +type redirectHandler struct { + url string; + code int; +} +func (rh *redirectHandler) ServeHTTP(c *Conn, req *Request) { + Redirect(c, rh.url, rh.code); +} + +// RedirectHandler returns a request handler that redirects +// each request it receives to the given url using the given +// status code. +func RedirectHandler(url string, code int) Handler { + return &redirectHandler{ url, code } +} + +// ServeMux is an HTTP request multiplexer. +// It matches the URL of each incoming request against a list of registered +// patterns and calls the handler for the pattern that +// most closely matches the URL. +// +// Patterns named fixed paths, like "/favicon.ico", +// or subtrees, like "/images/" (note the trailing slash). +// Patterns must begin with /. +// Longer patterns take precedence over shorter ones, so that +// if there are handlers registered for both "/images/" +// and "/images/thumbnails/", the latter handler will be +// called for paths beginning "/images/thumbnails/" and the +// former will receiver requests for any other paths in the +// "/images/" subtree. +// +// In the future, the pattern syntax may be relaxed to allow +// an optional host-name at the beginning of the pattern, +// so that a handler might register for the two patterns +// "/codesearch" and "codesearch.google.com/" +// without taking over requests for http://www.google.com/. +// +// ServeMux also takes care of sanitizing the URL request path, +// redirecting any request containing . or .. elements to an +// equivalent .- and ..-free URL. +type ServeMux struct { + m map[string] Handler +} + +// NewServeMux allocates and returns a new ServeMux. +func NewServeMux() *ServeMux { + return &ServeMux{make(map[string] Handler)}; +} + +// DefaultServeMux is the default ServeMux used by Serve. +var DefaultServeMux = NewServeMux(); + +// Does path match pattern? +func pathMatch(pattern, path string) bool { + if len(pattern) == 0 { + // should not happen + return false + } + n := len(pattern); + if pattern[n-1] != '/' { + return pattern == path + } + return len(path) >= n && path[0:n] == pattern; +} + +// Return the canonical path for p, eliminating . and .. elements. +func cleanPath(p string) string { + if p == "" { + return "/"; + } + if p[0] != '/' { + p = "/" + p; + } + np := path.Clean(p); + // path.Clean removes trailing slash except for root; + // put the trailing slash back if necessary. + if p[len(p)-1] == '/' && np != "/" { + np += "/"; + } + return np; +} + +// ServeHTTP dispatches the request to the handler whose +// pattern most closely matches the request URL. +func (mux *ServeMux) ServeHTTP(c *Conn, req *Request) { + // Clean path to canonical form and redirect. + if p := cleanPath(req.Url.Path); p != req.Url.Path { + c.SetHeader("Location", p); + c.WriteHeader(StatusMovedPermanently); + return; + } + + // Most-specific (longest) pattern wins. + var h Handler; + var n = 0; + for k, v := range mux.m { + if !pathMatch(k, req.Url.Path) { + continue; + } + if h == nil || len(k) > n { + n = len(k); + h = v; + } + } + if h == nil { + h = NotFoundHandler(); + } + h.ServeHTTP(c, req); +} + +// Handle registers the handler for the given pattern. +func (mux *ServeMux) Handle(pattern string, handler Handler) { + if pattern == "" || pattern[0] != '/' { + panicln("http: invalid pattern", pattern); + } + + mux.m[pattern] = handler; + + // Helpful behavior: + // If pattern is /tree/, insert permanent redirect for /tree. + n := len(pattern); + if n > 0 && pattern[n-1] == '/' { + mux.m[pattern[0:n-1]] = RedirectHandler(pattern, StatusMovedPermanently); + } +} + +// Handle registers the handler for the given pattern +// in the DefaultServeMux. +func Handle(pattern string, handler Handler) { + DefaultServeMux.Handle(pattern, handler); +} + +// Serve accepts incoming HTTP connections on the listener l, +// creating a new service thread for each. The service threads +// read requests and then call handler to reply to them. +// Handler is typically nil, in which case the DefaultServeMux is used. +func Serve(l net.Listener, handler Handler) os.Error { + if handler == nil { + handler = DefaultServeMux; + } + for { + rw, raddr, e := l.Accept(); + if e != nil { + return e + } + c, err := newConn(rw, raddr, handler); + if err != nil { + continue; + } + go c.serve(); + } + panic("not reached") +} + +// ListenAndServe listens on the TCP network address addr +// and then calls Serve with handler to handle requests +// on incoming connections. Handler is typically nil, +// in which case the DefaultServeMux is used. +// +// A trivial example server is: +// +// package main +// +// import ( +// "http"; +// "io"; +// ) +// +// // hello world, the web server +// func HelloServer(c *http.Conn, req *http.Request) { +// io.WriteString(c, "hello, world!\n"); +// } +// +// func main() { +// http.Handle("/hello", http.HandlerFunc(HelloServer)); +// err := http.ListenAndServe(":12345", nil); +// if err != nil { +// panic("ListenAndServe: ", err.String()) +// } +// } +func ListenAndServe(addr string, handler Handler) os.Error { + l, e := net.Listen("tcp", addr); + if e != nil { + return e + } + e = Serve(l, handler); + l.Close(); + return e +} + diff --git a/src/pkg/http/status.go b/src/pkg/http/status.go new file mode 100644 index 000000000..6d1c5ab28 --- /dev/null +++ b/src/pkg/http/status.go @@ -0,0 +1,101 @@ +// Copyright 2009 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 http + +// HTTP status codes, defined in RFC 2616. +const ( + StatusContinue = 100; + StatusSwitchingProtocols = 101; + + StatusOK = 200; + StatusCreated = 201; + StatusAccepted = 202; + StatusNonAuthoritativeInfo = 203; + StatusNoContent = 204; + StatusResetContent = 205; + StatusPartialContent = 206; + + StatusMultipleChoices = 300; + StatusMovedPermanently = 301; + StatusFound = 302; + StatusSeeOther = 303; + StatusNotModified = 304; + StatusUseProxy = 305; + StatusTemporaryRedirect = 307; + + StatusBadRequest = 400; + StatusUnauthorized = 401; + StatusPaymentRequired = 402; + StatusForbidden = 403; + StatusNotFound = 404; + StatusMethodNotAllowed = 405; + StatusNotAcceptable = 406; + StatusProxyAuthRequired = 407; + StatusRequestTimeout = 408; + StatusConflict = 409; + StatusGone = 410; + StatusLengthRequired = 411; + StatusPreconditionFailed = 412; + StatusRequestEntityTooLarge = 413; + StatusRequestURITooLong = 414; + StatusUnsupportedMediaType = 415; + StatusRequestedRangeNotSatisfiable = 416; + StatusExpectationFailed = 417; + + StatusInternalServerError = 500; + StatusNotImplemented = 501; + StatusBadGateway = 502; + StatusServiceUnavailable = 503; + StatusGatewayTimeout = 504; + StatusHTTPVersionNotSupported = 505; +) + +var statusText = map[int]string { + StatusContinue: "Continue", + StatusSwitchingProtocols: "Switching Protocols", + + StatusOK: "OK", + StatusCreated: "Created", + StatusAccepted: "Accepted", + StatusNonAuthoritativeInfo: "Non-Authoritative Information", + StatusNoContent: "No Content", + StatusResetContent: "Reset Content", + StatusPartialContent: "Partial Content", + + StatusMultipleChoices: "Multiple Choices", + StatusMovedPermanently: "Moved Permanently", + StatusFound: "Found", + StatusSeeOther: "See Other", + StatusNotModified: "Not Modified", + StatusUseProxy: "Use Proxy", + StatusTemporaryRedirect: "Temporary Redirect", + + StatusBadRequest: "Bad Request", + StatusUnauthorized: "Unauthorized", + StatusPaymentRequired: "Payment Required", + StatusForbidden: "Forbidden", + StatusNotFound: "Not Found", + StatusMethodNotAllowed: "Method Not Allowed", + StatusNotAcceptable: "Not Acceptable", + StatusProxyAuthRequired: "Proxy Authentication Required", + StatusRequestTimeout: "Request Timeout", + StatusConflict: "Conflict", + StatusGone: "Gone", + StatusLengthRequired: "Length Required", + StatusPreconditionFailed: "Precondition Failed", + StatusRequestEntityTooLarge: "Request Entity Too Large", + StatusRequestURITooLong: "Request URI Too Long", + StatusUnsupportedMediaType: "Unsupported Media Type", + StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", + StatusExpectationFailed: "Expectation Failed", + + StatusInternalServerError: "Internal Server Error", + StatusNotImplemented: "Not Implemented", + StatusBadGateway: "Bad Gateway", + StatusServiceUnavailable: "Service Unavailable", + StatusGatewayTimeout: "Gateway Timeout", + StatusHTTPVersionNotSupported: "HTTP Version Not Supported", +} + diff --git a/src/pkg/http/triv.go b/src/pkg/http/triv.go new file mode 100644 index 000000000..fc9501769 --- /dev/null +++ b/src/pkg/http/triv.go @@ -0,0 +1,159 @@ +// Copyright 2009 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 main + +import ( + "bufio"; + "exvar"; + "flag"; + "fmt"; + "http"; + "io"; + "log"; + "net"; + "os"; + "strconv"; +) + + +// hello world, the web server +var helloRequests = exvar.NewInt("hello-requests"); +func HelloServer(c *http.Conn, req *http.Request) { + helloRequests.Add(1); + io.WriteString(c, "hello, world!\n"); +} + +// Simple counter server. POSTing to it will set the value. +type Counter struct { + n int; +} + +// This makes Counter satisfy the exvar.Var interface, so we can export +// it directly. +func (ctr *Counter) String() string { + return fmt.Sprintf("%d", ctr.n) +} + +func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) { + switch req.Method { + case "GET": + ctr.n++; + case "POST": + buf := new(io.ByteBuffer); + io.Copy(req.Body, buf); + body := string(buf.Data()); + if n, err := strconv.Atoi(body); err != nil { + fmt.Fprintf(c, "bad POST: %v\nbody: [%v]\n", err, body); + } else { + ctr.n = n; + fmt.Fprint(c, "counter reset\n"); + } + } + fmt.Fprintf(c, "counter = %d\n", ctr.n); +} + +// simple file server +var webroot = flag.String("root", "/home/rsc", "web root directory") +var pathVar = exvar.NewMap("file-requests"); +func FileServer(c *http.Conn, req *http.Request) { + c.SetHeader("content-type", "text/plain; charset=utf-8"); + pathVar.Add(req.Url.Path, 1); + path := *webroot + req.Url.Path; // TODO: insecure: use os.CleanName + f, err := os.Open(path, os.O_RDONLY, 0); + if err != nil { + c.WriteHeader(http.StatusNotFound); + fmt.Fprintf(c, "open %s: %v\n", path, err); + return; + } + n, err1 := io.Copy(f, c); + fmt.Fprintf(c, "[%d bytes]\n", n); + f.Close(); +} + +// simple flag server +var booleanflag = flag.Bool("boolean", true, "another flag for testing") +func FlagServer(c *http.Conn, req *http.Request) { + c.SetHeader("content-type", "text/plain; charset=utf-8"); + fmt.Fprint(c, "Flags:\n"); + flag.VisitAll(func (f *flag.Flag) { + if f.Value.String() != f.DefValue { + fmt.Fprintf(c, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue); + } else { + fmt.Fprintf(c, "%s = %s\n", f.Name, f.Value.String()); + } + }); +} + +// simple argument server +func ArgServer(c *http.Conn, req *http.Request) { + for i, s := range os.Args { + fmt.Fprint(c, s, " "); + } +} + +// a channel (just for the fun of it) +type Chan chan int + +func ChanCreate() Chan { + c := make(Chan); + go func(c Chan) { + for x := 0;; x++ { + c <- x + } + }(c); + return c; +} + +func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) { + io.WriteString(c, fmt.Sprintf("channel send #%d\n", <-ch)); +} + +// exec a program, redirecting output +func DateServer(c *http.Conn, req *http.Request) { + c.SetHeader("content-type", "text/plain; charset=utf-8"); + r, w, err := os.Pipe(); + if err != nil { + fmt.Fprintf(c, "pipe: %s\n", err); + return; + } + pid, err := os.ForkExec("/bin/date", []string{"date"}, os.Environ(), "", []*os.File{nil, w, w}); + defer r.Close(); + w.Close(); + if err != nil { + fmt.Fprintf(c, "fork/exec: %s\n", err); + return; + } + io.Copy(r, c); + wait, err := os.Wait(pid, 0); + if err != nil { + fmt.Fprintf(c, "wait: %s\n", err); + return; + } + if !wait.Exited() || wait.ExitStatus() != 0 { + fmt.Fprintf(c, "date: %v\n", wait); + return; + } +} + +func main() { + flag.Parse(); + + // The counter is published as a variable directly. + ctr := new(Counter); + http.Handle("/counter", ctr); + exvar.Publish("counter", ctr); + + http.Handle("/go/", http.HandlerFunc(FileServer)); + http.Handle("/flags", http.HandlerFunc(FlagServer)); + http.Handle("/args", http.HandlerFunc(ArgServer)); + http.Handle("/go/hello", http.HandlerFunc(HelloServer)); + http.Handle("/chan", ChanCreate()); + http.Handle("/date", http.HandlerFunc(DateServer)); + err := http.ListenAndServe(":12345", nil); + if err != nil { + log.Crash("ListenAndServe: ", err) + } +} + diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go new file mode 100644 index 000000000..0325b04ee --- /dev/null +++ b/src/pkg/http/url.go @@ -0,0 +1,303 @@ +// Copyright 2009 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. + +// Parse URLs (actually URIs, but that seems overly pedantic). +// RFC 2396 + +package http + +import ( + "os"; + "strings" +) + +// Errors introduced by ParseURL. +type BadURL struct { + os.ErrorString +} + +func ishex(c byte) bool { + switch { + case '0' <= c && c <= '9': + return true; + case 'a' <= c && c <= 'f': + return true; + case 'A' <= c && c <= 'F': + return true; + } + return false +} + +func unhex(c byte) byte { + switch { + case '0' <= c && c <= '9': + return c - '0'; + case 'a' <= c && c <= 'f': + return c - 'a' + 10; + case 'A' <= c && c <= 'F': + return c - 'A' + 10; + } + return 0 +} + +// Return true if the specified character should be escaped when appearing in a +// URL string. +// +// TODO: for now, this is a hack; it only flags a few common characters that have +// special meaning in URLs. That will get the job done in the common cases. +func shouldEscape(c byte) bool { + switch c { + case ' ', '?', '&', '=', '#', '+', '%': + return true; + } + return false; +} + +// URLUnescape unescapes a URL-encoded string, +// converting %AB into the byte 0xAB and '+' into ' ' (space). +// It returns a BadURL error if any % is not followed +// by two hexadecimal digits. +func URLUnescape(s string) (string, os.Error) { + // Count %, check that they're well-formed. + n := 0; + anyPlusses := false; + for i := 0; i < len(s); { + switch s[i] { + case '%': + n++; + if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { + return "", BadURL{"invalid hexadecimal escape"} + } + i += 3; + case '+': + anyPlusses = true; + i++; + default: + i++ + } + } + + if n == 0 && !anyPlusses { + return s, nil + } + + t := make([]byte, len(s)-2*n); + j := 0; + for i := 0; i < len(s); { + switch s[i] { + case '%': + t[j] = unhex(s[i+1]) << 4 | unhex(s[i+2]); + j++; + i += 3; + case '+': + t[j] = ' '; + j++; + i++; + default: + t[j] = s[i]; + j++; + i++; + } + } + return string(t), nil; +} + +// URLEscape converts a string into URL-encoded form. +func URLEscape(s string) string { + spaceCount, hexCount := 0, 0; + for i := 0; i < len(s); i++ { + c := s[i]; + if (shouldEscape(c)) { + if (c == ' ') { + spaceCount++; + } else { + hexCount++; + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s; + } + + t := make([]byte, len(s)+2*hexCount); + j := 0; + for i := 0; i < len(s); i++ { + c := s[i]; + if !shouldEscape(c) { + t[j] = s[i]; + j++; + } else if (c == ' ') { + t[j] = '+'; + j++; + } else { + t[j] = '%'; + t[j+1] = "0123456789abcdef"[c>>4]; + t[j+2] = "0123456789abcdef"[c&15]; + j += 3; + } + } + return string(t); +} + +// A URL represents a parsed URL (technically, a URI reference). +// The general form represented is: +// scheme://[userinfo@]host/path[?query][#fragment] +// The Raw, RawPath, and RawQuery fields are in "wire format" (special +// characters must be hex-escaped if not meant to have special meaning). +// All other fields are logical values; '+' or '%' represent themselves. +// +// Note, the reason for using wire format for the query is that it needs +// to be split into key/value pairs before decoding. +type URL struct { + Raw string; // the original string + Scheme string; // scheme + RawPath string; // //[userinfo@]host/path[?query][#fragment] + Authority string; // [userinfo@]host + Userinfo string; // userinfo + Host string; // host + Path string; // /path + RawQuery string; // query + Fragment string; // fragment +} + +// Maybe rawurl is of the form scheme:path. +// (Scheme must be [a-zA-Z][a-zA-Z0-9+-.]*) +// If so, return scheme, path; else return "", rawurl. +func getscheme(rawurl string) (scheme, path string, err os.Error) { + for i := 0; i < len(rawurl); i++ { + c := rawurl[i]; + switch { + case 'a' <= c && c <= 'z' ||'A' <= c && c <= 'Z': + // do nothing + case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.': + if i == 0 { + return "", rawurl, nil + } + case c == ':': + if i == 0 { + return "", "", BadURL{"missing protocol scheme"} + } + return rawurl[0:i], rawurl[i+1:len(rawurl)], nil + } + } + return "", rawurl, nil +} + +// Maybe s is of the form t c u. +// If so, return t, c u (or t, u if cutc == true). +// If not, return s, "". +func split(s string, c byte, cutc bool) (string, string) { + for i := 0; i < len(s); i++ { + if s[i] == c { + if cutc { + return s[0:i], s[i+1:len(s)] + } + return s[0:i], s[i:len(s)] + } + } + return s, "" +} + +// BUG(rsc): ParseURL should canonicalize the path, +// removing unnecessary . and .. elements. + +// ParseURL parses rawurl into a URL structure. +// The string rawurl is assumed not to have a #fragment suffix. +// (Web browsers strip #fragment before sending the URL to a web server.) +func ParseURL(rawurl string) (url *URL, err os.Error) { + if rawurl == "" { + return nil, BadURL{"empty url"} + } + url = new(URL); + url.Raw = rawurl; + + // split off possible leading "http:", "mailto:", etc. + var path string; + if url.Scheme, path, err = getscheme(rawurl); err != nil { + return nil, err + } + url.RawPath = path; + + // RFC 2396: a relative URI (no scheme) has a ?query, + // but absolute URIs only have query if path begins with / + if url.Scheme == "" || len(path) > 0 && path[0] == '/' { + path, url.RawQuery = split(path, '?', true); + } + + // Maybe path is //authority/path + if len(path) > 2 && path[0:2] == "//" { + url.Authority, path = split(path[2:len(path)], '/', false); + } + + // If there's no @, split's default is wrong. Check explicitly. + if strings.Index(url.Authority, "@") < 0 { + url.Host = url.Authority; + } else { + url.Userinfo, url.Host = split(url.Authority, '@', true); + } + + // What's left is the path. + // TODO: Canonicalize (remove . and ..)? + if url.Path, err = URLUnescape(path); err != nil { + return nil, err + } + + // Remove escapes from the Authority and Userinfo fields, and verify + // that Scheme and Host contain no escapes (that would be illegal). + if url.Authority, err = URLUnescape(url.Authority); err != nil { + return nil, err + } + if url.Userinfo, err = URLUnescape(url.Userinfo); err != nil { + return nil, err + } + if (strings.Index(url.Scheme, "%") >= 0) { + return nil, BadURL{"hexadecimal escape in scheme"} + } + if (strings.Index(url.Host, "%") >= 0) { + return nil, BadURL{"hexadecimal escape in host"} + } + + return url, nil +} + +// ParseURLReference is like ParseURL but allows a trailing #fragment. +func ParseURLReference(rawurlref string) (url *URL, err os.Error) { + // Cut off #frag. + rawurl, frag := split(rawurlref, '#', true); + if url, err = ParseURL(rawurl); err != nil { + return nil, err + } + if url.Fragment, err = URLUnescape(frag); err != nil { + return nil, err + } + return url, nil +} + +// String reassembles url into a valid URL string. +// +// There are redundant fields stored in the URL structure: +// the String method consults Scheme, Path, Host, Userinfo, +// RawQuery, and Fragment, but not Raw, RawPath or Authority. +func (url *URL) String() string { + result := ""; + if url.Scheme != "" { + result += url.Scheme + ":"; + } + if url.Host != "" || url.Userinfo != "" { + result += "//"; + if url.Userinfo != "" { + result += URLEscape(url.Userinfo) + "@"; + } + result += url.Host; + } + result += URLEscape(url.Path); + if url.RawQuery != "" { + result += "?" + url.RawQuery; + } + if url.Fragment != "" { + result += "#" + URLEscape(url.Fragment); + } + return result; +} diff --git a/src/pkg/http/url_test.go b/src/pkg/http/url_test.go new file mode 100644 index 000000000..8d8fabad5 --- /dev/null +++ b/src/pkg/http/url_test.go @@ -0,0 +1,348 @@ +// Copyright 2009 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 http + +import ( + "fmt"; + "http"; + "os"; + "reflect"; + "testing"; +) + +// TODO(rsc): +// test URLUnescape +// test URLEscape +// test ParseURL + +type URLTest struct { + in string; + out *URL; + roundtrip string; // expected result of reserializing the URL; empty means same as "in". +} + +var urltests = []URLTest { + // no path + URLTest{ + "http://www.google.com", + &URL{ + "http://www.google.com", + "http", "//www.google.com", + "www.google.com", "", "www.google.com", + "", "", "" + }, + "" + }, + // path + URLTest{ + "http://www.google.com/", + &URL{ + "http://www.google.com/", + "http", "//www.google.com/", + "www.google.com", "", "www.google.com", + "/", "", "" + }, + "" + }, + // path with hex escaping... note that space roundtrips to + + URLTest{ + "http://www.google.com/file%20one%26two", + &URL{ + "http://www.google.com/file%20one%26two", + "http", "//www.google.com/file%20one%26two", + "www.google.com", "", "www.google.com", + "/file one&two", "", "" + }, + "http://www.google.com/file+one%26two" + }, + // user + URLTest{ + "ftp://webmaster@www.google.com/", + &URL{ + "ftp://webmaster@www.google.com/", + "ftp", "//webmaster@www.google.com/", + "webmaster@www.google.com", "webmaster", "www.google.com", + "/", "", "" + }, + "" + }, + // escape sequence in username + URLTest{ + "ftp://john%20doe@www.google.com/", + &URL{ + "ftp://john%20doe@www.google.com/", + "ftp", "//john%20doe@www.google.com/", + "john doe@www.google.com", "john doe", "www.google.com", + "/", "", "" + }, + "ftp://john+doe@www.google.com/" + }, + // query + URLTest{ + "http://www.google.com/?q=go+language", + &URL{ + "http://www.google.com/?q=go+language", + "http", "//www.google.com/?q=go+language", + "www.google.com", "", "www.google.com", + "/", "q=go+language", "" + }, + "" + }, + // query with hex escaping: NOT parsed + URLTest{ + "http://www.google.com/?q=go%20language", + &URL{ + "http://www.google.com/?q=go%20language", + "http", "//www.google.com/?q=go%20language", + "www.google.com", "", "www.google.com", + "/", "q=go%20language", "" + }, + "" + }, + // path without /, so no query parsing + URLTest{ + "http:www.google.com/?q=go+language", + &URL{ + "http:www.google.com/?q=go+language", + "http", "www.google.com/?q=go+language", + "", "", "", + "www.google.com/?q=go language", "", "" + }, + "http:www.google.com/%3fq%3dgo+language" + }, + // non-authority + URLTest{ + "mailto:/webmaster@golang.org", + &URL{ + "mailto:/webmaster@golang.org", + "mailto", "/webmaster@golang.org", + "", "", "", + "/webmaster@golang.org", "", "" + }, + "" + }, + // non-authority + URLTest{ + "mailto:webmaster@golang.org", + &URL{ + "mailto:webmaster@golang.org", + "mailto", "webmaster@golang.org", + "", "", "", + "webmaster@golang.org", "", "" + }, + "" + }, +} + +var urlnofragtests = []URLTest { + URLTest{ + "http://www.google.com/?q=go+language#foo", + &URL{ + "http://www.google.com/?q=go+language#foo", + "http", "//www.google.com/?q=go+language#foo", + "www.google.com", "", "www.google.com", + "/", "q=go+language#foo", "" + }, + "" + }, +} + +var urlfragtests = []URLTest { + URLTest{ + "http://www.google.com/?q=go+language#foo", + &URL{ + "http://www.google.com/?q=go+language", + "http", "//www.google.com/?q=go+language", + "www.google.com", "", "www.google.com", + "/", "q=go+language", "foo" + }, + "" + }, + URLTest{ + "http://www.google.com/?q=go+language#foo%26bar", + &URL{ + "http://www.google.com/?q=go+language", + "http", "//www.google.com/?q=go+language", + "www.google.com", "", "www.google.com", + "/", "q=go+language", "foo&bar" + }, + "" + }, +} + +// more useful string for debugging than fmt's struct printer +func ufmt(u *URL) string { + return fmt.Sprintf("%q, %q, %q, %q, %q, %q, %q, %q, %q", + u.Raw, u.Scheme, u.RawPath, u.Authority, u.Userinfo, + u.Host, u.Path, u.RawQuery, u.Fragment); +} + +func DoTest(t *testing.T, parse func(string) (*URL, os.Error), name string, tests []URLTest) { + for i, tt := range tests { + u, err := parse(tt.in); + if err != nil { + t.Errorf("%s(%q) returned error %s", name, tt.in, err); + continue; + } + if !reflect.DeepEqual(u, tt.out) { + t.Errorf("%s(%q):\n\thave %v\n\twant %v\n", + name, tt.in, ufmt(u), ufmt(tt.out)); + } + } +} + +func TestParseURL(t *testing.T) { + DoTest(t, ParseURL, "ParseURL", urltests); + DoTest(t, ParseURL, "ParseURL", urlnofragtests); +} + +func TestParseURLReference(t *testing.T) { + DoTest(t, ParseURLReference, "ParseURLReference", urltests); + DoTest(t, ParseURLReference, "ParseURLReference", urlfragtests); +} + +func DoTestString(t *testing.T, parse func(string) (*URL, os.Error), name string, tests []URLTest) { + for i, tt := range tests { + u, err := parse(tt.in); + if err != nil { + t.Errorf("%s(%q) returned error %s", name, tt.in, err); + continue; + } + s := u.String(); + expected := tt.in; + if len(tt.roundtrip) > 0 { + expected = tt.roundtrip; + } + if s != expected { + t.Errorf("%s(%q).String() == %q (expected %q)", name, tt.in, s, expected); + } + } +} + +func TestURLString(t *testing.T) { + DoTestString(t, ParseURL, "ParseURL", urltests); + DoTestString(t, ParseURL, "ParseURL", urlfragtests); + DoTestString(t, ParseURL, "ParseURL", urlnofragtests); + DoTestString(t, ParseURLReference, "ParseURLReference", urltests); + DoTestString(t, ParseURLReference, "ParseURLReference", urlfragtests); + DoTestString(t, ParseURLReference, "ParseURLReference", urlnofragtests); +} + +type URLEscapeTest struct { + in string; + out string; + err os.Error; +} + +var unescapeTests = []URLEscapeTest { + URLEscapeTest{ + "", + "", + nil + }, + URLEscapeTest{ + "abc", + "abc", + nil + }, + URLEscapeTest{ + "1%41", + "1A", + nil + }, + URLEscapeTest{ + "1%41%42%43", + "1ABC", + nil + }, + URLEscapeTest{ + "%4a", + "J", + nil + }, + URLEscapeTest{ + "%6F", + "o", + nil + }, + URLEscapeTest{ + "%", // not enough characters after % + "", + BadURL{"invalid hexadecimal escape"} + }, + URLEscapeTest{ + "%a", // not enough characters after % + "", + BadURL{"invalid hexadecimal escape"} + }, + URLEscapeTest{ + "%1", // not enough characters after % + "", + BadURL{"invalid hexadecimal escape"} + }, + URLEscapeTest{ + "123%45%6", // not enough characters after % + "", + BadURL{"invalid hexadecimal escape"} + }, + URLEscapeTest{ + "%zz", // invalid hex digits + "", + BadURL{"invalid hexadecimal escape"} + }, +} + +func TestURLUnescape(t *testing.T) { + for i, tt := range unescapeTests { + actual, err := URLUnescape(tt.in); + if actual != tt.out || (err != nil) != (tt.err != nil) { + t.Errorf("URLUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err); + } + } +} + +var escapeTests = []URLEscapeTest { + URLEscapeTest{ + "", + "", + nil + }, + URLEscapeTest{ + "abc", + "abc", + nil + }, + URLEscapeTest{ + "one two", + "one+two", + nil + }, + URLEscapeTest{ + "10%", + "10%25", + nil + }, + URLEscapeTest{ + " ?&=#+%!", + "+%3f%26%3d%23%2b%25!", + nil + }, +} + +func TestURLEscape(t *testing.T) { + for i, tt := range escapeTests { + actual := URLEscape(tt.in); + if tt.out != actual { + t.Errorf("URLEscape(%q) = %q, want %q", tt.in, actual, tt.out); + } + + // for bonus points, verify that escape:unescape is an identity. + roundtrip, err := URLUnescape(actual); + if roundtrip != tt.in || err != nil { + t.Errorf("URLUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]"); + } + } +} + diff --git a/src/pkg/io/Makefile b/src/pkg/io/Makefile new file mode 100644 index 000000000..219ea776b --- /dev/null +++ b/src/pkg/io/Makefile @@ -0,0 +1,70 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + bytebuffer.$O\ + io.$O\ + +O2=\ + pipe.$O\ + utils.$O\ + + +phases: a1 a2 +_obj$D/io.a: phases + +a1: $(O1) + $(AR) grc _obj$D/io.a bytebuffer.$O io.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/io.a pipe.$O utils.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/io.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/io.a + +packages: _obj$D/io.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/io.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/io.a diff --git a/src/pkg/io/bytebuffer.go b/src/pkg/io/bytebuffer.go new file mode 100644 index 000000000..921ddb17a --- /dev/null +++ b/src/pkg/io/bytebuffer.go @@ -0,0 +1,109 @@ +// Copyright 2009 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 io + +// Simple byte buffer for marshaling data. + +import ( + "io"; + "os"; +) + +func bytecopy(dst []byte, doff int, src []byte, soff int, count int) { + for ; count > 0; count-- { + dst[doff] = src[soff]; + doff++; + soff++; + } +} + +// A ByteBuffer is a simple implementation of the io.Read and io.Write interfaces +// connected to a buffer of bytes. +// The zero value for ByteBuffer is an empty buffer ready to use. +type ByteBuffer struct { + buf []byte; // contents are the bytes buf[off : len(buf)] + off int; // read at &buf[off], write at &buf[len(buf)] +} + +// Data returns the contents of the unread portion of the buffer; +// len(b.Data()) == b.Len(). +func (b *ByteBuffer) Data() []byte { + return b.buf[b.off : len(b.buf)] +} + +// Len returns the number of bytes of the unread portion of the buffer; +// b.Len() == len(b.Data()). +func (b *ByteBuffer) Len() int { + return len(b.buf) - b.off +} + +// Truncate discards all but the first n unread bytes from the buffer. +// It is an error to call b.Truncate(n) with n > b.Len(). +func (b *ByteBuffer) Truncate(n int) { + if n == 0 { + // Reuse buffer space. + b.off = 0; + } + b.buf = b.buf[0 : b.off + n]; +} + +// Reset resets the buffer so it has no content. +// b.Reset() is the same as b.Truncate(0). +func (b *ByteBuffer) Reset() { + b.Truncate(0); +} + +// Write appends the contents of p to the buffer. The return +// value n is the length of p; err is always nil. +func (b *ByteBuffer) Write(p []byte) (n int, err os.Error) { + m := b.Len(); + n = len(p); + + if len(b.buf) + n > cap(b.buf) { + // not enough space at end + buf := b.buf; + if m + n > cap(b.buf) { + // not enough space anywhere + buf = make([]byte, 2*cap(b.buf) + n) + } + bytecopy(buf, 0, b.buf, b.off, m); + b.buf = buf; + b.off = 0 + } + + b.buf = b.buf[0 : b.off + m + n]; + bytecopy(b.buf, b.off + m, p, 0, n); + return n, nil +} + +// WriteByte appends the byte c to the buffer. +// The returned error is always nil, but is included +// to match bufio.Writer's WriteByte. +func (b *ByteBuffer) WriteByte(c byte) os.Error { + b.Write([]byte{c}); + return nil; +} + +// Read reads the next len(p) bytes from the buffer or until the buffer +// is drained. The return value n is the number of bytes read; err is always nil. +func (b *ByteBuffer) Read(p []byte) (n int, err os.Error) { + m := b.Len(); + n = len(p); + + if n > m { + // more bytes requested than available + n = m + } + + bytecopy(p, 0, b.buf, b.off, n); + b.off += n; + return n, nil +} + +// NewByteBufferFromArray creates and initializes a new ByteBuffer +// with buf as its initial contents. +func NewByteBufferFromArray(buf []byte) *ByteBuffer { + return &ByteBuffer{buf, 0}; +} diff --git a/src/pkg/io/bytebuffer_test.go b/src/pkg/io/bytebuffer_test.go new file mode 100644 index 000000000..5a5432223 --- /dev/null +++ b/src/pkg/io/bytebuffer_test.go @@ -0,0 +1,158 @@ +// Copyright 2009 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 io + +import ( + "io"; + "rand"; + "testing"; +) + + +const N = 10000; // make this bigger for a larger (and slower) test +var data []byte; // test data for write tests + + +func init() { + data = make([]byte, N); + for i := 0; i < len(data); i++ { + data[i] = 'a' + byte(i % 26) + } +} + + +// Verify that contents of buf match the string s. +func check(t *testing.T, testname string, buf *ByteBuffer, s string) { + if buf.Len() != len(buf.Data()) { + t.Errorf("%s: buf.Len() == %d, len(buf.Data()) == %d\n", testname, buf.Len(), len(buf.Data())) + } + + if buf.Len() != len(s) { + t.Errorf("%s: buf.Len() == %d, len(s) == %d\n", testname, buf.Len(), len(s)) + } + + if string(buf.Data()) != s { + t.Errorf("%s: string(buf.Data()) == %q, s == %q\n", testname, string(buf.Data()), s) + } +} + + +// Fill buf through n writes of fub. +// The initial contents of buf corresponds to the string s; +// the result is the final contents of buf returned as a string. +func fill(t *testing.T, testname string, buf *ByteBuffer, s string, n int, fub []byte) string { + check(t, testname + " (fill 1)", buf, s); + for ; n > 0; n-- { + m, err := buf.Write(fub); + if m != len(fub) { + t.Errorf(testname + " (fill 2): m == %d, expected %d\n", m, len(fub)); + } + if err != nil { + t.Errorf(testname + " (fill 3): err should always be nil, found err == %s\n", err); + } + s += string(fub); + check(t, testname + " (fill 4)", buf, s); + } + return s; +} + + +// Empty buf through repeated reads into fub. +// The initial contents of buf corresponds to the string s. +func empty(t *testing.T, testname string, buf *ByteBuffer, s string, fub []byte) { + check(t, testname + " (empty 1)", buf, s); + + for { + n, err := buf.Read(fub); + if n == 0 { + break; + } + if err != nil { + t.Errorf(testname + " (empty 2): err should always be nil, found err == %s\n", err); + } + s = s[n : len(s)]; + check(t, testname + " (empty 3)", buf, s); + } + + check(t, testname + " (empty 4)", buf, ""); +} + + +func TestBasicOperations(t *testing.T) { + var buf ByteBuffer; + + for i := 0; i < 5; i++ { + check(t, "TestBasicOperations (1)", &buf, ""); + + buf.Reset(); + check(t, "TestBasicOperations (2)", &buf, ""); + + buf.Truncate(0); + check(t, "TestBasicOperations (3)", &buf, ""); + + n, err := buf.Write(data[0 : 1]); + if n != 1 { + t.Errorf("wrote 1 byte, but n == %d\n", n); + } + if err != nil { + t.Errorf("err should always be nil, but err == %s\n", err); + } + check(t, "TestBasicOperations (4)", &buf, "a"); + + buf.WriteByte(data[1]); + check(t, "TestBasicOperations (5)", &buf, "ab"); + + n, err = buf.Write(data[2 : 26]); + if n != 24 { + t.Errorf("wrote 25 bytes, but n == %d\n", n); + } + check(t, "TestBasicOperations (6)", &buf, string(data[0 : 26])); + + buf.Truncate(26); + check(t, "TestBasicOperations (7)", &buf, string(data[0 : 26])); + + buf.Truncate(20); + check(t, "TestBasicOperations (8)", &buf, string(data[0 : 20])); + + empty(t, "TestBasicOperations (9)", &buf, string(data[0 : 20]), make([]byte, 5)); + empty(t, "TestBasicOperations (10)", &buf, "", make([]byte, 100)); + } +} + + +func TestLargeWrites(t *testing.T) { + var buf ByteBuffer; + for i := 3; i < 30; i += 3 { + s := fill(t, "TestLargeWrites (1)", &buf, "", 5, data); + empty(t, "TestLargeWrites (2)", &buf, s, make([]byte, len(data)/i)); + } + check(t, "TestLargeWrites (3)", &buf, ""); +} + + +func TestLargeReads(t *testing.T) { + var buf ByteBuffer; + for i := 3; i < 30; i += 3 { + s := fill(t, "TestLargeReads (1)", &buf, "", 5, data[0 : len(data)/i]); + empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data))); + } + check(t, "TestLargeReads (3)", &buf, ""); +} + + +func TestMixedReadsAndWrites(t *testing.T) { + var buf ByteBuffer; + s := ""; + for i := 0; i < 50; i++ { + wlen := rand.Intn(len(data)); + s = fill(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0 : wlen]); + + rlen := rand.Intn(len(data)); + fub := make([]byte, rlen); + n, err := buf.Read(fub); + s = s[n : len(s)]; + } + empty(t, "TestMixedReadsAndWrites (2)", &buf, s, make([]byte, buf.Len())); +} diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go new file mode 100644 index 000000000..ba0449ac1 --- /dev/null +++ b/src/pkg/io/io.go @@ -0,0 +1,223 @@ +// Copyright 2009 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. + +// This package provides basic interfaces to I/O primitives. +// Its primary job is to wrap existing implementations of such primitives, +// such as those in package os, into shared public interfaces that +// abstract the functionality. +// It also provides buffering primitives and some other basic operations. +package io + +import ( + "bytes"; + "os"; +) + +// Error represents an unexpected I/O behavior. +type Error struct { + os.ErrorString +} + +// ErrEOF means that data was expected, but a read got EOF instead. +var ErrEOF os.Error = &Error{"EOF"} + +// ErrShortWrite means that a write accepted fewer bytes than requested +// but failed to return an explicit error. +var ErrShortWrite os.Error = &Error{"short write"} + + +// Reader is the interface that wraps the basic Read method. +// An implementation of Read is allowed to use all of p for +// scratch space during the call, even if it eventually returns +// n < len(p). +type Reader interface { + Read(p []byte) (n int, err os.Error); +} + +// Writer is the interface that wraps the basic Write method. +type Writer interface { + Write(p []byte) (n int, err os.Error); +} + +// Closer is the interface that wraps the basic Close method. +type Closer interface { + Close() os.Error; +} + +// ReadWrite is the interface that groups the basic Read and Write methods. +type ReadWriter interface { + Reader; + Writer; +} + +// ReadCloser is the interface that groups the basic Read and Close methods. +type ReadCloser interface { + Reader; + Closer; +} + +// WriteCloser is the interface that groups the basic Write and Close methods. +type WriteCloser interface { + Writer; + Closer; +} + +// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods. +type ReadWriteCloser interface { + Reader; + Writer; + Closer; +} + +// Convert a string to an array of bytes for easy marshaling. +func StringBytes(s string) []byte { + b := make([]byte, len(s)); + for i := 0; i < len(s); i++ { + b[i] = s[i]; + } + return b; +} + +// WriteString writes the contents of the string s to w, which accepts an array of bytes. +func WriteString(w Writer, s string) (n int, err os.Error) { + return w.Write(StringBytes(s)) +} + +// ReadAtLeast reads r into buf until at least min bytes have been read, +// or until EOF or error. +func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) { + n = 0; + for n < min { + nn, e := r.Read(buf[n:len(buf)]); + if nn > 0 { + n += nn + } + if e != nil { + return n, e + } + if nn <= 0 { + return n, ErrEOF // no error but insufficient data + } + } + return n, nil +} + +// FullRead reads r until the buffer buf is full, or until EOF or error. +func FullRead(r Reader, buf []byte) (n int, err os.Error) { + // TODO(rsc): 6g bug prevents obvious return + n, err = ReadAtLeast(r, buf, len(buf)); + return; +} + +// Convert something that implements Read into something +// whose Reads are always FullReads +type fullRead struct { + r Reader; +} + +func (fr *fullRead) Read(p []byte) (n int, err os.Error) { + n, err = FullRead(fr.r, p); + return n, err +} + +// MakeFullReader takes r, an implementation of Read, and returns an object +// that still implements Read but always calls FullRead underneath. +func MakeFullReader(r Reader) Reader { + if fr, ok := r.(*fullRead); ok { + // already a fullRead + return r + } + return &fullRead{r} +} + +// Copy n copies n bytes (or until EOF is reached) from src to dst. +// It returns the number of bytes copied and the error, if any. +func Copyn(src Reader, dst Writer, n int64) (written int64, err os.Error) { + buf := make([]byte, 32*1024); + for written < n { + l := len(buf); + if d := n - written; d < int64(l) { + l = int(d); + } + nr, er := src.Read(buf[0 : l]); + if nr > 0 { + nw, ew := dst.Write(buf[0 : nr]); + if nw > 0 { + written += int64(nw); + } + if ew != nil { + err = ew; + break; + } + if nr != nw { + err = os.EIO; + break; + } + } + if er != nil { + err = er; + break; + } + if nr == 0 { + err = ErrEOF; + break; + } + } + return written, err +} + +// Copy copies from src to dst until EOF is reached. +// It returns the number of bytes copied and the error, if any. +func Copy(src Reader, dst Writer) (written int64, err os.Error) { + buf := make([]byte, 32*1024); + for { + nr, er := src.Read(buf); + if nr > 0 { + nw, ew := dst.Write(buf[0:nr]); + if nw > 0 { + written += int64(nw); + } + if ew != nil { + err = ew; + break; + } + if nr != nw { + err = os.EIO; + break; + } + } + if er != nil { + err = er; + break; + } + if nr == 0 { + break; + } + } + return written, err +} + +// A ByteReader satisfies Reads by consuming data from a slice of bytes. +// Clients can call NewByteReader to create one or wrap pointers +// to their own slices: r := ByteReader{&data}. +type ByteReader struct { + Data *[]byte +} + +func (r ByteReader) Read(p []byte) (int, os.Error) { + n := len(p); + b := r.Data; + if n > len(b) { + n = len(b); + } + bytes.Copy(p, b[0:n]); + *b = b[n:len(b)]; + return n, nil; +} + +// NewByteReader returns a new ByteReader reading from data. +func NewByteReader(data []byte) ByteReader { + return ByteReader{ &data }; +} + diff --git a/src/pkg/io/pipe.go b/src/pkg/io/pipe.go new file mode 100644 index 000000000..1a443ddce --- /dev/null +++ b/src/pkg/io/pipe.go @@ -0,0 +1,226 @@ +// Copyright 2009 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. + +// Pipe adapter to connect code expecting an io.Read +// with code expecting an io.Write. + +package io + +import ( + "io"; + "os"; + "sync"; +) + +type pipeReturn struct { + n int; + err os.Error; +} + +// Shared pipe structure. +type pipe struct { + rclosed bool; // Read end closed? + rerr os.Error; // Error supplied to CloseReader + wclosed bool; // Write end closed? + werr os.Error; // Error supplied to CloseWriter + wpend []byte; // Written data waiting to be read. + wtot int; // Bytes consumed so far in current write. + cr chan []byte; // Write sends data here... + cw chan pipeReturn; // ... and reads the n, err back from here. +} + +func (p *pipe) Read(data []byte) (n int, err os.Error) { + if p == nil || p.rclosed { + return 0, os.EINVAL; + } + + // Wait for next write block if necessary. + if p.wpend == nil { + if !p.wclosed { + p.wpend = <-p.cr; + } + if p.wpend == nil { + return 0, p.werr; + } + p.wtot = 0; + } + + // Read from current write block. + n = len(data); + if n > len(p.wpend) { + n = len(p.wpend); + } + for i := 0; i < n; i++ { + data[i] = p.wpend[i]; + } + p.wtot += n; + p.wpend = p.wpend[n:len(p.wpend)]; + + // If write block is done, finish the write. + if len(p.wpend) == 0 { + p.wpend = nil; + p.cw <- pipeReturn{p.wtot, nil}; + p.wtot = 0; + } + + return n, nil; +} + +func (p *pipe) Write(data []byte) (n int, err os.Error) { + if p == nil || p.wclosed { + return 0, os.EINVAL; + } + if p.rclosed { + return 0, p.rerr; + } + + // Send data to reader. + p.cr <- data; + + // Wait for reader to finish copying it. + res := <-p.cw; + return res.n, res.err; +} + +func (p *pipe) CloseReader(rerr os.Error) os.Error { + if p == nil || p.rclosed { + return os.EINVAL; + } + + // Stop any future writes. + p.rclosed = true; + if rerr == nil { + rerr = os.EPIPE; + } + p.rerr = rerr; + + // Stop the current write. + if !p.wclosed { + p.cw <- pipeReturn{p.wtot, rerr}; + } + + return nil; +} + +func (p *pipe) CloseWriter(werr os.Error) os.Error { + if p == nil || p.wclosed { + return os.EINVAL; + } + + // Stop any future reads. + p.wclosed = true; + p.werr = werr; + + // Stop the current read. + if !p.rclosed { + p.cr <- nil; + } + + return nil; +} + +// Read/write halves of the pipe. +// They are separate structures for two reasons: +// 1. If one end becomes garbage without being Closed, +// its finisher can Close so that the other end +// does not hang indefinitely. +// 2. Clients cannot use interface conversions on the +// read end to find the Write method, and vice versa. + +// A PipeReader is the read half of a pipe. +type PipeReader struct { + lock sync.Mutex; + p *pipe; +} + +// Read implements the standard Read interface: +// it reads data from the pipe, blocking until a writer +// arrives or the write end is closed. +// If the write end is closed with an error, that error is +// returned as err; otherwise err is nil. +func (r *PipeReader) Read(data []byte) (n int, err os.Error) { + r.lock.Lock(); + defer r.lock.Unlock(); + + return r.p.Read(data); +} + +// Close closes the reader; subsequent writes to the +// write half of the pipe will return the error os.EPIPE. +func (r *PipeReader) Close() os.Error { + r.lock.Lock(); + defer r.lock.Unlock(); + + return r.p.CloseReader(nil); +} + +// CloseWithError closes the reader; subsequent writes +// to the write half of the pipe will return the error rerr. +func (r *PipeReader) CloseWithError(rerr os.Error) os.Error { + r.lock.Lock(); + defer r.lock.Unlock(); + + return r.p.CloseReader(rerr); +} + +func (r *PipeReader) finish() { + r.Close(); +} + +// Write half of pipe. +type PipeWriter struct { + lock sync.Mutex; + p *pipe; +} + +// Write implements the standard Write interface: +// it writes data to the pipe, blocking until readers +// have consumed all the data or the read end is closed. +// If the read end is closed with an error, that err is +// returned as err; otherwise err is os.EPIPE. +func (w *PipeWriter) Write(data []byte) (n int, err os.Error) { + w.lock.Lock(); + defer w.lock.Unlock(); + + return w.p.Write(data); +} + +// Close closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and a nil error. +func (w *PipeWriter) Close() os.Error { + w.lock.Lock(); + defer w.lock.Unlock(); + + return w.p.CloseWriter(nil); +} + +// CloseWithError closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and the error werr. +func (w *PipeWriter) CloseWithError(werr os.Error) os.Error { + w.lock.Lock(); + defer w.lock.Unlock(); + + return w.p.CloseWriter(werr); +} + +func (w *PipeWriter) finish() { + w.Close(); +} + +// Pipe creates a synchronous in-memory pipe. +// It can be used to connect code expecting an io.Reader +// with code expecting an io.Writer. +// Reads on one end are matched with writes on the other, +// copying data directly between the two; there is no internal buffering. +func Pipe() (*PipeReader, *PipeWriter) { + p := new(pipe); + p.cr = make(chan []byte, 1); + p.cw = make(chan pipeReturn, 1); + r := new(PipeReader); + r.p = p; + w := new(PipeWriter); + w.p = p; + return r, w; +} + diff --git a/src/pkg/io/pipe_test.go b/src/pkg/io/pipe_test.go new file mode 100644 index 000000000..277f44525 --- /dev/null +++ b/src/pkg/io/pipe_test.go @@ -0,0 +1,225 @@ +// Copyright 2009 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 io + +import ( + "fmt"; + "io"; + "os"; + "testing"; + "time"; +) + +func checkWrite(t *testing.T, w Writer, data []byte, c chan int) { + n, err := w.Write(data); + if err != nil { + t.Errorf("write: %v", err); + } + if n != len(data) { + t.Errorf("short write: %d != %d", n, len(data)); + } + c <- 0; +} + +// Test a single read/write pair. +func TestPipe1(t *testing.T) { + c := make(chan int); + r, w := Pipe(); + var buf = make([]byte, 64); + go checkWrite(t, w, StringBytes("hello, world"), c); + n, err := r.Read(buf); + if err != nil { + t.Errorf("read: %v", err); + } + else if n != 12 || string(buf[0:12]) != "hello, world" { + t.Errorf("bad read: got %q", buf[0:n]); + } + <-c; + r.Close(); + w.Close(); +} + +func reader(t *testing.T, r Reader, c chan int) { + var buf = make([]byte, 64); + for { + n, err := r.Read(buf); + if err != nil { + t.Errorf("read: %v", err); + } + c <- n; + if n == 0 { + break; + } + } +} + +// Test a sequence of read/write pairs. +func TestPipe2(t *testing.T) { + c := make(chan int); + r, w := Pipe(); + go reader(t, r, c); + var buf = make([]byte, 64); + for i := 0; i < 5; i++ { + p := buf[0:5+i*10]; + n, err := w.Write(p); + if n != len(p) { + t.Errorf("wrote %d, got %d", len(p), n); + } + if err != nil { + t.Errorf("write: %v", err); + } + nn := <-c; + if nn != n { + t.Errorf("wrote %d, read got %d", n, nn); + } + } + w.Close(); + nn := <-c; + if nn != 0 { + t.Errorf("final read got %d", nn); + } +} + +// Test a large write that requires multiple reads to satisfy. +func writer(w WriteCloser, buf []byte, c chan pipeReturn) { + n, err := w.Write(buf); + w.Close(); + c <- pipeReturn{n, err}; +} + +func TestPipe3(t *testing.T) { + c := make(chan pipeReturn); + r, w := Pipe(); + var wdat = make([]byte, 128); + for i := 0; i < len(wdat); i++ { + wdat[i] = byte(i); + } + go writer(w, wdat, c); + var rdat = make([]byte, 1024); + tot := 0; + for n := 1; n <= 256; n *= 2 { + nn, err := r.Read(rdat[tot:tot+n]); + if err != nil { + t.Fatalf("read: %v", err); + } + + // only final two reads should be short - 1 byte, then 0 + expect := n; + if n == 128 { + expect = 1; + } else if n == 256 { + expect = 0; + } + if nn != expect { + t.Fatalf("read %d, expected %d, got %d", n, expect, nn); + } + tot += nn; + } + pr := <-c; + if pr.n != 128 || pr.err != nil { + t.Fatalf("write 128: %d, %v", pr.n, pr.err); + } + if tot != 128 { + t.Fatalf("total read %d != 128", tot); + } + for i := 0; i < 128; i++ { + if rdat[i] != byte(i) { + t.Fatalf("rdat[%d] = %d", i, rdat[i]); + } + } +} + +// Test read after/before writer close. + +type closer interface { + CloseWithError(os.Error) os.Error; + Close() os.Error; +} + +type pipeTest struct { + async bool; + err os.Error; + closeWithError bool; +} + +func (p pipeTest) String() string { + return fmt.Sprintf("async=%v err=%v closeWithError=%v", p.async, p.err, p.closeWithError); +} + +var pipeTests = []pipeTest { + pipeTest{ true, nil, false }, + pipeTest{ true, nil, true }, + pipeTest{ true, io.ErrShortWrite, true }, + pipeTest{ false, nil, false }, + pipeTest{ false, nil, true }, + pipeTest{ false, io.ErrShortWrite, true }, +} + +func delayClose(t *testing.T, cl closer, ch chan int, tt pipeTest) { + time.Sleep(1e6); // 1 ms + var err os.Error; + if tt.closeWithError { + err = cl.CloseWithError(tt.err); + } else { + err = cl.Close(); + } + if err != nil { + t.Errorf("delayClose: %v", err); + } + ch <- 0; +} + +func TestPipeReadClose(t *testing.T) { + for _, tt := range pipeTests { + c := make(chan int, 1); + r, w := Pipe(); + if tt.async { + go delayClose(t, w, c, tt); + } else { + delayClose(t, w, c, tt); + } + var buf = make([]byte, 64); + n, err := r.Read(buf); + <-c; + if err != tt.err { + t.Errorf("read from closed pipe: %v want %v", err, tt.err); + } + if n != 0 { + t.Errorf("read on closed pipe returned %d", n); + } + if err = r.Close(); err != nil { + t.Errorf("r.Close: %v", err); + } + } +} + +// Test write after/before reader close. + +func TestPipeWriteClose(t *testing.T) { + for _, tt := range pipeTests { + c := make(chan int, 1); + r, w := Pipe(); + if tt.async { + go delayClose(t, r, c, tt); + } else { + delayClose(t, r, c, tt); + } + n, err := WriteString(w, "hello, world"); + <-c; + expect := tt.err; + if expect == nil { + expect = os.EPIPE; + } + if err != expect { + t.Errorf("write on closed pipe: %v want %v", err, expect); + } + if n != 0 { + t.Errorf("write on closed pipe returned %d", n); + } + if err = w.Close(); err != nil { + t.Errorf("w.Close: %v", err); + } + } +} diff --git a/src/pkg/io/utils.go b/src/pkg/io/utils.go new file mode 100644 index 000000000..a4cbb2d9a --- /dev/null +++ b/src/pkg/io/utils.go @@ -0,0 +1,29 @@ +// Copyright 2009 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. + +// Utility functions. + +package io + +import ( + "io"; + "os"; +) + +// ReadAll reads from r until an error or EOF and returns the data it read. +func ReadAll(r Reader) ([]byte, os.Error) { + var buf ByteBuffer; + n, err := io.Copy(r, &buf); + return buf.Data(), err; +} + +// ReadFile reads the file named by filename and returns the contents. +func ReadFile(filename string) ([]byte, os.Error) { + f, err := os.Open(filename, os.O_RDONLY, 0); + if err != nil { + return nil, err; + } + defer f.Close(); + return ReadAll(f); +} diff --git a/src/pkg/io/utils_test.go b/src/pkg/io/utils_test.go new file mode 100644 index 000000000..f35dad60c --- /dev/null +++ b/src/pkg/io/utils_test.go @@ -0,0 +1,37 @@ +// Copyright 2009 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 io + +import ( + "io"; + "os"; + "testing"; +) + +func checkSize(t *testing.T, path string, size uint64) { + dir, err := os.Stat(path); + if err != nil { + t.Fatalf("Stat %q (looking for size %d): %s", path, size, err); + } + if dir.Size != size { + t.Errorf("Stat %q: size %d want %d", path, dir.Size, size); + } +} + +func TestReadFile(t *testing.T) { + filename := "rumpelstilzchen"; + contents, err := ReadFile(filename); + if err == nil { + t.Fatalf("ReadFile %s: error expected, none found", filename); + } + + filename = "utils_test.go"; + contents, err = ReadFile(filename); + if err != nil { + t.Fatalf("ReadFile %s: %v", filename, err); + } + + checkSize(t, filename, uint64(len(contents))); +} diff --git a/src/pkg/json/Makefile b/src/pkg/json/Makefile new file mode 100644 index 000000000..2d70c2cf8 --- /dev/null +++ b/src/pkg/json/Makefile @@ -0,0 +1,69 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + parse.$O\ + +O2=\ + generic.$O\ + struct.$O\ + + +phases: a1 a2 +_obj$D/json.a: phases + +a1: $(O1) + $(AR) grc _obj$D/json.a parse.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/json.a generic.$O struct.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/json.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/json.a + +packages: _obj$D/json.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/json.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/json.a diff --git a/src/pkg/json/generic.go b/src/pkg/json/generic.go new file mode 100644 index 000000000..e3194eb17 --- /dev/null +++ b/src/pkg/json/generic.go @@ -0,0 +1,331 @@ +// Copyright 2009 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. + +// Generic representation of JSON objects. + +package json + +import ( + "container/vector"; + "fmt"; + "json"; + "math"; + "strconv"; + "strings"; +) + +// Integers identifying the data type in the Json interface. +const ( + StringKind = iota; + NumberKind; + MapKind; // JSON term is "Object", but in Go, it's a map + ArrayKind; + BoolKind; + NullKind; +) + +// The Json interface is implemented by all JSON objects. +type Json interface { + Kind() int; // StringKind, NumberKind, etc. + String() string; // a string form (any kind) + Number() float64; // numeric form (NumberKind) + Bool() bool; // boolean (BoolKind) + Get(s string) Json; // field lookup (MapKind) + Elem(i int) Json; // element lookup (ArrayKind) + Len() int; // length (ArrayKind, MapKind) +} + +// JsonToString returns the textual JSON syntax representation +// for the JSON object j. +// +// JsonToString differs from j.String() in the handling +// of string objects. If j represents the string abc, +// j.String() == `abc`, but JsonToString(j) == `"abc"`. +func JsonToString(j Json) string { + if j == nil { + return "null" + } + if j.Kind() == StringKind { + return Quote(j.String()) + } + return j.String() +} + +type _Null struct { } + +// Null is the JSON object representing the null data object. +var Null Json = &_Null{} + +func (*_Null) Kind() int { return NullKind } +func (*_Null) String() string { return "null" } +func (*_Null) Number() float64 { return 0 } +func (*_Null) Bool() bool { return false } +func (*_Null) Get(s string) Json { return Null } +func (*_Null) Elem(int) Json { return Null } +func (*_Null) Len() int { return 0 } + +type _String struct { s string; _Null } +func (j *_String) Kind() int { return StringKind } +func (j *_String) String() string { return j.s } + +type _Number struct { f float64; _Null } +func (j *_Number) Kind() int { return NumberKind } +func (j *_Number) Number() float64 { return j.f } +func (j *_Number) String() string { + if math.Floor(j.f) == j.f { + return fmt.Sprintf("%.0f", j.f); + } + return fmt.Sprintf("%g", j.f); +} + +type _Array struct { a *vector.Vector; _Null } +func (j *_Array) Kind() int { return ArrayKind } +func (j *_Array) Len() int { return j.a.Len() } +func (j *_Array) Elem(i int) Json { + if i < 0 || i >= j.a.Len() { + return Null + } + return j.a.At(i).(Json) +} +func (j *_Array) String() string { + s := "["; + for i := 0; i < j.a.Len(); i++ { + if i > 0 { + s += ","; + } + s += JsonToString(j.a.At(i).(Json)); + } + s += "]"; + return s; +} + +type _Bool struct { b bool; _Null } +func (j *_Bool) Kind() int { return BoolKind } +func (j *_Bool) Bool() bool { return j.b } +func (j *_Bool) String() string { + if j.b { + return "true" + } + return "false" +} + +type _Map struct { m map[string]Json; _Null } +func (j *_Map) Kind() int { return MapKind } +func (j *_Map) Len() int { return len(j.m) } +func (j *_Map) Get(s string) Json { + if j.m == nil { + return Null + } + v, ok := j.m[s]; + if !ok { + return Null + } + return v; +} +func (j *_Map) String() string { + s := "{"; + first := true; + for k,v := range j.m { + if first { + first = false; + } else { + s += ","; + } + s += Quote(k); + s += ":"; + s += JsonToString(v); + } + s += "}"; + return s; +} + +// Walk evaluates path relative to the JSON object j. +// Path is taken as a sequence of slash-separated field names +// or numbers that can be used to index into JSON map and +// array objects. +// +// For example, if j is the JSON object for +// {"abc": [true, false]}, then Walk(j, "abc/1") returns the +// JSON object for true. +func Walk(j Json, path string) Json { + for len(path) > 0 { + var elem string; + if i := strings.Index(path, "/"); i >= 0 { + elem = path[0:i]; + path = path[i+1:len(path)]; + } else { + elem = path; + path = ""; + } + switch j.Kind() { + case ArrayKind: + indx, err := strconv.Atoi(elem); + if err != nil { + return Null + } + j = j.Elem(indx); + case MapKind: + j = j.Get(elem); + default: + return Null + } + } + return j +} + +// Equal returns whether a and b are indistinguishable JSON objects. +func Equal(a, b Json) bool { + switch { + case a == nil && b == nil: + return true; + case a == nil || b == nil: + return false; + case a.Kind() != b.Kind(): + return false; + } + + switch a.Kind() { + case NullKind: + return true; + case StringKind: + return a.String() == b.String(); + case NumberKind: + return a.Number() == b.Number(); + case BoolKind: + return a.Bool() == b.Bool(); + case ArrayKind: + if a.Len() != b.Len() { + return false; + } + for i := 0; i < a.Len(); i++ { + if !Equal(a.Elem(i), b.Elem(i)) { + return false; + } + } + return true; + case MapKind: + m := a.(*_Map).m; + if len(m) != len(b.(*_Map).m) { + return false; + } + for k,v := range m { + if !Equal(v, b.Get(k)) { + return false; + } + } + return true; + } + + // invalid kind + return false; +} + + +// Parse builder for JSON objects. + +type _JsonBuilder struct { + // either writing to *ptr + ptr *Json; + + // or to a[i] (can't set ptr = &a[i]) + a *vector.Vector; + i int; + + // or to m[k] (can't set ptr = &m[k]) + m map[string] Json; + k string; +} + +func (b *_JsonBuilder) Put(j Json) { + switch { + case b.ptr != nil: + *b.ptr = j; + case b.a != nil: + b.a.Set(b.i, j); + case b.m != nil: + b.m[b.k] = j; + } +} + +func (b *_JsonBuilder) Get() Json { + switch { + case b.ptr != nil: + return *b.ptr; + case b.a != nil: + return b.a.At(b.i).(Json); + case b.m != nil: + return b.m[b.k]; + } + return nil +} + +func (b *_JsonBuilder) Float64(f float64) { + b.Put(&_Number{f, _Null{}}) +} + +func (b *_JsonBuilder) Int64(i int64) { + b.Float64(float64(i)) +} + +func (b *_JsonBuilder) Uint64(i uint64) { + b.Float64(float64(i)) +} + +func (b *_JsonBuilder) Bool(tf bool) { + b.Put(&_Bool{tf, _Null{}}) +} + +func (b *_JsonBuilder) Null() { + b.Put(Null) +} + +func (b *_JsonBuilder) String(s string) { + b.Put(&_String{s, _Null{}}) +} + + +func (b *_JsonBuilder) Array() { + b.Put(&_Array{vector.New(0), _Null{}}) +} + +func (b *_JsonBuilder) Map() { + b.Put(&_Map{make(map[string]Json), _Null{}}) +} + +func (b *_JsonBuilder) Elem(i int) Builder { + bb := new(_JsonBuilder); + bb.a = b.Get().(*_Array).a; + bb.i = i; + for i >= bb.a.Len() { + bb.a.Push(Null) + } + return bb +} + +func (b *_JsonBuilder) Key(k string) Builder { + bb := new(_JsonBuilder); + bb.m = b.Get().(*_Map).m; + bb.k = k; + bb.m[k] = Null; + return bb +} + +// StringToJson parses the string s as a JSON-syntax string +// and returns the generic JSON object representation. +// On success, StringToJson returns with ok set to true and errtok empty. +// If StringToJson encounters a syntax error, it returns with +// ok set to false and errtok set to a fragment of the offending syntax. +func StringToJson(s string) (json Json, ok bool, errtok string) { + var errindx int; + var j Json; + b := new(_JsonBuilder); + b.ptr = &j; + ok, errindx, errtok = Parse(s, b); + if !ok { + return nil, false, errtok + } + return j, true, "" +} + +// BUG(rsc): StringToJson should return an os.Error instead of a bool. diff --git a/src/pkg/json/generic_test.go b/src/pkg/json/generic_test.go new file mode 100644 index 000000000..68868d7a5 --- /dev/null +++ b/src/pkg/json/generic_test.go @@ -0,0 +1,76 @@ +// Copyright 2009 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 json + +import ( + "json"; + "testing"; +) + +var jsontests = []string { + `null`, + `true`, + `false`, + `"abc"`, + `123`, + `0.1`, + `1e-10`, + `[]`, + `[1,2,3,4]`, + `[1,2,"abc",null,true,false]`, + `{}`, + `{"a":1}`, +} + +func TestJson(t *testing.T) { + for i := 0; i < len(jsontests); i++ { + val, ok, errtok := StringToJson(jsontests[i]); + if !ok { + t.Errorf("StringToJson(%#q) => error near %v", jsontests[i], errtok); + continue; + } + str := JsonToString(val); + if str != jsontests[i] { + t.Errorf("JsonToString(StringToJson(%#q)) = %#q", jsontests[i], str); + continue; + } + } +} + +func TestJsonMap(t *testing.T) { + values := make(map[string]Json); + mapstr := "{"; + for i := 0; i < len(jsontests); i++ { + val, ok, errtok := StringToJson(jsontests[i]); + if !ok { + t.Errorf("StringToJson(%#q) => error near %v", jsontests[i], errtok); + } + if i > 0 { + mapstr += ","; + } + values[jsontests[i]] = val; + mapstr += Quote(jsontests[i]); + mapstr += ":"; + mapstr += JsonToString(val); + } + mapstr += "}"; + + mapv, ok, errtok := StringToJson(mapstr); + if !ok { + t.Fatalf("StringToJson(%#q) => error near %v", mapstr, errtok); + } + if mapv == nil { + t.Fatalf("StringToJson(%#q) => nil, %v, %v", mapstr, ok, errtok); + } + if cnt := mapv.Len(); cnt != len(jsontests) { + t.Errorf("StringToJson(%#q).Len() => %v, want %v", mapstr, cnt, + len(jsontests)); + } + for k,v := range values { + if v1 := mapv.Get(k); !Equal(v1, v) { + t.Errorf("MapTest: Walk(%#q) => %v, want %v", k, v1, v); + } + } +} diff --git a/src/pkg/json/parse.go b/src/pkg/json/parse.go new file mode 100644 index 000000000..e33b9dbc1 --- /dev/null +++ b/src/pkg/json/parse.go @@ -0,0 +1,419 @@ +// Copyright 2009 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. + +// JSON (JavaScript Object Notation) parser. +// See http://www.json.org/ + +// The json package implements a simple parser and +// representation for JSON (JavaScript Object Notation), +// as defined at http://www.json.org/. +package json + +import ( + "fmt"; + "io"; + "math"; + "strconv"; + "strings"; + "utf8"; +) + +// Strings +// +// Double quoted with escapes: \" \\ \/ \b \f \n \r \t \uXXXX. +// No literal control characters, supposedly. +// Have also seen \' and embedded newlines. + +func _UnHex(p string, r, l int) (v int, ok bool) { + v = 0; + for i := r; i < l; i++ { + if i >= len(p) { + return 0, false + } + v *= 16; + switch { + case '0' <= p[i] && p[i] <= '9': + v += int(p[i] - '0'); + case 'a' <= p[i] && p[i] <= 'f': + v += int(p[i] - 'a' + 10); + case 'A' <= p[i] && p[i] <= 'F': + v += int(p[i] - 'A' + 10); + default: + return 0, false; + } + } + return v, true; +} + +// Unquote unquotes the JSON-quoted string s, +// returning a raw string t. If s is not a valid +// JSON-quoted string, Unquote returns with ok set to false. +func Unquote(s string) (t string, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + b := make([]byte, len(s)); + w := 0; + for r := 1; r < len(s)-1; { + switch { + case s[r] == '\\': + r++; + if r >= len(s)-1 { + return + } + switch s[r] { + default: + return; + case '"', '\\', '/', '\'': + b[w] = s[r]; + r++; + w++; + case 'b': + b[w] = '\b'; + r++; + w++; + case 'f': + b[w] = '\f'; + r++; + w++; + case 'n': + b[w] = '\n'; + r++; + w++; + case 'r': + b[w] = '\r'; + r++; + w++; + case 't': + b[w] = '\t'; + r++; + w++; + case 'u': + r++; + rune, ok := _UnHex(s, r, 4); + if !ok { + return + } + r += 4; + w += utf8.EncodeRune(rune, b[w:len(b)]); + } + // Control characters are invalid, but we've seen raw \n. + case s[r] < ' ' && s[r] != '\n': + if s[r] == '\n' { + b[w] = '\n'; + r++; + w++; + break; + } + return; + // ASCII + case s[r] < utf8.RuneSelf: + b[w] = s[r]; + r++; + w++; + // Coerce to well-formed UTF-8. + default: + rune, size := utf8.DecodeRuneInString(s[r:len(s)]); + r += size; + w += utf8.EncodeRune(rune, b[w:len(b)]); + } + } + return string(b[0:w]), true +} + +// Quote quotes the raw string s using JSON syntax, +// so that Unquote(Quote(s)) = s, true. +func Quote(s string) string { + chr := make([]byte, utf8.UTFMax); + chr0 := chr[0:1]; + b := new(io.ByteBuffer); + chr[0] = '"'; + b.Write(chr0); + for i := 0; i < len(s); i++ { + switch { + case s[i]=='"' || s[i]=='\\': + chr[0] = '\\'; + chr[1] = s[i]; + b.Write(chr[0:2]); + + case s[i] == '\b': + chr[0] = '\\'; + chr[1] = 'b'; + b.Write(chr[0:2]); + + case s[i] == '\f': + chr[0] = '\\'; + chr[1] = 'f'; + b.Write(chr[0:2]); + + case s[i] == '\n': + chr[0] = '\\'; + chr[1] = 'n'; + b.Write(chr[0:2]); + + case s[i] == '\r': + chr[0] = '\\'; + chr[1] = 'r'; + b.Write(chr[0:2]); + + case s[i] == '\t': + chr[0] = '\\'; + chr[1] = 't'; + b.Write(chr[0:2]); + + case 0x20 <= s[i] && s[i] < utf8.RuneSelf: + chr[0] = s[i]; + b.Write(chr0); + } + } + chr[0] = '"'; + b.Write(chr0); + return string(b.Data()); +} + + +// _Lexer + +type _Lexer struct { + s string; + i int; + kind int; + token string; +} + +func punct(c byte) bool { + return c=='"' || c=='[' || c==']' || c==':' || c=='{' || c=='}' || c==',' +} + +func white(c byte) bool { + return c==' ' || c=='\t' || c=='\n' || c=='\v' +} + +func skipwhite(p string, i int) int { + for i < len(p) && white(p[i]) { + i++ + } + return i +} + +func skiptoken(p string, i int) int { + for i < len(p) && !punct(p[i]) && !white(p[i]) { + i++ + } + return i +} + +func skipstring(p string, i int) int { + for i++; i < len(p) && p[i] != '"'; i++ { + if p[i] == '\\' { + i++ + } + } + if i >= len(p) { + return i + } + return i+1 +} + +func (t *_Lexer) Next() { + i, s := t.i, t.s; + i = skipwhite(s, i); + if i >= len(s) { + t.kind = 0; + t.token = ""; + t.i = len(s); + return; + } + + c := s[i]; + switch { + case c == '-' || '0' <= c && c <= '9': + j := skiptoken(s, i); + t.kind = '1'; + t.token = s[i:j]; + i = j; + + case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': + j := skiptoken(s, i); + t.kind = 'a'; + t.token = s[i:j]; + i = j; + + case c == '"': + j := skipstring(s, i); + t.kind = '"'; + t.token = s[i:j]; + i = j; + + case c == '[', c == ']', c == ':', c == '{', c == '}', c == ',': + t.kind = int(c); + t.token = s[i:i+1]; + i++; + + default: + t.kind = '?'; + t.token = s[i:i+1]; + } + + t.i = i; +} + + +// Parser +// +// Implements parsing but not the actions. Those are +// carried out by the implementation of the Builder interface. +// A Builder represents the object being created. +// Calling a method like Int64(i) sets that object to i. +// Calling a method like Elem(i) or Key(s) creates a +// new builder for a subpiece of the object (logically, +// an array element or a map key). +// +// There are two Builders, in other files. +// The JsonBuilder builds a generic Json structure +// in which maps are maps. +// The StructBuilder copies data into a possibly +// nested data structure, using the "map keys" +// as struct field names. + +type _Value interface {} + +// BUG(rsc): The json Builder interface needs to be +// reconciled with the xml Builder interface. + +// A Builder is an interface implemented by clients and passed +// to the JSON parser. It gives clients full control over the +// eventual representation returned by the parser. +type Builder interface { + // Set value + Int64(i int64); + Uint64(i uint64); + Float64(f float64); + String(s string); + Bool(b bool); + Null(); + Array(); + Map(); + + // Create sub-Builders + Elem(i int) Builder; + Key(s string) Builder; +} + +func parse(lex *_Lexer, build Builder) bool { + ok := false; +Switch: + switch lex.kind { + case 0: + break; + case '1': + // If the number is exactly an integer, use that. + if i, err := strconv.Atoi64(lex.token); err == nil { + build.Int64(i); + ok = true; + } + else if i, err := strconv.Atoui64(lex.token); err == nil { + build.Uint64(i); + ok = true; + } + // Fall back to floating point. + else if f, err := strconv.Atof64(lex.token); err == nil { + build.Float64(f); + ok = true; + } + + case 'a': + switch lex.token { + case "true": + build.Bool(true); + ok = true; + case "false": + build.Bool(false); + ok = true; + case "null": + build.Null(); + ok = true; + } + + case '"': + if str, ok1 := Unquote(lex.token); ok1 { + build.String(str); + ok = true; + } + + case '[': + // array + build.Array(); + lex.Next(); + n := 0; + for lex.kind != ']' { + if n > 0 { + if lex.kind != ',' { + break Switch; + } + lex.Next(); + } + if !parse(lex, build.Elem(n)) { + break Switch; + } + n++; + } + ok = true; + + case '{': + // map + lex.Next(); + build.Map(); + n := 0; + for lex.kind != '}' { + if n > 0 { + if lex.kind != ',' { + break Switch; + } + lex.Next(); + } + if lex.kind != '"' { + break Switch; + } + key, ok := Unquote(lex.token); + if !ok { + break Switch; + } + lex.Next(); + if lex.kind != ':' { + break Switch; + } + lex.Next(); + if !parse(lex, build.Key(key)) { + break Switch; + } + n++; + } + ok = true; + } + + if ok { + lex.Next(); + } + return ok; +} + +// Parse parses the JSON syntax string s and makes calls to +// the builder to construct a parsed representation. +// On success, it returns with ok set to true. +// On error, it returns with ok set to false, errindx set +// to the byte index in s where a syntax error occurred, +// and errtok set to the offending token. +func Parse(s string, builder Builder) (ok bool, errindx int, errtok string) { + lex := new(_Lexer); + lex.s = s; + lex.Next(); + if parse(lex, builder) { + if lex.kind == 0 { // EOF + return true, 0, "" + } + } + return false, lex.i, lex.token +} + diff --git a/src/pkg/json/struct.go b/src/pkg/json/struct.go new file mode 100644 index 000000000..ac2689557 --- /dev/null +++ b/src/pkg/json/struct.go @@ -0,0 +1,269 @@ +// Copyright 2009 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. + +// Marshalling and unmarshalling of +// JSON data into Go structs using reflection. + +package json + +import ( + "json"; + "reflect"; +) + +type _StructBuilder struct { + val reflect.Value +} + +var nobuilder *_StructBuilder + +func setfloat(v reflect.Value, f float64) { + switch v.Kind() { + case reflect.FloatKind: + v.(reflect.FloatValue).Set(float(f)); + case reflect.Float32Kind: + v.(reflect.Float32Value).Set(float32(f)); + case reflect.Float64Kind: + v.(reflect.Float64Value).Set(float64(f)); + } +} + +func setint(v reflect.Value, i int64) { + switch v.Kind() { + case reflect.IntKind: + v.(reflect.IntValue).Set(int(i)); + case reflect.Int8Kind: + v.(reflect.Int8Value).Set(int8(i)); + case reflect.Int16Kind: + v.(reflect.Int16Value).Set(int16(i)); + case reflect.Int32Kind: + v.(reflect.Int32Value).Set(int32(i)); + case reflect.Int64Kind: + v.(reflect.Int64Value).Set(int64(i)); + case reflect.UintKind: + v.(reflect.UintValue).Set(uint(i)); + case reflect.Uint8Kind: + v.(reflect.Uint8Value).Set(uint8(i)); + case reflect.Uint16Kind: + v.(reflect.Uint16Value).Set(uint16(i)); + case reflect.Uint32Kind: + v.(reflect.Uint32Value).Set(uint32(i)); + case reflect.Uint64Kind: + v.(reflect.Uint64Value).Set(uint64(i)); + } +} + +func (b *_StructBuilder) Int64(i int64) { + if b == nil { + return + } + v := b.val; + switch v.Kind() { + case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind: + setfloat(v, float64(i)); + default: + setint(v, i); + } +} + +func (b *_StructBuilder) Uint64(i uint64) { + if b == nil { + return + } + v := b.val; + switch v.Kind() { + case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind: + setfloat(v, float64(i)); + default: + setint(v, int64(i)); + } +} + +func (b *_StructBuilder) Float64(f float64) { + if b == nil { + return + } + v := b.val; + switch v.Kind() { + case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind: + setfloat(v, f); + default: + setint(v, int64(f)); + } +} + +func (b *_StructBuilder) Null() { +} + +func (b *_StructBuilder) String(s string) { + if b == nil { + return + } + if v := b.val; v.Kind() == reflect.StringKind { + v.(reflect.StringValue).Set(s); + } +} + +func (b *_StructBuilder) Bool(tf bool) { + if b == nil { + return + } + if v := b.val; v.Kind() == reflect.BoolKind { + v.(reflect.BoolValue).Set(tf); + } +} + +func (b *_StructBuilder) Array() { + if b == nil { + return + } + if v := b.val; v.Kind() == reflect.PtrKind { + pv := v.(reflect.PtrValue); + psubtype := pv.Type().(reflect.PtrType).Sub(); + if pv.Get() == nil && psubtype.Kind() == reflect.ArrayKind { + av := reflect.NewSliceValue(psubtype.(reflect.ArrayType), 0, 8); + pv.SetSub(av); + } + } +} + +func (b *_StructBuilder) Elem(i int) Builder { + if b == nil || i < 0 { + return nobuilder + } + v := b.val; + if v.Kind() == reflect.PtrKind { + // If we have a pointer to an array, allocate or grow + // the array as necessary. Then set v to the array itself. + pv := v.(reflect.PtrValue); + psub := pv.Sub(); + if psub.Kind() == reflect.ArrayKind { + av := psub.(reflect.ArrayValue); + if i > av.Cap() { + n := av.Cap(); + if n < 8 { + n = 8 + } + for n <= i { + n *= 2 + } + av1 := reflect.NewSliceValue(av.Type().(reflect.ArrayType), av.Len(), n); + av1.CopyFrom(av, av.Len()); + pv.SetSub(av1); + av = av1; + } + } + v = psub; + } + if v.Kind() == reflect.ArrayKind { + // Array was grown above, or is fixed size. + av := v.(reflect.ArrayValue); + if av.Len() <= i && i < av.Cap() { + av.SetLen(i+1); + } + if i < av.Len() { + return &_StructBuilder{ av.Elem(i) } + } + } + return nobuilder +} + +func (b *_StructBuilder) Map() { + if b == nil { + return + } + if v := b.val; v.Kind() == reflect.PtrKind { + pv := v.(reflect.PtrValue); + if pv.Get() == nil { + pv.SetSub(reflect.NewZeroValue(pv.Type().(reflect.PtrType).Sub())) + } + } +} + +func (b *_StructBuilder) Key(k string) Builder { + if b == nil { + return nobuilder + } + v := b.val; + if v.Kind() == reflect.PtrKind { + v = v.(reflect.PtrValue).Sub(); + } + if v.Kind() == reflect.StructKind { + sv := v.(reflect.StructValue); + t := v.Type().(reflect.StructType); + for i := 0; i < t.Len(); i++ { + name, typ, tag, off := t.Field(i); + if k == name { + return &_StructBuilder{ sv.Field(i) } + } + } + } + return nobuilder +} + +// Unmarshal parses the JSON syntax string s and fills in +// an arbitrary struct or array pointed at by val. +// It uses the reflection library to assign to fields +// and arrays embedded in val. Well-formed data that does not fit +// into the struct is discarded. +// +// For example, given the following definitions: +// +// type Email struct { +// where string; +// addr string; +// } +// +// type Result struct { +// name string; +// phone string; +// emails []Email +// } +// +// var r = Result{ "name", "phone", nil } +// +// unmarshalling the JSON syntax string +// +// { +// "email": [ +// { +// "where": "home", +// "addr": "gre@example.com" +// }, +// { +// "where": "work", +// "addr": "gre@work.com" +// } +// ], +// "name": "Grace R. Emlin", +// "address": "123 Main Street" +// } +// +// via Unmarshal(s, &r) is equivalent to assigning +// +// r = Result{ +// "Grace R. Emlin", // name +// "phone", // no phone given +// []Email{ +// Email{ "home", "gre@example.com" }, +// Email{ "work", "gre@work.com" } +// } +// } +// +// Note that the field r.phone has not been modified and +// that the JSON field "address" was discarded. +// +// On success, Unmarshal returns with ok set to true. +// On a syntax error, it returns with ok set to false and errtok +// set to the offending token. +func Unmarshal(s string, val interface{}) (ok bool, errtok string) { + var errindx int; + var val1 interface{}; + b := &_StructBuilder{ reflect.NewValue(val) }; + ok, errindx, errtok = Parse(s, b); + if !ok { + return false, errtok + } + return true, "" +} diff --git a/src/pkg/json/struct_test.go b/src/pkg/json/struct_test.go new file mode 100644 index 000000000..88d9e9ec4 --- /dev/null +++ b/src/pkg/json/struct_test.go @@ -0,0 +1,82 @@ +// Copyright 2009 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 json + +import ( + "json"; + "testing"; +) + +type _MyStruct struct { + t bool; + f bool; + s string; + i8 int8; + i16 int16; + i32 int32; + i64 int64; + u8 uint8; + u16 uint16; + u32 uint32; + u64 uint64; + i int; + u uint; + fl float; + fl32 float32; + fl64 float64; + a *[]string; // TODO(rsc): Should be able to use []string. + my *_MyStruct; +}; + +const _Encoded = + `{"t":true,"f":false,"s":"abc","i8":1,"i16":2,"i32":3,"i64":4,` + ` "u8":5,"u16":6,"u32":7,"u64":8,` + ` "i":-9,"u":10,"bogusfield":"should be ignored",` + ` "fl":11.5,"fl32":12.25,"fl64":13.75,` + ` "a":["x","y","z"],"my":{"s":"subguy"}}`; + + +func _Check(t *testing.T, ok bool, name string, v interface{}) { + if !ok { + t.Errorf("%s = %v (BAD)", name, v); + } else { + t.Logf("%s = %v (good)", name, v); + } +} + +func TestUnmarshal(t *testing.T) { + var m _MyStruct; + m.f = true; + ok, errtok := Unmarshal(_Encoded, &m); + if !ok { + t.Fatalf("Unmarshal failed near %s", errtok); + } + _Check(t, m.t==true, "t", m.t); + _Check(t, m.f==false, "f", m.f); + _Check(t, m.s=="abc", "s", m.s); + _Check(t, m.i8==1, "i8", m.i8); + _Check(t, m.i16==2, "i16", m.i16); + _Check(t, m.i32==3, "i32", m.i32); + _Check(t, m.i64==4, "i64", m.i64); + _Check(t, m.u8==5, "u8", m.u8); + _Check(t, m.u16==6, "u16", m.u16); + _Check(t, m.u32==7, "u32", m.u32); + _Check(t, m.u64==8, "u64", m.u64); + _Check(t, m.i==-9, "i", m.i); + _Check(t, m.u==10, "u", m.u); + _Check(t, m.fl==11.5, "fl", m.fl); + _Check(t, m.fl32==12.25, "fl32", m.fl32); + _Check(t, m.fl64==13.75, "fl64", m.fl64); + _Check(t, m.a!=nil, "a", m.a); + if m.a != nil { + _Check(t, m.a[0]=="x", "a[0]", m.a[0]); + _Check(t, m.a[1]=="y", "a[1]", m.a[1]); + _Check(t, m.a[2]=="z", "a[2]", m.a[2]); + } + _Check(t, m.my!=nil, "my", m.my); + if m.my != nil { + _Check(t, m.my.s=="subguy", "my.s", m.my.s); + } +} diff --git a/src/pkg/log/Makefile b/src/pkg/log/Makefile new file mode 100644 index 000000000..4b1c4b5a2 --- /dev/null +++ b/src/pkg/log/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + log.$O\ + + +phases: a1 +_obj$D/log.a: phases + +a1: $(O1) + $(AR) grc _obj$D/log.a log.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/log.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/log.a + +packages: _obj$D/log.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/log.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/log.a diff --git a/src/pkg/log/log.go b/src/pkg/log/log.go new file mode 100644 index 000000000..8fcd73190 --- /dev/null +++ b/src/pkg/log/log.go @@ -0,0 +1,195 @@ +// Copyright 2009 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. + +// Rudimentary logging package. Defines a type, Logger, with simple +// methods for formatting output to one or two destinations. Also has +// predefined Loggers accessible through helper functions Stdout[f], +// Stderr[f], Exit[f], and Crash[f], which are easier to use than creating +// a Logger manually. +// Exit exits when written to. +// Crash causes a crash when written to. +package log + +import ( + "fmt"; + "io"; + "runtime"; + "os"; + "time"; +) + +// These flags define the properties of the Logger and the output they produce. +const ( + // Flags + Lok = iota; + Lexit; // terminate execution when written + Lcrash; // crash (panic) when written + // Bits or'ed together to control what's printed. There is no control over the + // order they appear (the order listed here) or the format they present (as + // described in the comments). A colon appears after these items: + // 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message + Ldate = 1 << iota; // the date: 2009/0123 + Ltime; // the time: 01:23:23 + Lmicroseconds; // microsecond resolution: 01:23:23.123123. assumes Ltime. + Llongfile; // full file name and line number: /a/b/c/d.go:23 + Lshortfile; // final file name element and line number: d.go:23. overrides Llongfile + lAllBits = Ldate | Ltime | Lmicroseconds | Llongfile | Lshortfile; +) + +// Logger represents an active logging object. +type Logger struct { + out0 io.Writer; // first destination for output + out1 io.Writer; // second destination for output; may be nil + prefix string; // prefix to write at beginning of each line + flag int; // properties +} + +// New creates a new Logger. The out0 and out1 variables set the +// destinations to which log data will be written; out1 may be nil. +// The prefix appears at the beginning of each generated log line. +// The flag argument defines the logging properties. +func New(out0, out1 io.Writer, prefix string, flag int) *Logger { + return &Logger{out0, out1, prefix, flag} +} + +var ( + stdout = New(os.Stdout, nil, "", Lok|Ldate|Ltime); + stderr = New(os.Stderr, nil, "", Lok|Ldate|Ltime); + exit = New(os.Stderr, nil, "", Lexit|Ldate|Ltime); + crash = New(os.Stderr, nil, "", Lcrash|Ldate|Ltime); +) + +var shortnames = make(map[string] string) // cache of short names to avoid allocation. + +// Cheap integer to fixed-width decimal ASCII. Use a negative width to avoid zero-padding +func itoa(i int, wid int) string { + var u uint = uint(i); + if u == 0 && wid <= 1 { + return "0" + } + + // Assemble decimal in reverse order. + var b [32]byte; + bp := len(b); + for ; u > 0 || wid > 0; u /= 10 { + bp--; + wid--; + b[bp] = byte(u%10) + '0'; + } + + return string(b[bp:len(b)]) +} + +func (l *Logger) formatHeader(ns int64, calldepth int) string { + h := l.prefix; + if l.flag & (Ldate | Ltime | Lmicroseconds) != 0 { + t := time.SecondsToLocalTime(ns/1e9); + if l.flag & (Ldate) != 0 { + h += itoa(int(t.Year), 4) + "/" + itoa(t.Month, 2) + itoa(t.Day, 2) + " " + } + if l.flag & (Ltime | Lmicroseconds) != 0 { + h += itoa(t.Hour, 2) + ":" + itoa(t.Minute, 2) + ":" + itoa(t.Second, 2); + if l.flag & Lmicroseconds != 0 { + h += "." + itoa(int(ns % 1e9)/1e3, 6); + } + h += " "; + } + } + if l.flag & (Lshortfile | Llongfile) != 0 { + pc, file, line, ok := runtime.Caller(calldepth); + if ok { + if l.flag & Lshortfile != 0 { + short, ok := shortnames[file]; + if !ok { + short = file; + for i := len(file) - 1; i > 0; i-- { + if file[i] == '/' { + short = file[i+1:len(file)]; + break; + } + } + shortnames[file] = short; + } + file = short; + } + } else { + file = "???"; + line = 0; + } + h += file + ":" + itoa(line, -1) + ": "; + } + return h; +} + +// Output writes the output for a logging event. The string s contains the text to print after +// the time stamp; calldepth is used to recover the PC. It is provided for generality, although +// at the moment on all pre-defined paths it will be 2. +func (l *Logger) Output(calldepth int, s string) { + now := time.Nanoseconds(); // get this early. + newline := "\n"; + if len(s) > 0 && s[len(s)-1] == '\n' { + newline = "" + } + s = l.formatHeader(now, calldepth+1) + s + newline; + io.WriteString(l.out0, s); + if l.out1 != nil { + io.WriteString(l.out1, s); + } + switch l.flag & ^lAllBits { + case Lcrash: + panic("log: fatal error"); + case Lexit: + os.Exit(1); + } +} + +// Logf is analogous to Printf() for a Logger. +func (l *Logger) Logf(format string, v ...) { + l.Output(2, fmt.Sprintf(format, v)) +} + +// Log is analogouts to Print() for a Logger. +func (l *Logger) Log(v ...) { + l.Output(2, fmt.Sprintln(v)) +} + +// Stdout is a helper function for easy logging to stdout. It is analogous to Print(). +func Stdout(v ...) { + stdout.Output(2, fmt.Sprint(v)) +} + +// Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr). +func Stderr(v ...) { + stderr.Output(2, fmt.Sprintln(v)) +} + +// Stdoutf is a helper functions for easy formatted logging to stdout. It is analogous to Printf(). +func Stdoutf(format string, v ...) { + stdout.Output(2, fmt.Sprintf(format, v)) +} + +// Stderrf is a helper function for easy formatted logging to stderr. It is analogous to Fprintf(os.Stderr). +func Stderrf(format string, v ...) { + stderr.Output(2, fmt.Sprintf(format, v)) +} + +// Exit is equivalent to Stderr() followed by a call to os.Exit(1). +func Exit(v ...) { + exit.Output(2, fmt.Sprintln(v)) +} + +// Exitf is equivalent to Stderrf() followed by a call to os.Exit(1). +func Exitf(format string, v ...) { + exit.Output(2, fmt.Sprintf(format, v)) +} + +// Crash is equivalent to Stderrf() followed by a call to panic(). +func Crash(v ...) { + crash.Output(2, fmt.Sprintln(v)) +} + +// Crashf is equivalent to Stderrf() followed by a call to panic(). +func Crashf(format string, v ...) { + crash.Output(2, fmt.Sprintf(format, v)) +} diff --git a/src/pkg/log/log_test.go b/src/pkg/log/log_test.go new file mode 100644 index 000000000..0cfb2e36f --- /dev/null +++ b/src/pkg/log/log_test.go @@ -0,0 +1,82 @@ +// Copyright 2009 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 log + +// These tests are too simple. + +import ( + "bufio"; + "log"; + "os"; + "regexp"; + "testing"; +) + +const ( + Rdate = `[0-9][0-9][0-9][0-9]/[0-9][0-9][0-9][0-9]`; + Rtime = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`; + Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`; + Rline = `[0-9]+:`; + Rlongfile = `/[A-Za-z0-9_/\-]+\.go:` + Rline; + Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline; +) + +type tester struct { + flag int; + prefix string; + pattern string; // regexp that log output must match; we add ^ and expected_text$ always +} + +var tests = []tester { + // individual pieces: + tester{ 0, "", "" }, + tester{ 0, "XXX", "XXX" }, + tester{ Lok|Ldate, "", Rdate+" " }, + tester{ Lok|Ltime, "", Rtime+" " }, + tester{ Lok|Ltime|Lmicroseconds, "", Rtime+Rmicroseconds+" " }, + tester{ Lok|Lmicroseconds, "", Rtime+Rmicroseconds+" " }, // microsec implies time + tester{ Lok|Llongfile, "", Rlongfile+" " }, + tester{ Lok|Lshortfile, "", Rshortfile+" " }, + tester{ Lok|Llongfile|Lshortfile, "", Rshortfile+" " }, // shortfile overrides longfile + // everything at once: + tester{ Lok|Ldate|Ltime|Lmicroseconds|Llongfile, "XXX", "XXX"+Rdate+" "+Rtime+Rmicroseconds+" "+Rlongfile+" " }, + tester{ Lok|Ldate|Ltime|Lmicroseconds|Lshortfile, "XXX", "XXX"+Rdate+" "+Rtime+Rmicroseconds+" "+Rshortfile+" " }, +} + +// Test using Log("hello", 23, "world") or using Logf("hello %d world", 23) +func testLog(t *testing.T, flag int, prefix string, pattern string, useLogf bool) { + r, w, err1 := os.Pipe(); + if err1 != nil { + t.Fatal("pipe", err1); + } + defer r.Close(); + defer w.Close(); + buf := bufio.NewReader(r); + l := New(w, nil, prefix, flag); + if useLogf { + l.Logf("hello %d world", 23); + } else { + l.Log("hello", 23, "world"); + } + line, err3 := buf.ReadLineString('\n', false); + if err3 != nil { + t.Fatal("log error", err3); + } + pattern = "^"+pattern+"hello 23 world$"; + matched, err4 := regexp.Match(pattern, line); + if err4 != nil{ + t.Fatal("pattern did not compile:", err4); + } + if !matched { + t.Errorf("log output should match %q is %q", pattern, line); + } +} + +func TestAllLog(t *testing.T) { + for i, testcase := range(tests) { + testLog(t, testcase.flag, testcase.prefix, testcase.pattern, false); + testLog(t, testcase.flag, testcase.prefix, testcase.pattern, true); + } +} diff --git a/src/pkg/malloc/Makefile b/src/pkg/malloc/Makefile new file mode 100644 index 000000000..61894f71f --- /dev/null +++ b/src/pkg/malloc/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + malloc.$O\ + + +phases: a1 +_obj$D/malloc.a: phases + +a1: $(O1) + $(AR) grc _obj$D/malloc.a malloc.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/malloc.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/malloc.a + +packages: _obj$D/malloc.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/malloc.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/malloc.a diff --git a/src/pkg/malloc/malloc.go b/src/pkg/malloc/malloc.go new file mode 100644 index 000000000..fec53f08f --- /dev/null +++ b/src/pkg/malloc/malloc.go @@ -0,0 +1,24 @@ +// Copyright 2009 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 declarations for malloc. +// The actual functions are written in C +// and part of the runtime library. + +package malloc + +type Stats struct { + Alloc uint64; + Sys uint64; + Stacks uint64; + InusePages uint64; + NextGC uint64; + EnableGC bool; +} + +func Alloc(uintptr) *byte +func Free(*byte) +func GetStats() *Stats +func Lookup(*byte) (*byte, uintptr) +func GC() diff --git a/src/pkg/math/Makefile b/src/pkg/math/Makefile new file mode 100644 index 000000000..058049072 --- /dev/null +++ b/src/pkg/math/Makefile @@ -0,0 +1,98 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + const.$O\ + fabs.$O\ + hypot.$O\ + pow10.$O\ + runtime.$O\ + +O2=\ + atan.$O\ + exp.$O\ + floor.$O\ + fmod.$O\ + log.$O\ + sin.$O\ + sqrt.$O\ + tan.$O\ + +O3=\ + asin.$O\ + atan2.$O\ + pow.$O\ + sinh.$O\ + +O4=\ + tanh.$O\ + + +phases: a1 a2 a3 a4 +_obj$D/math.a: phases + +a1: $(O1) + $(AR) grc _obj$D/math.a const.$O fabs.$O hypot.$O pow10.$O runtime.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/math.a atan.$O exp.$O floor.$O fmod.$O log.$O sin.$O sqrt.$O tan.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/math.a asin.$O atan2.$O pow.$O sinh.$O + rm -f $(O3) + +a4: $(O4) + $(AR) grc _obj$D/math.a tanh.$O + rm -f $(O4) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/math.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 +$(O5): a4 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/math.a + +packages: _obj$D/math.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/math.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/math.a diff --git a/src/pkg/math/all_test.go b/src/pkg/math/all_test.go new file mode 100644 index 000000000..c5d5c01c4 --- /dev/null +++ b/src/pkg/math/all_test.go @@ -0,0 +1,278 @@ +// Copyright 2009 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 math + +import ( + "math"; + "testing"; +) + +var vf = []float64 { + 4.9790119248836735e+00, + 7.7388724745781045e+00, + -2.7688005719200159e-01, + -5.0106036182710749e+00, + 9.6362937071984173e+00, + 2.9263772392439646e+00, + 5.2290834314593066e+00, + 2.7279399104360102e+00, + 1.8253080916808550e+00, + -8.6859247685756013e+00, +} +var asin = []float64 { + 5.2117697218417440e-01, + 8.8495619865825236e-01, + -2.7691544662819413e-02, + -5.2482360935268932e-01, + 1.3002662421166553e+00, + 2.9698415875871901e-01, + 5.5025938468083364e-01, + 2.7629597861677200e-01, + 1.8355989225745148e-01, + -1.0523547536021498e+00, +} +var atan = []float64 { + 1.3725902621296217e+00, + 1.4422906096452980e+00, + -2.7011324359471755e-01, + -1.3738077684543379e+00, + 1.4673921193587666e+00, + 1.2415173565870167e+00, + 1.3818396865615167e+00, + 1.2194305844639670e+00, + 1.0696031952318783e+00, + -1.4561721938838085e+00, +} +var exp = []float64 { + 1.4533071302642137e+02, + 2.2958822575694450e+03, + 7.5814542574851666e-01, + 6.6668778421791010e-03, + 1.5310493273896035e+04, + 1.8659907517999329e+01, + 1.8662167355098713e+02, + 1.5301332413189379e+01, + 6.2047063430646876e+00, + 1.6894712385826522e-04, +} +var floor = []float64 { + 4.0000000000000000e+00, + 7.0000000000000000e+00, + -1.0000000000000000e+00, + -6.0000000000000000e+00, + 9.0000000000000000e+00, + 2.0000000000000000e+00, + 5.0000000000000000e+00, + 2.0000000000000000e+00, + 1.0000000000000000e+00, + -9.0000000000000000e+00, +} +var log = []float64 { + 1.6052314626930630e+00, + 2.0462560018708768e+00, + -1.2841708730962657e+00, + 1.6115563905281544e+00, + 2.2655365644872018e+00, + 1.0737652208918380e+00, + 1.6542360106073545e+00, + 1.0035467127723465e+00, + 6.0174879014578053e-01, + 2.1617038728473527e+00, +} +var pow = []float64 { + 9.5282232631648415e+04, + 5.4811599352999900e+07, + 5.2859121715894400e-01, + 9.7587991957286472e-06, + 4.3280643293460450e+09, + 8.4406761805034551e+02, + 1.6946633276191194e+05, + 5.3449040147551940e+02, + 6.6881821384514159e+01, + 2.0609869004248744e-09, +} +var sin = []float64 { + -9.6466616586009283e-01, + 9.9338225271646543e-01, + -2.7335587039794395e-01, + 9.5586257685042800e-01, + -2.0994210667799692e-01, + 2.1355787807998605e-01, + -8.6945689711673619e-01, + 4.0195666811555783e-01, + 9.6778633541688000e-01, + -6.7344058690503452e-01, +} +var sinh = []float64 { + 7.2661916084208533e+01, + 1.1479409110035194e+03, + -2.8043136512812520e-01, + -7.4994290911815868e+01, + 7.6552466042906761e+03, + 9.3031583421672010e+00, + 9.3308157558281088e+01, + 7.6179893137269143e+00, + 3.0217691805496156e+00, + -2.9595057572444951e+03, +} +var sqrt = []float64 { + 2.2313699659365484e+00, + 2.7818829009464263e+00, + 5.2619393496314792e-01, + 2.2384377628763938e+00, + 3.1042380236055380e+00, + 1.7106657298385224e+00, + 2.2867189227054791e+00, + 1.6516476350711160e+00, + 1.3510396336454586e+00, + 2.9471892997524950e+00, +} +var tan = []float64 { + -3.6613165650402277e+00, + 8.6490023264859754e+00, + -2.8417941955033615e-01, + 3.2532901859747287e+00, + 2.1472756403802937e-01, + -2.1860091071106700e-01, + -1.7600028178723679e+00, + -4.3898089147528178e-01, + -3.8438855602011305e+00, + 9.1098879337768517e-01, +} +var tanh = []float64 { + 9.9990531206936328e-01, + 9.9999962057085307e-01, + -2.7001505097318680e-01, + -9.9991110943061700e-01, + 9.9999999146798441e-01, + 9.9427249436125233e-01, + 9.9994257600983156e-01, + 9.9149409509772863e-01, + 9.4936501296239700e-01, + -9.9999994291374019e-01, +} + +func tolerance(a,b,e float64) bool { + d := a-b; + if d < 0 { + d = -d; + } + + if a != 0 { + e = e*a; + if e < 0 { + e = -e; + } + } + return d < e; +} +func close(a,b float64) bool { + return tolerance(a, b, 1e-14); +} +func veryclose(a,b float64) bool { + return tolerance(a, b, 4e-16); +} + +func TestAsin(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Asin(vf[i]/10); !veryclose(asin[i], f) { + t.Errorf("math.Asin(%g) = %g, want %g\n", vf[i]/10, f, asin[i]); + } + } +} + +func TestAtan(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Atan(vf[i]); !veryclose(atan[i], f) { + t.Errorf("math.Atan(%g) = %g, want %g\n", vf[i], f, atan[i]); + } + } +} + +func TestExp(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Exp(vf[i]); !veryclose(exp[i], f) { + t.Errorf("math.Exp(%g) = %g, want %g\n", vf[i], f, exp[i]); + } + } +} + +func TestFloor(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Floor(vf[i]); floor[i] != f { + t.Errorf("math.Floor(%g) = %g, want %g\n", vf[i], f, floor[i]); + } + } +} + +func TestLog(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Fabs(vf[i]); + if f := math.Log(a); log[i] != f { + t.Errorf("math.Log(%g) = %g, want %g\n", a, f, log[i]); + } + } + if f := math.Log(10); f != math.Ln10 { + t.Errorf("math.Log(%g) = %g, want %g\n", 10, f, math.Ln10); + } +} + +func TestPow(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Pow(10, vf[i]); !close(pow[i], f) { + t.Errorf("math.Pow(10, %.17g) = %.17g, want %.17g\n", vf[i], f, pow[i]); + } + } +} + +func TestSin(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Sin(vf[i]); !close(sin[i], f) { + t.Errorf("math.Sin(%g) = %g, want %g\n", vf[i], f, sin[i]); + } + } +} + +func TestSinh(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Sinh(vf[i]); !veryclose(sinh[i], f) { + t.Errorf("math.Sinh(%g) = %g, want %g\n", vf[i], f, sinh[i]); + } + } +} + +func TestSqrt(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Fabs(vf[i]); + if f := math.Sqrt(a); !veryclose(sqrt[i], f) { + t.Errorf("math.Sqrt(%g) = %g, want %g\n", a, f, floor[i]); + } + } +} + +func TestTan(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Tan(vf[i]); !close(tan[i], f) { + t.Errorf("math.Tan(%g) = %g, want %g\n", vf[i], f, tan[i]); + } + } +} + +func TestTanh(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Tanh(vf[i]); !veryclose(tanh[i], f) { + t.Errorf("math.Tanh(%g) = %g, want %g\n", vf[i], f, tanh[i]); + } + } +} + +func TestHypot(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Fabs(tanh[i]*math.Sqrt(2)); + if f := math.Hypot(tanh[i], tanh[i]); !veryclose(a, f) { + t.Errorf("math.Hypot(%g, %g) = %g, want %g\n", tanh[i], tanh[i], f, a); + } + } +} diff --git a/src/pkg/math/asin.go b/src/pkg/math/asin.go new file mode 100644 index 000000000..23c9a1069 --- /dev/null +++ b/src/pkg/math/asin.go @@ -0,0 +1,46 @@ +// Copyright 2009 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 math + +import "math" + +/* + * asin(arg) and acos(arg) return the arcsin, arccos, + * respectively of their arguments. + * + * Arctan is called after appropriate range reduction. + */ + +// Asin returns the arc sine of x. +func Asin(x float64) float64 { + sign := false; + if x < 0 { + x = -x; + sign = true; + } + if x > 1 { + return NaN(); + } + + temp := Sqrt(1 - x*x); + if x > 0.7 { + temp = Pi/2 - Atan(temp/x); + } else { + temp = Atan(x/temp); + } + + if sign { + temp = -temp; + } + return temp; +} + +// Acos returns the arc cosine of x. +func Acos(x float64) float64 { + if x > 1 || x < -1 { + return NaN(); + } + return Pi/2 - Asin(x); +} diff --git a/src/pkg/math/atan.go b/src/pkg/math/atan.go new file mode 100644 index 000000000..4b18f76aa --- /dev/null +++ b/src/pkg/math/atan.go @@ -0,0 +1,67 @@ +// Copyright 2009 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 math + +import "math" + +/* + * floating-point arctangent + * + * atan returns the value of the arctangent of its + * argument in the range [-pi/2,pi/2]. + * there are no error returns. + * coefficients are #5077 from Hart & Cheney. (19.56D) + */ + +/* + * xatan evaluates a series valid in the + * range [-0.414...,+0.414...]. (tan(pi/8)) + */ +func xatan(arg float64) float64 { + const + ( + P4 = .161536412982230228262e2; + P3 = .26842548195503973794141e3; + P2 = .11530293515404850115428136e4; + P1 = .178040631643319697105464587e4; + P0 = .89678597403663861959987488e3; + Q4 = .5895697050844462222791e2; + Q3 = .536265374031215315104235e3; + Q2 = .16667838148816337184521798e4; + Q1 = .207933497444540981287275926e4; + Q0 = .89678597403663861962481162e3; + ) + sq := arg*arg; + value := ((((P4*sq + P3)*sq + P2)*sq + P1)*sq + P0); + value = value/(((((sq + Q4)*sq + Q3)*sq + Q2)*sq + Q1)*sq + Q0); + return value*arg; +} + +/* + * satan reduces its argument (known to be positive) + * to the range [0,0.414...] and calls xatan. + */ +func satan(arg float64) float64 { + if arg < Sqrt2 - 1 { + return xatan(arg); + } + if arg > Sqrt2 + 1 { + return Pi/2 - xatan(1/arg); + } + return Pi/4 + xatan((arg-1)/(arg+1)); +} + +/* + * atan makes its argument positive and + * calls the inner routine satan. + */ + +// Atan returns the arc tangent of x. +func Atan(x float64) float64 { + if x > 0 { + return satan(x); + } + return -satan(-x); +} diff --git a/src/pkg/math/atan2.go b/src/pkg/math/atan2.go new file mode 100644 index 000000000..72f2117b3 --- /dev/null +++ b/src/pkg/math/atan2.go @@ -0,0 +1,28 @@ +// Copyright 2009 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 math + +import "math" + +// Atan returns the arc tangent of y/x, using +// the signs of the two to determine the quadrant +// of the return value. +func Atan2(x, y float64) float64 { + // Determine the quadrant and call atan. + if x+y == x { + if x >= 0 { + return Pi/2; + } + return -Pi/2; + } + q := Atan(x/y); + if y < 0 { + if q <= 0 { + return q + Pi; + } + return q - Pi; + } + return q; +} diff --git a/src/pkg/math/const.go b/src/pkg/math/const.go new file mode 100644 index 000000000..259660fea --- /dev/null +++ b/src/pkg/math/const.go @@ -0,0 +1,26 @@ +// Copyright 2009 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. + +// The math package provides basic constants and mathematical functions. +package math + +// Mathematical constants. +// Reference: http://www.research.att.com/~njas/sequences/Axxxxxx +const ( + E = 2.71828182845904523536028747135266249775724709369995957496696763; // A001113 + Pi = 3.14159265358979323846264338327950288419716939937510582097494459; // A000796 + Phi = 1.61803398874989484820458683436563811772030917980576286213544862; // A001622 + + Sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974; // A002193 + SqrtE = 1.64872127070012814684865078781416357165377610071014801157507931; // A019774 + SqrtPi = 1.77245385090551602729816748334114518279754945612238712821380779; // A002161 + SqrtPhi = 1.27201964951406896425242246173749149171560804184009624861664038; // A139339 + + Ln2 = 0.693147180559945309417232121458176568075500134360255254120680009; // A002162 + Log2E = 1/Ln2; + Ln10 = 2.30258509299404568401799145468436420760110148862877297603332790; // A002392 + Log10E = 1/Ln10; +) + +// BUG(rsc): The manual should define the special cases for all of these functions. diff --git a/src/pkg/math/exp.go b/src/pkg/math/exp.go new file mode 100644 index 000000000..a32c7e1d5 --- /dev/null +++ b/src/pkg/math/exp.go @@ -0,0 +1,141 @@ +// Copyright 2009 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 math + +import "math" + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_exp.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. +// +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// exp(x) +// Returns the exponential of x. +// +// Method +// 1. Argument reduction: +// Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658. +// Given x, find r and integer k such that +// +// x = k*ln2 + r, |r| <= 0.5*ln2. +// +// Here r will be represented as r = hi-lo for better +// accuracy. +// +// 2. Approximation of exp(r) by a special rational function on +// the interval [0,0.34658]: +// Write +// R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ... +// We use a special Remes algorithm on [0,0.34658] to generate +// a polynomial of degree 5 to approximate R. The maximum error +// of this polynomial approximation is bounded by 2**-59. In +// other words, +// R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5 +// (where z=r*r, and the values of P1 to P5 are listed below) +// and +// | 5 | -59 +// | 2.0+P1*z+...+P5*z - R(z) | <= 2 +// | | +// The computation of exp(r) thus becomes +// 2*r +// exp(r) = 1 + ------- +// R - r +// r*R1(r) +// = 1 + r + ----------- (for better accuracy) +// 2 - R1(r) +// where +// 2 4 10 +// R1(r) = r - (P1*r + P2*r + ... + P5*r ). +// +// 3. Scale back to obtain exp(x): +// From step 1, we have +// exp(x) = 2^k * exp(r) +// +// Special cases: +// exp(INF) is INF, exp(NaN) is NaN; +// exp(-INF) is 0, and +// for finite argument, only exp(0)=1 is exact. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Misc. info. +// For IEEE double +// if x > 7.09782712893383973096e+02 then exp(x) overflow +// if x < -7.45133219101941108420e+02 then exp(x) underflow +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. + +// Exp returns e^x, the base-e exponential of x. +// +// Special cases are: +// Exp(+Inf) = +Inf +// Exp(NaN) = NaN +// Very large values overflow to -Inf or +Inf. +// Very small values underflow to 1. +func Exp(x float64) float64 { + const ( + Ln2Hi = 6.93147180369123816490e-01; + Ln2Lo = 1.90821492927058770002e-10; + Log2e = 1.44269504088896338700e+00; + + P1 = 1.66666666666666019037e-01; /* 0x3FC55555; 0x5555553E */ + P2 = -2.77777777770155933842e-03; /* 0xBF66C16C; 0x16BEBD93 */ + P3 = 6.61375632143793436117e-05; /* 0x3F11566A; 0xAF25DE2C */ + P4 = -1.65339022054652515390e-06; /* 0xBEBBBD41; 0xC5D26BF1 */ + P5 = 4.13813679705723846039e-08; /* 0x3E663769; 0x72BEA4D0 */ + + Overflow = 7.09782712893383973096e+02; + Underflow = -7.45133219101941108420e+02; + NearZero = 1.0/(1<<28); // 2^-28 + ) + + // special cases + switch { + case IsNaN(x) || IsInf(x, 1): + return x; + case IsInf(x, -1): + return 0; + case x > Overflow: + return Inf(1); + case x < Underflow: + return 0; + case -NearZero < x && x < NearZero: + return 1; + } + + // reduce; computed as r = hi - lo for extra precision. + var k int; + switch { + case x < 0: + k = int(Log2e*x - 0.5); + case x > 0: + k = int(Log2e*x + 0.5); + } + hi := x - float64(k)*Ln2Hi; + lo := float64(k)*Ln2Lo; + r := hi - lo; + + // compute + t := r * r; + c := r - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + y := 1 - ((lo - (r*c)/(2-c)) - hi); + // TODO(rsc): make sure Ldexp can handle boundary k + return Ldexp(y, k); +} diff --git a/src/pkg/math/fabs.go b/src/pkg/math/fabs.go new file mode 100644 index 000000000..9427c5726 --- /dev/null +++ b/src/pkg/math/fabs.go @@ -0,0 +1,14 @@ +// Copyright 2009 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 math + +// Fabs returns the absolute value of x. +func Fabs(x float64) float64 { + if x < 0 { + return -x; + } + return x; +} + diff --git a/src/pkg/math/floor.go b/src/pkg/math/floor.go new file mode 100644 index 000000000..48a1003f0 --- /dev/null +++ b/src/pkg/math/floor.go @@ -0,0 +1,25 @@ +// Copyright 2009 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 math + +import "math" + +// Floor returns the greatest integer value less than or equal to x. +func Floor(x float64) float64 { + if x < 0 { + d, fract := Modf(-x); + if fract != 0.0 { + d = d+1; + } + return -d; + } + d, fract := Modf(x); + return d; +} + +// Ceil returns the least integer value greater than or equal to x. +func Ceil(x float64) float64 { + return -Floor(-x); +} diff --git a/src/pkg/math/fmod.go b/src/pkg/math/fmod.go new file mode 100644 index 000000000..617f5408b --- /dev/null +++ b/src/pkg/math/fmod.go @@ -0,0 +1,41 @@ +// Copyright 2009 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 math + +import "math" + +/* + * floating-point mod func without infinity or NaN checking + */ + +// Fmod returns the floating-point remainder of x/y. +func Fmod(x, y float64) float64 { + if y == 0 { + return x; + } + if y < 0 { + y = -y; + } + + yfr, yexp := Frexp(y); + sign := false; + r := x; + if x < 0 { + r = -x; + sign = true; + } + + for r >= y { + rfr, rexp := Frexp(r); + if rfr < yfr { + rexp = rexp - 1; + } + r = r - Ldexp(y, rexp-yexp); + } + if sign { + r = -r; + } + return r; +} diff --git a/src/pkg/math/hypot.go b/src/pkg/math/hypot.go new file mode 100644 index 000000000..411f74e4f --- /dev/null +++ b/src/pkg/math/hypot.go @@ -0,0 +1,49 @@ +// Copyright 2009 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 math + +/* + * hypot -- sqrt(p*p + q*q), but overflows only if the result does. + * See Cleve Moler and Donald Morrison, + * Replacing Square Roots by Pythagorean Sums + * IBM Journal of Research and Development, + * Vol. 27, Number 6, pp. 577-581, Nov. 1983 + */ + +// Hypot computes Sqrt(p*p + q*q), taking care to avoid +// unnecessary overflow and underflow. +func Hypot(p, q float64) float64 { + if p < 0 { + p = -p; + } + if q < 0 { + q = -q; + } + + if p < q { + p, q = q, p; + } + + if p == 0 { + return 0; + } + + pfac := p; + q = q/p; + r := q; + p = 1; + for { + r = r*r; + s := r+4; + if s == 4 { + return p*pfac; + } + r = r/s; + p = p + 2*r*p; + q = q*r; + r = q/p; + } + panic("unreachable") +} diff --git a/src/pkg/math/log.go b/src/pkg/math/log.go new file mode 100644 index 000000000..b24175b63 --- /dev/null +++ b/src/pkg/math/log.go @@ -0,0 +1,131 @@ +// Copyright 2009 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 math + +import "math" + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c +// and came with this notice. The go code is a simpler +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_log(x) +// Return the logrithm of x +// +// Method : +// 1. Argument Reduction: find k and f such that +// x = 2^k * (1+f), +// where sqrt(2)/2 < 1+f < sqrt(2) . +// +// 2. Approximation of log(1+f). +// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) +// = 2s + 2/3 s**3 + 2/5 s**5 + ....., +// = 2s + s*R +// We use a special Reme algorithm on [0,0.1716] to generate +// a polynomial of degree 14 to approximate R The maximum error +// of this polynomial approximation is bounded by 2**-58.45. In +// other words, +// 2 4 6 8 10 12 14 +// R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s +L6*s +L7*s +// (the values of L1 to L7 are listed in the program) +// and +// | 2 14 | -58.45 +// | L1*s +...+L7*s - R(z) | <= 2 +// | | +// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. +// In order to guarantee error in log below 1ulp, we compute log +// by +// log(1+f) = f - s*(f - R) (if f is not too large) +// log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) +// +// 3. Finally, log(x) = k*Ln2 + log(1+f). +// = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo))) +// Here Ln2 is split into two floating point number: +// Ln2_hi + Ln2_lo, +// where n*Ln2_hi is always exact for |n| < 2000. +// +// Special cases: +// log(x) is NaN with signal if x < 0 (including -INF) ; +// log(+INF) is +INF; log(0) is -INF with signal; +// log(NaN) is that NaN with no signal. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. + +// Log returns the natural logarithm of x. +// +// Special cases are: +// Log(+Inf) = +Inf +// Log(0) = -Inf +// Log(x < 0) = NaN +// Log(NaN) = NaN +func Log(x float64) float64 { + const ( + Ln2Hi = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */ + Ln2Lo = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */ + L1 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ + L2 = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */ + L3 = 2.857142874366239149e-01; /* 3FD24924 94229359 */ + L4 = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */ + L5 = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */ + L6 = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */ + L7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + ) + + // special cases + switch { + case IsNaN(x) || IsInf(x, 1): + return x; + case x < 0: + return NaN(); + case x == 0: + return Inf(-1); + } + + // reduce + f1, ki := Frexp(x); + if f1 < Sqrt2/2 { + f1 *= 2; + ki--; + } + f := f1 - 1; + k := float64(ki); + + // compute + s := f/(2+f); + s2 := s*s; + s4 := s2*s2; + t1 := s2*(L1 + s4*(L3 + s4*(L5 + s4*L7))); + t2 := s4*(L2 + s4*(L4 + s4*L6)); + R := t1 + t2; + hfsq := 0.5*f*f; + return k*Ln2Hi - ((hfsq-(s*(hfsq+R)+k*Ln2Lo)) - f); +} + +// Log10 returns the decimal logarthm of x. +// The special cases are the same as for Log. +func Log10(x float64) float64 { + if x <= 0 { + return NaN(); + } + return Log(x) * (1/Ln10); +} + diff --git a/src/pkg/math/pow.go b/src/pkg/math/pow.go new file mode 100644 index 000000000..920d210b5 --- /dev/null +++ b/src/pkg/math/pow.go @@ -0,0 +1,80 @@ +// Copyright 2009 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 math + +import "math" + +// Pow returns x**y, the base-x exponential of y. +func Pow(x, y float64) float64 { + // TODO: x or y NaN, ±Inf, maybe ±0. + switch { + case y == 0: + return 1; + case y == 1: + return x; + case x == 0 && y > 0: + return 0; + case x == 0 && y < 0: + return Inf(1); + case y == 0.5: + return Sqrt(x); + case y == -0.5: + return 1 / Sqrt(x); + } + + absy := y; + flip := false; + if absy < 0 { + absy = -absy; + flip = true; + } + yi, yf := Modf(absy); + if yf != 0 && x < 0 { + return NaN(); + } + if yi >= 1<<63 { + return Exp(y * Log(x)); + } + + // ans = a1 * 2^ae (= 1 for now). + a1 := float64(1); + ae := 0; + + // ans *= x^yf + if yf != 0 { + if yf > 0.5 { + yf--; + yi++; + } + a1 = Exp(yf * Log(x)); + } + + // ans *= x^yi + // by multiplying in successive squarings + // of x according to bits of yi. + // accumulate powers of two into exp. + x1, xe := Frexp(x); + for i := int64(yi); i != 0; i >>= 1 { + if i&1 == 1 { + a1 *= x1; + ae += xe; + } + x1 *= x1; + xe <<= 1; + if x1 < .5 { + x1 += x1; + xe--; + } + } + + // ans = a1*2^ae + // if flip { ans = 1 / ans } + // but in the opposite order + if flip { + a1 = 1 / a1; + ae = -ae; + } + return Ldexp(a1, ae); +} diff --git a/src/pkg/math/pow10.go b/src/pkg/math/pow10.go new file mode 100644 index 000000000..fcdd6e0a1 --- /dev/null +++ b/src/pkg/math/pow10.go @@ -0,0 +1,37 @@ +// Copyright 2009 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 math + +/* + * this table might overflow 127-bit exponent representations. + * in that case, truncate it after 1.0e38. + * it is important to get all one can from this + * routine since it is used in atof to scale numbers. + * the presumption is that GO converts fp numbers better + * than multipication of lower powers of 10. + */ + +var pow10tab [70]float64; + +// Pow10 returns 10**x, the base-10 exponential of x. +func Pow10(e int) float64 { + if e < 0 { + return 1/Pow10(-e); + } + if e < len(pow10tab) { + return pow10tab[e]; + } + m := e/2; + return Pow10(m) * Pow10(e-m); +} + +func init() { + pow10tab[0] = 1.0e0; + pow10tab[1] = 1.0e1; + for i:=2; i<len(pow10tab); i++ { + m := i/2; + pow10tab[i] = pow10tab[m] * pow10tab[i-m]; + } +} diff --git a/src/pkg/math/runtime.go b/src/pkg/math/runtime.go new file mode 100644 index 000000000..69d333825 --- /dev/null +++ b/src/pkg/math/runtime.go @@ -0,0 +1,52 @@ +// Copyright 2009 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 math + +// implemented in C, in ../../runtime +// perhaps one day the implementations will move here. + +// Float32bits returns the IEEE 754 binary representation of f. +func Float32bits(f float32) (b uint32) + +// Float32frombits returns the floating point number corresponding +// to the IEEE 754 binary representation b. +func Float32frombits(b uint32) (f float32) + +// Float64bits returns the IEEE 754 binary representation of f. +func Float64bits(f float64) (b uint64) + +// Float64frombits returns the floating point number corresponding +// the IEEE 754 binary representation b. +func Float64frombits(b uint64) (f float64) + +// Frexp breaks f into a normalized fraction +// and an integral power of two. +// It returns frac and exp satisfying f == frac × 2<sup>exp</sup>, +// with the absolute value of frac in the interval [½, 1). +func Frexp(f float64) (frac float64, exp int) + +// Inf returns positive infinity if sign >= 0, negative infinity if sign < 0. +func Inf(sign int32) (f float64) + +// IsInf returns whether f is an infinity, according to sign. +// If sign > 0, IsInf returns whether f is positive infinity. +// If sign < 0, IsInf returns whether f is negative infinity. +// If sign == 0, IsInf returns whether f is either infinity. +func IsInf(f float64, sign int) (is bool) + +// IsNaN returns whether f is an IEEE 754 ``not-a-number'' value. +func IsNaN(f float64) (is bool) + +// Ldexp is the inverse of Frexp. +// It returns frac × 2<sup>exp</sup>. +func Ldexp(frac float64, exp int) (f float64) + +// Modf returns integer and fractional floating-point numbers +// that sum to f. +// Integer and frac have the same sign as f. +func Modf(f float64) (integer float64, frac float64) + +// NaN returns an IEEE 754 ``not-a-number'' value. +func NaN() (f float64) diff --git a/src/pkg/math/sin.go b/src/pkg/math/sin.go new file mode 100644 index 000000000..9fc69606c --- /dev/null +++ b/src/pkg/math/sin.go @@ -0,0 +1,65 @@ +// Copyright 2009 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 math + +import "math" + +func sinus(x float64, quad int) float64 { + // Coefficients are #3370 from Hart & Cheney (18.80D). + const + ( + P0 = .1357884097877375669092680e8; + P1 = -.4942908100902844161158627e7; + P2 = .4401030535375266501944918e6; + P3 = -.1384727249982452873054457e5; + P4 = .1459688406665768722226959e3; + Q0 = .8644558652922534429915149e7; + Q1 = .4081792252343299749395779e6; + Q2 = .9463096101538208180571257e4; + Q3 = .1326534908786136358911494e3; + ) + if(x < 0) { + x = -x; + quad = quad+2; + } + x = x * (2/Pi); /* underflow? */ + var y float64; + if x > 32764 { + var e float64; + e, y = Modf(x); + e = e + float64(quad); + temp1, f := Modf(0.25*e); + quad = int(e - 4*f); + } else { + k := int32(x); + y = x - float64(k); + quad = (quad + int(k)) & 3; + } + + if quad&1 != 0 { + y = 1-y; + } + if quad > 1 { + y = -y; + } + + yy := y*y; + temp1 := ((((P4*yy+P3)*yy+P2)*yy+P1)*yy+P0)*y; + temp2 := ((((yy+Q3)*yy+Q2)*yy+Q1)*yy+Q0); + return temp1/temp2; +} + +// Cos returns the cosine of x. +func Cos(x float64) float64 { + if x < 0 { + x = -x; + } + return sinus(x, 1); +} + +// Sin returns the sine of x. +func Sin(x float64) float64 { + return sinus(x, 0); +} diff --git a/src/pkg/math/sinh.go b/src/pkg/math/sinh.go new file mode 100644 index 000000000..ef70989fb --- /dev/null +++ b/src/pkg/math/sinh.go @@ -0,0 +1,70 @@ +// Copyright 2009 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 math + +import "math" + +/* + * Sinh(x) returns the hyperbolic sine of x + * + * The exponential func is called for arguments + * greater in magnitude than 0.5. + * + * A series is used for arguments smaller in magnitude than 0.5. + * + * Cosh(x) is computed from the exponential func for + * all arguments. + */ + +// Sinh returns the hyperbolic sine of x. +func Sinh(x float64) float64 { + // The coefficients are #2029 from Hart & Cheney. (20.36D) + const + ( + P0 = -0.6307673640497716991184787251e+6; + P1 = -0.8991272022039509355398013511e+5; + P2 = -0.2894211355989563807284660366e+4; + P3 = -0.2630563213397497062819489e+2; + Q0 = -0.6307673640497716991212077277e+6; + Q1 = 0.1521517378790019070696485176e+5; + Q2 = -0.173678953558233699533450911e+3; + ) + + sign := false; + if x < 0 { + x = -x; + sign = true; + } + + var temp float64; + switch true { + case x > 21: + temp = Exp(x)/2; + + case x > 0.5: + temp = (Exp(x) - Exp(-x))/2; + + default: + sq := x*x; + temp = (((P3*sq+P2)*sq+P1)*sq+P0)*x; + temp = temp/(((sq+Q2)*sq+Q1)*sq+Q0); + } + + if sign { + temp = -temp; + } + return temp; +} + +// Cosh returns the hyperbolic cosine of x. +func Cosh(x float64) float64 { + if x < 0 { + x = - x; + } + if x > 21 { + return Exp(x)/2; + } + return (Exp(x) + Exp(-x))/2; +} diff --git a/src/pkg/math/sqrt.go b/src/pkg/math/sqrt.go new file mode 100644 index 000000000..79384f648 --- /dev/null +++ b/src/pkg/math/sqrt.go @@ -0,0 +1,66 @@ +// Copyright 2009 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 math + +import "math" + +/* + * sqrt returns the square root of its floating + * point argument. Newton's method. + * + * calls frexp + */ + +// Sqrt returns the square root of x. +// +// Special cases are: +// Sqrt(+Inf) = +Inf +// Sqrt(0) = 0 +// Sqrt(x < 0) = NaN +func Sqrt(x float64) float64 { + if IsInf(x, 1) { + return x; + } + + if x <= 0 { + if x < 0 { + return NaN(); + } + return 0; + } + + y, exp := Frexp(x); + for y < 0.5 { + y = y*2; + exp = exp-1; + } + + if exp&1 != 0 { + y = y*2; + exp = exp-1; + } + temp := 0.5 * (1+y); + + for exp > 60 { + temp = temp * float64(1<<30); + exp = exp - 60; + } + for exp < -60 { + temp = temp / float64(1<<30); + exp = exp + 60; + } + if exp >= 0 { + exp = 1 << uint(exp/2); + temp = temp * float64(exp); + } else { + exp = 1 << uint(-exp/2); + temp = temp / float64(exp); + } + + for i:=0; i<=4; i++ { + temp = 0.5*(temp + x/temp); + } + return temp; +} diff --git a/src/pkg/math/tan.go b/src/pkg/math/tan.go new file mode 100644 index 000000000..2d4a044b8 --- /dev/null +++ b/src/pkg/math/tan.go @@ -0,0 +1,67 @@ +// Copyright 2009 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 math + +import "math" + +/* + * floating point tangent + */ + +// Tan returns the tangent of x. +func Tan(x float64) float64 { + // Coefficients are #4285 from Hart & Cheney. (19.74D) + const + ( + P0 = -.1306820264754825668269611177e+5; + P1 = .1055970901714953193602353981e+4; + P2 = -.1550685653483266376941705728e+2; + P3 = .3422554387241003435328470489e-1; + P4 = .3386638642677172096076369e-4; + Q0 = -.1663895238947119001851464661e+5; + Q1 = .4765751362916483698926655581e+4; + Q2 = -.1555033164031709966900124574e+3; + ) + + flag := false; + sign := false; + if(x < 0) { + x = -x; + sign = true; + } + x = x * (4/Pi); /* overflow? */ + var e float64; + e, x = Modf(x); + i := int32(e); + + switch i & 3 { + case 1: + x = 1 - x; + flag = true; + + case 2: + sign = !sign; + flag = true; + + case 3: + x = 1 - x; + sign = !sign; + } + + xsq := x*x; + temp := ((((P4*xsq+P3)*xsq+P2)*xsq+P1)*xsq+P0)*x; + temp = temp/(((xsq+Q2)*xsq+Q1)*xsq+Q0); + + if flag { + if(temp == 0) { + panic(NaN()); + } + temp = 1/temp; + } + if sign { + temp = -temp; + } + return temp; +} diff --git a/src/pkg/math/tanh.go b/src/pkg/math/tanh.go new file mode 100644 index 000000000..18d38ae8f --- /dev/null +++ b/src/pkg/math/tanh.go @@ -0,0 +1,30 @@ +// Copyright 2009 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 math + +import "math" + +/* + * tanh(x) computes the hyperbolic tangent of its floating + * point argument. + * + * sinh and cosh are called except for large arguments, which + * would cause overflow improperly. + */ + +// Tanh computes the hyperbolic tangent of x. +func Tanh(x float64) float64 { + if x < 0 { + x = -x; + if x > 21 { + return -1; + } + return -Sinh(x)/Cosh(x); + } + if x > 21 { + return 1; + } + return Sinh(x)/Cosh(x); +} diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile new file mode 100644 index 000000000..61c872089 --- /dev/null +++ b/src/pkg/net/Makefile @@ -0,0 +1,96 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m dnsclient.go dnsconfig.go dnsmsg.go fd.go fd_${GOOS}.go ip.go net.go parse.go port.go >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + dnsmsg.$O\ + parse.$O\ + +O2=\ + fd_$(GOOS).$O\ + ip.$O\ + port.$O\ + +O3=\ + dnsconfig.$O\ + fd.$O\ + +O4=\ + net.$O\ + +O5=\ + dnsclient.$O\ + + +phases: a1 a2 a3 a4 a5 +_obj$D/net.a: phases + +a1: $(O1) + $(AR) grc _obj$D/net.a dnsmsg.$O parse.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/net.a fd_$(GOOS).$O ip.$O port.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/net.a dnsconfig.$O fd.$O + rm -f $(O3) + +a4: $(O4) + $(AR) grc _obj$D/net.a net.$O + rm -f $(O4) + +a5: $(O5) + $(AR) grc _obj$D/net.a dnsclient.$O + rm -f $(O5) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/net.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 +$(O5): a4 +$(O6): a5 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/net.a + +packages: _obj$D/net.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/net.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/net.a diff --git a/src/pkg/net/dialgoogle_test.go b/src/pkg/net/dialgoogle_test.go new file mode 100644 index 000000000..1e0c0aaf0 --- /dev/null +++ b/src/pkg/net/dialgoogle_test.go @@ -0,0 +1,100 @@ +// Copyright 2009 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 net + +import ( + "flag"; + "fmt"; + "io"; + "net"; + "os"; + "syscall"; + "testing"; +) + +// If an IPv6 tunnel is running (see go/stubl), we can try dialing a real IPv6 address. +var ipv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present") + +// fd is already connected to the destination, port 80. +// Run an HTTP request to fetch the appropriate page. +func fetchGoogle(t *testing.T, fd net.Conn, network, addr string) { + req := io.StringBytes("GET /intl/en/privacy.html HTTP/1.0\r\nHost: www.google.com\r\n\r\n"); + n, err := fd.Write(req); + + buf := make([]byte, 1000); + n, err = io.FullRead(fd, buf); + + if n < 1000 { + t.Errorf("fetchGoogle: short HTTP read from %s %s - %v", network, addr, err); + return + } +} + +func doDial(t *testing.T, network, addr string) { + fd, err := net.Dial(network, "", addr); + if err != nil { + t.Errorf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err); + return + } + fetchGoogle(t, fd, network, addr); + fd.Close() +} + +func doDialTCP(t *testing.T, network, addr string) { + fd, err := net.DialTCP(network, "", addr); + if err != nil { + t.Errorf("net.DialTCP(%q, %q, %q) = _, %v", network, "", addr, err); + } else { + fetchGoogle(t, fd, network, addr); + } + fd.Close() +} + +var googleaddrs = []string { + "74.125.19.99:80", + "www.google.com:80", + "74.125.19.99:http", + "www.google.com:http", + "074.125.019.099:0080", + "[::ffff:74.125.19.99]:80", + "[::ffff:4a7d:1363]:80", + "[0:0:0:0:0000:ffff:74.125.19.99]:80", + "[0:0:0:0:000000:ffff:74.125.19.99]:80", + "[0:0:0:0:0:ffff::74.125.19.99]:80", + "[2001:4860:0:2001::68]:80" // ipv6.google.com; removed if ipv6 flag not set +} + +func TestDialGoogle(t *testing.T) { + // If no ipv6 tunnel, don't try the last address. + if !*ipv6 { + googleaddrs[len(googleaddrs)-1] = "" + } + + for i := 0; i < len(googleaddrs); i++ { + addr := googleaddrs[i]; + if addr == "" { + continue + } + t.Logf("-- %s --", addr); + doDial(t, "tcp", addr); + doDialTCP(t, "tcp", addr); + if addr[0] != '[' { + doDial(t, "tcp4", addr); + doDialTCP(t, "tcp4", addr); + + if !preferIPv4 { + // make sure preferIPv4 flag works. + preferIPv4 = true; + syscall.SocketDisableIPv6 = true; + doDial(t, "tcp4", addr); + doDialTCP(t, "tcp4", addr); + syscall.SocketDisableIPv6 = false; + preferIPv4 = false; + } + } + doDial(t, "tcp6", addr); + doDialTCP(t, "tcp6", addr) + } +} diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go new file mode 100644 index 000000000..cfd67eabe --- /dev/null +++ b/src/pkg/net/dnsclient.go @@ -0,0 +1,227 @@ +// Copyright 2009 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. + +// DNS client. +// Has to be linked into package net for Dial. + +// TODO(rsc): +// Check periodically whether /etc/resolv.conf has changed. +// Could potentially handle many outstanding lookups faster. +// Could have a small cache. +// Random UDP source port (net.Dial should do that for us). +// Random request IDs. +// More substantial error reporting. +// Remove use of fmt? + +package net + +import ( + "fmt"; + "io"; + "net"; + "once"; + "os"; + "strings"; +) + +// DNS errors returned by LookupHost. +type DNSError struct { + os.ErrorString +} +var ( + DNS_InternalError os.Error = &DNSError{"internal dns error"}; + DNS_MissingConfig os.Error = &DNSError{"no dns configuration"}; + DNS_No_Answer os.Error = &DNSError{"dns got no answer"}; + DNS_BadRequest os.Error = &DNSError{"malformed dns request"}; + DNS_BadReply os.Error = &DNSError{"malformed dns reply"}; + DNS_ServerFailure os.Error = &DNSError{"dns server failure"}; + DNS_NoServers os.Error = &DNSError{"no dns servers"}; + DNS_NameTooLong os.Error = &DNSError{"dns name too long"}; + DNS_RedirectLoop os.Error = &DNSError{"dns redirect loop"}; + DNS_NameNotFound os.Error = &DNSError{"dns name not found"}; +) + +// Send a request on the connection and hope for a reply. +// Up to cfg.attempts attempts. +func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error) { + if len(name) >= 256 { + return nil, DNS_NameTooLong + } + out := new(_DNS_Msg); + out.id = 0x1234; + out.question = []_DNS_Question{ + _DNS_Question{ name, _DNS_TypeA, _DNS_ClassINET } + }; + out.recursion_desired = true; + msg, ok := out.Pack(); + if !ok { + return nil, DNS_InternalError + } + + for attempt := 0; attempt < cfg.attempts; attempt++ { + n, err := c.Write(msg); + if err != nil { + return nil, err + } + + c.SetReadTimeout(1e9); // nanoseconds + + buf := make([]byte, 2000); // More than enough. + n, err = c.Read(buf); + if err == os.EAGAIN { + continue; + } + if err != nil { + return nil, err; + } + buf = buf[0:n]; + in := new(_DNS_Msg); + if !in.Unpack(buf) || in.id != out.id { + continue + } + return in, nil + } + return nil, DNS_No_Answer +} + + +// Find answer for name in dns message. +// On return, if err == nil, addrs != nil. +// TODO(rsc): Maybe return []IP instead? +func answer(name string, dns *_DNS_Msg) (addrs []string, err os.Error) { + addrs = make([]string, 0, len(dns.answer)); + + if dns.rcode == _DNS_RcodeNameError && dns.authoritative { + return nil, DNS_NameNotFound // authoritative "no such host" + } + if dns.rcode != _DNS_RcodeSuccess { + // None of the error codes make sense + // for the query we sent. If we didn't get + // a name error and we didn't get success, + // the server is behaving incorrectly. + return nil, DNS_ServerFailure + } + + // Look for the name. + // Presotto says it's okay to assume that servers listed in + // /etc/resolv.conf are recursive resolvers. + // We asked for recursion, so it should have included + // all the answers we need in this one packet. +Cname: + for cnameloop := 0; cnameloop < 10; cnameloop++ { + addrs = addrs[0:0]; + for i := 0; i < len(dns.answer); i++ { + rr := dns.answer[i]; + h := rr.Header(); + if h.class == _DNS_ClassINET && h.name == name { + switch h.rrtype { + case _DNS_TypeA: + n := len(addrs); + a := rr.(*_DNS_RR_A).a; + addrs = addrs[0:n+1]; + addrs[n] = fmt.Sprintf("%d.%d.%d.%d", (a>>24), (a>>16)&0xFF, (a>>8)&0xFF, a&0xFF); + case _DNS_TypeCNAME: + // redirect to cname + name = rr.(*_DNS_RR_CNAME).cname; + continue Cname + } + } + } + if len(addrs) == 0 { + return nil, DNS_NameNotFound + } + return addrs, nil + } + + // Too many redirects + return nil, DNS_RedirectLoop +} + +// Do a lookup for a single name, which must be rooted +// (otherwise answer will not find the answers). +func tryOneName(cfg *_DNS_Config, name string) (addrs []string, err os.Error) { + err = DNS_NoServers; + for i := 0; i < len(cfg.servers); i++ { + // Calling Dial here is scary -- we have to be sure + // not to dial a name that will require a DNS lookup, + // or Dial will call back here to translate it. + // The DNS config parser has already checked that + // all the cfg.servers[i] are IP addresses, which + // Dial will use without a DNS lookup. + c, cerr := Dial("udp", "", cfg.servers[i] + ":53"); + if cerr != nil { + err = cerr; + continue; + } + msg, merr := _Exchange(cfg, c, name); + c.Close(); + if merr != nil { + err = merr; + continue; + } + addrs, aerr := answer(name, msg); + if aerr != nil && aerr != DNS_NameNotFound { + err = aerr; + continue; + } + return addrs, aerr; + } + return; +} + +var cfg *_DNS_Config +var dnserr os.Error + +func loadConfig() { + cfg, dnserr = _DNS_ReadConfig(); +} + +// LookupHost looks up the host name using the local DNS resolver. +// It returns the canonical name for the host and an array of that +// host's addresses. +func LookupHost(name string) (cname string, addrs []string, err os.Error) +{ + // TODO(rsc): Pick out obvious non-DNS names to avoid + // sending stupid requests to the server? + + once.Do(loadConfig); + if dnserr != nil || cfg == nil { + // better error than file not found. + err = DNS_MissingConfig; + return; + } + + // If name is rooted (trailing dot) or has enough dots, + // try it by itself first. + rooted := len(name) > 0 && name[len(name)-1] == '.'; + if rooted || strings.Count(name, ".") >= cfg.ndots { + rname := name; + if !rooted { + rname += "."; + } + // Can try as ordinary name. + addrs, aerr := tryOneName(cfg, rname); + if aerr == nil { + return rname, addrs, nil; + } + err = aerr; + } + if rooted { + return + } + + // Otherwise, try suffixes. + for i := 0; i < len(cfg.search); i++ { + newname := name+"."+cfg.search[i]; + if newname[len(newname)-1] != '.' { + newname += "." + } + addrs, aerr := tryOneName(cfg, newname); + if aerr == nil { + return newname, addrs, nil; + } + err = aerr; + } + return +} diff --git a/src/pkg/net/dnsconfig.go b/src/pkg/net/dnsconfig.go new file mode 100644 index 000000000..e56d964f2 --- /dev/null +++ b/src/pkg/net/dnsconfig.go @@ -0,0 +1,113 @@ +// Copyright 2009 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. + +// Read system DNS config from /etc/resolv.conf + +package net + +import ( + "io"; + "net"; + "os"; + "strconv"; +) + +type _DNS_Config struct { + servers []string; // servers to use + search []string; // suffixes to append to local name + ndots int; // number of dots in name to trigger absolute lookup + timeout int; // seconds before giving up on packet + attempts int; // lost packets before giving up on server + rotate bool; // round robin among servers +} + +var _DNS_configError os.Error; + +// See resolv.conf(5) on a Linux machine. +// TODO(rsc): Supposed to call uname() and chop the beginning +// of the host name to get the default search domain. +// We assume it's in resolv.conf anyway. +func _DNS_ReadConfig() (*_DNS_Config, os.Error) { + // TODO(rsc): 6g won't let me say file, err := + var file *file; + var err os.Error; + file, err = open("/etc/resolv.conf"); + if err != nil { + return nil, err + } + conf := new(_DNS_Config); + conf.servers = make([]string, 3)[0:0]; // small, but the standard limit + conf.search = make([]string, 0); + conf.ndots = 1; + conf.timeout = 1; + conf.attempts = 1; + conf.rotate = false; + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + f := getFields(line); + if len(f) < 1 { + continue; + } + switch f[0] { + case "nameserver": // add one name server + a := conf.servers; + n := len(a); + if len(f) > 1 && n < cap(a) { + // One more check: make sure server name is + // just an IP address. Otherwise we need DNS + // to look it up. + name := f[1]; + if len(ParseIP(name)) != 0 { + a = a[0:n+1]; + a[n] = name; + conf.servers = a; + } + } + + case "domain": // set search path to just this domain + if len(f) > 1 { + conf.search = make([]string, 1); + conf.search[0] = f[1]; + } else { + conf.search = make([]string, 0) + } + + case "search": // set search path to given servers + conf.search = make([]string, len(f) - 1); + for i := 0; i < len(conf.search); i++ { + conf.search[i] = f[i+1]; + } + + case "options": // magic options + for i := 1; i < len(f); i++ { + s := f[i]; + switch { + case len(s) >= 6 && s[0:6] == "ndots:": + n, i, ok := dtoi(s, 6); + if n < 1 { + n = 1 + } + conf.ndots = n; + case len(s) >= 8 && s[0:8] == "timeout:": + n, i, ok := dtoi(s, 8); + if n < 1 { + n = 1 + } + conf.timeout = n; + case len(s) >= 8 && s[0:9] == "attempts:": + n, i, ok := dtoi(s, 9); + if n < 1 { + n = 1 + } + conf.attempts = n; + case s == "rotate": + conf.rotate = true; + } + } + } + } + file.close(); + + return conf, nil +} + diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go new file mode 100644 index 000000000..d7a467fc6 --- /dev/null +++ b/src/pkg/net/dnsmsg.go @@ -0,0 +1,679 @@ +// Copyright 2009 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. + +// DNS packet assembly. +// +// This is intended to support name resolution during net.Dial. +// It doesn't have to be blazing fast. +// +// Rather than write the usual handful of routines to pack and +// unpack every message that can appear on the wire, we use +// reflection to write a generic pack/unpack for structs and then +// use it. Thus, if in the future we need to define new message +// structs, no new pack/unpack/printing code needs to be written. +// +// The first half of this file defines the DNS message formats. +// The second half implements the conversion to and from wire format. +// A few of the structure elements have string tags to aid the +// generic pack/unpack routines. +// +// TODO(rsc) There are enough names defined in this file that they're all +// prefixed with _DNS_. Perhaps put this in its own package later. + +package net + +import ( + "fmt"; + "os"; + "reflect"; +) + +// Packet formats + +// Wire constants. +const ( + // valid _DNS_RR_Header.rrtype and _DNS_Question.qtype + _DNS_TypeA = 1; + _DNS_TypeNS = 2; + _DNS_TypeMD = 3; + _DNS_TypeMF = 4; + _DNS_TypeCNAME = 5; + _DNS_TypeSOA = 6; + _DNS_TypeMB = 7; + _DNS_TypeMG = 8; + _DNS_TypeMR = 9; + _DNS_TypeNULL = 10; + _DNS_TypeWKS = 11; + _DNS_TypePTR = 12; + _DNS_TypeHINFO = 13; + _DNS_TypeMINFO = 14; + _DNS_TypeMX = 15; + _DNS_TypeTXT = 16; + + // valid _DNS_Question.qtype only + _DNS_TypeAXFR = 252; + _DNS_TypeMAILB = 253; + _DNS_TypeMAILA = 254; + _DNS_TypeALL = 255; + + // valid _DNS_Question.qclass + _DNS_ClassINET = 1; + _DNS_ClassCSNET = 2; + _DNS_ClassCHAOS = 3; + _DNS_ClassHESIOD = 4; + _DNS_ClassANY = 255; + + // _DNS_Msg.rcode + _DNS_RcodeSuccess = 0; + _DNS_RcodeFormatError = 1; + _DNS_RcodeServerFailure = 2; + _DNS_RcodeNameError = 3; + _DNS_RcodeNotImplemented = 4; + _DNS_RcodeRefused = 5; +) + +// The wire format for the DNS packet header. +type __DNS_Header struct { + id uint16; + bits uint16; + qdcount, ancount, nscount, arcount uint16; +} + +const ( + // __DNS_Header.bits + _QR = 1<<15; // query/response (response=1) + _AA = 1<<10; // authoritative + _TC = 1<<9; // truncated + _RD = 1<<8; // recursion desired + _RA = 1<<7; // recursion available +) + +// DNS queries. +type _DNS_Question struct { + name string "domain-name"; // "domain-name" specifies encoding; see packers below + qtype uint16; + qclass uint16; +} + +// DNS responses (resource records). +// There are many types of messages, +// but they all share the same header. +type _DNS_RR_Header struct { + name string "domain-name"; + rrtype uint16; + class uint16; + ttl uint32; + rdlength uint16; // length of data after header +} + +func (h *_DNS_RR_Header) Header() *_DNS_RR_Header { + return h +} + +type _DNS_RR interface { + Header() *_DNS_RR_Header +} + + +// Specific DNS RR formats for each query type. + +type _DNS_RR_CNAME struct { + _DNS_RR_Header; + cname string "domain-name"; +} + +type _DNS_RR_HINFO struct { + _DNS_RR_Header; + cpu string; + os string; +} + +type _DNS_RR_MB struct { + _DNS_RR_Header; + mb string "domain-name"; +} + +type _DNS_RR_MG struct { + _DNS_RR_Header; + mg string "domain-name"; +} + +type _DNS_RR_MINFO struct { + _DNS_RR_Header; + rmail string "domain-name"; + email string "domain-name"; +} + +type _DNS_RR_MR struct { + _DNS_RR_Header; + mr string "domain-name"; +} + +type _DNS_RR_MX struct { + _DNS_RR_Header; + pref uint16; + mx string "domain-name"; +} + +type _DNS_RR_NS struct { + _DNS_RR_Header; + ns string "domain-name"; +} + +type _DNS_RR_PTR struct { + _DNS_RR_Header; + ptr string "domain-name"; +} + +type _DNS_RR_SOA struct { + _DNS_RR_Header; + ns string "domain-name"; + mbox string "domain-name"; + serial uint32; + refresh uint32; + retry uint32; + expire uint32; + minttl uint32; +} + +type _DNS_RR_TXT struct { + _DNS_RR_Header; + txt string; // not domain name +} + +type _DNS_RR_A struct { + _DNS_RR_Header; + a uint32 "ipv4"; +} + + +// Packing and unpacking. +// +// All the packers and unpackers take a (msg []byte, off int) +// and return (off1 int, ok bool). If they return ok==false, they +// also return off1==len(msg), so that the next unpacker will +// also fail. This lets us avoid checks of ok until the end of a +// packing sequence. + +// Map of constructors for each RR wire type. +var rr_mk = map[int] func()_DNS_RR { + _DNS_TypeCNAME: func() _DNS_RR { return new(_DNS_RR_CNAME) }, + _DNS_TypeHINFO: func() _DNS_RR { return new(_DNS_RR_HINFO) }, + _DNS_TypeMB: func() _DNS_RR { return new(_DNS_RR_MB) }, + _DNS_TypeMG: func() _DNS_RR { return new(_DNS_RR_MG) }, + _DNS_TypeMINFO: func() _DNS_RR { return new(_DNS_RR_MINFO) }, + _DNS_TypeMR: func() _DNS_RR { return new(_DNS_RR_MR) }, + _DNS_TypeMX: func() _DNS_RR { return new(_DNS_RR_MX) }, + _DNS_TypeNS: func() _DNS_RR { return new(_DNS_RR_NS) }, + _DNS_TypePTR: func() _DNS_RR { return new(_DNS_RR_PTR) }, + _DNS_TypeSOA: func() _DNS_RR { return new(_DNS_RR_SOA) }, + _DNS_TypeTXT: func() _DNS_RR { return new(_DNS_RR_TXT) }, + _DNS_TypeA: func() _DNS_RR { return new(_DNS_RR_A) }, +} + +// Pack a domain name s into msg[off:]. +// Domain names are a sequence of counted strings +// split at the dots. They end with a zero-length string. +func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) { + // Add trailing dot to canonicalize name. + if n := len(s); n == 0 || s[n-1] != '.' { + s += "."; + } + + // Each dot ends a segment of the name. + // We trade each dot byte for a length byte. + // There is also a trailing zero. + // Check that we have all the space we need. + tot := len(s) + 1; + if off+tot > len(msg) { + return len(msg), false + } + + // Emit sequence of counted strings, chopping at dots. + begin := 0; + for i := 0; i < len(s); i++ { + if s[i] == '.' { + if i - begin >= 1<<6 { // top two bits of length must be clear + return len(msg), false + } + msg[off] = byte(i - begin); + off++; + for j := begin; j < i; j++ { + msg[off] = s[j]; + off++; + } + begin = i+1; + } + } + msg[off] = 0; + off++; + return off, true +} + +// Unpack a domain name. +// In addition to the simple sequences of counted strings above, +// domain names are allowed to refer to strings elsewhere in the +// packet, to avoid repeating common suffixes when returning +// many entries in a single domain. The pointers are marked +// by a length byte with the top two bits set. Ignoring those +// two bits, that byte and the next give a 14 bit offset from msg[0] +// where we should pick up the trail. +// Note that if we jump elsewhere in the packet, +// we return off1 == the offset after the first pointer we found, +// which is where the next record will start. +// In theory, the pointers are only allowed to jump backward. +// We let them jump anywhere and stop jumping after a while. +func unpackDomainName(msg []byte, off int) (s string, off1 int, ok bool) { + s = ""; + ptr := 0; // number of pointers followed +Loop: + for { + if off >= len(msg) { + return "", len(msg), false + } + c := int(msg[off]); + off++; + switch c&0xC0 { + case 0x00: + if c == 0x00 { + // end of name + break Loop + } + // literal string + if off+c > len(msg) { + return "", len(msg), false + } + s += string(msg[off:off+c]) + "."; + off += c; + case 0xC0: + // pointer to somewhere else in msg. + // remember location after first ptr, + // since that's how many bytes we consumed. + // also, don't follow too many pointers -- + // maybe there's a loop. + if off >= len(msg) { + return "", len(msg), false + } + c1 := msg[off]; + off++; + if ptr == 0 { + off1 = off + } + if ptr++; ptr > 10 { + return "", len(msg), false + } + off = (c^0xC0)<<8 | int(c1); + default: + // 0x80 and 0x40 are reserved + return "", len(msg), false + } + } + if ptr == 0 { + off1 = off + } + return s, off1, true +} + +// Pack a reflect.StructValue into msg. Struct members can only be uint16, uint32, string, +// and other (often anonymous) structs. +func packStructValue(val reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.Len(); i++ { + fld := val.Field(i); + name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i); + switch fld.Kind() { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", fld.Type()); + return len(msg), false; + case reflect.StructKind: + off, ok = packStructValue(fld.(reflect.StructValue), msg, off); + case reflect.Uint16Kind: + i := fld.(reflect.Uint16Value).Get(); + if off+2 > len(msg) { + return len(msg), false + } + msg[off] = byte(i>>8); + msg[off+1] = byte(i); + off += 2; + case reflect.Uint32Kind: + i := fld.(reflect.Uint32Value).Get(); + if off+4 > len(msg) { + return len(msg), false + } + msg[off] = byte(i>>24); + msg[off+1] = byte(i>>16); + msg[off+2] = byte(i>>8); + msg[off+4] = byte(i); + off += 4; + case reflect.StringKind: + // There are multiple string encodings. + // The tag distinguishes ordinary strings from domain names. + s := fld.(reflect.StringValue).Get(); + switch tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", tag); + return len(msg), false; + case "domain-name": + off, ok = packDomainName(s, msg, off); + if !ok { + return len(msg), false + } + case "": + // Counted string: 1 byte length. + if len(s) > 255 || off + 1 + len(s) > len(msg) { + return len(msg), false + } + msg[off] = byte(len(s)); + off++; + for i := 0; i < len(s); i++ { + msg[off+i] = s[i]; + } + off += len(s); + } + } + } + return off, true +} + +func packStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue); + off, ok = packStructValue(val, msg, off); + return off, ok +} + +// Unpack a reflect.StructValue from msg. +// Same restrictions as packStructValue. +func unpackStructValue(val reflect.StructValue, msg []byte, off int) (off1 int, ok bool) { + for i := 0; i < val.Len(); i++ { + name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i); + fld := val.Field(i); + switch fld.Kind() { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", fld.Type()); + return len(msg), false; + case reflect.StructKind: + off, ok = unpackStructValue(fld.(reflect.StructValue), msg, off); + case reflect.Uint16Kind: + if off+2 > len(msg) { + return len(msg), false + } + i := uint16(msg[off])<<8 | uint16(msg[off+1]); + fld.(reflect.Uint16Value).Set(i); + off += 2; + case reflect.Uint32Kind: + if off+4 > len(msg) { + return len(msg), false + } + i := uint32(msg[off])<<24 | uint32(msg[off+1])<<16 | uint32(msg[off+2])<<8 | uint32(msg[off+3]); + fld.(reflect.Uint32Value).Set(i); + off += 4; + case reflect.StringKind: + var s string; + switch tag { + default: + fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", tag); + return len(msg), false; + case "domain-name": + s, off, ok = unpackDomainName(msg, off); + if !ok { + return len(msg), false + } + case "": + if off >= len(msg) || off+1+int(msg[off]) > len(msg) { + return len(msg), false + } + n := int(msg[off]); + off++; + b := make([]byte, n); + for i := 0; i < n; i++ { + b[i] = msg[off+i]; + } + off += n; + s = string(b); + } + fld.(reflect.StringValue).Set(s); + } + } + return off, true +} + +func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { + val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue); + off, ok = unpackStructValue(val, msg, off); + return off, ok +} + +// Generic struct printer. +// Doesn't care about the string tag "domain-name", +// but does look for an "ipv4" tag on uint32 variables, +// printing them as IP addresses. +func printStructValue(val reflect.StructValue) string { + s := "{"; + for i := 0; i < val.Len(); i++ { + if i > 0 { + s += ", "; + } + name, typ, tag, xxx := val.Type().(reflect.StructType).Field(i); + fld := val.Field(i); + if name != "" && name != "?" { // BUG? Shouldn't the reflect library hide "?" ? + s += name + "="; + } + kind := fld.Kind(); + switch { + case kind == reflect.StructKind: + s += printStructValue(fld.(reflect.StructValue)); + case kind == reflect.Uint32Kind && tag == "ipv4": + i := fld.(reflect.Uint32Value).Get(); + s += fmt.Sprintf("%d.%d.%d.%d", (i>>24)&0xFF, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF); + default: + s += fmt.Sprint(fld.Interface()) + } + } + s += "}"; + return s; +} + +func printStruct(any interface{}) string { + val := reflect.NewValue(any).(reflect.PtrValue).Sub().(reflect.StructValue); + s := printStructValue(val); + return s +} + +// Resource record packer. +func packRR(rr _DNS_RR, msg []byte, off int) (off2 int, ok bool) { + var off1 int; + // pack twice, once to find end of header + // and again to find end of packet. + // a bit inefficient but this doesn't need to be fast. + // off1 is end of header + // off2 is end of rr + off1, ok = packStruct(rr.Header(), msg, off); + off2, ok = packStruct(rr, msg, off); + if !ok { + return len(msg), false + } + // pack a third time; redo header with correct data length + rr.Header().rdlength = uint16(off2 - off1); + packStruct(rr.Header(), msg, off); + return off2, true +} + +// Resource record unpacker. +func unpackRR(msg []byte, off int) (rr _DNS_RR, off1 int, ok bool) { + // unpack just the header, to find the rr type and length + var h _DNS_RR_Header; + off0 := off; + if off, ok = unpackStruct(&h, msg, off); !ok { + return nil, len(msg), false + } + end := off+int(h.rdlength); + + // make an rr of that type and re-unpack. + // again inefficient but doesn't need to be fast. + mk, known := rr_mk[int(h.rrtype)]; + if !known { + return &h, end, true + } + rr = mk(); + off, ok = unpackStruct(rr, msg, off0); + if off != end { + return &h, end, true + } + return rr, off, ok +} + +// Usable representation of a DNS packet. + +// A manually-unpacked version of (id, bits). +// This is in its own struct for easy printing. +type __DNS_Msg_Top struct { + id uint16; + response bool; + opcode int; + authoritative bool; + truncated bool; + recursion_desired bool; + recursion_available bool; + rcode int; +} + +type _DNS_Msg struct { + __DNS_Msg_Top; + question []_DNS_Question; + answer []_DNS_RR; + ns []_DNS_RR; + extra []_DNS_RR; +} + + +func (dns *_DNS_Msg) Pack() (msg []byte, ok bool) { + var dh __DNS_Header; + + // Convert convenient _DNS_Msg into wire-like __DNS_Header. + dh.id = dns.id; + dh.bits = uint16(dns.opcode)<<11 | uint16(dns.rcode); + if dns.recursion_available { + dh.bits |= _RA; + } + if dns.recursion_desired { + dh.bits |= _RD; + } + if dns.truncated { + dh.bits |= _TC; + } + if dns.authoritative { + dh.bits |= _AA; + } + if dns.response { + dh.bits |= _QR; + } + + // Prepare variable sized arrays. + question := dns.question; + answer := dns.answer; + ns := dns.ns; + extra := dns.extra; + + dh.qdcount = uint16(len(question)); + dh.ancount = uint16(len(answer)); + dh.nscount = uint16(len(ns)); + dh.arcount = uint16(len(extra)); + + // Could work harder to calculate message size, + // but this is far more than we need and not + // big enough to hurt the allocator. + msg = make([]byte, 2000); + + // Pack it in: header and then the pieces. + off := 0; + off, ok = packStruct(&dh, msg, off); + for i := 0; i < len(question); i++ { + off, ok = packStruct(&question[i], msg, off); + } + for i := 0; i < len(answer); i++ { + off, ok = packStruct(answer[i], msg, off); + } + for i := 0; i < len(ns); i++ { + off, ok = packStruct(ns[i], msg, off); + } + for i := 0; i < len(extra); i++ { + off, ok = packStruct(extra[i], msg, off); + } + if !ok { + return nil, false + } + return msg[0:off], true +} + +func (dns *_DNS_Msg) Unpack(msg []byte) bool { + // Header. + var dh __DNS_Header; + off := 0; + var ok bool; + if off, ok = unpackStruct(&dh, msg, off); !ok { + return false + } + dns.id = dh.id; + dns.response = (dh.bits & _QR) != 0; + dns.opcode = int(dh.bits >> 11) & 0xF; + dns.authoritative = (dh.bits & _AA) != 0; + dns.truncated = (dh.bits & _TC) != 0; + dns.recursion_desired = (dh.bits & _RD) != 0; + dns.recursion_available = (dh.bits & _RA) != 0; + dns.rcode = int(dh.bits & 0xF); + + // Arrays. + dns.question = make([]_DNS_Question, dh.qdcount); + dns.answer = make([]_DNS_RR, dh.ancount); + dns.ns = make([]_DNS_RR, dh.nscount); + dns.extra = make([]_DNS_RR, dh.arcount); + + for i := 0; i < len(dns.question); i++ { + off, ok = unpackStruct(&dns.question[i], msg, off); + } + for i := 0; i < len(dns.answer); i++ { + dns.answer[i], off, ok = unpackRR(msg, off); + } + for i := 0; i < len(dns.ns); i++ { + dns.ns[i], off, ok = unpackRR(msg, off); + } + for i := 0; i < len(dns.extra); i++ { + dns.extra[i], off, ok = unpackRR(msg, off); + } + if !ok { + return false + } +// if off != len(msg) { +// println("extra bytes in dns packet", off, "<", len(msg)); +// } + return true +} + +func (dns *_DNS_Msg) String() string { + s := "DNS: "+printStruct(&dns.__DNS_Msg_Top)+"\n"; + if len(dns.question) > 0 { + s += "-- Questions\n"; + for i := 0; i < len(dns.question); i++ { + s += printStruct(&dns.question[i])+"\n"; + } + } + if len(dns.answer) > 0 { + s += "-- Answers\n"; + for i := 0; i < len(dns.answer); i++ { + s += printStruct(dns.answer[i])+"\n"; + } + } + if len(dns.ns) > 0 { + s += "-- Name servers\n"; + for i := 0; i < len(dns.ns); i++ { + s += printStruct(dns.ns[i])+"\n"; + } + } + if len(dns.extra) > 0 { + s += "-- Extra\n"; + for i := 0; i < len(dns.extra); i++ { + s += printStruct(dns.extra[i])+"\n"; + } + } + return s; +} diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go new file mode 100644 index 000000000..9404ed0bd --- /dev/null +++ b/src/pkg/net/fd.go @@ -0,0 +1,429 @@ +// Copyright 2009 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. + +// TODO(rsc): All the prints in this file should go to standard error. + +package net + +import ( + "net"; + "once"; + "os"; + "sync"; + "syscall"; +) + +// Network file descriptor. +type netFD struct { + // immutable until Close + fd int; + file *os.File; + cr chan *netFD; + cw chan *netFD; + net string; + laddr string; + raddr string; + + // owned by client + rdeadline_delta int64; + rdeadline int64; + rio sync.Mutex; + wdeadline_delta int64; + wdeadline int64; + wio sync.Mutex; + + // owned by fd wait server + ncr, ncw int; +} + +// A pollServer helps FDs determine when to retry a non-blocking +// read or write after they get EAGAIN. When an FD needs to wait, +// send the fd on s.cr (for a read) or s.cw (for a write) to pass the +// request to the poll server. Then receive on fd.cr/fd.cw. +// When the pollServer finds that i/o on FD should be possible +// again, it will send fd on fd.cr/fd.cw to wake any waiting processes. +// This protocol is implemented as s.WaitRead() and s.WaitWrite(). +// +// There is one subtlety: when sending on s.cr/s.cw, the +// poll server is probably in a system call, waiting for an fd +// to become ready. It's not looking at the request channels. +// To resolve this, the poll server waits not just on the FDs it has +// been given but also its own pipe. After sending on the +// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a +// byte to the pipe, causing the pollServer's poll system call to +// return. In response to the pipe being readable, the pollServer +// re-polls its request channels. +// +// Note that the ordering is "send request" and then "wake up server". +// If the operations were reversed, there would be a race: the poll +// server might wake up and look at the request channel, see that it +// was empty, and go back to sleep, all before the requester managed +// to send the request. Because the send must complete before the wakeup, +// the request channel must be buffered. A buffer of size 1 is sufficient +// for any request load. If many processes are trying to submit requests, +// one will succeed, the pollServer will read the request, and then the +// channel will be empty for the next process's request. A larger buffer +// might help batch requests. + +type pollServer struct { + cr, cw chan *netFD; // buffered >= 1 + pr, pw *os.File; + pending map[int] *netFD; + poll *pollster; // low-level OS hooks + deadline int64; // next deadline (nsec since 1970) +} +func (s *pollServer) Run(); + +func newPollServer() (s *pollServer, err os.Error) { + s = new(pollServer); + s.cr = make(chan *netFD, 1); + s.cw = make(chan *netFD, 1); + if s.pr, s.pw, err = os.Pipe(); err != nil { + return nil, err + } + var e int; + if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 { + Errno: + err = os.ErrnoToError(e); + Error: + s.pr.Close(); + s.pw.Close(); + return nil, err; + } + if e = syscall.SetNonblock(s.pw.Fd(), true); e != 0 { + goto Errno; + } + if s.poll, err = newpollster(); err != nil { + goto Error; + } + if err = s.poll.AddFD(s.pr.Fd(), 'r', true); err != nil { + s.poll.Close(); + goto Error + } + s.pending = make(map[int] *netFD); + go s.Run(); + return s, nil +} + +func (s *pollServer) AddFD(fd *netFD, mode int) { + // TODO(rsc): This check handles a race between + // one goroutine reading and another one closing, + // but it doesn't solve the race completely: + // it still could happen that one goroutine closes + // but we read fd.fd before it does, and then + // another goroutine creates a new open file with + // that fd, which we'd now be referring to. + // The fix is probably to send the Close call + // through the poll server too, except that + // not all Reads and Writes go through the poll + // server even now. + intfd := fd.fd; + if intfd < 0 { + // fd closed underfoot + if mode == 'r' { + fd.cr <- fd + } else { + fd.cw <- fd + } + return + } + if err := s.poll.AddFD(intfd, mode, false); err != nil { + panicln("pollServer AddFD ", intfd, ": ", err.String(), "\n"); + return + } + + var t int64; + key := intfd << 1; + if mode == 'r' { + fd.ncr++; + t = fd.rdeadline; + } else { + fd.ncw++; + key++; + t = fd.wdeadline; + } + s.pending[key] = fd; + if t > 0 && (s.deadline == 0 || t < s.deadline) { + s.deadline = t; + } +} + +func (s *pollServer) LookupFD(fd int, mode int) *netFD { + key := fd << 1; + if mode == 'w' { + key++; + } + netfd, ok := s.pending[key]; + if !ok { + return nil + } + s.pending[key] = nil, false; + return netfd +} + +func (s *pollServer) WakeFD(fd *netFD, mode int) { + if mode == 'r' { + for fd.ncr > 0 { + fd.ncr--; + fd.cr <- fd + } + } else { + for fd.ncw > 0 { + fd.ncw--; + fd.cw <- fd + } + } +} + +func (s *pollServer) Now() int64 { + sec, nsec, err := os.Time(); + if err != nil { + panic("net: os.Time: ", err.String()); + } + nsec += sec * 1e9; + return nsec; +} + +func (s *pollServer) CheckDeadlines() { + now := s.Now(); + // TODO(rsc): This will need to be handled more efficiently, + // probably with a heap indexed by wakeup time. + + var next_deadline int64; + for key, fd := range s.pending { + var t int64; + var mode int; + if key&1 == 0 { + mode = 'r'; + } else { + mode = 'w'; + } + if mode == 'r' { + t = fd.rdeadline; + } else { + t = fd.wdeadline; + } + if t > 0 { + if t <= now { + s.pending[key] = nil, false; + if mode == 'r' { + s.poll.DelFD(fd.fd, mode); + fd.rdeadline = -1; + } else { + s.poll.DelFD(fd.fd, mode); + fd.wdeadline = -1; + } + s.WakeFD(fd, mode); + } else if next_deadline == 0 || t < next_deadline { + next_deadline = t; + } + } + } + s.deadline = next_deadline; +} + +func (s *pollServer) Run() { + var scratch [100]byte; + for { + var t = s.deadline; + if t > 0 { + t = t - s.Now(); + if t < 0 { + s.CheckDeadlines(); + continue; + } + } + fd, mode, err := s.poll.WaitFD(t); + if err != nil { + print("pollServer WaitFD: ", err.String(), "\n"); + return + } + if fd < 0 { + // Timeout happened. + s.CheckDeadlines(); + continue; + } + if fd == s.pr.Fd() { + // Drain our wakeup pipe. + for nn, e := s.pr.Read(&scratch); nn > 0; { + nn, e = s.pr.Read(&scratch) + } + + // Read from channels + for fd, ok := <-s.cr; ok; fd, ok = <-s.cr { + s.AddFD(fd, 'r') + } + for fd, ok := <-s.cw; ok; fd, ok = <-s.cw { + s.AddFD(fd, 'w') + } + } else { + netfd := s.LookupFD(fd, mode); + if netfd == nil { + print("pollServer: unexpected wakeup for fd=", netfd, " mode=", string(mode), "\n"); + continue + } + s.WakeFD(netfd, mode); + } + } +} + +var wakeupbuf [1]byte; +func (s *pollServer) Wakeup() { + s.pw.Write(&wakeupbuf) +} + +func (s *pollServer) WaitRead(fd *netFD) { + s.cr <- fd; + s.Wakeup(); + <-fd.cr +} + +func (s *pollServer) WaitWrite(fd *netFD) { + s.cw <- fd; + s.Wakeup(); + <-fd.cw +} + + +// Network FD methods. +// All the network FDs use a single pollServer. + +var pollserver *pollServer + +func _StartServer() { + p, err := newPollServer(); + if err != nil { + print("Start pollServer: ", err.String(), "\n") + } + pollserver = p +} + +func newFD(fd int, net, laddr, raddr string) (f *netFD, err os.Error) { + if pollserver == nil { + once.Do(_StartServer); + } + if e := syscall.SetNonblock(fd, true); e != 0 { + return nil, os.ErrnoToError(e); + } + f = new(netFD); + f.fd = fd; + f.net = net; + f.laddr = laddr; + f.raddr = raddr; + f.file = os.NewFile(fd, "net: " + net + " " + laddr + " " + raddr); + f.cr = make(chan *netFD, 1); + f.cw = make(chan *netFD, 1); + return f, nil +} + +func (fd *netFD) Close() os.Error { + if fd == nil || fd.file == nil { + return os.EINVAL + } + + // In case the user has set linger, + // switch to blocking mode so the close blocks. + // As long as this doesn't happen often, + // we can handle the extra OS processes. + // Otherwise we'll need to use the pollserver + // for Close too. Sigh. + syscall.SetNonblock(fd.file.Fd(), false); + + e := fd.file.Close(); + fd.file = nil; + fd.fd = -1; + return e +} + +func (fd *netFD) Read(p []byte) (n int, err os.Error) { + if fd == nil || fd.file == nil { + return -1, os.EINVAL + } + fd.rio.Lock(); + defer fd.rio.Unlock(); + if fd.rdeadline_delta > 0 { + fd.rdeadline = pollserver.Now() + fd.rdeadline_delta; + } else { + fd.rdeadline = 0; + } + n, err = fd.file.Read(p); + for err == os.EAGAIN && fd.rdeadline >= 0 { + pollserver.WaitRead(fd); + n, err = fd.file.Read(p) + } + return n, err +} + +func (fd *netFD) Write(p []byte) (n int, err os.Error) { + if fd == nil || fd.file == nil { + return -1, os.EINVAL + } + fd.wio.Lock(); + defer fd.wio.Unlock(); + if fd.wdeadline_delta > 0 { + fd.wdeadline = pollserver.Now() + fd.wdeadline_delta; + } else { + fd.wdeadline = 0; + } + err = nil; + nn := 0; + for nn < len(p) { + n, err = fd.file.Write(p[nn:len(p)]); + if n > 0 { + nn += n + } + if nn == len(p) { + break; + } + if err == os.EAGAIN && fd.wdeadline >= 0 { + pollserver.WaitWrite(fd); + continue; + } + if n == 0 || err != nil { + break; + } + } + return nn, err +} + +func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) + +func (fd *netFD) accept() (nfd *netFD, err os.Error) { + if fd == nil || fd.file == nil { + return nil, os.EINVAL + } + + // See ../syscall/exec.go for description of ForkLock. + // It is okay to hold the lock across syscall.Accept + // because we have put fd.fd into non-blocking mode. + syscall.ForkLock.RLock(); + var s, e int; + var sa syscall.Sockaddr; + for { + s, sa, e = syscall.Accept(fd.fd); + if e != syscall.EAGAIN { + break; + } + syscall.ForkLock.RUnlock(); + pollserver.WaitRead(fd); + syscall.ForkLock.RLock(); + } + if e != 0 { + syscall.ForkLock.RUnlock(); + return nil, os.ErrnoToError(e) + } + syscall.CloseOnExec(s); + syscall.ForkLock.RUnlock(); + + raddr, err1 := sockaddrToString(sa); + if err1 != nil { + raddr = "invalid-address"; + } + if nfd, err = newFD(s, fd.net, fd.laddr, raddr); err != nil { + syscall.Close(s); + return nil, err + } + return nfd, nil +} + diff --git a/src/pkg/net/fd_darwin.go b/src/pkg/net/fd_darwin.go new file mode 100644 index 000000000..42bf51221 --- /dev/null +++ b/src/pkg/net/fd_darwin.go @@ -0,0 +1,115 @@ +// Copyright 2009 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. + +// Waiting for FDs via kqueue/kevent. + +package net + +import ( + "net"; + "os"; + "syscall"; +) + +var kqueuePhaseError = &Error{"kqueue phase error"} + +type pollster struct { + kq int; + eventbuf [10]syscall.Kevent_t; + events []syscall.Kevent_t; +} + +func newpollster() (p *pollster, err os.Error) { + p = new(pollster); + var e int; + if p.kq, e = syscall.Kqueue(); e != 0 { + return nil, os.ErrnoToError(e) + } + p.events = p.eventbuf[0:0]; + return p, nil +} + +func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error { + var kmode int; + if mode == 'r' { + kmode = syscall.EVFILT_READ + } else { + kmode = syscall.EVFILT_WRITE + } + var events [1]syscall.Kevent_t; + ev := &events[0]; + // EV_ADD - add event to kqueue list + // EV_RECEIPT - generate fake EV_ERROR as result of add, + // rather than waiting for real event + // EV_ONESHOT - delete the event the first time it triggers + flags := syscall.EV_ADD | syscall.EV_RECEIPT; + if !repeat { + flags |= syscall.EV_ONESHOT + } + syscall.SetKevent(ev, fd, kmode, flags); + + n, e := syscall.Kevent(p.kq, &events, &events, nil); + if e != 0 { + return os.ErrnoToError(e) + } + if n != 1 || (ev.Flags & syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { + return kqueuePhaseError + } + if ev.Data != 0 { + return os.ErrnoToError(int(ev.Data)) + } + return nil +} + +func (p *pollster) DelFD(fd int, mode int) { + var kmode int; + if mode == 'r' { + kmode = syscall.EVFILT_READ + } else { + kmode = syscall.EVFILT_WRITE + } + var events [1]syscall.Kevent_t; + ev := &events[0]; + // EV_DELETE - delete event from kqueue list + // EV_RECEIPT - generate fake EV_ERROR as result of add, + // rather than waiting for real event + syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE | syscall.EV_RECEIPT); + syscall.Kevent(p.kq, &events, &events, nil); +} + +func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) { + var t *syscall.Timespec; + for len(p.events) == 0 { + if nsec > 0 { + if t == nil { + t = new(syscall.Timespec); + } + *t = syscall.NsecToTimespec(nsec); + } + nn, e := syscall.Kevent(p.kq, nil, &p.eventbuf, t); + if e != 0 { + if e == syscall.EINTR { + continue + } + return -1, 0, os.ErrnoToError(e) + } + if nn == 0 { + return -1, 0, nil; + } + p.events = p.eventbuf[0:nn] + } + ev := &p.events[0]; + p.events = p.events[1:len(p.events)]; + fd = int(ev.Ident); + if ev.Filter == syscall.EVFILT_READ { + mode = 'r' + } else { + mode = 'w' + } + return fd, mode, nil +} + +func (p *pollster) Close() os.Error { + return os.ErrnoToError(syscall.Close(p.kq)) +} diff --git a/src/pkg/net/fd_linux.go b/src/pkg/net/fd_linux.go new file mode 100644 index 000000000..bd822589e --- /dev/null +++ b/src/pkg/net/fd_linux.go @@ -0,0 +1,150 @@ +// Copyright 2009 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. + +// Waiting for FDs via epoll(7). + +package net + +import ( + "net"; + "os"; + "syscall"; +) + +const ( + readFlags = syscall.EPOLLIN | syscall.EPOLLRDHUP; + writeFlags = syscall.EPOLLOUT +) + +type pollster struct { + epfd int; + + // Events we're already waiting for + events map[int] uint32; +} + +func newpollster() (p *pollster, err os.Error) { + p = new(pollster); + var e int; + + // The arg to epoll_create is a hint to the kernel + // about the number of FDs we will care about. + // We don't know. + if p.epfd, e = syscall.EpollCreate(16); e != 0 { + return nil, os.ErrnoToError(e) + } + p.events = make(map[int] uint32); + return p, nil +} + +func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error { + var ev syscall.EpollEvent; + var already bool; + ev.Fd = int32(fd); + ev.Events, already = p.events[fd]; + if !repeat { + ev.Events |= syscall.EPOLLONESHOT; + } + if mode == 'r' { + ev.Events |= readFlags; + } else { + ev.Events |= writeFlags; + } + + var op int; + if already { + op = syscall.EPOLL_CTL_MOD; + } else { + op = syscall.EPOLL_CTL_ADD; + } + if e := syscall.EpollCtl(p.epfd, op, fd, &ev); e != 0 { + return os.ErrnoToError(e) + } + p.events[fd] = ev.Events; + return nil +} + +func (p *pollster) StopWaiting(fd int, bits uint) { + events, already := p.events[fd]; + if !already { + print("Epoll unexpected fd=", fd, "\n"); + return; + } + + // If syscall.EPOLLONESHOT is not set, the wait + // is a repeating wait, so don't change it. + if events & syscall.EPOLLONESHOT == 0 { + return; + } + + // Disable the given bits. + // If we're still waiting for other events, modify the fd + // event in the kernel. Otherwise, delete it. + events &= ^uint32(bits); + if int32(events) & ^syscall.EPOLLONESHOT != 0 { + var ev syscall.EpollEvent; + ev.Fd = int32(fd); + ev.Events = events; + if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 { + print("Epoll modify fd=", fd, ": ", os.ErrnoToError(e).String(), "\n"); + } + p.events[fd] = events; + } else { + if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 { + print("Epoll delete fd=", fd, ": ", os.ErrnoToError(e).String(), "\n"); + } + p.events[fd] = 0, false; + } +} + +func (p *pollster) DelFD(fd int, mode int) { + if mode == 'r' { + p.StopWaiting(fd, readFlags); + } else { + p.StopWaiting(fd, writeFlags); + } +} + +func (p *pollster) WaitFD(nsec int64) (fd int, mode int, err os.Error) { + // Get an event. + var evarray [1]syscall.EpollEvent; + ev := &evarray[0]; + var msec int = -1; + if nsec > 0 { + msec = int((nsec + 1e6 - 1)/1e6); + } + n, e := syscall.EpollWait(p.epfd, &evarray, msec); + for e == syscall.EAGAIN || e == syscall.EINTR { + n, e = syscall.EpollWait(p.epfd, &evarray, msec); + } + if e != 0 { + return -1, 0, os.ErrnoToError(e); + } + if n == 0 { + return -1, 0, nil; + } + fd = int(ev.Fd); + + if ev.Events & writeFlags != 0 { + p.StopWaiting(fd, writeFlags); + return fd, 'w', nil; + } + if ev.Events & readFlags != 0 { + p.StopWaiting(fd, readFlags); + return fd, 'r', nil; + } + + // Other events are error conditions - wake whoever is waiting. + events, already := p.events[fd]; + if events & writeFlags != 0 { + p.StopWaiting(fd, writeFlags); + return fd, 'w', nil; + } + p.StopWaiting(fd, readFlags); + return fd, 'r', nil; +} + +func (p *pollster) Close() os.Error { + return os.ErrnoToError(syscall.Close(p.epfd)); +} diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go new file mode 100644 index 000000000..774f048ca --- /dev/null +++ b/src/pkg/net/ip.go @@ -0,0 +1,421 @@ +// Copyright 2009 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. + +// IP address manipulations +// +// IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes. +// An IPv4 address can be converted to an IPv6 address by +// adding a canonical prefix (10 zeros, 2 0xFFs). +// This library accepts either size of byte array but always +// returns 16-byte addresses. + +package net + +import ( + "net" +) + +// IP address lengths (bytes). +const ( + IPv4len = 4; + IPv6len = 16 +) + +// An IP is a single IP address, an array of bytes. +// Functions in this package accept either 4-byte (IP v4) +// or 16-byte (IP v6) arrays as input. Unless otherwise +// specified, functions in this package always return +// IP addresses in 16-byte form using the canonical +// embedding. +// +// Note that in this documentation, referring to an +// IP address as an IPv4 address or an IPv6 address +// is a semantic property of the address, not just the +// length of the byte array: a 16-byte array can still +// be an IPv4 address. +type IP []byte; + +// An IP mask is an IP address. +type IPMask []byte; + +// IPv4 returns the IP address (in 16-byte form) of the +// IPv4 address a.b.c.d. +func IPv4(a, b, c, d byte) IP { + p := make(IP, IPv6len); + for i := 0; i < 10; i++ { + p[i] = 0 + } + p[10] = 0xff; + p[11] = 0xff; + p[12] = a; + p[13] = b; + p[14] = c; + p[15] = d; + return p +} + +// Well-known IPv4 addresses +var ( + IPv4bcast = IPv4(255, 255, 255, 255); // broadcast + IPv4allsys = IPv4(224, 0, 0, 1); // all systems + IPv4allrouter = IPv4(224, 0, 0, 2); // all routers + IPv4zero = IPv4(0, 0, 0, 0); // all zeros +) + +// Well-known IPv6 addresses +var ( + IPzero = make(IP, IPv6len); // all zeros +) + +// Is p all zeros? +func isZeros(p IP) bool { + for i := 0; i < len(p); i++ { + if p[i] != 0 { + return false + } + } + return true +} + +// To4 converts the IPv4 address ip to a 4-byte representation. +// If ip is not an IPv4 address, To4 returns nil. +func (ip IP) To4() IP { + if len(ip) == IPv4len { + return ip + } + if len(ip) == IPv6len + && isZeros(ip[0:10]) + && ip[10] == 0xff + && ip[11] == 0xff { + return ip[12:16] + } + return nil +} + +// To16 converts the IP address ip to a 16-byte representation. +// If ip is not an IP address (it is the wrong length), To16 returns nil. +func (ip IP) To16() IP { + if len(ip) == IPv4len { + return IPv4(ip[0], ip[1], ip[2], ip[3]) + } + if len(ip) == IPv6len { + return ip + } + return nil +} + +// Default route masks for IPv4. +var ( + classAMask = IPMask(IPv4(0xff, 0, 0, 0)); + classBMask = IPMask(IPv4(0xff, 0xff, 0, 0)); + classCMask = IPMask(IPv4(0xff, 0xff, 0xff, 0)); +) + +// DefaultMask returns the default IP mask for the IP address ip. +// Only IPv4 addresses have default masks; DefaultMask returns +// nil if ip is not a valid IPv4 address. +func (ip IP) DefaultMask() IPMask { + if ip = ip.To4(); ip == nil { + return nil + } + switch true { + case ip[0] < 0x80: + return classAMask; + case ip[0] < 0xC0: + return classBMask; + default: + return classCMask; + } + return nil; // not reached +} + +// Mask returns the result of masking the IP address ip with mask. +func (ip IP) Mask(mask IPMask) IP { + n := len(ip); + if n != len(mask) { + return nil + } + out := make(IP, n); + for i := 0; i < n; i++ { + out[i] = ip[i] & mask[i]; + } + return out +} + +// Convert i to decimal string. +func itod(i uint) string { + if i == 0 { + return "0" + } + + // Assemble decimal in reverse order. + var b [32]byte; + bp := len(b); + for ; i > 0; i /= 10 { + bp--; + b[bp] = byte(i%10) + '0' + } + + return string(b[bp:len(b)]) +} + +// Convert i to hexadecimal string. +func itox(i uint) string { + if i == 0 { + return "0" + } + + // Assemble hexadecimal in reverse order. + var b [32]byte; + bp := len(b); + for ; i > 0; i /= 16 { + bp--; + b[bp] = "0123456789abcdef"[byte(i%16)] + } + + return string(b[bp:len(b)]) +} + +// String returns the string form of the IP address ip. +// If the address is an IPv4 address, the string representation +// is dotted decimal ("74.125.19.99"). Otherwise the representation +// is IPv6 ("2001:4860:0:2001::68"). +func (ip IP) String() string { + p := ip; + + // If IPv4, use dotted notation. + if p4 := p.To4(); len(p4) == 4 { + return itod(uint(p4[0]))+"." + +itod(uint(p4[1]))+"." + +itod(uint(p4[2]))+"." + +itod(uint(p4[3])) + } + if len(p) != IPv6len { + return "?" + } + + // Find longest run of zeros. + e0 := -1; + e1 := -1; + for i := 0; i < 16; i+=2 { + j := i; + for j < 16 && p[j] == 0 && p[j+1] == 0 { + j += 2 + } + if j > i && j - i > e1 - e0 { + e0 = i; + e1 = j + } + } + + // Print with possible :: in place of run of zeros + var s string; + for i := 0; i < 16; i += 2 { + if i == e0 { + s += "::"; + i = e1; + if i >= 16 { + break + } + } else if i > 0 { + s += ":" + } + s += itox((uint(p[i])<<8) | uint(p[i+1])) + } + return s +} + +// If mask is a sequence of 1 bits followed by 0 bits, +// return the number of 1 bits. +func simpleMaskLength(mask IPMask) int { + var i int; + for i = 0; i < len(mask); i++ { + if mask[i] != 0xFF { + break + } + } + n := 8*i; + v := mask[i]; + for v & 0x80 != 0 { + n++; + v <<= 1 + } + if v != 0 { + return -1 + } + for i++; i < len(mask); i++ { + if mask[i] != 0 { + return -1 + } + } + return n +} + +// String returns the string representation of mask. +// If the mask is in the canonical form--ones followed by zeros--the +// string representation is just the decimal number of ones. +// If the mask is in a non-canonical form, it is formatted +// as an IP address. +func (mask IPMask) String() string { + switch len(mask) { + case 4: + n := simpleMaskLength(mask); + if n >= 0 { + return itod(uint(n+(IPv6len-IPv4len)*8)) + } + case 16: + n := simpleMaskLength(mask); + if n >= 0 { + return itod(uint(n)) + } + } + return IP(mask).String(); +} + +// Parse IPv4 address (d.d.d.d). +func parseIPv4(s string) IP { + var p [IPv4len]byte; + i := 0; + for j := 0; j < IPv4len; j++ { + if j > 0 { + if s[i] != '.' { + return nil + } + i++; + } + var ( + n int; + ok bool + ) + n, i, ok = dtoi(s, i); + if !ok || n > 0xFF { + return nil + } + p[j] = byte(n) + } + if i != len(s) { + return nil + } + return IPv4(p[0], p[1], p[2], p[3]) +} + +// Parse IPv6 address. Many forms. +// The basic form is a sequence of eight colon-separated +// 16-bit hex numbers separated by colons, +// as in 0123:4567:89ab:cdef:0123:4567:89ab:cdef. +// Two exceptions: +// * A run of zeros can be replaced with "::". +// * The last 32 bits can be in IPv4 form. +// Thus, ::ffff:1.2.3.4 is the IPv4 address 1.2.3.4. +func parseIPv6(s string) IP { + p := make(IP, 16); + ellipsis := -1; // position of ellipsis in p + i := 0; // index in string s + + // Might have leading ellipsis + if len(s) >= 2 && s[0] == ':' && s[1] == ':' { + ellipsis = 0; + i = 2; + // Might be only ellipsis + if i == len(s) { + return p + } + } + + // Loop, parsing hex numbers followed by colon. + j := 0; +L: for j < IPv6len { + // Hex number. + n, i1, ok := xtoi(s, i); + if !ok || n > 0xFFFF { + return nil + } + + // If followed by dot, might be in trailing IPv4. + if i1 < len(s) && s[i1] == '.' { + if ellipsis < 0 && j != IPv6len - IPv4len { + // Not the right place. + return nil + } + if j+IPv4len > IPv6len { + // Not enough room. + return nil + } + p4 := parseIPv4(s[i:len(s)]); + if p4 == nil { + return nil + } + // BUG: p[j:j+4] = p4 + p[j] = p4[12]; + p[j+1] = p4[13]; + p[j+2] = p4[14]; + p[j+3] = p4[15]; + i = len(s); + j += 4; + break + } + + // Save this 16-bit chunk. + p[j] = byte(n>>8); + p[j+1] = byte(n); + j += 2; + + // Stop at end of string. + i = i1; + if i == len(s) { + break + } + + // Otherwise must be followed by colon and more. + if s[i] != ':' && i+1 == len(s) { + return nil + } + i++; + + // Look for ellipsis. + if s[i] == ':' { + if ellipsis >= 0 { // already have one + return nil + } + ellipsis = j; + if i++; i == len(s) { // can be at end + break + } + } + } + + // Must have used entire string. + if i != len(s) { + return nil + } + + // If didn't parse enough, expand ellipsis. + if j < IPv6len { + if ellipsis < 0 { + return nil + } + n := IPv6len - j; + for k := j-1; k >= ellipsis; k-- { + p[k+n] = p[k] + } + for k := ellipsis+n-1; k>=ellipsis; k-- { + p[k] = 0 + } + } + return p +} + +// ParseIP parses s as an IP address, returning the result. +// The string s can be in dotted decimal ("74.125.19.99") +// or IPv6 ("2001:4860:0:2001::68") form. +// If s is not a valid textual representation of an IP address, +// ParseIP returns nil. +func ParseIP(s string) IP { + p := parseIPv4(s); + if p != nil { + return p + } + return parseIPv6(s) +} + diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go new file mode 100644 index 000000000..fb2ae8216 --- /dev/null +++ b/src/pkg/net/ip_test.go @@ -0,0 +1,50 @@ +// Copyright 2009 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 net + +import ( + "net"; + "testing" +) + +func isEqual(a, b IP) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil || len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + return true +} + +type parseIPTest struct { + in string; + out IP; +} +var parseiptests = []parseIPTest{ + parseIPTest{"127.0.1.2", IPv4(127, 0, 1, 2)}, + parseIPTest{"127.0.0.1", IPv4(127, 0, 0, 1)}, + parseIPTest{"127.0.0.256", nil}, + parseIPTest{"abc", nil}, + parseIPTest{"::ffff:127.0.0.1", IPv4(127, 0, 0, 1)}, + parseIPTest{"2001:4860:0:2001::68", + IP{0x20,0x01, 0x48,0x60, 0,0, 0x20,0x01, + 0,0, 0,0, 0,0, 0x00,0x68}}, + parseIPTest{"::ffff:4a7d:1363", IPv4(74, 125, 19, 99)}, +} + +func TestParseIP(t *testing.T) { + for i := 0; i < len(parseiptests); i++ { + tt := parseiptests[i]; + if out := ParseIP(tt.in); !isEqual(out, tt.out) { + t.Errorf("ParseIP(%#q) = %v, want %v", tt.in, out, tt.out); + } + } +} diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go new file mode 100644 index 000000000..5c442e6a4 --- /dev/null +++ b/src/pkg/net/net.go @@ -0,0 +1,862 @@ +// Copyright 2009 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 net + +import ( + "net"; + "os"; + "strconv"; + "syscall"; +) + +var ( + BadAddress os.Error = &Error{"malformed address"}; + MissingAddress os.Error = &Error{"missing address"}; + UnknownNetwork os.Error = &Error{"unknown network"}; + UnknownHost os.Error = &Error{"unknown host"}; + UnknownSocketFamily os.Error = &Error{"unknown socket family"}; +) + + +// Conn is a generic network connection. +type Conn interface { + // Read blocks until data is ready from the connection + // and then reads into b. It returns the number + // of bytes read, or 0 if the connection has been closed. + Read(b []byte) (n int, err os.Error); + + // Write writes the data in b to the connection. + Write(b []byte) (n int, err os.Error); + + // Close closes the connection. + Close() os.Error; + + // For packet-based protocols such as UDP, + // ReadFrom reads the next packet from the network, + // returning the number of bytes read and the remote + // address that sent them. + ReadFrom(b []byte) (n int, addr string, err os.Error); + + // For packet-based protocols such as UDP, + // WriteTo writes the byte buffer b to the network + // as a single payload, sending it to the target address. + WriteTo(addr string, b []byte) (n int, err os.Error); + + // SetReadBuffer sets the size of the operating system's + // receive buffer associated with the connection. + SetReadBuffer(bytes int) os.Error; + + // SetReadBuffer sets the size of the operating system's + // transmit buffer associated with the connection. + SetWriteBuffer(bytes int) os.Error; + + // SetTimeout sets the read and write deadlines associated + // with the connection. + SetTimeout(nsec int64) os.Error; + + // SetReadTimeout sets the time (in nanoseconds) that + // Read will wait for data before returning os.EAGAIN. + // Setting nsec == 0 (the default) disables the deadline. + SetReadTimeout(nsec int64) os.Error; + + // SetWriteTimeout sets the time (in nanoseconds) that + // Write will wait to send its data before returning os.EAGAIN. + // Setting nsec == 0 (the default) disables the deadline. + // Even if write times out, it may return n > 0, indicating that + // some of the data was successfully written. + SetWriteTimeout(nsec int64) os.Error; + + // SetLinger sets the behavior of Close() on a connection + // which still has data waiting to be sent or to be acknowledged. + // + // If sec < 0 (the default), Close returns immediately and + // the operating system finishes sending the data in the background. + // + // If sec == 0, Close returns immediately and the operating system + // discards any unsent or unacknowledged data. + // + // If sec > 0, Close blocks for at most sec seconds waiting for + // data to be sent and acknowledged. + SetLinger(sec int) os.Error; + + // SetReuseAddr sets whether it is okay to reuse addresses + // from recent connections that were not properly closed. + SetReuseAddr(reuseaddr bool) os.Error; + + // SetDontRoute sets whether outgoing messages should + // bypass the system routing tables. + SetDontRoute(dontroute bool) os.Error; + + // SetKeepAlive sets whether the operating system should send + // keepalive messages on the connection. + SetKeepAlive(keepalive bool) os.Error; + + // BindToDevice binds a connection to a particular network device. + BindToDevice(dev string) os.Error; +} + +// Should we try to use the IPv4 socket interface if we're +// only dealing with IPv4 sockets? As long as the host system +// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 +// interface. That simplifies our code and is most general. +// Unfortunately, we need to run on kernels built without IPv6 support too. +// So probe the kernel to figure it out. +func kernelSupportsIPv6() bool { + fd, e := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP); + if fd >= 0 { + syscall.Close(fd) + } + return e == 0 +} + +var preferIPv4 = !kernelSupportsIPv6() + +// TODO(rsc): if syscall.OS == "linux", we're supposd to read +// /proc/sys/net/core/somaxconn, +// to take advantage of kernels that have raised the limit. +func listenBacklog() int { + return syscall.SOMAXCONN +} + +func LookupHost(name string) (cname string, addrs []string, err os.Error) + +// Split "host:port" into "host" and "port". +// Host cannot contain colons unless it is bracketed. +func splitHostPort(hostport string) (host, port string, err os.Error) { + // The port starts after the last colon. + var i int; + for i = len(hostport)-1; i >= 0; i-- { + if hostport[i] == ':' { + break + } + } + if i < 0 { + return "", "", BadAddress + } + + host = hostport[0:i]; + port = hostport[i+1:len(hostport)]; + + // Can put brackets around host ... + if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { + host = host[1:len(host)-1] + } else { + // ... but if there are no brackets, no colons. + if byteIndex(host, ':') >= 0 { + return "", "", BadAddress + } + } + return host, port, nil +} + +// Join "host" and "port" into "host:port". +// If host contains colons, will join into "[host]:port". +func joinHostPort(host, port string) string { + // If host has colons, have to bracket it. + if byteIndex(host, ':') >= 0 { + return "[" + host + "]:" + port + } + return host + ":" + port +} + +// Convert "host:port" into IP address and port. +// For now, host and port must be numeric literals. +// Eventually, we'll have name resolution. +func hostPortToIP(net, hostport, mode string) (ip IP, iport int, err os.Error) { + var host, port string; + host, port, err = splitHostPort(hostport); + if err != nil { + return nil, 0, err + } + + var addr IP; + if host == "" { + if mode == "listen" { + if preferIPv4 { + addr = IPv4zero; + } else { + addr = IPzero; // wildcard - listen to all + } + } else { + return nil, 0, MissingAddress; + } + } + + // Try as an IP address. + if addr == nil { + addr = ParseIP(host); + } + if addr == nil { + // Not an IP address. Try as a DNS name. + hostname, addrs, err := LookupHost(host); + if err != nil { + return nil, 0, err + } + if len(addrs) == 0 { + return nil, 0, UnknownHost + } + addr = ParseIP(addrs[0]); + if addr == nil { + // should not happen + return nil, 0, BadAddress + } + } + + p, i, ok := dtoi(port, 0); + if !ok || i != len(port) { + p, err = LookupPort(net, port); + if err != nil { + return nil, 0, err + } + } + if p < 0 || p > 0xFFFF { + return nil, 0, BadAddress + } + + return addr, p, nil +} + +func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) { + switch a := sa.(type) { + case *syscall.SockaddrInet4: + return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil; + case *syscall.SockaddrInet6: + return joinHostPort(IP(&a.Addr).String(), strconv.Itoa(a.Port)), nil; + case *syscall.SockaddrUnix: + return a.Name, nil; + } + return "", UnknownSocketFamily +} + +func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) { + switch family { + case syscall.AF_INET: + if ip = ip.To4(); ip == nil { + return nil, os.EINVAL + } + s := new(syscall.SockaddrInet4); + for i := 0; i < IPv4len; i++ { + s.Addr[i] = ip[i]; + } + s.Port = port; + return s, nil; + case syscall.AF_INET6: + // IPv4 callers use 0.0.0.0 to mean "announce on any available address". + // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", + // which it refuses to do. Rewrite to the IPv6 all zeros. + if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 { + ip = IPzero; + } + if ip = ip.To16(); ip == nil { + return nil, os.EINVAL + } + s := new(syscall.SockaddrInet6); + for i := 0; i < IPv6len; i++ { + s.Addr[i] = ip[i]; + } + s.Port = port; + return s, nil; + } + return nil, os.EINVAL; +} + +// Boolean to int. +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +// Generic socket creation. +func socket(net, laddr, raddr string, f, p, t int, la, ra syscall.Sockaddr) (fd *netFD, err os.Error) { + // See ../syscall/exec.go for description of ForkLock. + syscall.ForkLock.RLock(); + s, e := syscall.Socket(f, p, t); + if e != 0 { + syscall.ForkLock.RUnlock(); + return nil, os.ErrnoToError(e) + } + syscall.CloseOnExec(s); + syscall.ForkLock.RUnlock(); + + // Allow reuse of recently-used addresses. + syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); + + var r int64; + if la != nil { + e = syscall.Bind(s, la); + if e != 0 { + syscall.Close(s); + return nil, os.ErrnoToError(e) + } + } + + if ra != nil { + e = syscall.Connect(s, ra); + if e != 0 { + syscall.Close(s); + return nil, os.ErrnoToError(e) + } + } + + fd, err = newFD(s, net, laddr, raddr); + if err != nil { + syscall.Close(s); + return nil, err + } + + return fd, nil +} + + +// Generic implementation of Conn interface; not exported. +type connBase struct { + fd *netFD; + raddr string; +} + +func (c *connBase) File() *os.File { + if c == nil { + return nil + } + return c.fd.file; +} + +func (c *connBase) sysFD() int { + if c == nil || c.fd == nil { + return -1; + } + return c.fd.fd; +} + +func (c *connBase) Read(b []byte) (n int, err os.Error) { + n, err = c.fd.Read(b); + return n, err +} + +func (c *connBase) Write(b []byte) (n int, err os.Error) { + n, err = c.fd.Write(b); + return n, err +} + +func (c *connBase) ReadFrom(b []byte) (n int, raddr string, err os.Error) { + if c == nil { + return -1, "", os.EINVAL + } + n, err = c.Read(b); + return n, c.raddr, err +} + +func (c *connBase) WriteTo(raddr string, b []byte) (n int, err os.Error) { + if c == nil { + return -1, os.EINVAL + } + if raddr != c.raddr { + return -1, os.EINVAL + } + n, err = c.Write(b); + return n, err +} + +func (c *connBase) Close() os.Error { + if c == nil { + return os.EINVAL + } + return c.fd.Close() +} + + +func setsockoptInt(fd, level, opt int, value int) os.Error { + return os.ErrnoToError(syscall.SetsockoptInt(fd, level, opt, value)); +} + +func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { + var tv = syscall.NsecToTimeval(nsec); + return os.ErrnoToError(syscall.SetsockoptTimeval(fd, level, opt, &tv)); +} + +func (c *connBase) SetReadBuffer(bytes int) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes); +} + +func (c *connBase) SetWriteBuffer(bytes int) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes); +} + +func (c *connBase) SetReadTimeout(nsec int64) os.Error { + c.fd.rdeadline_delta = nsec; + return nil; +} + +func (c *connBase) SetWriteTimeout(nsec int64) os.Error { + c.fd.wdeadline_delta = nsec; + return nil; +} + +func (c *connBase) SetTimeout(nsec int64) os.Error { + if e := c.SetReadTimeout(nsec); e != nil { + return e + } + return c.SetWriteTimeout(nsec) +} + +func (c *connBase) SetReuseAddr(reuse bool) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)); +} + +func (c *connBase) BindToDevice(dev string) os.Error { + // TODO(rsc): call setsockopt with null-terminated string pointer + return os.EINVAL +} + +func (c *connBase) SetDontRoute(dontroute bool) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)); +} + +func (c *connBase) SetKeepAlive(keepalive bool) os.Error { + return setsockoptInt(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)); +} + +func (c *connBase) SetLinger(sec int) os.Error { + var l syscall.Linger; + if sec >= 0 { + l.Onoff = 1; + l.Linger = int32(sec); + } else { + l.Onoff = 0; + l.Linger = 0; + } + e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l); + return os.ErrnoToError(e); +} + + +// Internet sockets (TCP, UDP) + +func internetSocket(net, laddr, raddr string, proto int, mode string) (fd *netFD, err os.Error) { + // Parse addresses (unless they are empty). + var lip, rip IP; + var lport, rport int; + var lerr, rerr os.Error; + + if laddr != "" { + lip, lport, lerr = hostPortToIP(net, laddr, mode); + if lerr != nil { + return nil, lerr + } + } + if raddr != "" { + rip, rport, rerr = hostPortToIP(net, raddr, mode); + if rerr != nil { + return nil, rerr + } + } + + // Figure out IP version. + // If network has a suffix like "tcp4", obey it. + vers := 0; + switch net[len(net)-1] { + case '4': + vers = 4; + case '6': + vers = 6; + default: + // Otherwise, guess. + // If the addresses are IPv4 and we prefer IPv4, use 4; else 6. + if preferIPv4 && (lip == nil || lip.To4() != nil) && (rip == nil || rip.To4() != nil) { + vers = 4 + } else { + vers = 6 + } + } + + var family int; + if vers == 4 { + family = syscall.AF_INET + } else { + family = syscall.AF_INET6 + } + + var la, ra syscall.Sockaddr; + if lip != nil { + la, lerr = ipToSockaddr(family, lip, lport); + if lerr != nil { + return nil, lerr + } + } + if rip != nil { + ra, rerr = ipToSockaddr(family, rip, rport); + if rerr != nil { + return nil, rerr + } + } + + fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra); + return fd, err +} + + +// TCP connections. + +// ConnTCP is an implementation of the Conn interface +// for TCP network connections. +type ConnTCP struct { + connBase +} + +func (c *ConnTCP) SetNoDelay(nodelay bool) os.Error { + if c == nil { + return os.EINVAL + } + return setsockoptInt(c.sysFD(), syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(nodelay)) +} + +func newConnTCP(fd *netFD, raddr string) *ConnTCP { + c := new(ConnTCP); + c.fd = fd; + c.raddr = raddr; + c.SetNoDelay(true); + return c +} + +// DialTCP is like Dial but can only connect to TCP networks +// and returns a ConnTCP structure. +func DialTCP(net, laddr, raddr string) (c *ConnTCP, err os.Error) { + if raddr == "" { + return nil, MissingAddress + } + fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_STREAM, "dial"); + if e != nil { + return nil, e + } + return newConnTCP(fd, raddr), nil +} + + +// UDP connections. + +// TODO(rsc): UDP headers mode + +// ConnUDP is an implementation of the Conn interface +// for UDP network connections. +type ConnUDP struct { + connBase +} + +func newConnUDP(fd *netFD, raddr string) *ConnUDP { + c := new(ConnUDP); + c.fd = fd; + c.raddr = raddr; + return c +} + +// DialUDP is like Dial but can only connect to UDP networks +// and returns a ConnUDP structure. +func DialUDP(net, laddr, raddr string) (c *ConnUDP, err os.Error) { + if raddr == "" { + return nil, MissingAddress + } + fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial"); + if e != nil { + return nil, e + } + return newConnUDP(fd, raddr), nil +} + + +// TODO: raw IP connections + +// TODO: raw ethernet connections + + +// Unix domain sockets + +func unixSocket(net, laddr, raddr string, mode string) (fd *netFD, err os.Error) { + var proto int; + switch net { + default: + return nil, UnknownNetwork; + case "unix": + proto = syscall.SOCK_STREAM; + case "unix-dgram": + proto = syscall.SOCK_DGRAM; + } + + var la, ra syscall.Sockaddr; + switch mode { + case "dial": + if laddr != "" { + return nil, BadAddress; + } + if raddr == "" { + return nil, MissingAddress; + } + ra = &syscall.SockaddrUnix{Name: raddr}; + + case "listen": + if laddr == "" { + return nil, MissingAddress; + } + la = &syscall.SockaddrUnix{Name: laddr}; + if raddr != "" { + return nil, BadAddress; + } + } + + fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra); + return fd, err +} + +// ConnUnix is an implementation of the Conn interface +// for connections to Unix domain sockets. +type ConnUnix struct { + connBase +} + +func newConnUnix(fd *netFD, raddr string) *ConnUnix { + c := new(ConnUnix); + c.fd = fd; + c.raddr = raddr; + return c; +} + +// DialUnix is like Dial but can only connect to Unix domain sockets +// and returns a ConnUnix structure. The laddr argument must be +// the empty string; it is included only to match the signature of +// the other dial routines. +func DialUnix(net, laddr, raddr string) (c *ConnUnix, err os.Error) { + fd, e := unixSocket(net, laddr, raddr, "dial"); + if e != nil { + return nil, e + } + return newConnUnix(fd, raddr), nil; +} + +// ListenerUnix is a Unix domain socket listener. +// Clients should typically use variables of type Listener +// instead of assuming Unix domain sockets. +type ListenerUnix struct { + fd *netFD; + laddr string +} + +// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener. +// Net can be either "unix" (stream sockets) or "unix-dgram" (datagram sockets). +func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) { + fd, e := unixSocket(net, laddr, "", "listen"); + if e != nil { + // Check for socket ``in use'' but ``refusing connections,'' + // which means some program created it and exited + // without unlinking it from the file system. + // Clean up on that program's behalf and try again. + // Don't do this for Linux's ``abstract'' sockets, which begin with @. + if e != os.EADDRINUSE || laddr[0] == '@' { + return nil, e; + } + fd1, e1 := unixSocket(net, "", laddr, "dial"); + if e1 == nil { + fd1.Close(); + } + if e1 != os.ECONNREFUSED { + return nil, e; + } + syscall.Unlink(laddr); + fd1, e1 = unixSocket(net, laddr, "", "listen"); + if e1 != nil { + return nil, e; + } + fd = fd1; + } + e1 := syscall.Listen(fd.fd, 8); // listenBacklog()); + if e1 != 0 { + syscall.Close(fd.fd); + return nil, os.ErrnoToError(e1); + } + return &ListenerUnix{fd, laddr}, nil; +} + +// AcceptUnix accepts the next incoming call and returns the new connection +// and the remote address. +func (l *ListenerUnix) AcceptUnix() (c *ConnUnix, raddr string, err os.Error) { + if l == nil || l.fd == nil || l.fd.fd < 0 { + return nil, "", os.EINVAL + } + fd, e := l.fd.accept(); + if e != nil { + return nil, "", e + } + return newConnUnix(fd, fd.raddr), raddr, nil +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *ListenerUnix) Accept() (c Conn, raddr string, err os.Error) { + // TODO(rsc): 6g bug prevents saying + // c, raddr, err = l.AcceptUnix(); + // return; + c1, r1, e1 := l.AcceptUnix(); + return c1, r1, e1; +} + + +// Close stops listening on the Unix address. +// Already accepted connections are not closed. +func (l *ListenerUnix) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + + // The operating system doesn't clean up + // the file that announcing created, so + // we have to clean it up ourselves. + // There's a race here--we can't know for + // sure whether someone else has come along + // and replaced our socket name already-- + // but this sequence (remove then close) + // is at least compatible with the auto-remove + // sequence in ListenUnix. It's only non-Go + // programs that can mess us up. + if l.laddr[0] != '@' { + syscall.Unlink(l.laddr); + } + err := l.fd.Close(); + l.fd = nil; + return err; +} + +// Dial connects to the remote address raddr on the network net. +// If the string laddr is not empty, it is used as the local address +// for the connection. +// +// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), +// "udp", "udp4" (IPv4-only), and "udp6" (IPv6-only). +// +// For IP networks, addresses have the form host:port. If host is +// a literal IPv6 address, it must be enclosed in square brackets. +// +// Examples: +// Dial("tcp", "", "12.34.56.78:80") +// Dial("tcp", "", "google.com:80") +// Dial("tcp", "", "[de:ad:be:ef::ca:fe]:80") +// Dial("tcp", "127.0.0.1:123", "127.0.0.1:88") +func Dial(net, laddr, raddr string) (c Conn, err os.Error) { + switch net { + case "tcp", "tcp4", "tcp6": + c, err := DialTCP(net, laddr, raddr); + if err != nil { + return nil, err + } + return c, nil; + case "udp", "udp4", "upd6": + c, err := DialUDP(net, laddr, raddr); + return c, err; + case "unix", "unix-dgram": + c, err := DialUnix(net, laddr, raddr); + return c, err; +/* + case "ether": + c, err := DialEther(net, laddr, raddr); + return c, err; + case "ipv4": + c, err := DialIPv4(net, laddr, raddr); + return c, err; + case "ipv6": + c, err := DialIPv6(net, laddr, raddr); + return c, err +*/ + } + return nil, UnknownNetwork +} + +// A Listener is a generic network listener. +// Accept waits for the next connection and Close closes the connection. +type Listener interface { + Accept() (c Conn, raddr string, err os.Error); + Close() os.Error; +} + +// ListenerTCP is a TCP network listener. +// Clients should typically use variables of type Listener +// instead of assuming TCP. +type ListenerTCP struct { + fd *netFD; + laddr string +} + +// ListenTCP announces on the TCP address laddr and returns a TCP listener. +// Net must be "tcp", "tcp4", or "tcp6". +func ListenTCP(net, laddr string) (l *ListenerTCP, err os.Error) { + fd, e := internetSocket(net, laddr, "", syscall.SOCK_STREAM, "listen"); + if e != nil { + return nil, e + } + e1 := syscall.Listen(fd.fd, listenBacklog()); + if e1 != 0 { + syscall.Close(fd.fd); + return nil, os.ErrnoToError(e1) + } + l = new(ListenerTCP); + l.fd = fd; + return l, nil +} + +// AcceptTCP accepts the next incoming call and returns the new connection +// and the remote address. +func (l *ListenerTCP) AcceptTCP() (c *ConnTCP, raddr string, err os.Error) { + if l == nil || l.fd == nil || l.fd.fd < 0 { + return nil, "", os.EINVAL + } + fd, e := l.fd.accept(); + if e != nil { + return nil, "", e + } + return newConnTCP(fd, fd.raddr), fd.raddr, nil +} + +// Accept implements the Accept method in the Listener interface; +// it waits for the next call and returns a generic Conn. +func (l *ListenerTCP) Accept() (c Conn, raddr string, err os.Error) { + c1, r1, e1 := l.AcceptTCP(); + if e1 != nil { + return nil, "", e1 + } + return c1, r1, nil +} + +// Close stops listening on the TCP address. +// Already Accepted connections are not closed. +func (l *ListenerTCP) Close() os.Error { + if l == nil || l.fd == nil { + return os.EINVAL + } + return l.fd.Close() +} + +// Listen announces on the local network address laddr. +// The network string net must be "tcp", "tcp4", "tcp6", +// "unix", or "unix-dgram". +func Listen(net, laddr string) (l Listener, err os.Error) { + switch net { + case "tcp", "tcp4", "tcp6": + l, err := ListenTCP(net, laddr); + if err != nil { + return nil, err; + } + return l, nil; + case "unix", "unix-dgram": + l, err := ListenUnix(net, laddr); + if err != nil { + return nil, err; + } + return l, nil; +/* + more here +*/ + // BUG(rsc): Listen should support UDP. + } + return nil, UnknownNetwork +} + diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go new file mode 100644 index 000000000..de47cb812 --- /dev/null +++ b/src/pkg/net/parse.go @@ -0,0 +1,160 @@ +// Copyright 2009 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. + +// Simple file i/o and string manipulation, to avoid +// depending on strconv and bufio. + +package net + +import ( + "io"; + "os"; +) + +type Error struct { + os.ErrorString +} + +type file struct { + file *os.File; + data []byte; +} + +func (f *file) close() { + f.file.Close() +} + +func (f *file) getLineFromData() (s string, ok bool) { + data := f.data; + for i := 0; i < len(data); i++ { + if data[i] == '\n' { + s = string(data[0:i]); + ok = true; + // move data + i++; + n := len(data) - i; + for j := 0; j < n; j++ { + data[j] = data[i+j]; + } + f.data = data[0:n]; + return + } + } + return +} + +func (f *file) readLine() (s string, ok bool) { + if s, ok = f.getLineFromData(); ok { + return + } + if len(f.data) < cap(f.data) { + ln := len(f.data); + n, err := io.FullRead(f.file, f.data[ln:cap(f.data)]); + if n >= 0 { + f.data = f.data[0:ln+n]; + } + } + s, ok = f.getLineFromData(); + return +} + +func open(name string) (*file, os.Error) { + fd, err := os.Open(name, os.O_RDONLY, 0); + if err != nil { + return nil, err; + } + return &file{fd, make([]byte, 1024)[0:0]}, nil; +} + +func byteIndex(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} + +// Count occurrences in s of any bytes in t. +func countAnyByte(s string, t string) int { + n := 0; + for i := 0; i < len(s); i++ { + if byteIndex(t, s[i]) >= 0 { + n++; + } + } + return n +} + +// Split s at any bytes in t. +func splitAtBytes(s string, t string) []string { + a := make([]string, 1+countAnyByte(s, t)); + n := 0; + last := 0; + for i := 0; i < len(s); i++ { + if byteIndex(t, s[i]) >= 0 { + if last < i { + a[n] = string(s[last:i]); + n++; + } + last = i+1; + } + } + if last < len(s) { + a[n] = string(s[last:len(s)]); + n++; + } + return a[0:n]; +} + +func getFields(s string) []string { + return splitAtBytes(s, " \r\t\n"); +} + +// Bigger than we need, not too big to worry about overflow +const big = 0xFFFFFF + +// Decimal to integer starting at &s[i0]. +// Returns number, new offset, success. +func dtoi(s string, i0 int) (n int, i int, ok bool) { + n = 0; + for i = i0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + n = n*10 + int(s[i] - '0'); + if n >= big { + return 0, i, false + } + } + if i == i0 { + return 0, i, false + } + return n, i, true +} + +// Hexadecimal to integer starting at &s[i0]. +// Returns number, new offset, success. +func xtoi(s string, i0 int) (n int, i int, ok bool) { + n = 0; + for i = i0; i < len(s); i++ { + if '0' <= s[i] && s[i] <= '9' { + n *= 16; + n += int(s[i] - '0') + } else if 'a' <= s[i] && s[i] <= 'f' { + n *= 16; + n += int(s[i] - 'a') + 10 + } else if 'A' <= s[i] && s[i] <= 'F' { + n *= 16; + n += int(s[i] -'A') + 10 + } else { + break + } + if n >= big { + return 0, i, false + } + } + if i == i0 { + return 0, i, false + } + return n, i, true +} + diff --git a/src/pkg/net/parse_test.go b/src/pkg/net/parse_test.go new file mode 100644 index 000000000..ce0bb4709 --- /dev/null +++ b/src/pkg/net/parse_test.go @@ -0,0 +1,44 @@ +// Copyright 2009 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 net + +import ( + "bufio"; + "net"; + "os"; + "testing"; +) + +func TestReadLine(t *testing.T) { + filename := "/etc/services"; // a nice big file + + fd, err := os.Open(filename, os.O_RDONLY, 0); + if err != nil { + t.Fatalf("open %s: %v", filename, err); + } + br := bufio.NewReader(fd); + + var file *file; + file, err = open(filename); + if file == nil { + t.Fatalf("net.open(%s) = nil", filename); + } + + lineno := 1; + byteno := 0; + for { + bline, berr := br.ReadLineString('\n', false); + line, ok := file.readLine(); + if (berr != nil) != !ok || bline != line { + t.Fatalf("%s:%d (#%d)\nbufio => %q, %v\nnet => %q, %v", + filename, lineno, byteno, bline, berr, line, ok); + } + if !ok { + break + } + lineno++; + byteno += len(line) + 1; + } +} diff --git a/src/pkg/net/port.go b/src/pkg/net/port.go new file mode 100644 index 000000000..21e3b48aa --- /dev/null +++ b/src/pkg/net/port.go @@ -0,0 +1,77 @@ +// Copyright 2009 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. + +// Read system port mappings from /etc/services + +package net + +import ( + "io"; + "net"; + "once"; + "os"; + "strconv"; +) + +// The error returned by LookupPort when a network service +// is not listed in the database. +var ErrNoService = &Error{"unknown network service"}; + +var services map[string] map[string] int +var servicesError os.Error + +func readServices() { + services = make(map[string] map[string] int); + var file *file; + file, servicesError = open("/etc/services"); + for line, ok := file.readLine(); ok; line, ok = file.readLine() { + // "http 80/tcp www www-http # World Wide Web HTTP" + if i := byteIndex(line, '#'); i >= 0 { + line = line[0:i]; + } + f := getFields(line); + if len(f) < 2 { + continue; + } + portnet := f[1]; // "tcp/80" + port, j, ok := dtoi(portnet, 0); + if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' { + continue + } + netw := portnet[j+1:len(portnet)]; // "tcp" + m, ok1 := services[netw]; + if !ok1 { + m = make(map[string] int); + services[netw] = m; + } + for i := 0; i < len(f); i++ { + if i != 1 { // f[1] was port/net + m[f[i]] = port; + } + } + } + file.close(); +} + +// LookupPort looks up the port for the given network and service. +func LookupPort(network, service string) (port int, err os.Error) { + once.Do(readServices); + + switch network { + case "tcp4", "tcp6": + network = "tcp"; + case "udp4", "udp6": + network = "udp"; + } + + m, ok := services[network]; + if !ok { + return 0, ErrNoService; + } + port, ok = m[service]; + if !ok { + return 0, ErrNoService; + } + return port, nil; +} diff --git a/src/pkg/net/port_test.go b/src/pkg/net/port_test.go new file mode 100644 index 000000000..c535d4caa --- /dev/null +++ b/src/pkg/net/port_test.go @@ -0,0 +1,59 @@ +// Copyright 2009 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 net + +import ( + "net"; + "testing"; +) + +type portTest struct { + netw string; + name string; + port int; + ok bool; +} + +var porttests = []portTest { + portTest{ "tcp", "echo", 7, true }, + portTest{ "tcp", "discard", 9, true }, + portTest{ "tcp", "systat", 11, true }, + portTest{ "tcp", "daytime", 13, true }, + portTest{ "tcp", "chargen", 19, true }, + portTest{ "tcp", "ftp-data", 20, true }, + portTest{ "tcp", "ftp", 21, true }, + portTest{ "tcp", "ssh", 22, true }, + portTest{ "tcp", "telnet", 23, true }, + portTest{ "tcp", "smtp", 25, true }, + portTest{ "tcp", "time", 37, true }, + portTest{ "tcp", "domain", 53, true }, + portTest{ "tcp", "gopher", 70, true }, + portTest{ "tcp", "finger", 79, true }, + portTest{ "tcp", "http", 80, true }, + + portTest{ "udp", "echo", 7, true }, + portTest{ "udp", "tacacs", 49, true }, + portTest{ "udp", "tftp", 69, true }, + portTest{ "udp", "bootpc", 68, true }, + portTest{ "udp", "bootps", 67, true }, + portTest{ "udp", "domain", 53, true }, + portTest{ "udp", "ntp", 123, true }, + portTest{ "udp", "snmp", 161, true }, + portTest{ "udp", "syslog", 514, true }, + portTest{ "udp", "nfs", 2049, true }, + + portTest{ "--badnet--", "zzz", 0, false }, + portTest{ "tcp", "--badport--", 0, false }, +} + +func TestLookupPort(t *testing.T) { + for i := 0; i < len(porttests); i++ { + tt := porttests[i]; + if port, err := LookupPort(tt.netw, tt.name); port != tt.port || (err == nil) != tt.ok { + t.Errorf("LookupPort(%q, %q) = %v, %s; want %v", + tt.netw, tt.name, port, err, tt.port); + } + } +} diff --git a/src/pkg/net/server_test.go b/src/pkg/net/server_test.go new file mode 100644 index 000000000..586b55365 --- /dev/null +++ b/src/pkg/net/server_test.go @@ -0,0 +1,93 @@ +// Copyright 2009 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 net + +import ( + "io"; + "net"; + "os"; + "syscall"; + "testing"; +) + +func runEcho(fd io.ReadWriter, done chan<- int) { + var buf [1024]byte; + + for { + n, err := fd.Read(&buf); + if err != nil || n == 0 { + break; + } + fd.Write(buf[0:n]) + } + done <- 1 +} + +func runServe(t *testing.T, network, addr string, listening, done chan<- int) { + l, err := net.Listen(network, addr); + if err != nil { + t.Fatalf("net.Listen(%q, %q) = _, %v", network, addr, err); + } + listening <- 1; + + for { + fd, addr, err := l.Accept(); + if err != nil { + break; + } + echodone := make(chan int); + go runEcho(fd, echodone); + <-echodone; // make sure Echo stops + l.Close(); + } + done <- 1 +} + +func connect(t *testing.T, network, addr string) { + fd, err := net.Dial(network, "", addr); + if err != nil { + t.Fatalf("net.Dial(%q, %q, %q) = _, %v", network, "", addr, err); + } + + b := io.StringBytes("hello, world\n"); + var b1 [100]byte; + + n, errno := fd.Write(b); + if n != len(b) { + t.Fatalf("fd.Write(%q) = %d, %v", b, n, errno); + } + + n, errno = fd.Read(&b1); + if n != len(b) { + t.Fatalf("fd.Read() = %d, %v", n, errno); + } + fd.Close(); +} + +func doTest(t *testing.T, network, listenaddr, dialaddr string) { + t.Logf("Test %s %s %s\n", network, listenaddr, dialaddr); + listening := make(chan int); + done := make(chan int); + go runServe(t, network, listenaddr, listening, done); + <-listening; // wait for server to start + connect(t, network, dialaddr); + <-done; // make sure server stopped +} + +func TestTcpServer(t *testing.T) { + doTest(t, "tcp", "0.0.0.0:9997", "127.0.0.1:9997"); + doTest(t, "tcp", "[::]:9997", "[::ffff:127.0.0.1]:9997"); + doTest(t, "tcp", "[::]:9997", "127.0.0.1:9997"); + doTest(t, "tcp", ":9997", "127.0.0.1:9997"); + doTest(t, "tcp", "0.0.0.0:9997", "[::ffff:127.0.0.1]:9997"); +} + +func TestUnixServer(t *testing.T) { + doTest(t, "unix", "/tmp/gotest.net", "/tmp/gotest.net"); + if syscall.OS == "linux" { + // Test abstract unix domain socket, a Linux-ism + doTest(t, "unix", "@gotest/net", "@gotest/net"); + } +} diff --git a/src/pkg/net/timeout_test.go b/src/pkg/net/timeout_test.go new file mode 100644 index 000000000..e08ce88ce --- /dev/null +++ b/src/pkg/net/timeout_test.go @@ -0,0 +1,42 @@ +// Copyright 2009 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 net + +import ( + "net"; + "os"; + "testing"; + "time"; +) + +func testTimeout(t *testing.T, network, addr string) { + fd, err := net.Dial(network, "", addr); + defer fd.Close(); + if err != nil { + t.Errorf("dial %s %s failed: %v", network, addr, err); + } + t0 := time.Nanoseconds(); + fd.SetReadTimeout(1e8); // 100ms + var b [100]byte; + n, err1 := fd.Read(&b); + t1 := time.Nanoseconds(); + if n != 0 || err1 != os.EAGAIN { + t.Errorf("fd.Read on %s %s did not return 0, EAGAIN: %v, %v", network, addr, n, err1); + } + if t1 - t0 < 0.5e8 || t1 - t0 > 1.5e8 { + t.Errorf("fd.Read on %s %s took %f seconds, expected 0.1", network, addr, float64(t1 - t0) / 1e9); + } +} + +func TestTimeoutUDP(t *testing.T) { + testTimeout(t, "udp", "127.0.0.1:53"); +} + +func TestTimeoutTCP(t *testing.T) { + // 74.125.19.99 is www.google.com. + // could use dns, but dns depends on + // timeouts and this is the timeout test. + testTimeout(t, "tcp", "74.125.19.99:80"); +} diff --git a/src/pkg/once/Makefile b/src/pkg/once/Makefile new file mode 100644 index 000000000..6350402c2 --- /dev/null +++ b/src/pkg/once/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + once.$O\ + + +phases: a1 +_obj$D/once.a: phases + +a1: $(O1) + $(AR) grc _obj$D/once.a once.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/once.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/once.a + +packages: _obj$D/once.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/once.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/once.a diff --git a/src/pkg/once/once.go b/src/pkg/once/once.go new file mode 100644 index 000000000..6047df236 --- /dev/null +++ b/src/pkg/once/once.go @@ -0,0 +1,46 @@ +// Copyright 2009 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. + +// This package provides a single function, Do, to run a function +// exactly once, usually used as part of initialization. +package once + +import "sync" + +type job struct { + done bool; + sync.Mutex; // should probably be sync.Notification or some such +} + +var jobs = make(map[func()]*job) +var joblock sync.Mutex; + +// Do is the the only exported piece of the package. +// For one-time initialization that is not done during init, +// wrap the initialization in a niladic function f() and call +// Do(f) +// If multiple processes call Do(f) simultaneously +// with the same f argument, only one will call f, and the +// others will block until f finishes running. +func Do(f func()) { + joblock.Lock(); + j, present := jobs[f]; + if !present { + // run it + j = new(job); + j.Lock(); + jobs[f] = j; + joblock.Unlock(); + f(); + j.done = true; + j.Unlock(); + } else { + // wait for it + joblock.Unlock(); + if j.done != true { + j.Lock(); + j.Unlock(); + } + } +} diff --git a/src/pkg/once/once_test.go b/src/pkg/once/once_test.go new file mode 100644 index 000000000..9506ff3d7 --- /dev/null +++ b/src/pkg/once/once_test.go @@ -0,0 +1,31 @@ +// Copyright 2009 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 once + +import ( + "once"; + "testing"; +) + +var ncall int; +func call() { + ncall++ +} + +func TestOnce(t *testing.T) { + ncall = 0; + once.Do(call); + if ncall != 1 { + t.Fatalf("once.Do(call) didn't call(): ncall=%d", ncall); + } + once.Do(call); + if ncall != 1 { + t.Fatalf("second once.Do(call) did call(): ncall=%d", ncall); + } + once.Do(call); + if ncall != 1 { + t.Fatalf("third once.Do(call) did call(): ncall=%d", ncall); + } +} diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile new file mode 100644 index 000000000..c5f790f15 --- /dev/null +++ b/src/pkg/os/Makefile @@ -0,0 +1,91 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m dir_${GOOS}_${GOARCH}.go env.go error.go file.go path.go stat_${GOOS}_${GOARCH}.go time.go types.go exec.go proc.go getwd.go >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + error.$O\ + types.$O\ + +O2=\ + proc.$O\ + stat_$(GOOS)_$(GOARCH).$O\ + time.$O\ + +O3=\ + env.$O\ + file.$O\ + +O4=\ + dir_$(GOOS)_$(GOARCH).$O\ + exec.$O\ + getwd.$O\ + path.$O\ + + +phases: a1 a2 a3 a4 +_obj$D/os.a: phases + +a1: $(O1) + $(AR) grc _obj$D/os.a error.$O types.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/os.a proc.$O stat_$(GOOS)_$(GOARCH).$O time.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/os.a env.$O file.$O + rm -f $(O3) + +a4: $(O4) + $(AR) grc _obj$D/os.a dir_$(GOOS)_$(GOARCH).$O exec.$O getwd.$O path.$O + rm -f $(O4) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/os.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 +$(O5): a4 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/os.a + +packages: _obj$D/os.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/os.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/os.a diff --git a/src/pkg/os/dir_darwin_386.go b/src/pkg/os/dir_darwin_386.go new file mode 100644 index 000000000..2803ecee2 --- /dev/null +++ b/src/pkg/os/dir_darwin_386.go @@ -0,0 +1,76 @@ +// Copyright 2009 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 + +import ( + "os"; + "syscall"; + "unsafe"; +) + +const ( + blockSize = 4096 // TODO(r): use statfs +) + +// Negative count means read until EOF. +func readdirnames(file *File, count int) (names []string, err Error) { + // If this file has no dirinfo, create one. + if file.dirinfo == nil { + file.dirinfo = new(dirInfo); + // The buffer must be at least a block long. + // TODO(r): use fstatfs to find fs block size. + file.dirinfo.buf = make([]byte, blockSize); + } + d := file.dirinfo; + size := count; + if size < 0 { + size = 100 + } + names = make([]string, 0, size); // Empty with room to grow. + for count != 0 { + // Refill the buffer if necessary + if d.bufp >= d.nbuf { + var errno int; + d.bufp = 0; + // Final argument is (basep *uintptr) and the syscall doesn't take nil. + d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr)); + if errno != 0 { + d.nbuf = 0; + return names, ErrnoToError(errno) + } + if d.nbuf == 0 { + break // EOF + } + } + // Drain the buffer + for count != 0 && d.bufp < d.nbuf { + dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp])); + if dirent.Reclen == 0 { + d.bufp = d.nbuf; + break + } + d.bufp += int(dirent.Reclen); + if dirent.Ino == 0 { // File absent in directory. + continue + } + bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])); + var name = string(bytes[0:dirent.Namlen]); + if name == "." || name == ".." { // Useless names + continue + } + count--; + if len(names) == cap(names) { + nnames := make([]string, len(names), 2*len(names)); + for i := 0; i < len(names); i++ { + nnames[i] = names[i] + } + names = nnames; + } + names = names[0:len(names)+1]; + names[len(names)-1] = name; + } + } + return names, nil +} diff --git a/src/pkg/os/dir_darwin_amd64.go b/src/pkg/os/dir_darwin_amd64.go new file mode 100644 index 000000000..2803ecee2 --- /dev/null +++ b/src/pkg/os/dir_darwin_amd64.go @@ -0,0 +1,76 @@ +// Copyright 2009 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 + +import ( + "os"; + "syscall"; + "unsafe"; +) + +const ( + blockSize = 4096 // TODO(r): use statfs +) + +// Negative count means read until EOF. +func readdirnames(file *File, count int) (names []string, err Error) { + // If this file has no dirinfo, create one. + if file.dirinfo == nil { + file.dirinfo = new(dirInfo); + // The buffer must be at least a block long. + // TODO(r): use fstatfs to find fs block size. + file.dirinfo.buf = make([]byte, blockSize); + } + d := file.dirinfo; + size := count; + if size < 0 { + size = 100 + } + names = make([]string, 0, size); // Empty with room to grow. + for count != 0 { + // Refill the buffer if necessary + if d.bufp >= d.nbuf { + var errno int; + d.bufp = 0; + // Final argument is (basep *uintptr) and the syscall doesn't take nil. + d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr)); + if errno != 0 { + d.nbuf = 0; + return names, ErrnoToError(errno) + } + if d.nbuf == 0 { + break // EOF + } + } + // Drain the buffer + for count != 0 && d.bufp < d.nbuf { + dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp])); + if dirent.Reclen == 0 { + d.bufp = d.nbuf; + break + } + d.bufp += int(dirent.Reclen); + if dirent.Ino == 0 { // File absent in directory. + continue + } + bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])); + var name = string(bytes[0:dirent.Namlen]); + if name == "." || name == ".." { // Useless names + continue + } + count--; + if len(names) == cap(names) { + nnames := make([]string, len(names), 2*len(names)); + for i := 0; i < len(names); i++ { + nnames[i] = names[i] + } + names = nnames; + } + names = names[0:len(names)+1]; + names[len(names)-1] = name; + } + } + return names, nil +} diff --git a/src/pkg/os/dir_linux_386.go b/src/pkg/os/dir_linux_386.go new file mode 100644 index 000000000..c4594a52d --- /dev/null +++ b/src/pkg/os/dir_linux_386.go @@ -0,0 +1,83 @@ +// Copyright 2009 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. + +// TODO(rsc): Once the porting dust settles, consider +// whether this file should be dir_linux.go (and similarly +// dir_darwin.go) instead of having one copy per architecture. + +package os + +import ( + "os"; + "syscall"; + "unsafe"; +) + +const ( + blockSize = 4096 // TODO(r): use statfs +) + +func clen(n []byte) int { + for i := 0; i < len(n); i++ { + if n[i] == 0 { + return i + } + } + return len(n) +} + +// Negative count means read until EOF. +func readdirnames(file *File, count int) (names []string, err Error) { + // If this file has no dirinfo, create one. + if file.dirinfo == nil { + file.dirinfo = new(dirInfo); + // The buffer must be at least a block long. + // TODO(r): use fstatfs to find fs block size. + file.dirinfo.buf = make([]byte, blockSize); + } + d := file.dirinfo; + size := count; + if size < 0 { + size = 100 + } + names = make([]string, 0, size); // Empty with room to grow. + for count != 0 { + // Refill the buffer if necessary + if d.bufp >= d.nbuf { + var errno int; + d.nbuf, errno = syscall.Getdents(file.fd, d.buf); + if d.nbuf < 0 { + return names, ErrnoToError(errno) + } + if d.nbuf == 0 { + break // EOF + } + d.bufp = 0; + } + // Drain the buffer + for count != 0 && d.bufp < d.nbuf { + dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp])); + d.bufp += int(dirent.Reclen); + if dirent.Ino == 0 { // File absent in directory. + continue + } + bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])); + var name = string(bytes[0:clen(bytes)]); + if name == "." || name == ".." { // Useless names + continue + } + count--; + if len(names) == cap(names) { + nnames := make([]string, len(names), 2*len(names)); + for i := 0; i < len(names); i++ { + nnames[i] = names[i] + } + names = nnames; + } + names = names[0:len(names)+1]; + names[len(names)-1] = name; + } + } + return names, nil; +} diff --git a/src/pkg/os/dir_linux_amd64.go b/src/pkg/os/dir_linux_amd64.go new file mode 100644 index 000000000..05b3d4c65 --- /dev/null +++ b/src/pkg/os/dir_linux_amd64.go @@ -0,0 +1,79 @@ +// Copyright 2009 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 + +import ( + "os"; + "syscall"; + "unsafe"; +) + +const ( + blockSize = 4096 // TODO(r): use statfs +) + +func clen(n []byte) int { + for i := 0; i < len(n); i++ { + if n[i] == 0 { + return i + } + } + return len(n) +} + +// Negative count means read until EOF. +func readdirnames(file *File, count int) (names []string, err Error) { + // If this file has no dirinfo, create one. + if file.dirinfo == nil { + file.dirinfo = new(dirInfo); + // The buffer must be at least a block long. + // TODO(r): use fstatfs to find fs block size. + file.dirinfo.buf = make([]byte, blockSize); + } + d := file.dirinfo; + size := count; + if size < 0 { + size = 100 + } + names = make([]string, 0, size); // Empty with room to grow. + for count != 0 { + // Refill the buffer if necessary + if d.bufp >= d.nbuf { + var errno int; + d.nbuf, errno = syscall.Getdents(file.fd, d.buf); + if d.nbuf < 0 { + return names, ErrnoToError(errno) + } + if d.nbuf == 0 { + break // EOF + } + d.bufp = 0; + } + // Drain the buffer + for count != 0 && d.bufp < d.nbuf { + dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp])); + d.bufp += int(dirent.Reclen); + if dirent.Ino == 0 { // File absent in directory. + continue + } + bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])); + var name = string(bytes[0:clen(bytes)]); + if name == "." || name == ".." { // Useless names + continue + } + count--; + if len(names) == cap(names) { + nnames := make([]string, len(names), 2*len(names)); + for i := 0; i < len(names); i++ { + nnames[i] = names[i] + } + names = nnames; + } + names = names[0:len(names)+1]; + names[len(names)-1] = name; + } + } + return names, nil; +} diff --git a/src/pkg/os/env.go b/src/pkg/os/env.go new file mode 100644 index 000000000..748750413 --- /dev/null +++ b/src/pkg/os/env.go @@ -0,0 +1,80 @@ +// Copyright 2009 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. + +// Environment variables. + +package os + +import ( + "once"; + "os"; +) + +// ENOENV is the Error indicating that an environment variable does not exist. +var ENOENV = NewError("no such environment variable"); + +var env map[string] string; + + +func copyenv() { + env = make(map[string] string); + for i, s := range os.Envs { + for j := 0; j < len(s); j++ { + if s[j] == '=' { + env[s[0:j]] = s[j+1:len(s)]; + break; + } + } + } +} + +// Getenv retrieves the value of the environment variable named by the key. +// It returns the value and an error, if any. +func Getenv(key string) (value string, err Error) { + once.Do(copyenv); + + if len(key) == 0 { + return "", EINVAL; + } + v, ok := env[key]; + if !ok { + return "", ENOENV; + } + return v, nil; +} + +// Setenv sets the value of the environment variable named by the key. +// It returns an Error, if any. +func Setenv(key, value string) Error { + once.Do(copyenv); + + if len(key) == 0 { + return EINVAL; + } + env[key] = value; + return nil; +} + +// Clearenv deletes all environment variables. +func Clearenv() { + once.Do(copyenv); // prevent copyenv in Getenv/Setenv + env = make(map[string] string); +} + +// Environ returns an array of strings representing the environment, +// in the form "key=value". +func Environ() []string { + once.Do(copyenv); + a := make([]string, len(env)); + i := 0; + for k, v := range(env) { + // check i < len(a) for safety, + // in case env is changing underfoot. + if i < len(a) { + a[i] = k + "=" + v; + i++; + } + } + return a[0:i]; +} diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go new file mode 100644 index 000000000..718499b21 --- /dev/null +++ b/src/pkg/os/error.go @@ -0,0 +1,83 @@ +// Copyright 2009 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 + +import syscall "syscall" + +// An Error can represent any printable error condition. +type Error interface { + String() string +} + +// A helper type that can be embedded or wrapped to simplify satisfying +// Error. +type ErrorString string +func (e ErrorString) String() string { + return string(e) +} + +// NewError converts s to an ErrorString, which satisfies the Error interface. +func NewError(s string) Error { + return ErrorString(s) +} + +// Errno is the Unix error number. Names such as EINVAL are simple +// wrappers to convert the error number into an Error. +type Errno int64 +func (e Errno) String() string { + return syscall.Errstr(int(e)) +} + +// ErrnoToError converts errno to an Error (underneath, an Errno). +// It returns nil for the "no error" errno. +func ErrnoToError(errno int) Error { + if errno == 0 { + return nil + } + return Errno(errno) +} + +// Commonly known Unix errors. +var ( + EPERM Error = Errno(syscall.EPERM); + ENOENT Error = Errno(syscall.ENOENT); + ESRCH Error = Errno(syscall.ESRCH); + EINTR Error = Errno(syscall.EINTR); + EIO Error = Errno(syscall.EIO); + ENXIO Error = Errno(syscall.ENXIO); + E2BIG Error = Errno(syscall.E2BIG); + ENOEXEC Error = Errno(syscall.ENOEXEC); + EBADF Error = Errno(syscall.EBADF); + ECHILD Error = Errno(syscall.ECHILD); + EDEADLK Error = Errno(syscall.EDEADLK); + ENOMEM Error = Errno(syscall.ENOMEM); + EACCES Error = Errno(syscall.EACCES); + EFAULT Error = Errno(syscall.EFAULT); + ENOTBLK Error = Errno(syscall.ENOTBLK); + EBUSY Error = Errno(syscall.EBUSY); + EEXIST Error = Errno(syscall.EEXIST); + EXDEV Error = Errno(syscall.EXDEV); + ENODEV Error = Errno(syscall.ENODEV); + ENOTDIR Error = Errno(syscall.ENOTDIR); + EISDIR Error = Errno(syscall.EISDIR); + EINVAL Error = Errno(syscall.EINVAL); + ENFILE Error = Errno(syscall.ENFILE); + EMFILE Error = Errno(syscall.EMFILE); + ENOTTY Error = Errno(syscall.ENOTTY); + ETXTBSY Error = Errno(syscall.ETXTBSY); + EFBIG Error = Errno(syscall.EFBIG); + ENOSPC Error = Errno(syscall.ENOSPC); + ESPIPE Error = Errno(syscall.ESPIPE); + EROFS Error = Errno(syscall.EROFS); + EMLINK Error = Errno(syscall.EMLINK); + EPIPE Error = Errno(syscall.EPIPE); + EAGAIN Error = Errno(syscall.EAGAIN); + EDOM Error = Errno(syscall.EDOM); + ERANGE Error = Errno(syscall.ERANGE); + EADDRINUSE Error = Errno(syscall.EADDRINUSE); + ECONNREFUSED Error = Errno(syscall.ECONNREFUSED); + ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG); +) + diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go new file mode 100644 index 000000000..d283c7267 --- /dev/null +++ b/src/pkg/os/exec.go @@ -0,0 +1,101 @@ +// Copyright 2009 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 + +import ( + "os"; + "syscall"; +) + +// ForkExec forks the current process and invokes Exec with the file, arguments, +// and environment specified by argv0, argv, and envv. It returns the process +// id of the forked process and an Error, if any. The fd array specifies the +// file descriptors to be set up in the new process: fd[0] will be Unix file +// descriptor 0 (standard input), fd[1] descriptor 1, and so on. A nil entry +// will cause the child to have no open file descriptor with that index. +// If dir is not empty, the child chdirs into the directory before execing the program. +func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*File) + (pid int, err Error) +{ + // Create array of integer (system) fds. + intfd := make([]int, len(fd)); + for i, f := range(fd) { + if f == nil { + intfd[i] = -1; + } else { + intfd[i] = f.Fd(); + } + } + + p, e := syscall.ForkExec(argv0, argv, envv, dir, intfd); + return int(p), ErrnoToError(e); +} + +// Exec replaces the current process with an execution of the program +// named by argv0, with arguments argv and environment envv. +// If successful, Exec never returns. If it fails, it returns an Error. +// ForkExec is almost always a better way to execute a program. +func Exec(argv0 string, argv []string, envv []string) Error { + if envv == nil { + envv = Environ(); + } + e := syscall.Exec(argv0, argv, envv); + return ErrnoToError(e); +} + +// TODO(rsc): Should os implement its own syscall.WaitStatus +// wrapper with the methods, or is exposing the underlying one enough? +// +// TODO(rsc): Certainly need to have os.Rusage struct, +// since syscall one might have different field types across +// different OS. + +// Waitmsg stores the information about an exited process as reported by Wait. +type Waitmsg struct { + Pid int; // The process's id. + syscall.WaitStatus; // System-dependent status info. + Rusage *syscall.Rusage; // System-dependent resource usage info. +} + +// Options for Wait. +const ( + WNOHANG = syscall.WNOHANG; // Don't wait if no process has exited. + WSTOPPED = syscall.WSTOPPED; // If set, status of stopped subprocesses is also reported. + WUNTRACED = WSTOPPED; + WRUSAGE = 1<<30; // Record resource usage. +) + +// Wait waits for process pid to exit or stop, and then returns a +// Waitmsg describing its status and an Error, if any. The options +// (WNOHANG etc.) affect the behavior of the Wait call. +func Wait(pid int, options int) (w *Waitmsg, err Error) { + var status syscall.WaitStatus; + var rusage *syscall.Rusage; + if options & WRUSAGE != 0 { + rusage = new(syscall.Rusage); + options ^= WRUSAGE; + } + pid1, e := syscall.Wait4(pid, &status, options, rusage); + if e != 0 { + return nil, ErrnoToError(e); + } + w = new(Waitmsg); + w.Pid = pid; + w.WaitStatus = status; + w.Rusage = rusage; + return w, nil; +} + +// Getpid returns the process id of the caller. +func Getpid() int { + p, r2, e := syscall.Syscall(syscall.SYS_GETPID, 0, 0, 0); + return int(p) +} + +// Getppid returns the process id of the caller's parent. +func Getppid() int { + p, r2, e := syscall.Syscall(syscall.SYS_GETPPID, 0, 0, 0); + return int(p) +} diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go new file mode 100644 index 000000000..1562b1b0e --- /dev/null +++ b/src/pkg/os/file.go @@ -0,0 +1,383 @@ +// Copyright 2009 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. + +// The os package provides a platform-independent interface to operating +// system functionality. The design is Unix-like. +package os + +import ( + "os"; + "syscall"; +) + +// Auxiliary information if the File describes a directory +type dirInfo struct { + buf []byte; // buffer for directory I/O + nbuf int; // length of buf; return value from Getdirentries + bufp int; // location of next record in buf. +} + +// File represents an open file descriptor. +type File struct { + fd int; + name string; + dirinfo *dirInfo; // nil unless directory being read + nepipe int; // number of consecutive EPIPE in Write +} + +// Fd returns the integer Unix file descriptor referencing the open file. +func (file *File) Fd() int { + return file.fd +} + +// Name returns the name of the file as presented to Open. +func (file *File) Name() string { + return file.name +} + +// NewFile returns a new File with the given file descriptor and name. +func NewFile(file int, name string) *File { + if file < 0 { + return nil + } + return &File{file, name, nil, 0} +} + +// Stdin, Stdout, and Stderr are open Files pointing to the standard input, +// standard output, and standard error file descriptors. +var ( + Stdin = NewFile(0, "/dev/stdin"); + Stdout = NewFile(1, "/dev/stdout"); + Stderr = NewFile(2, "/dev/stderr"); +) + +// Flags to Open wrapping those of the underlying system. Not all flags +// may be implemented on a given system. +const ( + O_RDONLY = syscall.O_RDONLY; // open the file read-only. + O_WRONLY = syscall.O_WRONLY; // open the file write-only. + O_RDWR = syscall.O_RDWR; // open the file read-write. + O_APPEND = syscall.O_APPEND; // open the file append-only. + O_ASYNC = syscall.O_ASYNC; // generate a signal when I/O is available. + O_CREAT = syscall.O_CREAT; // create a new file if none exists. + O_NOCTTY = syscall.O_NOCTTY; // do not make file the controlling tty. + O_NONBLOCK = syscall.O_NONBLOCK; // open in non-blocking mode. + O_NDELAY = O_NONBLOCK; // synonym for O_NONBLOCK + O_SYNC = syscall.O_SYNC; // open for synchronous I/O. + O_TRUNC = syscall.O_TRUNC; // if possible, truncate file when opened. +) + +// Open opens the named file with specified flag (O_RDONLY etc.) and perm, (0666 etc.) +// if applicable. If successful, methods on the returned File can be used for I/O. +// It returns the File and an Error, if any. +func Open(name string, flag int, perm int) (file *File, err Error) { + r, e := syscall.Open(name, flag | syscall.O_CLOEXEC, perm); + if e != 0 { + return nil, ErrnoToError(e); + } + + // There's a race here with fork/exec, which we are + // content to live with. See ../syscall/exec.go + if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported + syscall.CloseOnExec(r); + } + + return NewFile(r, name), ErrnoToError(e) +} + +// Close closes the File, rendering it unusable for I/O. +// It returns an Error, if any. +func (file *File) Close() Error { + if file == nil { + return EINVAL + } + err := ErrnoToError(syscall.Close(file.fd)); + file.fd = -1; // so it can't be closed again + return err; +} + +// Read reads up to len(b) bytes from the File. +// It returns the number of bytes read and an Error, if any. +// EOF is signaled by a zero count with a nil Error. +// TODO(r): Add Pread, Pwrite (maybe ReadAt, WriteAt). +func (file *File) Read(b []byte) (ret int, err Error) { + if file == nil { + return 0, EINVAL + } + n, e := syscall.Read(file.fd, b); + if n < 0 { + n = 0; + } + return n, ErrnoToError(e); +} + +// Write writes len(b) bytes to the File. +// It returns the number of bytes written and an Error, if any. +// If the byte count differs from len(b), it usually implies an error occurred. +func (file *File) Write(b []byte) (ret int, err Error) { + if file == nil { + return 0, EINVAL + } + n, e := syscall.Write(file.fd, b); + if n < 0 { + n = 0 + } + if e == syscall.EPIPE { + file.nepipe++; + if file.nepipe >= 10 { + os.Exit(syscall.EPIPE); + } + } else { + file.nepipe = 0; + } + return n, ErrnoToError(e) +} + +// Seek sets the offset for the next Read or Write on file to offset, interpreted +// according to whence: 0 means relative to the origin of the file, 1 means +// relative to the current offset, and 2 means relative to the end. +// It returns the new offset and an Error, if any. +func (file *File) Seek(offset int64, whence int) (ret int64, err Error) { + r, e := syscall.Seek(file.fd, offset, whence); + if e != 0 { + return -1, ErrnoToError(e) + } + if file.dirinfo != nil && r != 0 { + return -1, ErrnoToError(syscall.EISDIR) + } + return r, nil +} + +// WriteString is like Write, but writes the contents of string s rather than +// an array of bytes. +func (file *File) WriteString(s string) (ret int, err Error) { + if file == nil { + return 0, EINVAL + } + b := syscall.StringByteSlice(s); + b = b[0:len(b)-1]; + r, e := syscall.Write(file.fd, b); + if r < 0 { + r = 0 + } + return int(r), ErrnoToError(e) +} + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an Error, if any. +func Pipe() (r *File, w *File, err Error) { + var p [2]int; + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock(); + e := syscall.Pipe(&p); + if e != 0 { + syscall.ForkLock.RUnlock(); + return nil, nil, ErrnoToError(e) + } + syscall.CloseOnExec(p[0]); + syscall.CloseOnExec(p[1]); + syscall.ForkLock.RUnlock(); + + return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil +} + +// Mkdir creates a new directory with the specified name and permission bits. +// It returns an error, if any. +func Mkdir(name string, perm int) Error { + return ErrnoToError(syscall.Mkdir(name, perm)); +} + +// Stat returns a Dir structure describing the named file and an error, if any. +// If name names a valid symbolic link, the returned Dir describes +// the file pointed at by the link and has dir.FollowedSymlink set to true. +// If name names an invalid symbolic link, the returned Dir describes +// the link itself and has dir.FollowedSymlink set to false. +func Stat(name string) (dir *Dir, err Error) { + var lstat, stat syscall.Stat_t; + e := syscall.Lstat(name, &lstat); + if e != 0 { + return nil, ErrnoToError(e); + } + statp := &lstat; + if lstat.Mode & syscall.S_IFMT == syscall.S_IFLNK { + e := syscall.Stat(name, &stat); + if e == 0 { + statp = &stat; + } + } + return dirFromStat(name, new(Dir), &lstat, statp), nil +} + +// Stat returns the Dir structure describing file. +// It returns the Dir and an error, if any. +func (file *File) Stat() (dir *Dir, err Error) { + var stat syscall.Stat_t; + e := syscall.Fstat(file.fd, &stat); + if e != 0 { + return nil, ErrnoToError(e) + } + return dirFromStat(file.name, new(Dir), &stat, &stat), nil +} + +// Lstat returns the Dir structure describing the named file and an error, if any. +// If the file is a symbolic link, the returned Dir describes the +// symbolic link. Lstat makes no attempt to follow the link. +func Lstat(name string) (dir *Dir, err Error) { + var stat syscall.Stat_t; + e := syscall.Lstat(name, &stat); + if e != 0 { + return nil, ErrnoToError(e) + } + return dirFromStat(name, new(Dir), &stat, &stat), nil +} + +// Readdirnames has a non-portable implemenation so its code is separated into an +// operating-system-dependent file. +func readdirnames(file *File, count int) (names []string, err Error) + +// Readdirnames reads the contents of the directory associated with file and +// returns an array of up to count names, in directory order. Subsequent +// calls on the same file will yield further names. +// A negative count means to read until EOF. +// Readdirnames returns the array and an Error, if any. +func (file *File) Readdirnames(count int) (names []string, err Error) { + return readdirnames(file, count); +} + +// Readdir reads the contents of the directory associated with file and +// returns an array of up to count Dir structures, as would be returned +// by Stat, in directory order. Subsequent calls on the same file will yield further Dirs. +// A negative count means to read until EOF. +// Readdir returns the array and an Error, if any. +func (file *File) Readdir(count int) (dirs []Dir, err Error) { + dirname := file.name; + if dirname == "" { + dirname = "."; + } + dirname += "/"; + names, err1 := file.Readdirnames(count); + if err1 != nil { + return nil, err1 + } + dirs = make([]Dir, len(names)); + for i, filename := range names { + dirp, err := Stat(dirname + filename); + if dirp == nil || err != nil { + dirs[i].Name = filename // rest is already zeroed out + } else { + dirs[i] = *dirp + } + } + return +} + +// Chdir changes the current working directory to the named directory. +func Chdir(dir string) Error { + return ErrnoToError(syscall.Chdir(dir)); +} + +// Chdir changes the current working directory to the file, +// which must be a directory. +func (f *File) Chdir() Error { + return ErrnoToError(syscall.Fchdir(f.fd)); +} + +// Remove removes the named file or directory. +func Remove(name string) Error { + // System call interface forces us to know + // whether name is a file or directory. + // Try both: it is cheaper on average than + // doing a Stat plus the right one. + e := syscall.Unlink(name); + if e == 0 { + return nil; + } + e1 := syscall.Rmdir(name); + if e1 == 0 { + return nil; + } + + // Both failed: figure out which error to return. + // OS X and Linux differ on whether unlink(dir) + // returns EISDIR, so can't use that. However, + // both agree that rmdir(file) returns ENOTDIR, + // so we can use that to decide which error is real. + // Rmdir might also return ENOTDIR if given a bad + // file path, like /etc/passwd/foo, but in that case, + // both errors will be ENOTDIR, so it's okay to + // use the error from unlink. + if e1 != syscall.ENOTDIR { + e = e1; + } + return ErrnoToError(e); +} + +// Link creates a hard link. +func Link(oldname, newname string) Error { + return ErrnoToError(syscall.Link(oldname, newname)); +} + +// Symlink creates a symbolic link. +func Symlink(oldname, newname string) Error { + return ErrnoToError(syscall.Symlink(oldname, newname)); +} + +// Readlink reads the contents of a symbolic link: the destination of +// the link. It returns the contents and an Error, if any. +func Readlink(name string) (string, Error) { + for len := 128; ; len *= 2 { + b := make([]byte, len); + n, e := syscall.Readlink(name, b); + if e != 0 { + return "", ErrnoToError(e); + } + if n < len { + return string(b[0:n]), nil; + } + } + // Silence 6g. + return "", nil; +} + +// Chmod changes the mode of the named file to mode. +// If the file is a symbolic link, it changes the uid and gid of the link's target. +func Chmod(name string, mode int) Error { + return ErrnoToError(syscall.Chmod(name, mode)); +} + +// Chmod changes the mode of the file to mode. +func (f *File) Chmod(mode int) Error { + return ErrnoToError(syscall.Fchmod(f.fd, mode)); +} + +// Chown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link's target. +func Chown(name string, uid, gid int) Error { + return ErrnoToError(syscall.Chown(name, uid, gid)); +} + +// Lchown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link itself. +func Lchown(name string, uid, gid int) Error { + return ErrnoToError(syscall.Lchown(name, uid, gid)); +} + +// Chown changes the numeric uid and gid of the named file. +func (f *File) Chown(uid, gid int) Error { + return ErrnoToError(syscall.Fchown(f.fd, uid, gid)); +} + +// Truncate changes the size of the named file. +// If the file is a symbolic link, it changes the size of the link's target. +func Truncate(name string, size int64) Error { + return ErrnoToError(syscall.Truncate(name, size)); +} + +// Truncate changes the size of the file. +// It does not change the I/O offset. +func (f *File) Truncate(size int64) Error { + return ErrnoToError(syscall.Ftruncate(f.fd, size)); +} + diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go new file mode 100644 index 000000000..2d7b754b5 --- /dev/null +++ b/src/pkg/os/getwd.go @@ -0,0 +1,94 @@ +// Copyright 2009 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 + +import ( + "os"; + "syscall" +) + +// Getwd returns a rooted path name corresponding to the +// current directory. If the current directory can be +// reached via multiple paths (due to symbolic links), +// Getwd may return any one of them. +func Getwd() (string, Error) { + // If the operating system provides a Getwd call, use it. + if syscall.ImplementsGetwd { + s, e := syscall.Getwd(); + return s, ErrnoToError(e); + } + + // Otherwise, we're trying to find our way back to ".". + dot, err := Stat("."); + if err != nil { + return "", err; + } + + // Clumsy but widespread kludge: + // if $PWD is set and matches ".", use it. + pwd, _ := Getenv("PWD"); + if len(pwd) > 0 && pwd[0] == '/' { + d, err := Stat(pwd); + if err == nil && d.Dev == dot.Dev && d.Ino == dot.Ino { + return pwd, nil + } + } + + // Root is a special case because it has no parent + // and ends in a slash. + root, err := Stat("/"); + if err != nil { + // Can't stat root - no hope. + return "", err; + } + if root.Dev == dot.Dev && root.Ino == dot.Ino { + return "/", nil + } + + // General algorithm: find name in parent + // and then find name of parent. Each iteration + // adds /name to the beginning of pwd. + elem := make([]string, 0, 16); + pwd = ""; + for parent := "..";; parent = "../" + parent { + if len(parent) >= 1024 { // Sanity check + return "", ENAMETOOLONG; + } + fd, err := Open(parent, O_RDONLY, 0); + if err != nil { + return "", err; + } + + for { + names, err := fd.Readdirnames(100); + if err != nil { + fd.Close(); + return "", err; + } + for i, name := range names { + d, err := Lstat(parent + "/" + name); + if d.Dev == dot.Dev && d.Ino == dot.Ino { + pwd = "/" + name + pwd; + goto Found; + } + } + } + fd.Close(); + return "", ENOENT; + + Found: + pd, err := fd.Stat(); + if err != nil { + return "", err; + } + fd.Close(); + if pd.Dev == root.Dev && pd.Ino == root.Ino { + break; + } + // Set up for next round. + dot = pd; + } + return pwd, nil +} diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go new file mode 100644 index 000000000..9f3e833f3 --- /dev/null +++ b/src/pkg/os/os_test.go @@ -0,0 +1,549 @@ +// Copyright 2009 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 + +import ( + "fmt"; + "io"; + "os"; + "testing"; +) + +var dot = []string{ + "dir_darwin_amd64.go", + "dir_linux_amd64.go", + "env.go", + "error.go", + "file.go", + "os_test.go", + "time.go", + "types.go", + "stat_darwin_amd64.go", + "stat_linux_amd64.go" +} + +var etc = []string{ + "group", + "hosts", + "passwd", +} + +func size(name string, t *testing.T) uint64 { + file, err := Open(name, O_RDONLY, 0); + defer file.Close(); + if err != nil { + t.Fatal("open failed:", err); + } + var buf [100]byte; + len := 0; + for { + n, e := file.Read(&buf); + if n < 0 || e != nil { + t.Fatal("read failed:", err); + } + if n == 0 { + break + } + len += n; + } + return uint64(len) +} + +func TestStat(t *testing.T) { + dir, err := Stat("/etc/passwd"); + if err != nil { + t.Fatal("stat failed:", err); + } + if dir.Name != "passwd" { + t.Error("name should be passwd; is", dir.Name); + } + filesize := size("/etc/passwd", t); + if dir.Size != filesize { + t.Error("size should be ", filesize, "; is", dir.Size); + } +} + +func TestFstat(t *testing.T) { + file, err1 := Open("/etc/passwd", O_RDONLY, 0); + defer file.Close(); + if err1 != nil { + t.Fatal("open failed:", err1); + } + dir, err2 := file.Stat(); + if err2 != nil { + t.Fatal("fstat failed:", err2); + } + if dir.Name != "passwd" { + t.Error("name should be passwd; is", dir.Name); + } + filesize := size("/etc/passwd", t); + if dir.Size != filesize { + t.Error("size should be ", filesize, "; is", dir.Size); + } +} + +func TestLstat(t *testing.T) { + dir, err := Lstat("/etc/passwd"); + if err != nil { + t.Fatal("lstat failed:", err); + } + if dir.Name != "passwd" { + t.Error("name should be passwd; is", dir.Name); + } + filesize := size("/etc/passwd", t); + if dir.Size != filesize { + t.Error("size should be ", filesize, "; is", dir.Size); + } +} + +func testReaddirnames(dir string, contents []string, t *testing.T) { + file, err := Open(dir, O_RDONLY, 0); + defer file.Close(); + if err != nil { + t.Fatalf("open %q failed: %v", dir, err); + } + s, err2 := file.Readdirnames(-1); + if err2 != nil { + t.Fatalf("readdirnames %q failed: %v", err2); + } + for i, m := range contents { + found := false; + for j, n := range s { + if n == "." || n == ".." { + t.Errorf("got %s in directory", n); + } + if m == n { + if found { + t.Error("present twice:", m); + } + found = true + } + } + if !found { + t.Error("could not find", m); + } + } +} + +func testReaddir(dir string, contents []string, t *testing.T) { + file, err := Open(dir, O_RDONLY, 0); + defer file.Close(); + if err != nil { + t.Fatalf("open %q failed: %v", dir, err); + } + s, err2 := file.Readdir(-1); + if err2 != nil { + t.Fatalf("readdir %q failed: %v", dir, err2); + } + for i, m := range contents { + found := false; + for j, n := range s { + if m == n.Name { + if found { + t.Error("present twice:", m); + } + found = true + } + } + if !found { + t.Error("could not find", m); + } + } +} + +func TestReaddirnames(t *testing.T) { + testReaddirnames(".", dot, t); + testReaddirnames("/etc", etc, t); +} + +func TestReaddir(t *testing.T) { + testReaddir(".", dot, t); + testReaddir("/etc", etc, t); +} + +// Read the directory one entry at a time. +func smallReaddirnames(file *File, length int, t *testing.T) []string { + names := make([]string, length); + count := 0; + for { + d, err := file.Readdirnames(1); + if err != nil { + t.Fatalf("readdir %q failed: %v", file.Name(), err); + } + if len(d) == 0 { + break + } + names[count] = d[0]; + count++; + } + return names[0:count] +} + +// Check that reading a directory one entry at a time gives the same result +// as reading it all at once. +func TestReaddirnamesOneAtATime(t *testing.T) { + dir := "/usr/bin"; // big directory that doesn't change often. + file, err := Open(dir, O_RDONLY, 0); + defer file.Close(); + if err != nil { + t.Fatalf("open %q failed: %v", dir, err); + } + all, err1 := file.Readdirnames(-1); + if err1 != nil { + t.Fatalf("readdirnames %q failed: %v", dir, err1); + } + file1, err2 := Open(dir, O_RDONLY, 0); + if err2 != nil { + t.Fatalf("open %q failed: %v", dir, err2); + } + small := smallReaddirnames(file1, len(all)+100, t); // +100 in case we screw up + for i, n := range all { + if small[i] != n { + t.Errorf("small read %q %q mismatch: %v", small[i], n); + } + } +} + +func TestHardLink(t *testing.T) { + from, to := "hardlinktestfrom", "hardlinktestto"; + Remove(from); // Just in case. + file, err := Open(to, O_CREAT | O_WRONLY, 0666); + if err != nil { + t.Fatalf("open %q failed: %v", to, err); + } + defer Remove(to); + if err = file.Close(); err != nil { + t.Errorf("close %q failed: %v", to, err); + } + err = Link(to, from); + if err != nil { + t.Fatalf("link %q, %q failed: %v", to, from, err); + } + defer Remove(from); + tostat, err := Stat(to); + if err != nil { + t.Fatalf("stat %q failed: %v", to, err); + } + fromstat, err := Stat(from); + if err != nil { + t.Fatalf("stat %q failed: %v", from, err); + } + if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino { + t.Errorf("link %q, %q did not create hard link", to, from); + } +} + +func TestSymLink(t *testing.T) { + from, to := "symlinktestfrom", "symlinktestto"; + Remove(from); // Just in case. + file, err := Open(to, O_CREAT | O_WRONLY, 0666); + if err != nil { + t.Fatalf("open %q failed: %v", to, err); + } + defer Remove(to); + if err = file.Close(); err != nil { + t.Errorf("close %q failed: %v", to, err); + } + err = Symlink(to, from); + if err != nil { + t.Fatalf("symlink %q, %q failed: %v", to, from, err); + } + defer Remove(from); + tostat, err := Stat(to); + if err != nil { + t.Fatalf("stat %q failed: %v", to, err); + } + if tostat.FollowedSymlink { + t.Fatalf("stat %q claims to have followed a symlink", to); + } + fromstat, err := Stat(from); + if err != nil { + t.Fatalf("stat %q failed: %v", from, err); + } + if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino { + t.Errorf("symlink %q, %q did not create symlink", to, from); + } + fromstat, err = Lstat(from); + if err != nil { + t.Fatalf("lstat %q failed: %v", from, err); + } + if !fromstat.IsSymlink() { + t.Fatalf("symlink %q, %q did not create symlink", to, from); + } + fromstat, err = Stat(from); + if err != nil { + t.Fatalf("stat %q failed: %v", from, err); + } + if !fromstat.FollowedSymlink { + t.Fatalf("stat %q did not follow symlink"); + } + s, err := Readlink(from); + if err != nil { + t.Fatalf("readlink %q failed: %v", from, err); + } + if s != to { + t.Fatalf("after symlink %q != %q", s, to); + } + file, err = Open(from, O_RDONLY, 0); + if err != nil { + t.Fatalf("open %q failed: %v", from, err); + } + file.Close(); +} + +func TestLongSymlink(t *testing.T) { + s := "0123456789abcdef"; + s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s + s; + from := "longsymlinktestfrom"; + err := Symlink(s, from); + if err != nil { + t.Fatalf("symlink %q, %q failed: %v", s, from, err); + } + defer Remove(from); + r, err := Readlink(from); + if err != nil { + t.Fatalf("readlink %q failed: %v", from, err); + } + if r != s { + t.Fatalf("after symlink %q != %q", r, s); + } +} + +func TestForkExec(t *testing.T) { + r, w, err := Pipe(); + if err != nil { + t.Fatalf("Pipe: %v", err); + } + pid, err := ForkExec("/bin/pwd", []string{"pwd"}, nil, "/", []*File{nil, w, Stderr}); + if err != nil { + t.Fatalf("ForkExec: %v", err); + } + w.Close(); + + var b io.ByteBuffer; + io.Copy(r, &b); + output := string(b.Data()); + expect := "/\n"; + if output != expect { + t.Errorf("exec /bin/pwd returned %q wanted %q", output, expect); + } + Wait(pid, 0); +} + +func checkMode(t *testing.T, path string, mode uint32) { + dir, err := Stat(path); + if err != nil { + t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err); + } + if dir.Mode & 0777 != mode { + t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode, 0777); + } +} + +func TestChmod(t *testing.T) { + MkdirAll("_obj", 0777); + const Path = "_obj/_TestChmod_"; + fd, err := Open(Path, O_WRONLY | O_CREAT, 0666); + if err != nil { + t.Fatalf("create %s: %s", Path, err); + } + + if err = Chmod(Path, 0456); err != nil { + t.Fatalf("chmod %s 0456: %s", Path, err); + } + checkMode(t, Path, 0456); + + if err = fd.Chmod(0123); err != nil { + t.Fatalf("fchmod %s 0123: %s", Path, err); + } + checkMode(t, Path, 0123); + + fd.Close(); + Remove(Path); +} + +func checkUidGid(t *testing.T, path string, uid, gid int) { + dir, err := Stat(path); + if err != nil { + t.Fatalf("Stat %q (looking for uid/gid %#o/%#o): %s", path, uid, gid, err); + } + if dir.Uid != uint32(uid) { + t.Errorf("Stat %q: uid %#o want %#o", path, dir.Uid, uid); + } + if dir.Gid != uint32(gid) { + t.Errorf("Stat %q: gid %#o want %#o", path, dir.Gid, uid); + } +} + +func TestChown(t *testing.T) { + // Use /tmp, not _obj, to make sure we're on a local file system, + // so that the group ids returned by Getgroups will be allowed + // on the file. If _obj is on NFS, the Getgroups groups are + // basically useless. + + const Path = "/tmp/_TestChown_"; + fd, err := Open(Path, O_WRONLY | O_CREAT, 0666); + if err != nil { + t.Fatalf("create %s: %s", Path, err); + } + dir, err := fd.Stat(); + if err != nil { + t.Fatalf("fstat %s: %s", Path, err); + } + defer fd.Close(); + defer Remove(Path); + + // Can't change uid unless root, but can try + // changing the group id. First try our current group. + gid := Getgid(); + if err = Chown(Path, -1, gid); err != nil { + t.Fatalf("chown %s -1 %d: %s", Path, gid, err); + } + checkUidGid(t, Path, int(dir.Uid), gid); + + // Then try all the auxiliary groups. + groups, err := Getgroups(); + if err != nil { + t.Fatalf("getgroups: %s", err); + } + for i, g := range groups { + if err = Chown(Path, -1, g); err != nil { + t.Fatalf("chown %s -1 %d: %s", Path, g, err); + } + checkUidGid(t, Path, int(dir.Uid), g); + + // change back to gid to test fd.Chown + if err = fd.Chown(-1, gid); err != nil { + t.Fatalf("fchown %s -1 %d: %s", Path, gid, err); + } + checkUidGid(t, Path, int(dir.Uid), gid); + } +} + +func checkSize(t *testing.T, path string, size uint64) { + dir, err := Stat(path); + if err != nil { + t.Fatalf("Stat %q (looking for size %d): %s", path, size, err); + } + if dir.Size != size { + t.Errorf("Stat %q: size %d want %d", path, dir.Size, size); + } +} + +func TestTruncate(t *testing.T) { + MkdirAll("_obj", 0777); + const Path = "_obj/_TestTruncate_"; + fd, err := Open(Path, O_WRONLY | O_CREAT, 0666); + if err != nil { + t.Fatalf("create %s: %s", Path, err); + } + + checkSize(t, Path, 0); + fd.Write(io.StringBytes("hello, world\n")); + checkSize(t, Path, 13); + fd.Truncate(10); + checkSize(t, Path, 10); + fd.Truncate(1024); + checkSize(t, Path, 1024); + fd.Truncate(0); + checkSize(t, Path, 0); + fd.Write(io.StringBytes("surprise!")); + checkSize(t, Path, 13 + 9); // wrote at offset past where hello, world was. + fd.Close(); + Remove(Path); +} + +func TestChdirAndGetwd(t *testing.T) { + fd, err := Open(".", O_RDONLY, 0); + if err != nil { + t.Fatalf("Open .: %s", err); + } + // These are chosen carefully not to be symlinks on a Mac + // (unlike, say, /var, /etc, and /tmp). + dirs := []string{ "/bin", "/", "/usr/local/bin" }; + for mode := 0; mode < 2; mode++ { + for i, d := range dirs { + if mode == 0 { + err = Chdir(d); + } else { + fd1, err := Open(d, O_RDONLY, 0); + if err != nil { + t.Errorf("Open %s: %s", d, err); + continue; + } + err = fd1.Chdir(); + fd1.Close(); + } + pwd, err1 := Getwd(); + err2 := fd.Chdir(); + if err2 != nil { + // We changed the current directory and cannot go back. + // Don't let the tests continue; they'll scribble + // all over some other directory. + fmt.Fprintf(Stderr, "fchdir back to dot failed: %s\n", err2); + Exit(1); + } + if err != nil { + fd.Close(); + t.Fatalf("Chdir %s: %s", d, err); + } + if err1 != nil { + fd.Close(); + t.Fatalf("Getwd in %s: %s", d, err1); + } + if pwd != d { + fd.Close(); + t.Fatalf("Getwd returned %q want %q", pwd, d); + } + } + } + fd.Close(); +} + +func TestTime(t *testing.T) { + // Just want to check that Time() is getting something. + // A common failure mode on Darwin is to get 0, 0, + // because it returns the time in registers instead of + // filling in the structure passed to the system call. + // TODO(rsc): Too bad the compiler doesn't know that + // 365.24*86400 is an integer. + sec, nsec, err := Time(); + if sec < (2009-1970)*36524*864 { + t.Errorf("Time() = %d, %d, %s; not plausible", sec, nsec, err); + } +} + +func TestSeek(t *testing.T) { + f, err := Open("_obj/seektest", O_CREAT|O_RDWR|O_TRUNC, 0666); + if err != nil { + t.Fatalf("open _obj/seektest: %s", err); + } + + const data = "hello, world\n"; + io.WriteString(f, data); + + type test struct { + in int64; + whence int; + out int64; + } + var tests = []test { + test{ 0, 1, int64(len(data)) }, + test{ 0, 0, 0 }, + test{ 5, 0, 5 }, + test{ 0, 2, int64(len(data)) }, + test{ 0, 0, 0 }, + test{ -1, 2, int64(len(data)) - 1 }, + test{ 1<<40, 0, 1<<40 }, + test{ 1<<40, 2, 1<<40 + int64(len(data)) } + }; + for i, tt := range tests { + off, err := f.Seek(tt.in, tt.whence); + if off != tt.out || err != nil { + t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out); + } + } + f.Close(); +} diff --git a/src/pkg/os/path.go b/src/pkg/os/path.go new file mode 100644 index 000000000..0b86b8f8b --- /dev/null +++ b/src/pkg/os/path.go @@ -0,0 +1,121 @@ +// Copyright 2009 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 + +import "os" + +// PathError reports an error and the file path where it occurred. +type PathError struct { + Path string; + Error Error; +} + +func (p *PathError) String() string { + return p.Path + ": " + p.Error.String(); +} + +// MkdirAll creates a directory named path, +// along with any necessary parents, and returns nil, +// or else returns an error. +// The permission bits perm are used for all +// directories that MkdirAll creates. +// If path is already a directory, MkdirAll does nothing +// and returns nil. +func MkdirAll(path string, perm int) Error { + // If path exists, stop with success or error. + dir, err := os.Lstat(path); + if err == nil { + if dir.IsDirectory() { + return nil; + } + return &PathError{path, ENOTDIR}; + } + + // Doesn't already exist; make sure parent does. + i := len(path); + for i > 0 && path[i-1] == '/' { // Skip trailing slashes. + i--; + } + + j := i; + for j > 0 && path[j-1] != '/' { // Scan backward over element. + j--; + } + + if j > 0 { + // Create parent + err = MkdirAll(path[0:j-1], perm); + if err != nil { + return err; + } + } + + // Now parent exists, try to create. + err = Mkdir(path, perm); + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := os.Lstat(path); + if err1 == nil && dir.IsDirectory() { + return nil; + } + return &PathError{path, err}; + } + return nil; +} + +// RemoveAll removes path and any children it contains. +// It removes everything it can but returns the first error +// it encounters. +func RemoveAll(path string) Error { + // Simple case: if Remove works, we're done. + err := Remove(path); + if err == nil { + return nil; + } + + // Otherwise, is this a directory we need to recurse into? + dir, err1 := os.Lstat(path); + if err1 != nil { + return &PathError{path, err1}; + } + if !dir.IsDirectory() { + // Not a directory; return the error from Remove. + return &PathError{path, err}; + } + + // Directory. + fd, err := Open(path, os.O_RDONLY, 0); + if err != nil { + return &PathError{path, err}; + } + defer fd.Close(); + + // Remove contents & return first error. + err = nil; + for { + names, err1 := fd.Readdirnames(100); + for i, name := range names { + err1 := RemoveAll(path + "/" + name); + if err1 != nil && err == nil { + err = err1; + } + } + // If Readdirnames returned an error, use it. + if err1 != nil && err == nil { + err = &PathError{path, err1}; + } + if len(names) == 0 { + break; + } + } + + // Remove directory. + err1 = Remove(path); + if err1 != nil && err == nil { + err = &PathError{path, err1}; + } + return err; +} diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go new file mode 100644 index 000000000..bb6148920 --- /dev/null +++ b/src/pkg/os/path_test.go @@ -0,0 +1,152 @@ +// Copyright 2009 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 + +import ( + "os"; + "testing"; +) + +func TestMkdirAll(t *testing.T) { + // Create new dir, in _obj so it will get + // cleaned up by make if not by us. + path := "_obj/_TestMkdirAll_/dir/./dir2"; + err := MkdirAll(path, 0777); + if err != nil { + t.Fatalf("MkdirAll %q: %s", path, err); + } + + // Already exists, should succeed. + err = MkdirAll(path, 0777); + if err != nil { + t.Fatalf("MkdirAll %q (second time): %s", path, err); + } + + // Make file. + fpath := path + "/file"; + fd, err := os.Open(fpath, os.O_WRONLY | os.O_CREAT, 0666); + if err != nil { + t.Fatalf("create %q: %s", fpath, err); + } + + // Can't make directory named after file. + err = MkdirAll(fpath, 0777); + if err == nil { + t.Fatalf("MkdirAll %q: no error"); + } + perr, ok := err.(*PathError); + if !ok { + t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err); + } + if perr.Path != fpath { + t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, perr.Path, fpath); + } + + // Can't make subdirectory of file. + ffpath := fpath + "/subdir"; + err = MkdirAll(ffpath, 0777); + if err == nil { + t.Fatalf("MkdirAll %q: no error"); + } + perr, ok = err.(*PathError); + if !ok { + t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err); + } + if perr.Path != fpath { + t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, perr.Path, fpath); + } + + RemoveAll("_obj/_TestMkdirAll_"); +} + +func TestRemoveAll(t *testing.T) { + // Work directory. + path := "_obj/_TestRemoveAll_"; + fpath := path + "/file"; + dpath := path + "/dir"; + + // Make directory with 1 file and remove. + if err := MkdirAll(path, 0777); err != nil { + t.Fatalf("MkdirAll %q: %s", path, err); + } + fd, err := os.Open(fpath, os.O_WRONLY | os.O_CREAT, 0666); + if err != nil { + t.Fatalf("create %q: %s", fpath, err); + } + fd.Close(); + if err = RemoveAll(path); err != nil { + t.Fatalf("RemoveAll %q (first): %s", path, err); + } + if dir, err := os.Lstat(path); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path); + } + + // Make directory with file and subdirectory and remove. + if err = MkdirAll(dpath, 0777); err != nil { + t.Fatalf("MkdirAll %q: %s", dpath, err); + } + fd, err = os.Open(fpath, os.O_WRONLY | os.O_CREAT, 0666); + if err != nil { + t.Fatalf("create %q: %s", fpath, err); + } + fd.Close(); + fd, err = os.Open(dpath+"/file", os.O_WRONLY | os.O_CREAT, 0666); + if err != nil { + t.Fatalf("create %q: %s", fpath, err); + } + fd.Close(); + if err = RemoveAll(path); err != nil { + t.Fatalf("RemoveAll %q (second): %s", path, err); + } + if dir, err := os.Lstat(path); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path); + } + + // Make directory with file and subdirectory and trigger error. + if err = MkdirAll(dpath, 0777); err != nil { + t.Fatalf("MkdirAll %q: %s", dpath, err); + } + + // TODO(rsc): toss tmp once bug152 is fixed + tmp := []string{fpath, dpath+"/file1", path+"/zzz"}; + for i, s := range tmp { + fd, err = os.Open(s, os.O_WRONLY | os.O_CREAT, 0666); + if err != nil { + t.Fatalf("create %q: %s", s, err); + } + fd.Close(); + } + if err = os.Chmod(dpath, 0); err != nil { + t.Fatalf("Chmod %q 0: %s", dpath, err); + } + if err = RemoveAll(path); err == nil { + dir, err := Lstat(path); + if err == nil { + t.Errorf("Can lstat %q after supposed RemoveAll", path); + } + t.Fatalf("RemoveAll %q succeeded with chmod 0 subdirectory", path, err); + } + perr, ok := err.(*PathError); + if !ok { + t.Fatalf("RemoveAll %q returned %T not *PathError", path, err); + } + if perr.Path != dpath { + t.Fatalf("RemoveAll %q failed at %q not %q", path, perr.Path, dpath); + } + if err = os.Chmod(dpath, 0777); err != nil { + t.Fatalf("Chmod %q 0777: %s", dpath, err); + } + for i, s := range []string{fpath, path+"/zzz"} { + if dir, err := os.Lstat(s); err == nil { + t.Fatalf("Lstat %q succeeded after partial RemoveAll", s); + } + } + if err = RemoveAll(path); err != nil { + t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err); + } + if dir, err := os.Lstat(path); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path); + } +} diff --git a/src/pkg/os/proc.go b/src/pkg/os/proc.go new file mode 100644 index 000000000..d2fd6493e --- /dev/null +++ b/src/pkg/os/proc.go @@ -0,0 +1,50 @@ +// Copyright 2009 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. + +// Process etc. + +package os + +import ( + "syscall"; + "os"; + "unsafe"; +) + +var Args []string; // provided by runtime +var Envs []string; // provided by runtime + + +// Getuid returns the numeric user id of the caller. +func Getuid() int { + return syscall.Getuid(); +} + +// Geteuid returns the numeric effective user id of the caller. +func Geteuid() int { + return syscall.Geteuid(); +} + +// Getgid returns the numeric group id of the caller. +func Getgid() int { + return syscall.Getgid(); +} + +// Getegid returns the numeric effective group id of the caller. +func Getegid() int { + return syscall.Getegid(); +} + +// Getgroups returns a list of the numeric ids of groups that the caller belongs to. +func Getgroups() ([]int, os.Error) { + gids, errno := syscall.Getgroups(); + return gids, ErrnoToError(errno); +} + +// Exit causes the current program to exit with the given status code. +// Conventionally, code zero indicates success, non-zero an error. +func Exit(code int) { + syscall.Exit(code); +} + diff --git a/src/pkg/os/proc_linux.go b/src/pkg/os/proc_linux.go new file mode 100644 index 000000000..a802284f3 --- /dev/null +++ b/src/pkg/os/proc_linux.go @@ -0,0 +1,20 @@ +// Copyright 2009 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 + +import ( + "os"; + "syscall"; +) + +var Args []string; // provided by runtime +var Envs []string; // provided by runtime + +// Exit causes the current program to exit with the given status code. +// Conventionally, code zero indicates success, non-zero an error. +func Exit(code int) { + syscall.Syscall(syscall.SYS_EXIT_GROUP, int64(code), 0, 0) +} + diff --git a/src/pkg/os/stat_darwin_386.go b/src/pkg/os/stat_darwin_386.go new file mode 100644 index 000000000..a6d7b78d1 --- /dev/null +++ b/src/pkg/os/stat_darwin_386.go @@ -0,0 +1,41 @@ +// Copyright 2009 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. + +// 386, Darwin + +package os + +import syscall "syscall" +import os "os" + +func isSymlink(stat *syscall.Stat_t) bool { + return stat.Mode & syscall.S_IFMT == syscall.S_IFLNK +} + +func dirFromStat(name string, dir *Dir, lstat, stat *syscall.Stat_t) *Dir { + dir.Dev = uint64(stat.Dev); + dir.Ino = stat.Ino; + dir.Nlink = uint64(stat.Nlink); + dir.Mode = uint32(stat.Mode); + dir.Uid = stat.Uid; + dir.Gid = stat.Gid; + dir.Rdev = uint64(stat.Rdev); + dir.Size = uint64(stat.Size); + dir.Blksize = uint64(stat.Blksize); + dir.Blocks = uint64(stat.Blocks); + dir.Atime_ns = uint64(syscall.TimespecToNsec(stat.Atimespec)); + dir.Mtime_ns = uint64(syscall.TimespecToNsec(stat.Mtimespec)); + dir.Ctime_ns = uint64(syscall.TimespecToNsec(stat.Ctimespec)); + for i := len(name) - 1; i >= 0; i-- { + if name[i] == '/' { + name = name[i+1:len(name)]; + break; + } + } + dir.Name = name; + if isSymlink(lstat) && !isSymlink(stat) { + dir.FollowedSymlink = true; + } + return dir; +} diff --git a/src/pkg/os/stat_darwin_amd64.go b/src/pkg/os/stat_darwin_amd64.go new file mode 100644 index 000000000..1771ca160 --- /dev/null +++ b/src/pkg/os/stat_darwin_amd64.go @@ -0,0 +1,41 @@ +// Copyright 2009 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. + +// AMD64, Darwin + +package os + +import syscall "syscall" +import os "os" + +func isSymlink(stat *syscall.Stat_t) bool { + return stat.Mode & syscall.S_IFMT == syscall.S_IFLNK +} + +func dirFromStat(name string, dir *Dir, lstat, stat *syscall.Stat_t) *Dir { + dir.Dev = uint64(stat.Dev); + dir.Ino = stat.Ino; + dir.Nlink = uint64(stat.Nlink); + dir.Mode = uint32(stat.Mode); + dir.Uid = stat.Uid; + dir.Gid = stat.Gid; + dir.Rdev = uint64(stat.Rdev); + dir.Size = uint64(stat.Size); + dir.Blksize = uint64(stat.Blksize); + dir.Blocks = uint64(stat.Blocks); + dir.Atime_ns = uint64(syscall.TimespecToNsec(stat.Atimespec)); + dir.Mtime_ns = uint64(syscall.TimespecToNsec(stat.Mtimespec)); + dir.Ctime_ns = uint64(syscall.TimespecToNsec(stat.Ctimespec)); + for i := len(name) - 1; i >= 0; i-- { + if name[i] == '/' { + name = name[i+1:len(name)]; + break; + } + } + dir.Name = name; + if isSymlink(lstat) && !isSymlink(stat) { + dir.FollowedSymlink = true; + } + return dir; +} diff --git a/src/pkg/os/stat_linux_386.go b/src/pkg/os/stat_linux_386.go new file mode 100644 index 000000000..13ee942c9 --- /dev/null +++ b/src/pkg/os/stat_linux_386.go @@ -0,0 +1,47 @@ +// Copyright 2009 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. + +// TODO(rsc): Once the porting dust settles, consider +// whether this file should be stat_linux.go (and similarly +// stat_darwin.go) instead of having one copy per architecture. + +// 386, Linux + +package os + +import ( + "os"; + "syscall"; +) + +func isSymlink(stat *syscall.Stat_t) bool { + return stat.Mode & syscall.S_IFMT == syscall.S_IFLNK +} + +func dirFromStat(name string, dir *Dir, lstat, stat *syscall.Stat_t) *Dir { + dir.Dev = stat.Dev; + dir.Ino = uint64(stat.Ino); + dir.Nlink = uint64(stat.Nlink); + dir.Mode = stat.Mode; + dir.Uid = stat.Uid; + dir.Gid = stat.Gid; + dir.Rdev = stat.Rdev; + dir.Size = uint64(stat.Size); + dir.Blksize = uint64(stat.Blksize); + dir.Blocks = uint64(stat.Blocks); + dir.Atime_ns = uint64(syscall.TimespecToNsec(stat.Atim)); + dir.Mtime_ns = uint64(syscall.TimespecToNsec(stat.Mtim)); + dir.Ctime_ns = uint64(syscall.TimespecToNsec(stat.Ctim)); + for i := len(name) - 1; i >= 0; i-- { + if name[i] == '/' { + name = name[i+1:len(name)]; + break; + } + } + dir.Name = name; + if isSymlink(lstat) && !isSymlink(stat) { + dir.FollowedSymlink = true; + } + return dir; +} diff --git a/src/pkg/os/stat_linux_amd64.go b/src/pkg/os/stat_linux_amd64.go new file mode 100644 index 000000000..9b3018178 --- /dev/null +++ b/src/pkg/os/stat_linux_amd64.go @@ -0,0 +1,41 @@ +// Copyright 2009 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. + +// AMD64, Linux + +package os + +import syscall "syscall" +import os "os" + +func isSymlink(stat *syscall.Stat_t) bool { + return stat.Mode & syscall.S_IFMT == syscall.S_IFLNK +} + +func dirFromStat(name string, dir *Dir, lstat, stat *syscall.Stat_t) *Dir { + dir.Dev = stat.Dev; + dir.Ino = stat.Ino; + dir.Nlink = stat.Nlink; + dir.Mode = stat.Mode; + dir.Uid = stat.Uid; + dir.Gid = stat.Gid; + dir.Rdev = stat.Rdev; + dir.Size = uint64(stat.Size); + dir.Blksize = uint64(stat.Blksize); + dir.Blocks = uint64(stat.Blocks); + dir.Atime_ns = uint64(syscall.TimespecToNsec(stat.Atim)); + dir.Mtime_ns = uint64(syscall.TimespecToNsec(stat.Mtim)); + dir.Ctime_ns = uint64(syscall.TimespecToNsec(stat.Ctim)); + for i := len(name) - 1; i >= 0; i-- { + if name[i] == '/' { + name = name[i+1:len(name)]; + break; + } + } + dir.Name = name; + if isSymlink(lstat) && !isSymlink(stat) { + dir.FollowedSymlink = true; + } + return dir; +} diff --git a/src/pkg/os/time.go b/src/pkg/os/time.go new file mode 100644 index 000000000..3eee243cc --- /dev/null +++ b/src/pkg/os/time.go @@ -0,0 +1,24 @@ +// Copyright 2009 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 + +import ( + "os"; + "syscall" +) + + +// Time returns the current time, in whole seconds and +// fractional nanoseconds, plus an Error if any. The current +// time is thus 1e9*sec+nsec, in nanoseconds. The zero of +// time is the Unix epoch. +func Time() (sec int64, nsec int64, err Error) { + var tv syscall.Timeval; + if errno := syscall.Gettimeofday(&tv); errno != 0 { + return 0, 0, ErrnoToError(errno) + } + return int64(tv.Sec), int64(tv.Usec)*1000, err; +} + diff --git a/src/pkg/os/types.go b/src/pkg/os/types.go new file mode 100644 index 000000000..b5db86660 --- /dev/null +++ b/src/pkg/os/types.go @@ -0,0 +1,75 @@ +// Copyright 2009 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 + +import "syscall" + +// An operating-system independent representation of Unix data structures. +// OS-specific routines in this directory convert the OS-local versions to these. + +// Getpagesize returns the underlying system's memory page size. +func Getpagesize() int{ + return syscall.Getpagesize() +} + +// A Dir describes a file and is returned by Stat, Fstat, and Lstat +type Dir struct { + Dev uint64; // device number of file system holding file. + Ino uint64; // inode number. + Nlink uint64; // number of hard links. + Mode uint32; // permission and mode bits. + Uid uint32; // user id of owner. + Gid uint32; // group id of owner. + Rdev uint64; // device type for special file. + Size uint64; // length in bytes. + Blksize uint64; // size of blocks, in bytes. + Blocks uint64; // number of blocks allocated for file. + Atime_ns uint64; // access time; nanoseconds since epoch. + Mtime_ns uint64; // modified time; nanoseconds since epoch. + Ctime_ns uint64; // status change time; nanoseconds since epoch. + Name string; // name of file as presented to Open. + FollowedSymlink bool; // followed a symlink to get this information +} + +// IsFifo reports whether the Dir describes a FIFO file. +func (dir *Dir) IsFifo() bool { + return (dir.Mode & syscall.S_IFMT) == syscall.S_IFIFO +} + +// IsChar reports whether the Dir describes a character special file. +func (dir *Dir) IsChar() bool { + return (dir.Mode & syscall.S_IFMT) == syscall.S_IFCHR +} + +// IsDirectory reports whether the Dir describes a directory. +func (dir *Dir) IsDirectory() bool { + return (dir.Mode & syscall.S_IFMT) == syscall.S_IFDIR +} + +// IsBlock reports whether the Dir describes a block special file. +func (dir *Dir) IsBlock() bool { + return (dir.Mode & syscall.S_IFMT) == syscall.S_IFBLK +} + +// IsRegular reports whether the Dir describes a regular file. +func (dir *Dir) IsRegular() bool { + return (dir.Mode & syscall.S_IFMT) == syscall.S_IFREG +} + +// IsSymlink reports whether the Dir describes a symbolic link. +func (dir *Dir) IsSymlink() bool { + return (dir.Mode & syscall.S_IFMT) == syscall.S_IFLNK +} + +// IsSocket reports whether the Dir describes a socket. +func (dir *Dir) IsSocket() bool { + return (dir.Mode & syscall.S_IFMT) == syscall.S_IFSOCK +} + +// Permission returns the file permission bits. +func (dir *Dir) Permission() int { + return int(dir.Mode & 0777) +} + diff --git a/src/pkg/path/Makefile b/src/pkg/path/Makefile new file mode 100644 index 000000000..d9f9fd562 --- /dev/null +++ b/src/pkg/path/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + path.$O\ + + +phases: a1 +_obj$D/path.a: phases + +a1: $(O1) + $(AR) grc _obj$D/path.a path.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/path.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/path.a + +packages: _obj$D/path.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/path.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/path.a diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go new file mode 100644 index 000000000..a7e2c26c3 --- /dev/null +++ b/src/pkg/path/path.go @@ -0,0 +1,136 @@ +// Copyright 2009 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. + +// The path package implements utility routines for manipulating +// slash-separated filename paths. +package path + +import "io" + +// Clean returns the shortest path name equivalent to path +// by purely lexical processing. It applies the following rules +// iteratively until no further processing can be done: +// +// 1. Replace multiple slashes with a single slash. +// 2. Eliminate each . path name element (the current directory). +// 3. Eliminate each inner .. path name element (the parent directory) +// along with the non-.. element that precedes it. +// 4. Eliminate .. elements that begin a rooted path: +// that is, replace "/.." by "/" at the beginning of a path. +// +// If the result of this process is an empty string, Clean +// returns the string ".". +// +// See also Rob Pike, ``Lexical File Names in Plan 9 or +// Getting Dot-Dot right,'' +// http://plan9.bell-labs.com/sys/doc/lexnames.html +func Clean(path string) string { + if path == "" { + return "." + } + + rooted := path[0] == '/'; + n := len(path); + + // Invariants: + // reading from path; r is index of next byte to process. + // writing to buf; w is index of next byte to write. + // dotdot is index in buf where .. must stop, either because + // it is the leading slash or it is a leading ../../.. prefix. + buf := io.StringBytes(path); + r, w, dotdot := 0, 0, 0; + if rooted { + r, w, dotdot = 1, 1, 1; + } + + for r < n { + switch { + case path[r] == '/': + // empty path element + r++; + case path[r] == '.' && (r+1 == n || path[r+1] == '/'): + // . element + r++; + case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == '/'): + // .. element: remove to last / + r += 2; + switch { + case w > dotdot: + // can backtrack + w--; + for w > dotdot && buf[w] != '/' { + w--; + } + case !rooted: + // cannot backtrack, but not rooted, so append .. element. + if w > 0 { + buf[w] = '/'; + w++; + } + buf[w] = '.'; + w++; + buf[w] = '.'; + w++; + dotdot = w; + } + default: + // real path element. + // add slash if needed + if rooted && w != 1 || !rooted && w != 0 { + buf[w] = '/'; + w++; + } + // copy element + for ; r < n && path[r] != '/'; r++ { + buf[w] = path[r]; + w++; + } + } + } + + // Turn empty string into "." + if w == 0 { + buf[w] = '.'; + w++; + } + + return string(buf[0:w]); +} + +// Split splits path immediately following the final slash, +// separating it into a directory and file name component. +// If there is no slash in path, DirFile returns an empty dir and +// file set to path. +func Split(path string) (dir, file string) { + for i := len(path)-1; i >= 0; i-- { + if path[i] == '/' { + return path[0:i+1], path[i+1:len(path)]; + } + } + return "", path +} + +// Join joins dir and file into a single path, adding a separating +// slash if necessary. If dir is empty, it returns file. +func Join(dir, file string) string { + if dir == "" { + return file; + } + return Clean(dir + "/" + file); +} + +// Ext returns the file name extension used by path. +// The extension is the suffix beginning at the final dot +// in the final slash-separated element of path; +// it is empty if there is no dot. +func Ext(path string) string { + dot := -1; + for i := len(path)-1; i >= 0 && path[i] != '/'; i-- { + if path[i] == '.' { + return path[i:len(path)]; + } + } + return "" +} + diff --git a/src/pkg/path/path_test.go b/src/pkg/path/path_test.go new file mode 100644 index 000000000..1238ac1cd --- /dev/null +++ b/src/pkg/path/path_test.go @@ -0,0 +1,136 @@ +// Copyright 2009 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 path + +import ( + "path"; + "testing" +) + +type CleanTest struct { + path, clean string +} + +var cleantests = []CleanTest { + // Already clean + CleanTest{"", "."}, + CleanTest{"abc", "abc"}, + CleanTest{"abc/def", "abc/def"}, + CleanTest{"a/b/c", "a/b/c"}, + CleanTest{".", "."}, + CleanTest{"..", ".."}, + CleanTest{"../..", "../.."}, + CleanTest{"../../abc", "../../abc"}, + CleanTest{"/abc", "/abc"}, + CleanTest{"/", "/"}, + + // Remove trailing slash + CleanTest{"abc/", "abc"}, + CleanTest{"abc/def/", "abc/def"}, + CleanTest{"a/b/c/", "a/b/c"}, + CleanTest{"./", "."}, + CleanTest{"../", ".."}, + CleanTest{"../../", "../.."}, + CleanTest{"/abc/", "/abc"}, + + // Remove doubled slash + CleanTest{"abc//def//ghi", "abc/def/ghi"}, + CleanTest{"//abc", "/abc"}, + CleanTest{"///abc", "/abc"}, + CleanTest{"//abc//", "/abc"}, + CleanTest{"abc//", "abc"}, + + // Remove . elements + CleanTest{"abc/./def", "abc/def"}, + CleanTest{"/./abc/def", "/abc/def"}, + CleanTest{"abc/.", "abc"}, + + // Remove .. elements + CleanTest{"abc/def/ghi/../jkl", "abc/def/jkl"}, + CleanTest{"abc/def/../ghi/../jkl", "abc/jkl"}, + CleanTest{"abc/def/..", "abc"}, + CleanTest{"abc/def/../..", "."}, + CleanTest{"/abc/def/../..", "/"}, + CleanTest{"abc/def/../../..", ".."}, + CleanTest{"/abc/def/../../..", "/"}, + CleanTest{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, + + // Combinations + CleanTest{"abc/./../def", "def"}, + CleanTest{"abc//./../def", "def"}, + CleanTest{"abc/../../././../def", "../../def"}, +} + +func TestClean(t *testing.T) { + for i, test := range cleantests { + if s := Clean(test.path); s != test.clean { + t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.clean); + } + } +} + +type SplitTest struct { + path, dir, file string +} + +var splittests = []SplitTest { + SplitTest{"a/b", "a/", "b"}, + SplitTest{"a/b/", "a/b/", ""}, + SplitTest{"a/", "a/", ""}, + SplitTest{"a", "", "a"}, + SplitTest{"/", "/", ""}, +} + +func TestSplit(t *testing.T) { + for i, test := range splittests { + if d, f := Split(test.path); d != test.dir || f != test.file { + t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file); + } + } +} + +type JoinTest struct { + dir, file, path string +} + +var jointests = []JoinTest { + JoinTest{"a", "b", "a/b"}, + JoinTest{"a", "", "a"}, + JoinTest{"", "b", "b"}, + JoinTest{"/", "a", "/a"}, + JoinTest{"/", "", "/"}, + JoinTest{"a/", "b", "a/b"}, + JoinTest{"a/", "", "a"}, +} + +func TestJoin(t *testing.T) { + for i, test := range jointests { + if p := Join(test.dir, test.file); p != test.path { + t.Errorf("Join(%q, %q) = %q, want %q", test.dir, test.file, p, test.path); + } + } +} + +type ExtTest struct { + path, ext string +} + +var exttests = []ExtTest { + ExtTest{"path.go", ".go"}, + ExtTest{"path.pb.go", ".go"}, + ExtTest{"path", ""}, + ExtTest{"a.dir/b", ""}, + ExtTest{"a.dir/b.go", ".go"}, + ExtTest{"a.dir/", ""}, +} + +func TestExt(t *testing.T) { + for i, test := range exttests { + if x := Ext(test.path); x != test.ext { + t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext); + } + } +} + diff --git a/src/pkg/rand/Makefile b/src/pkg/rand/Makefile new file mode 100644 index 000000000..63d26ac99 --- /dev/null +++ b/src/pkg/rand/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + rand.$O\ + + +phases: a1 +_obj$D/rand.a: phases + +a1: $(O1) + $(AR) grc _obj$D/rand.a rand.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/rand.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/rand.a + +packages: _obj$D/rand.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/rand.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/rand.a diff --git a/src/pkg/rand/rand.go b/src/pkg/rand/rand.go new file mode 100644 index 000000000..bc986cbcf --- /dev/null +++ b/src/pkg/rand/rand.go @@ -0,0 +1,318 @@ +// Copyright 2009 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. + +// Uniformly distributed pseudo-random numbers. +package rand + +/* + * algorithm by + * DP Mitchell and JA Reeds + */ + +const ( + _LEN = 607; + _TAP = 273; + _MAX = 1<<63; + _MASK = _MAX-1; + _A = 48271; + _M = (1<<31)-1; + _Q = 44488; + _R = 3399; +) + +var ( + rng_tap int; // index into vector + rng_feed int; // index into vector + rng_vec [_LEN]int64; // current feedback register + + // cooked random numbers + // the state of the rng + // after 780e10 iterations + rng_cooked [_LEN]int64 = [...]int64 { + 5041579894721019882, 4646389086726545243, 1395769623340756751, 5333664234075297259, + 2875692520355975054, 9033628115061424579, 7143218595135194537, 4812947590706362721, + 7937252194349799378, 5307299880338848416, 8209348851763925077, 2115741599318814044, + 4593015457530856296, 8140875735541888011, 3319429241265089026, 8619815648190321034, + 1727074043483619500, 113108499721038619, 4569519971459345583, 5062833859075314731, + 2387618771259064424, 2716131344356686112, 6559392774825876886, 7650093201692370310, + 7684323884043752161, 257867835996031390, 6593456519409015164, 271327514973697897, + 2789386447340118284, 1065192797246149621, 3344507881999356393, 4459797941780066633, + 7465081662728599889, 1014950805555097187, 4449440729345990775, 3481109366438502643, + 2418672789110888383, 5796562887576294778, 4484266064449540171, 3738982361971787048, + 4523597184512354423, 10530508058128498, 8633833783282346118, 2625309929628791628, + 8660405965245884302, 10162832508971942, 6540714680961817391, 7031802312784620857, + 6240911277345944669, 831864355460801054, 8004434137542152891, 2116287251661052151, + 2202309800992166967, 9161020366945053561, 4069299552407763864, 4936383537992622449, + 457351505131524928, 342195045928179354, 2847771682816600509, 2068020115986376518, + 4368649989588021065, 887231587095185257, 5563591506886576496, 6816225200251950296, + 5616972787034086048, 8471809303394836566, 1686575021641186857, 4045484338074262002, + 4244156215201778923, 7848217333783577387, 5632136521049761902, 833283142057835272, + 9029726508369077193, 3243583134664087292, 4316371101804477087, 8937849979965997980, + 6446940406810434101, 1679342092332374735, 6050638460742422078, 6993520719509581582, + 7640877852514293609, 5881353426285907985, 812786550756860885, 4541845584483343330, + 2725470216277009086, 4980675660146853729, 5210769080603236061, 8894283318990530821, + 6326442804750084282, 1495812843684243920, 7069751578799128019, 7370257291860230865, + 6756929275356942261, 4706794511633873654, 7824520467827898663, 8549875090542453214, + 33650829478596156, 1328918435751322643, 7297902601803624459, 1011190183918857495, + 2238025036817854944, 5147159997473910359, 896512091560522982, 2659470849286379941, + 6097729358393448602, 1731725986304753684, 4106255841983812711, 8327155210721535508, + 8477511620686074402, 5803876044675762232, 8435417780860221662, 5988852856651071244, + 4715837297103951910, 7566171971264485114, 505808562678895611, 5070098180695063370, + 842110666775871513, 572156825025677802, 1791881013492340891, 3393267094866038768, + 3778721850472236509, 2352769483186201278, 1292459583847367458, 8897907043675088419, + 5781809037144163536, 2733958794029492513, 5092019688680754699, 8996124554772526841, + 4234737173186232084, 5027558287275472836, 4635198586344772304, 8687338893267139351, + 5907508150730407386, 784756255473944452, 972392927514829904, 5422057694808175112, + 5158420642969283891, 9048531678558643225, 2407211146698877100, 7583282216521099569, + 3940796514530962282, 3341174631045206375, 3095313889586102949, 7405321895688238710, + 5832080132947175283, 7890064875145919662, 8184139210799583195, 1149859861409226130, + 1464597243840211302, 4641648007187991873, 3516491885471466898, 956288521791657692, + 6657089965014657519, 5220884358887979358, 1796677326474620641, 5340761970648932916, + 1147977171614181568, 5066037465548252321, 2574765911837859848, 1085848279845204775, + 3350107529868390359, 6116438694366558490, 2107701075971293812, 1803294065921269267, + 2469478054175558874, 7368243281019965984, 3791908367843677526, 185046971116456637, + 2257095756513439648, 7217693971077460129, 909049953079504259, 7196649268545224266, + 5637660345400869599, 3955544945427965183, 8057528650917418961, 4139268440301127643, + 6621926588513568059, 1373361136802681441, 6527366231383600011, 3507654575162700890, + 9202058512774729859, 1954818376891585542, 6640380907130175705, 8299563319178235687, + 3901867355218954373, 7046310742295574065, 6847195391333990232, 1572638100518868053, + 8850422670118399721, 3631909142291992901, 5158881091950831288, 2882958317343121593, + 4763258931815816403, 6280052734341785344, 4243789408204964850, 2043464728020827976, + 6545300466022085465, 4562580375758598164, 5495451168795427352, 1738312861590151095, + 553004618757816492, 6895160632757959823, 8233623922264685171, 7139506338801360852, + 8550891222387991669, 5535668688139305547, 2430933853350256242, 5401941257863201076, + 8159640039107728799, 6157493831600770366, 7632066283658143750, 6308328381617103346, + 3681878764086140361, 3289686137190109749, 6587997200611086848, 244714774258135476, + 4079788377417136100, 8090302575944624335, 2945117363431356361, 864324395848741045, + 3009039260312620700, 8430027460082534031, 401084700045993341, 7254622446438694921, + 4707864159563588614, 5640248530963493951, 5982507712689997893, 3315098242282210105, + 5503847578771918426, 3941971367175193882, 8118566580304798074, 3839261274019871296, + 7062410411742090847, 741381002980207668, 6027994129690250817, 2497829994150063930, + 6251390334426228834, 1368930247903518833, 8809096399316380241, 6492004350391900708, + 2462145737463489636, 404828418920299174, 4153026434231690595, 261785715255475940, + 5464715384600071357, 592710404378763017, 6764129236657751224, 8513655718539357449, + 5820343663801914208, 385298524683789911, 5224135003438199467, 6303131641338802145, + 7150122561309371392, 368107899140673753, 3115186834558311558, 2915636353584281051, + 4782583894627718279, 6718292300699989587, 8387085186914375220, 3387513132024756289, + 4654329375432538231, 8930667561363381602, 5374373436876319273, 7623042350483453954, + 7725442901813263321, 9186225467561587250, 4091027289597503355, 2357631606492579800, + 2530936820058611833, 1636551876240043639, 5564664674334965799, 1452244145334316253, + 2061642381019690829, 1279580266495294036, 9108481583171221009, 6023278686734049809, + 5007630032676973346, 2153168792952589781, 6720334534964750538, 6041546491134794105, + 3433922409283786309, 2285479922797300912, 3110614940896576130, 6366559590722842893, + 5418791419666136509, 7163298419643543757, 4891138053923696990, 580618510277907015, + 1684034065251686769, 4429514767357295841, 330346578555450005, 1119637995812174675, + 7177515271653460134, 4589042248470800257, 7693288629059004563, 143607045258444228, + 246994305896273627, 866417324803099287, 6473547110565816071, 3092379936208876896, + 2058427839513754051, 5133784708526867938, 8785882556301281247, 6149332666841167611, + 8585842181454472135, 6137678347805511274, 2070447184436970006, 5708223427705576541, + 5999657892458244504, 4358391411789012426, 325123008708389849, 6837621693887290924, + 4843721905315627004, 6010651222149276415, 5398352198963874652, 4602025990114250980, + 1044646352569048800, 9106614159853161675, 829256115228593269, 4919284369102997000, + 2681532557646850893, 3681559472488511871, 5307999518958214035, 6334130388442829274, + 2658708232916537604, 1163313865052186287, 581945337509520675, 3648778920718647903, + 4423673246306544414, 1620799783996955743, 220828013409515943, 8150384699999389761, + 4287360518296753003, 4590000184845883843, 5513660857261085186, 6964829100392774275, + 478991688350776035, 8746140185685648781, 228500091334420247, 1356187007457302238, + 3019253992034194581, 3152601605678500003, 430152752706002213, 5559581553696971176, + 4916432985369275664, 663574931734554391, 3420773838927732076, 2868348622579915573, + 1999319134044418520, 3328689518636282723, 2587672709781371173, 1517255313529399333, + 3092343956317362483, 3662252519007064108, 972445599196498113, 7664865435875959367, + 1708913533482282562, 6917817162668868494, 3217629022545312900, 2570043027221707107, + 8739788839543624613, 2488075924621352812, 4694002395387436668, 4559628481798514356, + 2997203966153298104, 1282559373026354493, 240113143146674385, 8665713329246516443, + 628141331766346752, 4571950817186770476, 1472811188152235408, 7596648026010355826, + 6091219417754424743, 7834161864828164065, 7103445518877254909, 4390861237357459201, + 4442653864240571734, 8903482404847331368, 622261699494173647, 6037261250297213248, + 504404948065709118, 7275215526217113061, 1011176780856001400, 2194750105623461063, + 2623071828615234808, 5157313728073836108, 3738405111966602044, 2539767524076729570, + 2467284396349269342, 5256026990536851868, 7841086888628396109, 6640857538655893162, + 1202087339038317498, 2113514992440715978, 7534350895342931403, 4925284734898484745, + 5145623771477493805, 8225140880134972332, 2719520354384050532, 9132346697815513771, + 4332154495710163773, 7137789594094346916, 6994721091344268833, 6667228574869048934, + 655440045726677499, 59934747298466858, 6124974028078036405, 8957774780655365418, + 2332206071942466437, 1701056712286369627, 3154897383618636503, 1637766181387607527, + 2460521277767576533, 197309393502684135, 643677854385267315, 2543179307861934850, + 4350769010207485119, 4754652089410667672, 2015595502641514512, 7999059458976458608, + 4287946071480840813, 8362686366770308971, 6486469209321732151, 3617727845841796026, + 7554353525834302244, 4450022655153542367, 1605195740213535749, 5327014565305508387, + 4626575813550328320, 2692222020597705149, 241045573717249868, 5098046974627094010, + 7916882295460730264, 884817090297530579, 5329160409530630596, 7790979528857726136, + 4955070238059373407, 4918537275422674302, 3008076183950404629, 3007769226071157901, + 2470346235617803020, 8928702772696731736, 7856187920214445904, 4474874585391974885, + 7900176660600710914, 2140571127916226672, 2425445057265199971, 2486055153341847830, + 4186670094382025798, 1883939007446035042, 8808666044074867985, 3734134241178479257, + 4065968871360089196, 6953124200385847784, 1305686814738899057, 1637739099014457647, + 3656125660947993209, 3966759634633167020, 3106378204088556331, 6328899822778449810, + 4565385105440252958, 1979884289539493806, 2331793186920865425, 3783206694208922581, + 8464961209802336085, 2843963751609577687, 3030678195484896323, 4793717574095772604, + 4459239494808162889, 402587895800087237, 8057891408711167515, 4541888170938985079, + 1042662272908816815, 5557303057122568958, 2647678726283249984, 2144477441549833761, + 5806352215355387087, 7117771003473903623, 5916597177708541638, 462597715452321361, + 8833658097025758785, 5970273481425315300, 563813119381731307, 2768349550652697015, + 1598828206250873866, 5206393647403558110, 6235043485709261823, 3152217402014639496, + 8469693267274066490, 125672920241807416, 5311079624024060938, 6663754932310491587, + 8736848295048751716, 4488039774992061878, 5923302823487327109, 140891791083103236, + 7414942793393574290, 7990420780896957397, 4317817392807076702, 3625184369705367340, + 2740722765288122703, 5743100009702758344, 5997898640509039159, 8854493341352484163, + 5242208035432907801, 701338899890987198, 7609280429197514109, 3020985755112334161, + 6651322707055512866, 2635195723621160615, 5144520864246028816, 1035086515727829828, + 1567242097116389047, 8172389260191636581, 6337820351429292273, 2163012566996458925, + 2743190902890262681, 1906367633221323427, 6011544915663598137, 5932255307352610768, + 2241128460406315459, 895504896216695588, 3094483003111372717, 4583857460292963101, + 9079887171656594975, 8839289181930711403, 5762740387243057873, 4225072055348026230, + 1838220598389033063, 3801620336801580414, 8823526620080073856, 1776617605585100335, + 7899055018877642622, 5421679761463003041, 5521102963086275121, 4248279443559365898, + 8735487530905098534, 1760527091573692978, 7142485049657745894, 8222656872927218123, + 4969531564923704323, 3394475942196872480, 6424174453260338141, 359248545074932887, + 3273651282831730598, 6797106199797138596, 3030918217665093212, 145600834617314036, + 6036575856065626233, 740416251634527158, 7080427635449935582, 6951781370868335478, + 399922722363687927, 294902314447253185, 7844950936339178523, 880320858634709042, + 6192655680808675579, 411604686384710388, 9026808440365124461, 6440783557497587732, + 4615674634722404292, 539897290441580544, 2096238225866883852, 8751955639408182687, + 1907224908052289603, 7381039757301768559, 6157238513393239656, 7749994231914157575, + 8629571604380892756, 5280433031239081479, 7101611890139813254, 2479018537985767835, + 7169176924412769570, 7942066497793203302, 1357759729055557688, 2278447439451174845, + 3625338785743880657, 6477479539006708521, 8976185375579272206, 5511371554711836120, + 1326024180520890843, 7537449876596048829, 5464680203499696154, 3189671183162196045, + 6346751753565857109, 241159987320630307, 3095793449658682053, 8978332846736310159, + 2902794662273147216, 7208698530190629697, 7276901792339343736, 1732385229314443140, + 4133292154170828382, 2918308698224194548, 1519461397937144458, 5293934712616591764, + 4922828954023452664, 2879211533496425641, 5896236396443472108, 8465043815351752425, + 7329020396871624740, 8915471717014488588, 2944902635677463047, 7052079073493465134, + 8382142935188824023, 9103922860780351547, 4152330101494654406 }; +) + +// seed rng x[n+1] = 48271 * x[n] mod (2**31 - 1) +func seedrand(x int32) int32 { + hi := x / _Q; + lo := x % _Q; + x = _A*lo - _R*hi; + if x < 0 { + x += _M; + } + return x; +} + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +func Seed(seed int32) { + rng_tap = 0; + rng_feed = _LEN-_TAP; + + seed = seed%_M; + if seed < 0 { + seed += _M; + } + if seed == 0 { + seed = 89482311; + } + + x := seed; + for i := -20; i < _LEN; i++ { + x = seedrand(x); + if i >= 0 { + var u int64; + u = int64(x) << 40; + x = seedrand(x); + u ^= int64(x) << 20; + x = seedrand(x); + u ^= int64(x); + u ^= rng_cooked[i]; + rng_vec[i] = u & _MASK; + } + } +} + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64. +func Int63() int64 { + rng_tap--; + if rng_tap < 0 { + rng_tap += _LEN; + } + + rng_feed--; + if rng_feed < 0 { + rng_feed += _LEN; + } + + x := (rng_vec[rng_feed] + rng_vec[rng_tap]) & _MASK; + rng_vec[rng_feed] = x; + return x; +} + +// Uint32 returns a pseudo-random 32-bit value as a uint32. +func Uint32() uint32 { + return uint32(Int63() >> 31); +} + +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32. +func Int31() int32 { + return int32(Int63() >> 32); +} + +// Int returns a non-negative pseudo-random int. All bits but the top bit are random. +func Int() int { + u := uint(Int63()); + return int(u << 1 >> 1); // clear sign bit if int == int32 +} + +// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n). +func Int63n(n int64) int64 { + if n <= 0 { + return 0 + } + max := int64((1<<63)-1 - (1<<63) % uint64(n)); + v := Int63(); + for v > max { + v = Int63() + } + return v % n +} + +// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n). +func Int31n(n int32) int32 { + return int32(Int63n(int64(n))) +} + +// Intn returns, as an int, a non-negative pseudo-random number in [0,n). +func Intn(n int) int { + return int(Int63n(int64(n))) +} + +// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0). +func Float64() float64 { + x := float64(Int63()) / float64(_MAX); + for x >= 1 { + x = float64(Int63()) / float64(_MAX); + } + return x; +} + +// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0). +func Float32() float32 { + return float32(Float64()) +} + +// Float returns, as a float, a pseudo-random number in [0.0,1.0). +func Float() float +{ + return float(Float64()) +} + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n). +func Perm(n int) []int { + m := make([]int, n); + for i:=0; i<n; i++ { + m[i] = i; + } + for i:=0; i<n; i++ { + j := Intn(n); + m[i],m[j] = m[j],m[i]; + } + return m; +} + +func init() { + Seed(1); +} diff --git a/src/pkg/reflect/Makefile b/src/pkg/reflect/Makefile new file mode 100644 index 000000000..d622ffd51 --- /dev/null +++ b/src/pkg/reflect/Makefile @@ -0,0 +1,78 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + type.$O\ + typestring.$O\ + +O2=\ + value.$O\ + +O3=\ + deepequal.$O\ + tostring.$O\ + + +phases: a1 a2 a3 +_obj$D/reflect.a: phases + +a1: $(O1) + $(AR) grc _obj$D/reflect.a type.$O typestring.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/reflect.a value.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/reflect.a deepequal.$O tostring.$O + rm -f $(O3) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/reflect.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/reflect.a + +packages: _obj$D/reflect.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/reflect.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/reflect.a diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go new file mode 100644 index 000000000..903b0f526 --- /dev/null +++ b/src/pkg/reflect/all_test.go @@ -0,0 +1,613 @@ +// Copyright 2009 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 reflect + +import ( + "io"; + "os"; + "reflect"; + "testing"; + "unsafe"; +) + +var doprint bool = false + +func is_digit(c uint8) bool { + return '0' <= c && c <= '9' +} + +// streq, but '@' in t matches a string of digits +func match(s, t string) bool { + for i, j := 0, 0; i < len(s) && j < len(t); i, j = i+1, j+1 { + if s[i] == t[j] { + continue + } + if is_digit(s[i]) && t[j] == '@' { + for is_digit(s[i+1]) { + i++ + } + } else { + return false + } + } + return true; +} + +func assert(s, t string) { + if doprint { + println(t) + } + if !match(s, t) { + panicln(s, t) + } +} + +func typedump(s, t string) { + typ := ParseTypeString("", s); + assert(typeToString(typ, true), t); +} + +func valuedump(s, t string) { + typ := ParseTypeString("", s); + v := NewZeroValue(typ); + if v == nil { + panicln("valuedump", s); + } + switch v.Kind() { + case IntKind: + v.(IntValue).Set(132); + case Int8Kind: + v.(Int8Value).Set(8); + case Int16Kind: + v.(Int16Value).Set(16); + case Int32Kind: + v.(Int32Value).Set(32); + case Int64Kind: + v.(Int64Value).Set(64); + case UintKind: + v.(UintValue).Set(132); + case Uint8Kind: + v.(Uint8Value).Set(8); + case Uint16Kind: + v.(Uint16Value).Set(16); + case Uint32Kind: + v.(Uint32Value).Set(32); + case Uint64Kind: + v.(Uint64Value).Set(64); + case FloatKind: + v.(FloatValue).Set(3200.0); + case Float32Kind: + v.(Float32Value).Set(32.1); + case Float64Kind: + v.(Float64Value).Set(64.2); + case StringKind: + v.(StringValue).Set("stringy cheese"); + case BoolKind: + v.(BoolValue).Set(true); + } + assert(valueToString(v), t); +} + +type T struct { a int; b float64; c string; d *int } + +func TestAll(tt *testing.T) { // TODO(r): wrap up better + var s string; + var t Type; + + // Types + typedump("missing", "$missing$"); + typedump("int", "int"); + typedump("int8", "int8"); + typedump("int16", "int16"); + typedump("int32", "int32"); + typedump("int64", "int64"); + typedump("uint", "uint"); + typedump("uint8", "uint8"); + typedump("uint16", "uint16"); + typedump("uint32", "uint32"); + typedump("uint64", "uint64"); + typedump("float", "float"); + typedump("float32", "float32"); + typedump("float64", "float64"); + typedump("int8", "int8"); + typedump("whoknows.whatsthis", "$missing$"); + typedump("**int8", "**int8"); + typedump("**P.integer", "**P.integer"); + typedump("[32]int32", "[32]int32"); + typedump("[]int8", "[]int8"); + typedump("map[string]int32", "map[string]int32"); + typedump("chan<-string", "chan<-string"); + typedump("struct {c chan *int32; d float32}", "struct{c chan*int32; d float32}"); + typedump("func(a int8, b int32)", "func(a int8, b int32)"); + typedump("struct {c func(? chan *P.integer, ? *int8)}", "struct{c func(chan*P.integer, *int8)}"); + typedump("struct {a int8; b int32}", "struct{a int8; b int32}"); + typedump("struct {a int8; b int8; b int32}", "struct{a int8; b int8; b int32}"); + typedump("struct {a int8; b int8; c int8; b int32}", "struct{a int8; b int8; c int8; b int32}"); + typedump("struct {a int8; b int8; c int8; d int8; b int32}", "struct{a int8; b int8; c int8; d int8; b int32}"); + typedump("struct {a int8; b int8; c int8; d int8; e int8; b int32}", "struct{a int8; b int8; c int8; d int8; e int8; b int32}"); + typedump("struct {a int8 \"hi there\"; }", "struct{a int8 \"hi there\"}"); + typedump("struct {a int8 \"hi \\x00there\\t\\n\\\"\\\\\"; }", "struct{a int8 \"hi \\x00there\\t\\n\\\"\\\\\"}"); + typedump("struct {f func(args ...)}", "struct{f func(args ...)}"); + typedump("interface { a(? func(? func(? int) int) func(? func(? int)) int); b() }", "interface{a (func(func(int)(int))(func(func(int))(int))); b ()}"); + + // Values + valuedump("int8", "8"); + valuedump("int16", "16"); + valuedump("int32", "32"); + valuedump("int64", "64"); + valuedump("uint8", "8"); + valuedump("uint16", "16"); + valuedump("uint32", "32"); + valuedump("uint64", "64"); + valuedump("float32", "32.1"); + valuedump("float64", "64.2"); + valuedump("string", "stringy cheese"); + valuedump("bool", "true"); + valuedump("*int8", "*int8(0)"); + valuedump("**int8", "**int8(0)"); + valuedump("[5]int32", "[5]int32{0, 0, 0, 0, 0}"); + valuedump("**P.integer", "**P.integer(0)"); + valuedump("map[string]int32", "map[string]int32{<can't iterate on maps>}"); + valuedump("chan<-string", "chan<-string"); + valuedump("struct {c chan *int32; d float32}", "struct{c chan*int32; d float32}{chan*int32, 0}"); + valuedump("func(a int8, b int32)", "func(a int8, b int32)(0)"); + valuedump("struct {c func(? chan *P.integer, ? *int8)}", "struct{c func(chan*P.integer, *int8)}{func(chan*P.integer, *int8)(0)}"); + valuedump("struct {a int8; b int32}", "struct{a int8; b int32}{0, 0}"); + valuedump("struct {a int8; b int8; b int32}", "struct{a int8; b int8; b int32}{0, 0, 0}"); + + { var tmp = 123; + value := NewValue(tmp); + assert(valueToString(value), "123"); + } + { var tmp = 123.4; + value := NewValue(tmp); + assert(valueToString(value), "123.4"); + } + { + var tmp = byte(123); + value := NewValue(tmp); + assert(valueToString(value), "123"); + assert(typeToString(value.Type(), false), "uint8"); + } + { var tmp = "abc"; + value := NewValue(tmp); + assert(valueToString(value), "abc"); + } + { + var i int = 7; + var tmp = &T{123, 456.75, "hello", &i}; + value := NewValue(tmp); + assert(valueToString(value.(PtrValue).Sub()), "reflect.T{123, 456.75, hello, *int(@)}"); + } + { + type C chan *T; // TODO: should not be necessary + var tmp = new(C); + value := NewValue(tmp); + assert(valueToString(value), "*reflect.C·all_test(@)"); + } +// { +// type A [10]int; +// var tmp A = A{1,2,3,4,5,6,7,8,9,10}; +// value := NewValue(&tmp); +// assert(valueToString(value.(PtrValue).Sub()), "reflect.A·all_test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"); +// value.(PtrValue).Sub().(ArrayValue).Elem(4).(IntValue).Set(123); +// assert(valueToString(value.(PtrValue).Sub()), "reflect.A·all_test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"); +// } + { + type AA []int; + var tmp = AA{1,2,3,4,5,6,7,8,9,10}; + value := NewValue(&tmp); // TODO: NewValue(tmp) too + assert(valueToString(value.(PtrValue).Sub()), "reflect.AA·all_test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"); + value.(PtrValue).Sub().(ArrayValue).Elem(4).(IntValue).Set(123); + assert(valueToString(value.(PtrValue).Sub()), "reflect.AA·all_test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"); + } + + { + var ip *int32; + var i int32 = 1234; + vip := NewValue(&ip); + vi := NewValue(i); + vip.(PtrValue).Sub().(PtrValue).SetSub(vi); + if *ip != 1234 { + panicln("SetSub failure", *ip); + } + } + + var pt PtrType; + var st StructType; + var mt MapType; + var at ArrayType; + var ct ChanType; + var name string; + var typ Type; + var tag string; + var offset int; + + // Type strings + t = ParseTypeString("", "int8"); + assert(t.String(), "int8"); + + t = ParseTypeString("", "*int8"); + assert(t.String(), "*int8"); + pt = t.(PtrType); + assert(pt.Sub().String(), "int8"); + + t = ParseTypeString("", "*struct {c chan *int32; d float32}"); + assert(t.String(), "*struct {c chan *int32; d float32}"); + pt = t.(PtrType); + assert(pt.Sub().String(), "struct {c chan *int32; d float32}"); + st = pt.Sub().(StructType); + name, typ, tag, offset = st.Field(0); + assert(typ.String(), "chan *int32"); + name, typ, tag, offset = st.Field(1); + assert(typ.String(), "float32"); + + t = ParseTypeString("", "interface {a() *int}"); + assert(t.String(), "interface {a() *int}"); + + t = ParseTypeString("", "func(a int8, b int32)"); + assert(t.String(), "func(a int8, b int32)"); + + t = ParseTypeString("", "func(a int8, b int32) float"); + assert(t.String(), "func(a int8, b int32) float"); + + t = ParseTypeString("", "func(a int8, b int32) (a float, b float)"); + assert(t.String(), "func(a int8, b int32) (a float, b float)"); + + t = ParseTypeString("", "[32]int32"); + assert(t.String(), "[32]int32"); + at = t.(ArrayType); + assert(at.Elem().String(), "int32"); + + t = ParseTypeString("", "map[string]*int32"); + assert(t.String(), "map[string]*int32"); + mt = t.(MapType); + assert(mt.Key().String(), "string"); + assert(mt.Elem().String(), "*int32"); + + t = ParseTypeString("", "chan<-string"); + assert(t.String(), "chan<-string"); + ct = t.(ChanType); + assert(ct.Elem().String(), "string"); + + // make sure tag strings are not part of element type + t = ParseTypeString("", "struct{d []uint32 \"TAG\"}"); + st = t.(StructType); + name, typ, tag, offset = st.Field(0); + assert(typ.String(), "[]uint32"); + + t = ParseTypeString("", "[]int32"); + v := NewSliceValue(t.(ArrayType), 5, 10); + t1 := ParseTypeString("", "*[]int32"); + v1 := NewZeroValue(t1); + if v1 == nil { panic("V1 is nil"); } + v1.(PtrValue).SetSub(v); + a := v1.Interface().(*[]int32); + println(&a, len(a), cap(a)); + for i := 0; i < len(a); i++ { + v.Elem(i).(Int32Value).Set(int32(i)); + } + for i := 0; i < len(a); i++ { + println(a[i]); + } +} + +func TestInterfaceGet(t *testing.T) { + var inter struct { e interface{ } }; + inter.e = 123.456; + v1 := NewValue(&inter); + v2 := v1.(PtrValue).Sub().(StructValue).Field(0); + assert(v2.Type().String(), "interface { }"); + i2 := v2.(InterfaceValue).Get(); + v3 := NewValue(i2); + assert(v3.Type().String(), "float"); +} + +func TestInterfaceValue(t *testing.T) { + var inter struct { e interface{ } }; + inter.e = 123.456; + v1 := NewValue(&inter); + v2 := v1.(PtrValue).Sub().(StructValue).Field(0); + assert(v2.Type().String(), "interface { }"); + v3 := v2.(InterfaceValue).Value(); + assert(v3.Type().String(), "float"); + + i3 := v2.Interface(); + if f, ok := i3.(float); !ok { + a, typ, c := unsafe.Reflect(i3); + t.Error("v2.Interface() did not return float, got ", typ); + } +} + +func TestFunctionValue(t *testing.T) { + v := NewValue(func() {}); + if v.Interface() != v.Interface() { + t.Fatalf("TestFunction != itself"); + } + assert(v.Type().String(), "func()"); +} + +func TestCopyArray(t *testing.T) { + a := []int{ 1, 2, 3, 4, 10, 9, 8, 7 }; + b := []int{ 11, 22, 33, 44, 1010, 99, 88, 77, 66, 55, 44 }; + c := []int{ 11, 22, 33, 44, 1010, 99, 88, 77, 66, 55, 44 }; + va := NewValue(&a); + vb := NewValue(&b); + for i := 0; i < len(b); i++ { + if b[i] != c[i] { + t.Fatalf("b != c before test"); + } + } + for tocopy := 1; tocopy <= 7; tocopy++ { + vb.(PtrValue).Sub().(ArrayValue).CopyFrom(va.(PtrValue).Sub().(ArrayValue), tocopy); + for i := 0; i < tocopy; i++ { + if a[i] != b[i] { + t.Errorf("1 tocopy=%d a[%d]=%d, b[%d]=%d", + tocopy, i, a[i], i, b[i]); + } + } + for i := tocopy; i < len(b); i++ { + if b[i] != c[i] { + if i < len(a) { + t.Errorf("2 tocopy=%d a[%d]=%d, b[%d]=%d, c[%d]=%d", + tocopy, i, a[i], i, b[i], i, c[i]); + } else { + t.Errorf("3 tocopy=%d b[%d]=%d, c[%d]=%d", + tocopy, i, b[i], i, c[i]); + } + } else { + t.Logf("tocopy=%d elem %d is okay\n", tocopy, i); + } + } + } +} + +func TestBigUnnamedStruct(t *testing.T) { + b := struct{a,b,c,d int64}{1, 2, 3, 4}; + v := NewValue(b); + b1 := v.Interface().(struct{a,b,c,d int64}); + if b1.a != b.a || b1.b != b.b || b1.c != b.c || b1.d != b.d { + t.Errorf("NewValue(%v).Interface().(Big) = %v", b, b1); + } +} + +type big struct { + a, b, c, d, e int64 +} +func TestBigStruct(t *testing.T) { + b := big{1, 2, 3, 4, 5}; + v := NewValue(b); + b1 := v.Interface().(big); + if b1.a != b.a || b1.b != b.b || b1.c != b.c || b1.d != b.d || b1.e != b.e { + t.Errorf("NewValue(%v).Interface().(big) = %v", b, b1); + } +} + +type Basic struct { + x int; + y float32 +} + +type NotBasic Basic + +type Recursive struct { + x int; + r *Recursive +} + +type Complex struct { + a int; + b [3]*Complex; + c *string; + d map[float]float +} + +type DeepEqualTest struct { + a, b interface{}; + eq bool; +} + +var deepEqualTests = []DeepEqualTest { + // Equalities + DeepEqualTest{ 1, 1, true }, + DeepEqualTest{ int32(1), int32(1), true }, + DeepEqualTest{ 0.5, 0.5, true }, + DeepEqualTest{ float32(0.5), float32(0.5), true }, + DeepEqualTest{ "hello", "hello", true }, + DeepEqualTest{ make([]int, 10), make([]int, 10), true }, + DeepEqualTest{ &[3]int{ 1, 2, 3 }, &[3]int{ 1, 2, 3 }, true }, + DeepEqualTest{ Basic{ 1, 0.5 }, Basic{ 1, 0.5 }, true }, + // Inequalities + DeepEqualTest{ 1, 2, false }, + DeepEqualTest{ int32(1), int32(2), false }, + DeepEqualTest{ 0.5, 0.6, false }, + DeepEqualTest{ float32(0.5), float32(0.6), false }, + DeepEqualTest{ "hello", "hey", false }, + DeepEqualTest{ make([]int, 10), make([]int, 11), false }, + DeepEqualTest{ &[3]int{ 1, 2, 3 }, &[3]int{ 1, 2, 4 }, false }, + DeepEqualTest{ Basic{ 1, 0.5 }, Basic{ 1, 0.6 }, false }, + // Mismatched types + DeepEqualTest{ 1, 1.0, false }, + DeepEqualTest{ int32(1), int64(1), false }, + DeepEqualTest{ 0.5, "hello", false }, + DeepEqualTest{ []int{ 1, 2, 3 }, [3]int{ 1, 2, 3 }, false }, + DeepEqualTest{ &[3]interface{} { 1, 2, 4 }, &[3]interface{} { 1, 2, "s" }, false }, + DeepEqualTest{ Basic{ 1, 0.5 }, NotBasic{ 1, 0.5 }, false }, +} + +func TestDeepEqual(t *testing.T) { + for i, test := range deepEqualTests { + if r := DeepEqual(test.a, test.b); r != test.eq { + t.Errorf("DeepEqual(%v, %v) = %v, want %v", test.a, test.b, r, test.eq); + } + } +} + +func TestDeepEqualRecursiveStruct(t *testing.T) { + a, b := new(Recursive), new(Recursive); + *a = Recursive{ 12, a }; + *b = Recursive{ 12, b }; + if !DeepEqual(a, b) { + t.Error("DeepEqual(recursive same) = false, want true"); + } +} + +func TestDeepEqualComplexStruct(t *testing.T) { + m := make(map[float]float); + stra, strb := "hello", "hello"; + a, b := new(Complex), new(Complex); + *a = Complex{5, [3]*Complex{a, b, a}, &stra, m}; + *b = Complex{5, [3]*Complex{b, a, a}, &strb, m}; + if !DeepEqual(a, b) { + t.Error("DeepEqual(complex same) = false, want true"); + } +} + +func TestDeepEqualComplexStructInequality(t *testing.T) { + m := make(map[float]float); + stra, strb := "hello", "helloo"; // Difference is here + a, b := new(Complex), new(Complex); + *a = Complex{5, [3]*Complex{a, b, a}, &stra, m}; + *b = Complex{5, [3]*Complex{b, a, a}, &strb, m}; + if DeepEqual(a, b) { + t.Error("DeepEqual(complex different) = true, want false"); + } +} + + +func check2ndField(x interface{}, offs uintptr, t *testing.T) { + s := NewValue(x).(StructValue); + name, ftype, tag, reflect_offset := s.Type().(StructType).Field(1); + if uintptr(reflect_offset) != offs { + t.Error("mismatched offsets in structure alignment:", reflect_offset, offs); + } +} + +// Check that structure alignment & offsets viewed through reflect agree with those +// from the compiler itself. +func TestAlignment(t *testing.T) { + type T1inner struct { + a int + } + type T1 struct { + T1inner; + f int; + } + type T2inner struct { + a, b int + } + type T2 struct { + T2inner; + f int; + } + + x := T1{T1inner{2}, 17}; + check2ndField(x, uintptr(unsafe.Pointer(&x.f)) - uintptr(unsafe.Pointer(&x)), t); + + x1 := T2{T2inner{2, 3}, 17}; + check2ndField(x1, uintptr(unsafe.Pointer(&x1.f)) - uintptr(unsafe.Pointer(&x1)), t); +} + +type Nillable interface { + IsNil() bool +} + +func Nil(a interface{}, t *testing.T) { + n := NewValue(a).(Nillable); + if !n.IsNil() { + t.Errorf("%v should be nil", a) + } +} + +func NotNil(a interface{}, t *testing.T) { + n := NewValue(a).(Nillable); + if n.IsNil() { + t.Errorf("value of type %v should not be nil", NewValue(a).Type().String()) + } +} + +func TestIsNil(t *testing.T) { + // These do not implement IsNil + doNotNil := []string{"int", "float32", "struct { a int }"}; + // These do implement IsNil + doNil := []string{"*int", "interface{}", "map[string]int", "func() bool", "chan int", "[]string"}; + for i, ts := range doNotNil { + ty := ParseTypeString("", ts); + v := NewZeroValue(ty); + if nilable, ok := v.(Nillable); ok { + t.Errorf("%s is nilable; should not be", ts) + } + } + + for i, ts := range doNil { + ty := ParseTypeString("", ts); + v := NewZeroValue(ty); + if nilable, ok := v.(Nillable); !ok { + t.Errorf("%s %T is not nilable; should be", ts, v) + } + } + // Check the implementations + var pi *int; + Nil(pi, t); + pi = new(int); + NotNil(pi, t); + + var si []int; + Nil(si, t); + si = make([]int, 10); + NotNil(si, t); + + // TODO: map and chan don't work yet + + var ii interface {}; + Nil(ii, t); + ii = pi; + NotNil(ii, t); + + var fi func(t *testing.T); + Nil(fi, t); + fi = TestIsNil; + NotNil(fi, t); +} + +func TestInterfaceExtraction(t *testing.T) { + var s struct { + w io.Writer; + } + + s.w = os.Stdout; + v := Indirect(NewValue(&s)).(StructValue).Field(0).Interface(); + if v != s.w.(interface{}) { + t.Errorf("Interface() on interface: ", v, s.w); + } +} + +func TestInterfaceEditing(t *testing.T) { + // strings are bigger than one word, + // so the interface conversion allocates + // memory to hold a string and puts that + // pointer in the interface. + var i interface{} = "hello"; + + // if i pass the interface value by value + // to NewValue, i should get a fresh copy + // of the value. + v := NewValue(i); + + // and setting that copy to "bye" should + // not change the value stored in i. + v.(StringValue).Set("bye"); + if i.(string) != "hello" { + t.Errorf(`Set("bye") changed i to %s`, i.(string)); + } + + // the same should be true of smaller items. + i = 123; + v = NewValue(i); + v.(IntValue).Set(234); + if i.(int) != 123 { + t.Errorf("Set(234) changed i to %d", i.(int)); + } +} diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go new file mode 100644 index 000000000..57b52485f --- /dev/null +++ b/src/pkg/reflect/deepequal.go @@ -0,0 +1,83 @@ +// Copyright 2009 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. + +// Deep equality test via reflection + +package reflect + +import "reflect" + +// Tests for deep equality using reflected types. The map argument tracks +// comparisons that have already been seen, which allows short circuiting on +// recursive types. +func deepValueEqual(v1, v2 Value, visited map[Addr]Addr) bool { + if v1.Kind() != v2.Kind() { + return false; + } + + // Short circuit if references are identical or already seen + addr1 := v1.Addr(); + addr2 := v2.Addr(); + + if addr1 == addr2 { + return true; + } + if vaddr, ok := visited[addr1]; ok && vaddr == addr2 { + return true; + } + visited[addr1] = addr2; + + switch v1.Kind() { + case ArrayKind: + arr1 := v1.(ArrayValue); + arr2 := v2.(ArrayValue); + if arr1.IsSlice() != arr2.IsSlice() || arr1.Len() != arr2.Len() { + return false; + } + for i := 0; i < arr1.Len(); i++ { + if !deepValueEqual(arr1.Elem(i), arr2.Elem(i), visited) { + return false; + } + } + return true; + case InterfaceKind: + return deepValueEqual(NewValue(v1.(InterfaceValue).Get()), + NewValue(v2.(InterfaceValue).Get()), visited); + case MapKind: + // TODO(dnadasi): Implement this fully once MapValue is implemented + return v1.Interface() == v2.Interface(); + case PtrKind: + return deepValueEqual(v1.(PtrValue).Sub(), v2.(PtrValue).Sub(), visited); + case StructKind: + struct1 := v1.(StructValue); + struct2 := v2.(StructValue); + if struct1.Len() != struct2.Len() { + return false; + } + for i := 0; i < struct1.Len(); i++ { + if !deepValueEqual(struct1.Field(i), struct2.Field(i), visited) { + return false; + } + } + return true; + default: + // Normal equality suffices + return v1.Interface() == v2.Interface(); + } + + panic("Not reached"); +} + +// DeepEqual tests for deep equality. It uses normal == equality where possible +// but will scan members of arrays, slices, and fields of structs. It correctly +// handles recursive types. Until reflection supports maps, maps are equal iff +// they are identical. +func DeepEqual(a1, a2 interface{}) bool { + v1 := NewValue(a1); + v2 := NewValue(a2); + if !equalType(v1.Type(), v2.Type()) { + return false; + } + return deepValueEqual(v1, v2, make(map[Addr]Addr)); +} diff --git a/src/pkg/reflect/tostring.go b/src/pkg/reflect/tostring.go new file mode 100644 index 000000000..43be4b9e8 --- /dev/null +++ b/src/pkg/reflect/tostring.go @@ -0,0 +1,234 @@ +// Copyright 2009 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. + +// Reflection library. +// Formatting of reflection types and values for debugging. +// Not defined as methods so they do not need to be linked into most binaries; +// the functions are not used by the library itself, only in tests. + +package reflect + +import ( + "reflect"; + "strconv"; +) + +func typeToString(typ Type, expand bool) string +func valueToString(val Value) string + +func doubleQuote(s string) string { + out := "\""; + for i := 0; i < len(s); i++ { + c := s[i]; + switch c { + case '\n': + out += `\n`; + case '\t': + out += `\t`; + case '\x00': + out += `\x00`; + case '"': + out += `\"`; + case '\\': + out += `\\`; + default: + out += string(c); + } + } + out += "\""; + return out; +} + +type hasFields interface { + Field(i int) (name string, typ Type, tag string, offset int); + Len() int; +} + +func typeFieldsToString(t hasFields, sep string, iface bool) string { + var str string; + for i := 0; i < t.Len(); i++ { + str1, typ, tag, offset := t.Field(i); + if str1 != "" { + str1 += " " + } + str2 := typeToString(typ, false); + if iface && str2[0:4] == "func" { + str2 = str2[4:len(str2)] + } + str1 += str2; + if tag != "" { + str1 += " " + doubleQuote(tag); + } + if i < t.Len() - 1 { + str1 += sep + " "; + } + str += str1; + } + return str; +} + +// typeToString returns a textual representation of typ. The expand +// flag specifies whether to expand the contents of type names; if false, +// the name itself is used as the representation. +// Meant for debugging only; typ.String() serves for most purposes. +func typeToString(typ Type, expand bool) string { + var str string; + if name := typ.Name(); !expand && name != "" { + return name + } + switch(typ.Kind()) { + case MissingKind: + return "$missing$"; + case IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind, + UintKind, Uint8Kind, Uint16Kind, Uint32Kind, Uint64Kind, + FloatKind, Float32Kind, Float64Kind, + StringKind, + DotDotDotKind: + return typ.Name(); + case PtrKind: + p := typ.(PtrType); + return "*" + typeToString(p.Sub(), false); + case ArrayKind: + a := typ.(ArrayType); + if a.IsSlice() { + str = "[]" + } else { + str = "[" + strconv.Itoa64(int64(a.Len())) + "]" + } + return str + typeToString(a.Elem(), false); + case MapKind: + m := typ.(MapType); + str = "map[" + typeToString(m.Key(), false) + "]"; + return str + typeToString(m.Elem(), false); + case ChanKind: + c := typ.(ChanType); + switch c.Dir() { + case RecvDir: + str = "<-chan"; + case SendDir: + str = "chan<-"; + case BothDir: + str = "chan"; + default: + panicln("reflect.typeToString: unknown chan direction"); + } + return str + typeToString(c.Elem(), false); + case StructKind: + return "struct{" + typeFieldsToString(typ.(StructType), ";", false) + "}"; + case InterfaceKind: + return "interface{" + typeFieldsToString(typ.(InterfaceType), ";", true) + "}"; + case FuncKind: + f := typ.(FuncType); + str = "func(" + typeFieldsToString(f.In(), ",", false) + ")"; + if f.Out() != nil { + str += "(" + typeFieldsToString(f.Out(), ",", false) + ")"; + } + return str; + default: + panicln("reflect.typeToString: can't print type ", typ.Kind()); + } + return "reflect.typeToString: can't happen"; +} + +// TODO: want an unsigned one too +func integer(v int64) string { + return strconv.Itoa64(v); +} + +// valueToString returns a textual representation of the reflection value val. +// For debugging only. +func valueToString(val Value) string { + var str string; + typ := val.Type(); + switch(val.Kind()) { + case MissingKind: + return "missing"; + case IntKind: + return integer(int64(val.(IntValue).Get())); + case Int8Kind: + return integer(int64(val.(Int8Value).Get())); + case Int16Kind: + return integer(int64(val.(Int16Value).Get())); + case Int32Kind: + return integer(int64(val.(Int32Value).Get())); + case Int64Kind: + return integer(int64(val.(Int64Value).Get())); + case UintKind: + return integer(int64(val.(UintValue).Get())); + case Uint8Kind: + return integer(int64(val.(Uint8Value).Get())); + case Uint16Kind: + return integer(int64(val.(Uint16Value).Get())); + case Uint32Kind: + return integer(int64(val.(Uint32Value).Get())); + case Uint64Kind: + return integer(int64(val.(Uint64Value).Get())); + case FloatKind: + if strconv.FloatSize == 32 { + return strconv.Ftoa32(float32(val.(FloatValue).Get()), 'g', -1); + } else { + return strconv.Ftoa64(float64(val.(FloatValue).Get()), 'g', -1); + } + case Float32Kind: + return strconv.Ftoa32(val.(Float32Value).Get(), 'g', -1); + case Float64Kind: + return strconv.Ftoa64(val.(Float64Value).Get(), 'g', -1); + case StringKind: + return val.(StringValue).Get(); + case BoolKind: + if val.(BoolValue).Get() { + return "true" + } else { + return "false" + } + case PtrKind: + v := val.(PtrValue); + return typeToString(typ, false) + "(" + integer(int64(uintptr(v.Get()))) + ")"; + case ArrayKind: + t := typ.(ArrayType); + v := val.(ArrayValue); + str += typeToString(t, false); + str += "{"; + for i := 0; i < v.Len(); i++ { + if i > 0 { + str += ", " + } + str += valueToString(v.Elem(i)); + } + str += "}"; + return str; + case MapKind: + t := typ.(MapType); + v := val.(MapValue); + str = typeToString(t, false); + str += "{"; + str += "<can't iterate on maps>"; + str += "}"; + return str; + case ChanKind: + str = typeToString(typ, false); + return str; + case StructKind: + t := typ.(StructType); + v := val.(StructValue); + str += typeToString(t, false); + str += "{"; + for i := 0; i < v.Len(); i++ { + if i > 0 { + str += ", " + } + str += valueToString(v.Field(i)); + } + str += "}"; + return str; + case InterfaceKind: + return "can't print interfaces yet"; + case FuncKind: + v := val.(FuncValue); + return typeToString(typ, false) + "(" + integer(int64(uintptr(v.Get()))) + ")"; + default: + panicln("reflect.valueToString: can't print type ", val.Kind()); + } + return "reflect.valueToString: can't happen"; +} diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go new file mode 100644 index 000000000..c8542183a --- /dev/null +++ b/src/pkg/reflect/type.go @@ -0,0 +1,1047 @@ +// Copyright 2009 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. + +// Reflection library. +// Types and parsing of type strings. + +// This package implements data ``reflection''. A program can use it to analyze types +// and values it does not know at compile time, such as the values passed in a call +// to a function with a ... parameter. This is achieved by extracting the dynamic +// contents of an interface value. +package reflect + +import ( + "sync"; + "unsafe"; + "utf8"; +) + +type Type interface + +func ExpandType(name string) Type + +func typestrings() string // implemented in C; declared here + +// These constants identify what kind of thing a Type represents: an int, struct, etc. +const ( + MissingKind = iota; + ArrayKind; + BoolKind; + ChanKind; + DotDotDotKind; + FloatKind; + Float32Kind; + Float64Kind; + FuncKind; + IntKind; + Int16Kind; + Int32Kind; + Int64Kind; + Int8Kind; + InterfaceKind; + MapKind; + PtrKind; + StringKind; + StructKind; + UintKind; + Uint16Kind; + Uint32Kind; + Uint64Kind; + Uint8Kind; + UintptrKind; +) + +// For sizes and alignments. + +type allTypes struct { + xarray []byte; + xbool bool; + xchan chan byte; + xfloat float; + xfloat32 float32; + xfloat64 float64; + xfunc func(); + xint int; + xint16 int16; + xint32 int32; + xint64 int64; + xint8 int8; + xinterface interface {}; + xmap map[byte]byte; + xptr *byte; + xslice []byte; + xstring string; + xuint uint; + xuint16 uint16; + xuint32 uint32; + xuint64 uint64; + xuint8 uint8; + xuintptr uintptr; +} + +var ( + x allTypes; + minStruct struct { uint8 }; +) + +const ( + minStructAlignMask = unsafe.Sizeof(minStruct) - 1; + ptrsize = unsafe.Sizeof(&x); + interfacesize = unsafe.Sizeof(x.xinterface); +) + +const missingString = "$missing$" // syntactic name for undefined type names +const dotDotDotString = "..." + +// Type is the generic interface to reflection types. Once its Kind is known, +// such as ArrayKind, the Type can be narrowed to the appropriate, more +// specific interface, such as ArrayType. Such narrowed types still implement +// the Type interface. +type Type interface { + // The kind of thing described: ArrayKind, BoolKind, etc. + Kind() int; + // The name declared for the type ("int", "BoolArray", etc.). + Name() string; + // For a named type, same as Name(); otherwise a representation of the type such as "[]int". + String() string; + // The number of bytes needed to store a value; analogous to unsafe.Sizeof(). + Size() int; + // The alignment of a value of this type when used as a field in a struct. + FieldAlign() int; +} + +// Fields and methods common to all types +type commonType struct { + kind int; + str string; + name string; + size int; +} + +func (c *commonType) Kind() int { + return c.kind +} + +func (c *commonType) Name() string { + return c.name +} + +func (c *commonType) String() string { + // If there is a name, show that instead of its expansion. + // This is important for reflection: a named type + // might have methods that the unnamed type does not. + if c.name != "" { + return c.name + } + return c.str +} + +func (c *commonType) Size() int { + return c.size +} + +// -- Basic + +type basicType struct { + commonType; + fieldAlign int; +} + +func newBasicType(name string, kind int, size int, fieldAlign int) Type { + return &basicType{ commonType{kind, name, name, size}, fieldAlign } +} + +func (t *basicType) FieldAlign() int { + return t.fieldAlign +} + +// Prebuilt basic Type objects representing the predeclared basic types. +// Most are self-evident except: +// Missing represents types whose representation cannot be discovered; usually an error. +// DotDotDot represents the pseudo-type of a ... parameter. +var ( + Missing = newBasicType(missingString, MissingKind, 1, 1); + DotDotDot = newBasicType(dotDotDotString, DotDotDotKind, unsafe.Sizeof(x.xinterface), unsafe.Alignof(x.xinterface)); + Bool = newBasicType("bool", BoolKind, unsafe.Sizeof(x.xbool), unsafe.Alignof(x.xbool)); + Int = newBasicType("int", IntKind, unsafe.Sizeof(x.xint), unsafe.Alignof(x.xint)); + Int8 = newBasicType("int8", Int8Kind, unsafe.Sizeof(x.xint8), unsafe.Alignof(x.xint8)); + Int16 = newBasicType("int16", Int16Kind, unsafe.Sizeof(x.xint16), unsafe.Alignof(x.xint16)); + Int32 = newBasicType("int32", Int32Kind, unsafe.Sizeof(x.xint32), unsafe.Alignof(x.xint32)); + Int64 = newBasicType("int64", Int64Kind, unsafe.Sizeof(x.xint64), unsafe.Alignof(x.xint64)); + Uint = newBasicType("uint", UintKind, unsafe.Sizeof(x.xuint), unsafe.Alignof(x.xuint)); + Uint8 = newBasicType("uint8", Uint8Kind, unsafe.Sizeof(x.xuint8), unsafe.Alignof(x.xuint8)); + Uint16 = newBasicType("uint16", Uint16Kind, unsafe.Sizeof(x.xuint16), unsafe.Alignof(x.xuint16)); + Uint32 = newBasicType("uint32", Uint32Kind, unsafe.Sizeof(x.xuint32), unsafe.Alignof(x.xuint32)); + Uint64 = newBasicType("uint64", Uint64Kind, unsafe.Sizeof(x.xuint64), unsafe.Alignof(x.xuint64)); + Uintptr = newBasicType("uintptr", UintptrKind, unsafe.Sizeof(x.xuintptr), unsafe.Alignof(x.xuintptr)); + Float = newBasicType("float", FloatKind, unsafe.Sizeof(x.xfloat), unsafe.Alignof(x.xfloat)); + Float32 = newBasicType("float32", Float32Kind, unsafe.Sizeof(x.xfloat32), unsafe.Alignof(x.xfloat32)); + Float64 = newBasicType("float64", Float64Kind, unsafe.Sizeof(x.xfloat64), unsafe.Alignof(x.xfloat64)); + String = newBasicType("string", StringKind, unsafe.Sizeof(x.xstring), unsafe.Alignof(x.xstring)); +) + +// Stub types allow us to defer evaluating type names until needed. +// If the name is empty, the type must be non-nil. + +type stubType struct { + name string; + typ Type; +} + +func newStubType(name string, typ Type) *stubType { + return &stubType{name, typ} +} + +func (t *stubType) Get() Type { + if t.typ == nil { + t.typ = ExpandType(t.name) + } + return t.typ +} + +// -- Pointer + +// PtrType represents a pointer. +type PtrType interface { + Type; + Sub() Type // The type of the pointed-to item; for "*int", it will be "int". +} + +type ptrTypeStruct struct { + commonType; + sub *stubType; +} + +func newPtrTypeStruct(name, typestring string, sub *stubType) *ptrTypeStruct { + return &ptrTypeStruct{ commonType{PtrKind, typestring, name, ptrsize}, sub} +} + +func (t *ptrTypeStruct) FieldAlign() int { + return unsafe.Alignof(x.xptr); +} + +func (t *ptrTypeStruct) Sub() Type { + return t.sub.Get() +} + +// -- Array + +// ArrayType represents an array or slice type. +type ArrayType interface { + Type; + IsSlice() bool; // True for slices, false for arrays. + Len() int; // 0 for slices, the length for array types. + Elem() Type; // The type of the elements. +} + +type arrayTypeStruct struct { + commonType; + elem *stubType; + isslice bool; // otherwise fixed array + len int; +} + +func newArrayTypeStruct(name, typestring string, open bool, len int, elem *stubType) *arrayTypeStruct { + return &arrayTypeStruct{ commonType{ArrayKind, typestring, name, 0 }, elem, open, len} +} + +func (t *arrayTypeStruct) Size() int { + if t.isslice { + return unsafe.Sizeof(x.xslice); + } + return t.len * t.elem.Get().Size(); +} + +func (t *arrayTypeStruct) FieldAlign() int { + if t.isslice { + return unsafe.Alignof(x.xslice); + } + return t.elem.Get().FieldAlign(); +} + +func (t *arrayTypeStruct) IsSlice() bool { + return t.isslice +} + +func (t *arrayTypeStruct) Len() int { + if t.isslice { + return 0 + } + return t.len +} + +func (t *arrayTypeStruct) Elem() Type { + return t.elem.Get() +} + +// -- Map + +// MapType represents a map type. +type MapType interface { + Type; + Key() Type; // The type of the keys. + Elem() Type; // The type of the elements/values. +} + +type mapTypeStruct struct { + commonType; + key *stubType; + elem *stubType; +} + +func newMapTypeStruct(name, typestring string, key, elem *stubType) *mapTypeStruct { + return &mapTypeStruct{ commonType{MapKind, typestring, name, ptrsize}, key, elem} +} + +func (t *mapTypeStruct) FieldAlign() int { + return unsafe.Alignof(x.xmap); +} + +func (t *mapTypeStruct) Key() Type { + return t.key.Get() +} + +func (t *mapTypeStruct) Elem() Type { + return t.elem.Get() +} + +// -- Chan + +// ChanType represents a chan type. +type ChanType interface { + Type; + Dir() int; // The direction of the channel. + Elem() Type; // The type of the elements. +} + +// Channel direction. +const ( + SendDir = 1 << iota; + RecvDir; + BothDir = SendDir | RecvDir; +) + +type chanTypeStruct struct { + commonType; + elem *stubType; + dir int; +} + +func newChanTypeStruct(name, typestring string, dir int, elem *stubType) *chanTypeStruct { + return &chanTypeStruct{ commonType{ChanKind, typestring, name, ptrsize}, elem, dir} +} + +func (t *chanTypeStruct) FieldAlign() int { + return unsafe.Alignof(x.xchan); +} + +func (t *chanTypeStruct) Dir() int { + return t.dir +} + +func (t *chanTypeStruct) Elem() Type { + return t.elem.Get() +} + +// -- Struct + +// StructType represents a struct type. +type StructType interface { + Type; + // Field returns, for field i, its name, Type, tag information, and byte offset. + // The indices are in declaration order starting at 0. + Field(i int) (name string, typ Type, tag string, offset int); + // Len is the number of fields. + Len() int; +} + +type structField struct { + name string; + typ *stubType; + tag string; + offset int; +} + +type structTypeStruct struct { + commonType; + field []structField; + fieldAlign int; +} + +func newStructTypeStruct(name, typestring string, field []structField) *structTypeStruct { + return &structTypeStruct{ commonType{StructKind, typestring, name, 0}, field, 0} +} + +func (t *structTypeStruct) FieldAlign() int { + t.Size(); // Compute size and alignment. + return t.fieldAlign +} + +func (t *structTypeStruct) Size() int { + if t.size > 0 { + return t.size + } + size := 0; + structAlignMask := 0; + for i := 0; i < len(t.field); i++ { + typ := t.field[i].typ.Get(); + elemsize := typ.Size(); + alignMask := typ.FieldAlign() - 1; + if alignMask > structAlignMask { + structAlignMask = alignMask + } + if alignMask > 0 { + size = (size + alignMask) &^ alignMask; + } + t.field[i].offset = size; + size += elemsize; + } + if (structAlignMask > 0) { + // 6g etc. always aligns structs to a minimum size, typically int64 + if structAlignMask < minStructAlignMask { + structAlignMask = minStructAlignMask + } + // TODO: In the PPC64 ELF ABI, floating point fields + // in a struct are aligned to a 4-byte boundary, but + // if the first field in the struct is a 64-bit float, + // the whole struct is aligned to an 8-byte boundary. + size = (size + structAlignMask) &^ structAlignMask; + t.fieldAlign = structAlignMask + 1; + } + t.size = size; + return size; +} + +func (t *structTypeStruct) Field(i int) (name string, typ Type, tag string, offset int) { + if t.field[i].offset == 0 { + t.Size(); // will compute offsets + } + return t.field[i].name, t.field[i].typ.Get(), t.field[i].tag, t.field[i].offset +} + +func (t *structTypeStruct) Len() int { + return len(t.field) +} + +// -- Interface + +// InterfaceType represents an interface type. +// It behaves much like a StructType, treating the methods as fields. +type InterfaceType interface { + Type; + // Field returns, for method i, its name, Type, the empty string, and 0. + // The indices are in declaration order starting at 0. TODO: is this true? + Field(int) (name string, typ Type, tag string, offset int); + Len() int; +} + +type interfaceTypeStruct struct { + commonType; + field []structField; +} + +func newInterfaceTypeStruct(name, typestring string, field []structField) *interfaceTypeStruct { + return &interfaceTypeStruct{ commonType{InterfaceKind, typestring, name, interfacesize}, field } +} + +func (t *interfaceTypeStruct) FieldAlign() int { + return unsafe.Alignof(x.xinterface); +} + +func (t *interfaceTypeStruct) Field(i int) (name string, typ Type, tag string, offset int) { + return t.field[i].name, t.field[i].typ.Get(), "", 0 +} + +func (t *interfaceTypeStruct) Len() int { + return len(t.field) +} + +var nilInterface = newInterfaceTypeStruct("nil", "", make([]structField, 0)); + +// -- Func + +// FuncType represents a function type. +type FuncType interface { + Type; + In() StructType; // The parameters in the form of a StructType. + Out() StructType; // The results in the form of a StructType. +} + +type funcTypeStruct struct { + commonType; + in *structTypeStruct; + out *structTypeStruct; +} + +func newFuncTypeStruct(name, typestring string, in, out *structTypeStruct) *funcTypeStruct { + return &funcTypeStruct{ commonType{FuncKind, typestring, name, ptrsize}, in, out } +} + +func (t *funcTypeStruct) FieldAlign() int { + return unsafe.Alignof(x.xfunc); +} + +func (t *funcTypeStruct) In() StructType { + return t.in +} + +func (t *funcTypeStruct) Out() StructType { + if t.out == nil { // nil.(StructType) != nil so make sure caller sees real nil + return nil + } + return t.out +} + +// Cache of expanded types keyed by type name. +var types map[string] Type + +// List of typename, typestring pairs +var typestring map[string] string +var initialized bool = false + +// Map of basic types to prebuilt stubTypes +var basicstub map[string] *stubType + +var missingStub *stubType; +var dotDotDotStub *stubType; + +// The database stored in the maps is global; use locking to guarantee safety. +var typestringlock sync.Mutex + +func lock() { + typestringlock.Lock() +} + +func unlock() { + typestringlock.Unlock() +} + +func init() { + lock(); // not necessary because of init ordering but be safe. + + types = make(map[string] Type); + typestring = make(map[string] string); + basicstub = make(map[string] *stubType); + + // Basics go into types table + types[missingString] = Missing; + types[dotDotDotString] = DotDotDot; + types["int"] = Int; + types["int8"] = Int8; + types["int16"] = Int16; + types["int32"] = Int32; + types["int64"] = Int64; + types["uint"] = Uint; + types["uint8"] = Uint8; + types["uint16"] = Uint16; + types["uint32"] = Uint32; + types["uint64"] = Uint64; + types["uintptr"] = Uintptr; + types["float"] = Float; + types["float32"] = Float32; + types["float64"] = Float64; + types["string"] = String; + types["bool"] = Bool; + + // Basics get prebuilt stubs + missingStub = newStubType(missingString, Missing); + dotDotDotStub = newStubType(dotDotDotString, DotDotDot); + basicstub[missingString] = missingStub; + basicstub[dotDotDotString] = dotDotDotStub; + basicstub["int"] = newStubType("int", Int); + basicstub["int8"] = newStubType("int8", Int8); + basicstub["int16"] = newStubType("int16", Int16); + basicstub["int32"] = newStubType("int32", Int32); + basicstub["int64"] = newStubType("int64", Int64); + basicstub["uint"] = newStubType("uint", Uint); + basicstub["uint8"] = newStubType("uint8", Uint8); + basicstub["uint16"] = newStubType("uint16", Uint16); + basicstub["uint32"] = newStubType("uint32", Uint32); + basicstub["uint64"] = newStubType("uint64", Uint64); + basicstub["uintptr"] = newStubType("uintptr", Uintptr); + basicstub["float"] = newStubType("float", Float); + basicstub["float32"] = newStubType("float32", Float32); + basicstub["float64"] = newStubType("float64", Float64); + basicstub["string"] = newStubType("string", String); + basicstub["bool"] = newStubType("bool", Bool); + + unlock(); +} + +/* + Parsing of type strings. These strings are how the run-time recovers type + information dynamically. + + Grammar + + stubtype = - represent as StubType when possible + type + identifier = + name + '?' + type = + basictypename - int8, string, etc. + typename + arraytype + structtype + interfacetype + chantype + maptype + pointertype + functiontype + typename = + name '.' name + doublequotedstring = + string in " "; escapes are \x00 (NUL) \n \t \" \\ + fieldlist = + [ field { [ ',' | ';' ] field } ] + field = + identifier stubtype [ doublequotedstring ] + arraytype = + '[' [ number ] ']' stubtype + structtype = + 'struct' '{' fieldlist '}' + interfacetype = + 'interface' '{' fieldlist '}' + chantype = + '<-' 'chan' stubtype + 'chan' '<-' stubtype + 'chan' stubtype + maptype = + 'map' '[' stubtype ']' stubtype + pointertype = + '*' stubtype + functiontype = + [ 'func' ] '(' fieldlist ')' [ '(' fieldlist ')' | stubtype ] + + In functiontype 'func' is optional because it is omitted in + the reflection string for interface types. + +*/ + +// Helper functions for token scanning +func isdigit(c uint8) bool { + return '0' <= c && c <= '9' +} + +func special(c uint8) bool { + s := "*[](){}<;,"; // Note: '.' is not in this list. "P.T" is an identifer, as is "?". + for i := 0; i < len(s); i++ { + if c == s[i] { + return true + } + } + return false; +} + +func hex00(s string, i int) bool { + return i + 2 < len(s) && s[i] == '0' && s[i+1] == '0' +} + +// Process backslashes. String known to be well-formed. +// Initial double-quote is left in, as an indication this token is a string. +func unescape(s string, backslash bool) string { + if !backslash { + return s + } + out := "\""; + for i := 1; i < len(s); i++ { + c := s[i]; + if c == '\\' { + i++; + c = s[i]; + switch c { + case 'n': + c = '\n'; + case 't': + c = '\t'; + case 'x': + if hex00(s, i+1) { + i += 2; + c = 0; + break; + } + // otherwise just put an 'x'; erroneous but safe. + // default is correct already; \\ is \; \" is " + } + } + out += string(c); + } + return out; +} + +// Simple parser for type strings +type typeParser struct { + str string; // string being parsed + token string; // the token being parsed now + tokstart int; // starting position of token + prevend int; // (one after) ending position of previous token + index int; // next character position in str +} + +// Return typestring starting at position i. It will finish at the +// end of the previous token (before trailing white space). +func (p *typeParser) TypeString(i int) string { + return p.str[i:p.prevend]; +} + +// Load next token into p.token +func (p *typeParser) Next() { + p.prevend = p.index; + token := ""; + for ; p.index < len(p.str) && p.str[p.index] == ' '; p.index++ { + } + p.tokstart = p.index; + if p.index >= len(p.str) { + p.token = ""; + return; + } + start := p.index; + c, w := utf8.DecodeRuneInString(p.str[p.index:len(p.str)]); + p.index += w; + switch { + case c == '<': + if p.index < len(p.str) && p.str[p.index] == '-' { + p.index++; + p.token = "<-"; + return; + } + fallthrough; // shouldn't happen but let the parser figure it out + case c == '.': + if p.index < len(p.str)+2 && p.str[p.index-1:p.index+2] == dotDotDotString { + p.index += 2; + p.token = dotDotDotString; + return; + } + fallthrough; // shouldn't happen but let the parser figure it out + case special(uint8(c)): + p.token = string(c); + return; + case isdigit(uint8(c)): + for p.index < len(p.str) && isdigit(p.str[p.index]) { + p.index++ + } + p.token = p.str[start : p.index]; + return; + case c == '"': // double-quoted string for struct field annotation + backslash := false; + for p.index < len(p.str) && p.str[p.index] != '"' { + if p.str[p.index] == '\\' { + if p.index+1 == len(p.str) { // bad final backslash + break; + } + p.index++; // skip (and accept) backslash + backslash = true; + } + p.index++ + } + p.token = unescape(p.str[start : p.index], backslash); + if p.index < len(p.str) { // properly terminated string + p.index++; // skip the terminating double-quote + } + return; + } + for p.index < len(p.str) && p.str[p.index] != ' ' && !special(p.str[p.index]) { + p.index++ + } + p.token = p.str[start : p.index]; +} + +func (p *typeParser) Type(name string) *stubType + +func (p *typeParser) Array(name string, tokstart int) *stubType { + size := 0; + open := true; + if p.token != "]" { + if len(p.token) == 0 || !isdigit(p.token[0]) { + return missingStub + } + // write our own (trivial and simpleminded) atoi to avoid dependency + size = 0; + for i := 0; i < len(p.token); i++ { + size = size * 10 + int(p.token[i]) - '0' + } + p.Next(); + open = false; + } + if p.token != "]" { + return missingStub + } + p.Next(); + elemtype := p.Type(""); + return newStubType(name, newArrayTypeStruct(name, p.TypeString(tokstart), open, size, elemtype)); +} + +func (p *typeParser) Map(name string, tokstart int) *stubType { + if p.token != "[" { + return missingStub + } + p.Next(); + keytype := p.Type(""); + if p.token != "]" { + return missingStub + } + p.Next(); + elemtype := p.Type(""); + return newStubType(name, newMapTypeStruct(name, p.TypeString(tokstart), keytype, elemtype)); +} + +func (p *typeParser) Chan(name string, tokstart, dir int) *stubType { + if p.token == "<-" { + if dir != BothDir { + return missingStub + } + p.Next(); + dir = SendDir; + } + elemtype := p.Type(""); + return newStubType(name, newChanTypeStruct(name, p.TypeString(tokstart), dir, elemtype)); +} + +// Parse array of fields for struct, interface, and func arguments +func (p *typeParser) Fields(sep, term string) []structField { + a := make([]structField, 10); + nf := 0; + for p.token != "" && p.token != term { + if nf == len(a) { + a1 := make([]structField, 2*nf); + for i := 0; i < nf; i++ { + a1[i] = a[i]; + } + a = a1; + } + name := p.token; + if name == "?" { // used to represent a missing name + name = "" + } + a[nf].name = name; + p.Next(); + a[nf].typ = p.Type(""); + if p.token != "" && p.token[0] == '"' { + a[nf].tag = p.token[1:len(p.token)]; + p.Next(); + } + nf++; + if p.token != sep { + break; + } + p.Next(); // skip separator + } + return a[0:nf]; +} + +// A single type packaged as a field for a function return +func (p *typeParser) OneField() []structField { + a := make([]structField, 1); + a[0].name = ""; + a[0].typ = p.Type(""); + return a; +} + +func (p *typeParser) Struct(name string, tokstart int) *stubType { + f := p.Fields(";", "}"); + if p.token != "}" { + return missingStub; + } + p.Next(); + return newStubType(name, newStructTypeStruct(name, p.TypeString(tokstart), f)); +} + +func (p *typeParser) Interface(name string, tokstart int) *stubType { + f := p.Fields(";", "}"); + if p.token != "}" { + return missingStub; + } + p.Next(); + return newStubType(name, newInterfaceTypeStruct(name, p.TypeString(tokstart), f)); +} + +func (p *typeParser) Func(name string, tokstart int) *stubType { + // may be 1 or 2 parenthesized lists + f1 := newStructTypeStruct("", "", p.Fields(",", ")")); + if p.token != ")" { + return missingStub; + } + p.Next(); + if p.token != "(" { + // 1 list: the in parameters are a list. Is there a single out parameter? + switch p.token { + case "", "}", ")", ",", ";": + return newStubType(name, newFuncTypeStruct(name, p.TypeString(tokstart), f1, nil)); + } + // A single out parameter. + f2 := newStructTypeStruct("", "", p.OneField()); + return newStubType(name, newFuncTypeStruct(name, p.TypeString(tokstart), f1, f2)); + } else { + p.Next(); + } + f2 := newStructTypeStruct("", "", p.Fields(",", ")")); + if p.token != ")" { + return missingStub; + } + p.Next(); + // 2 lists: the in and out parameters are present + return newStubType(name, newFuncTypeStruct(name, p.TypeString(tokstart), f1, f2)); +} + +func (p *typeParser) Type(name string) *stubType { + dir := BothDir; + tokstart := p.tokstart; + switch { + case p.token == "": + return nil; + case p.token == "*": + p.Next(); + sub := p.Type(""); + return newStubType(name, newPtrTypeStruct(name, p.TypeString(tokstart), sub)); + case p.token == "[": + p.Next(); + return p.Array(name, tokstart); + case p.token == "map": + p.Next(); + return p.Map(name, tokstart); + case p.token == "<-": + p.Next(); + dir = RecvDir; + if p.token != "chan" { + return missingStub; + } + fallthrough; + case p.token == "chan": + p.Next(); + return p.Chan(name, tokstart, dir); + case p.token == "struct": + p.Next(); + if p.token != "{" { + return missingStub + } + p.Next(); + return p.Struct(name, tokstart); + case p.token == "interface": + p.Next(); + if p.token != "{" { + return missingStub + } + p.Next(); + return p.Interface(name, tokstart); + case p.token == "func": + p.Next(); + if p.token != "(" { + return missingStub + } + p.Next(); + return p.Func(name, tokstart); + case p.token == "(": + p.Next(); + return p.Func(name, tokstart); + case isdigit(p.token[0]): + p.Next(); + return missingStub; + case special(p.token[0]): + p.Next(); + return missingStub; + } + // must be an identifier. is it basic? if so, we have a stub + if s, ok := basicstub[p.token]; ok { + p.Next(); + if name != "" { + // Need to make a copy because we are renaming a basic type + b := s.Get(); + s = newStubType(name, newBasicType(name, b.Kind(), b.Size(), b.FieldAlign())); + } + return s + } + // not a basic - must be of the form "P.T" + ndot := 0; + for i := 0; i < len(p.token); i++ { + if p.token[i] == '.' { + ndot++ + } + } + if ndot != 1 { + p.Next(); + return missingStub; + } + s := newStubType(p.token, nil); + p.Next(); + return s; +} + +// ParseTypeString takes a type name and type string (such as "[]int") and +// returns the Type structure representing a type name specifying the corresponding +// type. An empty typestring represents (the type of) a nil interface value. +func ParseTypeString(name, typestring string) Type { + if typestring == "" { + // If the typestring is empty, it represents (the type of) a nil interface value + return nilInterface + } + p := new(typeParser); + p.str = typestring; + p.Next(); + return p.Type(name).Get(); +} + +// Create typestring map from reflect.typestrings() data. Lock is held. +func initializeTypeStrings() { + if initialized { + return + } + initialized = true; + s := typestrings(); + slen := len(s); + for i := 0; i < slen; { + // "reflect.PtrType interface { Sub () (? reflect.Type) }\n" + // find the identifier + idstart := i; + for ; i < slen && s[i] != ' '; i++ { + } + if i == slen { + print("reflect.InitializeTypeStrings: bad identifier\n"); + return; + } + idend := i; + i++; + // find the end of the line, terminating the type + typestart := i; + for ; i < slen && s[i] != '\n'; i++ { + } + if i == slen { + print("reflect.InitializeTypeStrings: bad type string\n"); + return; + } + typeend := i; + i++; //skip newline + typestring[s[idstart:idend]] = s[typestart:typeend]; + } +} + +// Look up type string associated with name. Lock is held. +func typeNameToTypeString(name string) string { + s, ok := typestring[name]; + if !ok { + initializeTypeStrings(); + s, ok = typestring[name]; + if !ok { + s = missingString; + typestring[name] = s; + } + } + return s +} + +// ExpandType takes the name of a type and returns its Type structure, +// unpacking the associated type string if necessary. +func ExpandType(name string) Type { + lock(); + t, ok := types[name]; + if ok { + unlock(); + return t + } + types[name] = Missing; // prevent recursion; will overwrite + t1 := ParseTypeString(name, typeNameToTypeString(name)); + types[name] = t1; + unlock(); + return t1; +} diff --git a/src/pkg/reflect/typestring.c b/src/pkg/reflect/typestring.c new file mode 100644 index 000000000..667037bb1 --- /dev/null +++ b/src/pkg/reflect/typestring.c @@ -0,0 +1,37 @@ +// Copyright 2009 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. + + +extern char gotypestrings[]; // 4-byte count followed by byte[count] + +void FLUSH(void*); + +typedef struct String String; +struct String +{ + char* str; + char len[4]; + char cap[4]; +}; + +void +reflect·typestrings(String str) +{ + char *s; + int i; + + s = gotypestrings; + + // repeat the count twice + // once for len, once for cap + for(i=0; i<4; i++) { + str.len[i] = s[i]; + str.cap[i] = s[i]; + } + + // and the pointer + str.str = s+4; + + FLUSH(&str); +} diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go new file mode 100644 index 000000000..d4783d546 --- /dev/null +++ b/src/pkg/reflect/value.go @@ -0,0 +1,996 @@ +// Copyright 2009 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. + +// Reflection library. +// Handling values. + +package reflect + +import ( + "reflect"; + "unsafe"; +) + +// Addr is shorthand for unsafe.Pointer and is used to represent the address of Values. +type Addr unsafe.Pointer + +func equalType(a, b Type) bool { + return a.String() == b.String() +} + +// Value is the generic interface to reflection values. Once its Kind is known, +// such as BoolKind, the Value can be narrowed to the appropriate, more +// specific interface, such as BoolValue. Such narrowed values still implement +// the Value interface. +type Value interface { + // The kind of thing described: ArrayKind, BoolKind, etc. + Kind() int; + // The reflection Type of the value. + Type() Type; + // The address of the value. + Addr() Addr; + // The value itself is the dynamic value of an empty interface. + Interface() interface {}; +} + +func NewValue(e interface{}) Value; + +// commonValue fields and functionality for all values + +type commonValue struct { + kind int; + typ Type; + addr Addr; +} + +func (c *commonValue) Kind() int { + return c.kind +} + +func (c *commonValue) Type() Type { + return c.typ +} + +func (c *commonValue) Addr() Addr { + return c.addr +} + +func (c *commonValue) Interface() interface {} { + var i interface {}; + switch { + case c.typ.Kind() == InterfaceKind: + panic("not reached"); // InterfaceValue overrides this method + case c.typ.Size() > unsafe.Sizeof(uintptr(0)): + i = unsafe.Unreflect(uint64(uintptr(c.addr)), c.typ.String(), true); + default: + if uintptr(c.addr) == 0 { + panicln("reflect: address 0 for", c.typ.String()); + } + i = unsafe.Unreflect(uint64(uintptr(*(*Addr)(c.addr))), c.typ.String(), false); + } + return i; +} + +func newValueAddr(typ Type, addr Addr) Value + +type creatorFn func(typ Type, addr Addr) Value + + +// -- Missing + +// MissingValue represents a value whose type is not known. It usually +// indicates an error. +type MissingValue interface { + Value; +} + +type missingValueStruct struct { + commonValue +} + +func missingCreator(typ Type, addr Addr) Value { + return &missingValueStruct{ commonValue{MissingKind, typ, addr} } +} + +// -- Int + +// IntValue represents an int value. +type IntValue interface { + Value; + Get() int; // Get the underlying int. + Set(int); // Set the underlying int. +} + +type intValueStruct struct { + commonValue +} + +func intCreator(typ Type, addr Addr) Value { + return &intValueStruct{ commonValue{IntKind, typ, addr} } +} + +func (v *intValueStruct) Get() int { + return *(*int)(v.addr) +} + +func (v *intValueStruct) Set(i int) { + *(*int)(v.addr) = i +} + +// -- Int8 + +// Int8Value represents an int8 value. +type Int8Value interface { + Value; + Get() int8; // Get the underlying int8. + Set(int8); // Set the underlying int8. +} + +type int8ValueStruct struct { + commonValue +} + +func int8Creator(typ Type, addr Addr) Value { + return &int8ValueStruct{ commonValue{Int8Kind, typ, addr} } +} + +func (v *int8ValueStruct) Get() int8 { + return *(*int8)(v.addr) +} + +func (v *int8ValueStruct) Set(i int8) { + *(*int8)(v.addr) = i +} + +// -- Int16 + +// Int16Value represents an int16 value. +type Int16Value interface { + Value; + Get() int16; // Get the underlying int16. + Set(int16); // Set the underlying int16. +} + +type int16ValueStruct struct { + commonValue +} + +func int16Creator(typ Type, addr Addr) Value { + return &int16ValueStruct{ commonValue{Int16Kind, typ, addr} } +} + +func (v *int16ValueStruct) Get() int16 { + return *(*int16)(v.addr) +} + +func (v *int16ValueStruct) Set(i int16) { + *(*int16)(v.addr) = i +} + +// -- Int32 + +// Int32Value represents an int32 value. +type Int32Value interface { + Value; + Get() int32; // Get the underlying int32. + Set(int32); // Set the underlying int32. +} + +type int32ValueStruct struct { + commonValue +} + +func int32Creator(typ Type, addr Addr) Value { + return &int32ValueStruct{ commonValue{Int32Kind, typ, addr} } +} + +func (v *int32ValueStruct) Get() int32 { + return *(*int32)(v.addr) +} + +func (v *int32ValueStruct) Set(i int32) { + *(*int32)(v.addr) = i +} + +// -- Int64 + +// Int64Value represents an int64 value. +type Int64Value interface { + Value; + Get() int64; // Get the underlying int64. + Set(int64); // Set the underlying int64. +} + +type int64ValueStruct struct { + commonValue +} + +func int64Creator(typ Type, addr Addr) Value { + return &int64ValueStruct{ commonValue{Int64Kind, typ, addr} } +} + +func (v *int64ValueStruct) Get() int64 { + return *(*int64)(v.addr) +} + +func (v *int64ValueStruct) Set(i int64) { + *(*int64)(v.addr) = i +} + +// -- Uint + +// UintValue represents a uint value. +type UintValue interface { + Value; + Get() uint; // Get the underlying uint. + Set(uint); // Set the underlying uint. +} + +type uintValueStruct struct { + commonValue +} + +func uintCreator(typ Type, addr Addr) Value { + return &uintValueStruct{ commonValue{UintKind, typ, addr} } +} + +func (v *uintValueStruct) Get() uint { + return *(*uint)(v.addr) +} + +func (v *uintValueStruct) Set(i uint) { + *(*uint)(v.addr) = i +} + +// -- Uint8 + +// Uint8Value represents a uint8 value. +type Uint8Value interface { + Value; + Get() uint8; // Get the underlying uint8. + Set(uint8); // Set the underlying uint8. +} + +type uint8ValueStruct struct { + commonValue +} + +func uint8Creator(typ Type, addr Addr) Value { + return &uint8ValueStruct{ commonValue{Uint8Kind, typ, addr} } +} + +func (v *uint8ValueStruct) Get() uint8 { + return *(*uint8)(v.addr) +} + +func (v *uint8ValueStruct) Set(i uint8) { + *(*uint8)(v.addr) = i +} + +// -- Uint16 + +// Uint16Value represents a uint16 value. +type Uint16Value interface { + Value; + Get() uint16; // Get the underlying uint16. + Set(uint16); // Set the underlying uint16. +} + +type uint16ValueStruct struct { + commonValue +} + +func uint16Creator(typ Type, addr Addr) Value { + return &uint16ValueStruct{ commonValue{Uint16Kind, typ, addr} } +} + +func (v *uint16ValueStruct) Get() uint16 { + return *(*uint16)(v.addr) +} + +func (v *uint16ValueStruct) Set(i uint16) { + *(*uint16)(v.addr) = i +} + +// -- Uint32 + +// Uint32Value represents a uint32 value. +type Uint32Value interface { + Value; + Get() uint32; // Get the underlying uint32. + Set(uint32); // Set the underlying uint32. +} + +type uint32ValueStruct struct { + commonValue +} + +func uint32Creator(typ Type, addr Addr) Value { + return &uint32ValueStruct{ commonValue{Uint32Kind, typ, addr} } +} + +func (v *uint32ValueStruct) Get() uint32 { + return *(*uint32)(v.addr) +} + +func (v *uint32ValueStruct) Set(i uint32) { + *(*uint32)(v.addr) = i +} + +// -- Uint64 + +// Uint64Value represents a uint64 value. +type Uint64Value interface { + Value; + Get() uint64; // Get the underlying uint64. + Set(uint64); // Set the underlying uint64. +} + +type uint64ValueStruct struct { + commonValue +} + +func uint64Creator(typ Type, addr Addr) Value { + return &uint64ValueStruct{ commonValue{Uint64Kind, typ, addr} } +} + +func (v *uint64ValueStruct) Get() uint64 { + return *(*uint64)(v.addr) +} + +func (v *uint64ValueStruct) Set(i uint64) { + *(*uint64)(v.addr) = i +} + +// -- Uintptr + +// UintptrValue represents a uintptr value. +type UintptrValue interface { + Value; + Get() uintptr; // Get the underlying uintptr. + Set(uintptr); // Set the underlying uintptr. +} + +type uintptrValueStruct struct { + commonValue +} + +func uintptrCreator(typ Type, addr Addr) Value { + return &uintptrValueStruct{ commonValue{UintptrKind, typ, addr} } +} + +func (v *uintptrValueStruct) Get() uintptr { + return *(*uintptr)(v.addr) +} + +func (v *uintptrValueStruct) Set(i uintptr) { + *(*uintptr)(v.addr) = i +} + +// -- Float + +// FloatValue represents a float value. +type FloatValue interface { + Value; + Get() float; // Get the underlying float. + Set(float); // Get the underlying float. +} + +type floatValueStruct struct { + commonValue +} + +func floatCreator(typ Type, addr Addr) Value { + return &floatValueStruct{ commonValue{FloatKind, typ, addr} } +} + +func (v *floatValueStruct) Get() float { + return *(*float)(v.addr) +} + +func (v *floatValueStruct) Set(f float) { + *(*float)(v.addr) = f +} + +// -- Float32 + +// Float32Value represents a float32 value. +type Float32Value interface { + Value; + Get() float32; // Get the underlying float32. + Set(float32); // Get the underlying float32. +} + +type float32ValueStruct struct { + commonValue +} + +func float32Creator(typ Type, addr Addr) Value { + return &float32ValueStruct{ commonValue{Float32Kind, typ, addr} } +} + +func (v *float32ValueStruct) Get() float32 { + return *(*float32)(v.addr) +} + +func (v *float32ValueStruct) Set(f float32) { + *(*float32)(v.addr) = f +} + +// -- Float64 + +// Float64Value represents a float64 value. +type Float64Value interface { + Value; + Get() float64; // Get the underlying float64. + Set(float64); // Get the underlying float64. +} + +type float64ValueStruct struct { + commonValue +} + +func float64Creator(typ Type, addr Addr) Value { + return &float64ValueStruct{ commonValue{Float64Kind, typ, addr} } +} + +func (v *float64ValueStruct) Get() float64 { + return *(*float64)(v.addr) +} + +func (v *float64ValueStruct) Set(f float64) { + *(*float64)(v.addr) = f +} + +// -- String + +// StringValue represents a string value. +type StringValue interface { + Value; + Get() string; // Get the underlying string value. + Set(string); // Set the underlying string value. +} + +type stringValueStruct struct { + commonValue +} + +func stringCreator(typ Type, addr Addr) Value { + return &stringValueStruct{ commonValue{StringKind, typ, addr} } +} + +func (v *stringValueStruct) Get() string { + return *(*string)(v.addr) +} + +func (v *stringValueStruct) Set(s string) { + *(*string)(v.addr) = s +} + +// -- Bool + +// BoolValue represents a bool value. +type BoolValue interface { + Value; + Get() bool; // Get the underlying bool value. + Set(bool); // Set the underlying bool value. +} + +type boolValueStruct struct { + commonValue +} + +func boolCreator(typ Type, addr Addr) Value { + return &boolValueStruct{ commonValue{BoolKind, typ, addr} } +} + +func (v *boolValueStruct) Get() bool { + return *(*bool)(v.addr) +} + +func (v *boolValueStruct) Set(b bool) { + *(*bool)(v.addr) = b +} + +// -- Pointer + +// PtrValue represents a pointer value. +type PtrValue interface { + Value; + Sub() Value; // The Value pointed to. + Get() Addr; // Get the address stored in the pointer. + SetSub(Value); // Set the the pointed-to Value. + IsNil() bool; +} + +type ptrValueStruct struct { + commonValue +} + +func (v *ptrValueStruct) Get() Addr { + return *(*Addr)(v.addr) +} + +func (v *ptrValueStruct) Sub() Value { + return newValueAddr(v.typ.(PtrType).Sub(), v.Get()); +} + +func (v *ptrValueStruct) SetSub(subv Value) { + a := v.typ.(PtrType).Sub(); + b := subv.Type(); + if !equalType(a, b) { + panicln("reflect: incompatible types in PtrValue.SetSub:", + a.String(), b.String()); + } + *(*Addr)(v.addr) = subv.Addr(); +} + +func (v *ptrValueStruct) IsNil() bool { + return uintptr(*(*Addr)(v.addr)) == 0 +} + +func ptrCreator(typ Type, addr Addr) Value { + return &ptrValueStruct{ commonValue{PtrKind, typ, addr} }; +} + +// -- Array +// Slices and arrays are represented by the same interface. + +// ArrayValue represents an array or slice value. +type ArrayValue interface { + Value; + IsSlice() bool; // Is this a slice (true) or array (false)? + Len() int; // The length of the array/slice. + Cap() int; // The capacity of the array/slice (==Len() for arrays). + Elem(i int) Value; // The Value of the i'th element. + SetLen(len int); // Set the length; slice only. + Set(src ArrayValue); // Set the underlying Value; slice only for src and dest both. + CopyFrom(src ArrayValue, n int); // Copy the elements from src; lengths must match. + IsNil() bool; +} + +func copyArray(dst ArrayValue, src ArrayValue, n int); + +/* + Run-time representation of slices looks like this: + struct Slice { + byte* array; // actual data + uint32 nel; // number of elements + uint32 cap; + }; +*/ +type runtimeSlice struct { + data Addr; + len uint32; + cap uint32; +} + +type sliceValueStruct struct { + commonValue; + elemtype Type; + elemsize int; + slice *runtimeSlice; +} + +func (v *sliceValueStruct) IsSlice() bool { + return true +} + +func (v *sliceValueStruct) Len() int { + return int(v.slice.len); +} + +func (v *sliceValueStruct) Cap() int { + return int(v.slice.cap); +} + +func (v *sliceValueStruct) SetLen(len int) { + if len > v.Cap() { + panicln("reflect: sliceValueStruct.SetLen", len, v.Cap()); + } + v.slice.len = uint32(len); +} + +func (v *sliceValueStruct) Set(src ArrayValue) { + if !src.IsSlice() { + panic("can't set slice from array"); + } + s := src.(*sliceValueStruct); + if !equalType(v.typ, s.typ) { + panicln("incompatible types in ArrayValue.Set()"); + } + *v.slice = *s.slice; +} + +func (v *sliceValueStruct) Elem(i int) Value { + data_uint := uintptr(v.slice.data) + uintptr(i * v.elemsize); + return newValueAddr(v.elemtype, Addr(data_uint)); +} + +func (v *sliceValueStruct) CopyFrom(src ArrayValue, n int) { + copyArray(v, src, n); +} + +func (v *sliceValueStruct) IsNil() bool { + return uintptr(v.slice.data) == 0 +} + +type arrayValueStruct struct { + commonValue; + elemtype Type; + elemsize int; + len int; +} + +func (v *arrayValueStruct) IsSlice() bool { + return false +} + +func (v *arrayValueStruct) Len() int { + return v.len +} + +func (v *arrayValueStruct) Cap() int { + return v.len +} + +func (v *arrayValueStruct) SetLen(len int) { + panicln("can't set len of array"); +} + +func (v *arrayValueStruct) Set(src ArrayValue) { + panicln("can't set array"); +} + +func (v *arrayValueStruct) Elem(i int) Value { + data_uint := uintptr(v.addr) + uintptr(i * v.elemsize); + return newValueAddr(v.elemtype, Addr(data_uint)); +} + +func (v *arrayValueStruct) CopyFrom(src ArrayValue, n int) { + copyArray(v, src, n); +} + +func (v *arrayValueStruct) IsNil() bool { + return false +} + +func arrayCreator(typ Type, addr Addr) Value { + arraytype := typ.(ArrayType); + if arraytype.IsSlice() { + v := new(sliceValueStruct); + v.kind = ArrayKind; + v.addr = addr; + v.typ = typ; + v.elemtype = arraytype.Elem(); + v.elemsize = v.elemtype.Size(); + v.slice = (*runtimeSlice)(addr); + return v; + } + v := new(arrayValueStruct); + v.kind = ArrayKind; + v.addr = addr; + v.typ = typ; + v.elemtype = arraytype.Elem(); + v.elemsize = v.elemtype.Size(); + v.len = arraytype.Len(); + return v; +} + +// -- Map TODO: finish and test + +// MapValue represents a map value. +// Its implementation is incomplete. +type MapValue interface { + Value; + Len() int; // The number of elements; currently always returns 0. + Elem(key Value) Value; // The value indexed by key; unimplemented. + IsNil() bool; +} + +type mapValueStruct struct { + commonValue +} + +func mapCreator(typ Type, addr Addr) Value { + return &mapValueStruct{ commonValue{MapKind, typ, addr} } +} + +func (v *mapValueStruct) Len() int { + return 0 // TODO: probably want this to be dynamic +} + +func (v *mapValueStruct) IsNil() bool { + return false // TODO: implement this properly +} + +func (v *mapValueStruct) Elem(key Value) Value { + panic("map value element"); + return nil +} + +// -- Chan + +// ChanValue represents a chan value. +// Its implementation is incomplete. +type ChanValue interface { + Value; + IsNil() bool; +} + +type chanValueStruct struct { + commonValue +} + +func (v *chanValueStruct) IsNil() bool { + return false // TODO: implement this properly +} + +func chanCreator(typ Type, addr Addr) Value { + return &chanValueStruct{ commonValue{ChanKind, typ, addr} } +} + +// -- Struct + +// StructValue represents a struct value. +type StructValue interface { + Value; + Len() int; // The number of fields. + Field(i int) Value; // The Value of field i. +} + +type structValueStruct struct { + commonValue; + field []Value; +} + +func (v *structValueStruct) Len() int { + return len(v.field) +} + +func (v *structValueStruct) Field(i int) Value { + return v.field[i] +} + +func structCreator(typ Type, addr Addr) Value { + t := typ.(StructType); + nfield := t.Len(); + v := &structValueStruct{ commonValue{StructKind, typ, addr}, make([]Value, nfield) }; + for i := 0; i < nfield; i++ { + name, ftype, str, offset := t.Field(i); + addr_uint := uintptr(addr) + uintptr(offset); + v.field[i] = newValueAddr(ftype, Addr(addr_uint)); + } + v.typ = typ; + return v; +} + +// -- Interface + +// InterfaceValue represents an interface value. +type InterfaceValue interface { + Value; + Get() interface {}; // Get the underlying interface{} value. + Value() Value; + IsNil() bool; +} + +type interfaceValueStruct struct { + commonValue +} + +func (v *interfaceValueStruct) Get() interface{} { + // There are two different representations of interface values, + // one if the interface type has methods and one if it doesn't. + // These two representations require different expressions + // to extract correctly. + if v.Type().(InterfaceType).Len() == 0 { + // Extract as interface value without methods. + return *(*interface{})(v.addr) + } + // Extract from v.addr as interface value with methods. + return *(*interface{ m() })(v.addr) +} + +func (v *interfaceValueStruct) Interface() interface{} { + return v.Get(); +} + +func (v *interfaceValueStruct) Value() Value { + i := v.Get(); + if i == nil { + return nil; + } + return NewValue(i); +} + +func (v *interfaceValueStruct) IsNil() bool { + return *(*interface{})(v.addr) == nil +} + +func interfaceCreator(typ Type, addr Addr) Value { + return &interfaceValueStruct{ commonValue{InterfaceKind, typ, addr} } +} + +// -- Func + + +// FuncValue represents a func value. +// Its implementation is incomplete. +type FuncValue interface { + Value; + Get() Addr; // The address of the function. + IsNil() bool; +} + +type funcValueStruct struct { + commonValue +} + +func (v *funcValueStruct) Get() Addr { + return *(*Addr)(v.addr) +} + +func (v *funcValueStruct) IsNil() bool { + return *(*Addr)(v.addr) == nil +} + +func funcCreator(typ Type, addr Addr) Value { + return &funcValueStruct{ commonValue{FuncKind, typ, addr} } +} + +var creator = map[int] creatorFn { + MissingKind : missingCreator, + IntKind : intCreator, + Int8Kind : int8Creator, + Int16Kind : int16Creator, + Int32Kind : int32Creator, + Int64Kind : int64Creator, + UintKind : uintCreator, + Uint8Kind : uint8Creator, + Uint16Kind : uint16Creator, + Uint32Kind : uint32Creator, + Uint64Kind : uint64Creator, + UintptrKind : uintptrCreator, + FloatKind : floatCreator, + Float32Kind : float32Creator, + Float64Kind : float64Creator, + StringKind : stringCreator, + BoolKind : boolCreator, + PtrKind : ptrCreator, + ArrayKind : arrayCreator, + MapKind : mapCreator, + ChanKind : chanCreator, + StructKind : structCreator, + InterfaceKind : interfaceCreator, + FuncKind : funcCreator, +} + +var typecache = make(map[string] Type); + +func newValueAddr(typ Type, addr Addr) Value { + c, ok := creator[typ.Kind()]; + if !ok { + panicln("no creator for type" , typ.String()); + } + return c(typ, addr); +} + +// NewZeroValue creates a new, zero-initialized Value for the specified Type. +func NewZeroValue(typ Type) Value { + size := typ.Size(); + if size == 0 { + size = 1; + } + data := make([]uint8, size); + return newValueAddr(typ, Addr(&data[0])); +} + +// NewSliceValue creates a new, zero-initialized slice value (ArrayValue) for the specified +// slice type (ArrayType), length, and capacity. +func NewSliceValue(typ ArrayType, len, cap int) ArrayValue { + if !typ.IsSlice() { + return nil + } + + array := new(runtimeSlice); + size := typ.Elem().Size() * cap; + if size == 0 { + size = 1; + } + data := make([]uint8, size); + array.data = Addr(&data[0]); + array.len = uint32(len); + array.cap = uint32(cap); + + return newValueAddr(typ, Addr(array)).(ArrayValue); +} + +// Works on both slices and arrays +func copyArray(dst ArrayValue, src ArrayValue, n int) { + if n == 0 { + return + } + dt := dst.Type().(ArrayType).Elem(); + st := src.Type().(ArrayType).Elem(); + if !equalType(dt, st) { + panicln("reflect: incompatible types in CopyArray:", + dt.String(), st.String()); + } + if n < 0 || n > dst.Len() || n > src.Len() { + panicln("reflect: CopyArray: invalid count", n); + } + dstp := uintptr(dst.Elem(0).Addr()); + srcp := uintptr(src.Elem(0).Addr()); + end := uintptr(n)*uintptr(dt.Size()); + if end % 8 == 0 { + for i := uintptr(0); i < end; i += 8{ + di := Addr(dstp + i); + si := Addr(srcp + i); + *(*uint64)(di) = *(*uint64)(si); + } + } else { + for i := uintptr(0); i < end; i++ { + di := Addr(dstp + i); + si := Addr(srcp + i); + *(*byte)(di) = *(*byte)(si); + } + } +} + +// NewValue creates a new Value from the interface{} object provided. +func NewValue(e interface {}) Value { + value, typestring, indir := unsafe.Reflect(e); + typ, ok := typecache[typestring]; + if !ok { + typ = ParseTypeString("", typestring); + if typ.Kind() == MissingKind { + // This can not happen: unsafe.Reflect should only + // ever tell us the names of types that exist. + // Of course it does happen, and when it does + // it is more helpful to catch it in action here than + // to see $missing$ in a later print. + panicln("missing type for", typestring); + } + typecache[typestring] = typ; + } + var ap Addr; + if indir { + // Content of interface is large and didn't + // fit, so it's a pointer to the actual content. + // We have an address, but we need to + // make a copy to avoid letting the caller + // edit the content inside the interface. + n := uintptr(typ.Size()); + data := make([]byte, n); + p1 := uintptr(Addr(&data[0])); + p2 := uintptr(value); + for i := uintptr(0); i < n; i++ { + *(*byte)(Addr(p1+i)) = *(*byte)(Addr(p2+i)); + } + ap = Addr(&data[0]); + } else { + // Content of interface is small and stored + // inside the interface. Make a copy so we + // can take its address. + x := new(uint64); + *x = value; + ap = Addr(x); + } + return newValueAddr(typ, ap); +} + +// Indirect indirects one level through a value, if it is a pointer. +// If not a pointer, the value is returned unchanged. +// Useful when walking arbitrary data structures. +func Indirect(v Value) Value { + if v.Kind() == PtrKind { + p := v.(PtrValue); + if p.Get() == nil { + return nil + } + v = p.Sub() + } + return v +} diff --git a/src/pkg/regexp/Makefile b/src/pkg/regexp/Makefile new file mode 100644 index 000000000..0312d510e --- /dev/null +++ b/src/pkg/regexp/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + regexp.$O\ + + +phases: a1 +_obj$D/regexp.a: phases + +a1: $(O1) + $(AR) grc _obj$D/regexp.a regexp.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/regexp.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/regexp.a + +packages: _obj$D/regexp.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/regexp.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/regexp.a diff --git a/src/pkg/regexp/all_test.go b/src/pkg/regexp/all_test.go new file mode 100644 index 000000000..a9f275893 --- /dev/null +++ b/src/pkg/regexp/all_test.go @@ -0,0 +1,235 @@ +// Copyright 2009 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 regexp + +import ( + "os"; + "regexp"; + "testing"; +) + +var good_re = []string{ + ``, + `.`, + `^.$`, + `a`, + `a*`, + `a+`, + `a?`, + `a|b`, + `a*|b*`, + `(a*|b)(c*|d)`, + `[a-z]`, + `[a-abc-c\-\]\[]`, + `[a-z]+`, + `[]`, + `[abc]`, + `[^1234]`, +} + +// TODO: nice to do this with a map +type stringError struct { + re string; + err os.Error; +} +var bad_re = []stringError{ + stringError{ `*`, regexp.ErrBareClosure }, + stringError{ `(abc`, regexp.ErrUnmatchedLpar }, + stringError{ `abc)`, regexp.ErrUnmatchedRpar }, + stringError{ `x[a-z`, regexp.ErrUnmatchedLbkt }, + stringError{ `abc]`, regexp.ErrUnmatchedRbkt }, + stringError{ `[z-a]`, regexp.ErrBadRange }, + stringError{ `abc\`, regexp.ErrExtraneousBackslash }, + stringError{ `a**`, regexp.ErrBadClosure }, + stringError{ `a*+`, regexp.ErrBadClosure }, + stringError{ `a??`, regexp.ErrBadClosure }, + stringError{ `*`, regexp.ErrBareClosure }, + stringError{ `\x`, regexp.ErrBadBackslash }, +} + +type vec []int; + +type tester struct { + re string; + text string; + match vec; +} + +var matches = []tester { + tester{ ``, "", vec{0,0} }, + tester{ `a`, "a", vec{0,1} }, + tester{ `x`, "y", vec{} }, + tester{ `b`, "abc", vec{1,2} }, + tester{ `.`, "a", vec{0,1} }, + tester{ `.*`, "abcdef", vec{0,6} }, + tester{ `^abcd$`, "abcd", vec{0,4} }, + tester{ `^bcd'`, "abcdef", vec{} }, + tester{ `^abcd$`, "abcde", vec{} }, + tester{ `a+`, "baaab", vec{1,4} }, + tester{ `a*`, "baaab", vec{0,0} }, + tester{ `[a-z]+`, "abcd", vec{0,4} }, + tester{ `[^a-z]+`, "ab1234cd", vec{2,6} }, + tester{ `[a\-\]z]+`, "az]-bcz", vec{0,4} }, + tester{ `[日本語]+`, "日本語日本語", vec{0,18} }, + tester{ `()`, "", vec{0,0, 0,0} }, + tester{ `(a)`, "a", vec{0,1, 0,1} }, + tester{ `(.)(.)`, "日a", vec{0,4, 0,3, 3,4} }, + tester{ `(.*)`, "", vec{0,0, 0,0} }, + tester{ `(.*)`, "abcd", vec{0,4, 0,4} }, + tester{ `(..)(..)`, "abcd", vec{0,4, 0,2, 2,4} }, + tester{ `(([^xyz]*)(d))`, "abcd", vec{0,4, 0,4, 0,3, 3,4} }, + tester{ `((a|b|c)*(d))`, "abcd", vec{0,4, 0,4, 2,3, 3,4} }, + tester{ `(((a|b|c)*)(d))`, "abcd", vec{0,4, 0,4, 0,3, 2,3, 3,4} }, + tester{ `a*(|(b))c*`, "aacc", vec{0,4, 2,2, -1,-1} }, +} + +func compileTest(t *testing.T, expr string, error os.Error) *regexp.Regexp { + re, err := regexp.Compile(expr); + if err != error { + t.Error("compiling `", expr, "`; unexpected error: ", err.String()); + } + return re +} + +func printVec(t *testing.T, m []int) { + l := len(m); + if l == 0 { + t.Log("\t<no match>"); + } else { + for i := 0; i < l; i = i+2 { + t.Log("\t", m[i], ",", m[i+1]) + } + } +} + +func printStrings(t *testing.T, m []string) { + l := len(m); + if l == 0 { + t.Log("\t<no match>"); + } else { + for i := 0; i < l; i = i+2 { + t.Logf("\t%q", m[i]) + } + } +} + +func equal(m1, m2 []int) bool { + l := len(m1); + if l != len(m2) { + return false + } + for i := 0; i < l; i++ { + if m1[i] != m2[i] { + return false + } + } + return true +} + +func equalStrings(m1, m2 []string) bool { + l := len(m1); + if l != len(m2) { + return false + } + for i := 0; i < l; i++ { + if m1[i] != m2[i] { + return false + } + } + return true +} + +func executeTest(t *testing.T, expr string, str string, match []int) { + re := compileTest(t, expr, nil); + if re == nil { + return + } + m := re.Execute(str); + if !equal(m, match) { + t.Error("Execute failure on `", expr, "` matching `", str, "`:"); + printVec(t, m); + t.Log("should be:"); + printVec(t, match); + } +} + +func TestGoodCompile(t *testing.T) { + for i := 0; i < len(good_re); i++ { + compileTest(t, good_re[i], nil); + } +} + +func TestBadCompile(t *testing.T) { + for i := 0; i < len(bad_re); i++ { + compileTest(t, bad_re[i].re, bad_re[i].err) + } +} + +func TestExecute(t *testing.T) { + for i := 0; i < len(matches); i++ { + test := &matches[i]; + executeTest(t, test.re, test.text, test.match) + } +} + +func matchTest(t *testing.T, expr string, str string, match []int) { + re := compileTest(t, expr, nil); + if re == nil { + return + } + m := re.Match(str); + if m != (len(match) > 0) { + t.Error("Match failure on `", expr, "` matching `", str, "`:", m, "should be", len(match) > 0); + } +} + +func TestMatch(t *testing.T) { + for i := 0; i < len(matches); i++ { + test := &matches[i]; + matchTest(t, test.re, test.text, test.match) + } +} + +func matchStringsTest(t *testing.T, expr string, str string, match []int) { + re := compileTest(t, expr, nil); + if re == nil { + return + } + strs := make([]string, len(match)/2); + for i := 0; i < len(match); i++ { + strs[i/2] = str[match[i] : match[i+1]] + } + m := re.MatchStrings(str); + if !equalStrings(m, strs) { + t.Error("MatchStrings failure on `", expr, "` matching `", str, "`:"); + printStrings(t, m); + t.Log("should be:"); + printStrings(t, strs); + } +} + +func TestMatchStrings(t *testing.T) { + for i := 0; i < len(matches); i++ { + test := &matches[i]; + matchTest(t, test.re, test.text, test.match) + } +} + +func matchFunctionTest(t *testing.T, expr string, str string, match []int) { + m, err := Match(expr, str); + if err == nil { + return + } + if m != (len(match) > 0) { + t.Error("function Match failure on `", expr, "` matching `", str, "`:", m, "should be", len(match) > 0); + } +} + +func TestMatchFunction(t *testing.T) { + for i := 0; i < len(matches); i++ { + test := &matches[i]; + matchFunctionTest(t, test.re, test.text, test.match) + } +} diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go new file mode 100644 index 000000000..b79800dd9 --- /dev/null +++ b/src/pkg/regexp/regexp.go @@ -0,0 +1,764 @@ +// Copyright 2009 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 regexp implements a simple regular expression library. +// +// The syntax of the regular expressions accepted is: +// +// regexp: +// concatenation { '|' concatenation } +// concatenation: +// { closure } +// closure: +// term [ '*' | '+' | '?' ] +// term: +// '^' +// '$' +// '.' +// character +// '[' [ '^' ] character-ranges ']' +// '(' regexp ')' +// +package regexp + +import ( + "container/vector"; + "os"; + "runtime"; + "utf8"; +) + +var debug = false; + +// Error codes returned by failures to parse an expression. +var ( + ErrInternal = os.NewError("internal error"); + ErrUnmatchedLpar = os.NewError("unmatched '('"); + ErrUnmatchedRpar = os.NewError("unmatched ')'"); + ErrUnmatchedLbkt = os.NewError("unmatched '['"); + ErrUnmatchedRbkt = os.NewError("unmatched ']'"); + ErrBadRange = os.NewError("bad range in character class"); + ErrExtraneousBackslash = os.NewError("extraneous backslash"); + ErrBadClosure = os.NewError("repeated closure (**, ++, etc.)"); + ErrBareClosure = os.NewError("closure applies to nothing"); + ErrBadBackslash = os.NewError("illegal backslash escape"); +) + +// An instruction executed by the NFA +type instr interface { + kind() int; // the type of this instruction: _CHAR, _ANY, etc. + next() instr; // the instruction to execute after this one + setNext(i instr); + index() int; + setIndex(i int); + print(); +} + +// Fields and methods common to all instructions +type common struct { + _next instr; + _index int; +} + +func (c *common) next() instr { return c._next } +func (c *common) setNext(i instr) { c._next = i } +func (c *common) index() int { return c._index } +func (c *common) setIndex(i int) { c._index = i } + +// The representation of a compiled regular expression. +// The public interface is entirely through methods. +type Regexp struct { + expr string; // the original expression + ch chan<- *Regexp; // reply channel when we're done + error os.Error; // compile- or run-time error; nil if OK + inst *vector.Vector; + start instr; + nbra int; // number of brackets in expression, for subexpressions +} + +const ( + _START // beginning of program + = iota; + _END; // end of program: success + _BOT; // '^' beginning of text + _EOT; // '$' end of text + _CHAR; // 'a' regular character + _CHARCLASS; // [a-z] character class + _ANY; // '.' any character + _BRA; // '(' parenthesized expression + _EBRA; // ')'; end of '(' parenthesized expression + _ALT; // '|' alternation + _NOP; // do nothing; makes it easy to link without patching +) + +// --- START start of program +type _Start struct { + common +} + +func (start *_Start) kind() int { return _START } +func (start *_Start) print() { print("start") } + +// --- END end of program +type _End struct { + common +} + +func (end *_End) kind() int { return _END } +func (end *_End) print() { print("end") } + +// --- BOT beginning of text +type _Bot struct { + common +} + +func (bot *_Bot) kind() int { return _BOT } +func (bot *_Bot) print() { print("bot") } + +// --- EOT end of text +type _Eot struct { + common +} + +func (eot *_Eot) kind() int { return _EOT } +func (eot *_Eot) print() { print("eot") } + +// --- CHAR a regular character +type _Char struct { + common; + char int; +} + +func (char *_Char) kind() int { return _CHAR } +func (char *_Char) print() { print("char ", string(char.char)) } + +func newChar(char int) *_Char { + c := new(_Char); + c.char = char; + return c; +} + +// --- CHARCLASS [a-z] + +type _CharClass struct { + common; + char int; + negate bool; // is character class negated? ([^a-z]) + // vector of int, stored pairwise: [a-z] is (a,z); x is (x,x): + ranges *vector.IntVector; +} + +func (cclass *_CharClass) kind() int { return _CHARCLASS } + +func (cclass *_CharClass) print() { + print("charclass"); + if cclass.negate { + print(" (negated)"); + } + for i := 0; i < cclass.ranges.Len(); i += 2 { + l := cclass.ranges.At(i); + r := cclass.ranges.At(i+1); + if l == r { + print(" [", string(l), "]"); + } else { + print(" [", string(l), "-", string(r), "]"); + } + } +} + +func (cclass *_CharClass) addRange(a, b int) { + // range is a through b inclusive + cclass.ranges.Push(a); + cclass.ranges.Push(b); +} + +func (cclass *_CharClass) matches(c int) bool { + for i := 0; i < cclass.ranges.Len(); i = i+2 { + min := cclass.ranges.At(i); + max := cclass.ranges.At(i+1); + if min <= c && c <= max { + return !cclass.negate + } + } + return cclass.negate +} + +func newCharClass() *_CharClass { + c := new(_CharClass); + c.ranges = vector.NewIntVector(0); + return c; +} + +// --- ANY any character +type _Any struct { + common +} + +func (any *_Any) kind() int { return _ANY } +func (any *_Any) print() { print("any") } + +// --- BRA parenthesized expression +type _Bra struct { + common; + n int; // subexpression number +} + +func (bra *_Bra) kind() int { return _BRA } +func (bra *_Bra) print() { print("bra", bra.n); } + +// --- EBRA end of parenthesized expression +type _Ebra struct { + common; + n int; // subexpression number +} + +func (ebra *_Ebra) kind() int { return _EBRA } +func (ebra *_Ebra) print() { print("ebra ", ebra.n); } + +// --- ALT alternation +type _Alt struct { + common; + left instr; // other branch +} + +func (alt *_Alt) kind() int { return _ALT } +func (alt *_Alt) print() { print("alt(", alt.left.index(), ")"); } + +// --- NOP no operation +type _Nop struct { + common +} + +func (nop *_Nop) kind() int { return _NOP } +func (nop *_Nop) print() { print("nop") } + +// report error and exit compiling/executing goroutine +func (re *Regexp) setError(err os.Error) { + re.error = err; + re.ch <- re; + runtime.Goexit(); +} + +func (re *Regexp) add(i instr) instr { + i.setIndex(re.inst.Len()); + re.inst.Push(i); + return i; +} + +type parser struct { + re *Regexp; + nlpar int; // number of unclosed lpars + pos int; + ch int; +} + +const endOfFile = -1 + +func (p *parser) c() int { + return p.ch; +} + +func (p *parser) nextc() int { + if p.pos >= len(p.re.expr) { + p.ch = endOfFile + } else { + c, w := utf8.DecodeRuneInString(p.re.expr[p.pos:len(p.re.expr)]); + p.ch = c; + p.pos += w; + } + return p.ch; +} + +func newParser(re *Regexp) *parser { + p := new(parser); + p.re = re; + p.nextc(); // load p.ch + return p; +} + +func (p *parser) regexp() (start, end instr) + +var iNULL instr + +func special(c int) bool { + s := `\.+*?()|[]`; + for i := 0; i < len(s); i++ { + if c == int(s[i]) { + return true + } + } + return false +} + +func specialcclass(c int) bool { + s := `\-[]`; + for i := 0; i < len(s); i++ { + if c == int(s[i]) { + return true + } + } + return false +} + +func (p *parser) charClass() instr { + cc := newCharClass(); + p.re.add(cc); + if p.c() == '^' { + cc.negate = true; + p.nextc(); + } + left := -1; + for { + switch c := p.c(); c { + case ']', endOfFile: + if left >= 0 { + p.re.setError(ErrBadRange); + } + return cc; + case '-': // do this before backslash processing + p.re.setError(ErrBadRange); + case '\\': + c = p.nextc(); + switch { + case c == endOfFile: + p.re.setError(ErrExtraneousBackslash); + case c == 'n': + c = '\n'; + case specialcclass(c): + // c is as delivered + default: + p.re.setError(ErrBadBackslash); + } + fallthrough; + default: + p.nextc(); + switch { + case left < 0: // first of pair + if p.c() == '-' { // range + p.nextc(); + left = c; + } else { // single char + cc.addRange(c, c); + } + case left <= c: // second of pair + cc.addRange(left, c); + left = -1; + default: + p.re.setError(ErrBadRange); + } + } + } + return iNULL +} + +func (p *parser) term() (start, end instr) { + switch c := p.c(); c { + case '|', endOfFile: + return iNULL, iNULL; + case '*', '+': + p.re.setError(ErrBareClosure); + case ')': + if p.nlpar == 0 { + p.re.setError(ErrUnmatchedRpar); + } + return iNULL, iNULL; + case ']': + p.re.setError(ErrUnmatchedRbkt); + case '^': + p.nextc(); + start = p.re.add(new(_Bot)); + return start, start; + case '$': + p.nextc(); + start = p.re.add(new(_Eot)); + return start, start; + case '.': + p.nextc(); + start = p.re.add(new(_Any)); + return start, start; + case '[': + p.nextc(); + start = p.charClass(); + if p.c() != ']' { + p.re.setError(ErrUnmatchedLbkt); + } + p.nextc(); + return start, start; + case '(': + p.nextc(); + p.nlpar++; + p.re.nbra++; // increment first so first subexpr is \1 + nbra := p.re.nbra; + start, end = p.regexp(); + if p.c() != ')' { + p.re.setError(ErrUnmatchedLpar); + } + p.nlpar--; + p.nextc(); + bra := new(_Bra); + p.re.add(bra); + ebra := new(_Ebra); + p.re.add(ebra); + bra.n = nbra; + ebra.n = nbra; + if start == iNULL { + if end == iNULL { + p.re.setError(ErrInternal) + } + start = ebra + } else { + end.setNext(ebra); + } + bra.setNext(start); + return bra, ebra; + case '\\': + c = p.nextc(); + switch { + case c == endOfFile: + p.re.setError(ErrExtraneousBackslash); + case c == 'n': + c = '\n'; + case special(c): + // c is as delivered + default: + p.re.setError(ErrBadBackslash); + } + fallthrough; + default: + p.nextc(); + start = newChar(c); + p.re.add(start); + return start, start + } + panic("unreachable"); +} + +func (p *parser) closure() (start, end instr) { + start, end = p.term(); + if start == iNULL { + return + } + switch p.c() { + case '*': + // (start,end)*: + alt := new(_Alt); + p.re.add(alt); + end.setNext(alt); // after end, do alt + alt.left = start; // alternate brach: return to start + start = alt; // alt becomes new (start, end) + end = alt; + case '+': + // (start,end)+: + alt := new(_Alt); + p.re.add(alt); + end.setNext(alt); // after end, do alt + alt.left = start; // alternate brach: return to start + end = alt; // start is unchanged; end is alt + case '?': + // (start,end)?: + alt := new(_Alt); + p.re.add(alt); + nop := new(_Nop); + p.re.add(nop); + alt.left = start; // alternate branch is start + alt.setNext(nop); // follow on to nop + end.setNext(nop); // after end, go to nop + start = alt; // start is now alt + end = nop; // end is nop pointed to by both branches + default: + return + } + switch p.nextc() { + case '*', '+', '?': + p.re.setError(ErrBadClosure); + } + return +} + +func (p *parser) concatenation() (start, end instr) { + start, end = iNULL, iNULL; + for { + nstart, nend := p.closure(); + switch { + case nstart == iNULL: // end of this concatenation + if start == iNULL { // this is the empty string + nop := p.re.add(new(_Nop)); + return nop, nop; + } + return; + case start == iNULL: // this is first element of concatenation + start, end = nstart, nend; + default: + end.setNext(nstart); + end = nend; + } + } + panic("unreachable"); +} + +func (p *parser) regexp() (start, end instr) { + start, end = p.concatenation(); + for { + switch p.c() { + default: + return; + case '|': + p.nextc(); + nstart, nend := p.concatenation(); + alt := new(_Alt); + p.re.add(alt); + alt.left = start; + alt.setNext(nstart); + nop := new(_Nop); + p.re.add(nop); + end.setNext(nop); + nend.setNext(nop); + start, end = alt, nop; + } + } + panic("unreachable"); +} + +func unNop(i instr) instr { + for i.kind() == _NOP { + i = i.next() + } + return i +} + +func (re *Regexp) eliminateNops() { + for i := 0; i < re.inst.Len(); i++ { + inst := re.inst.At(i).(instr); + if inst.kind() == _END { + continue + } + inst.setNext(unNop(inst.next())); + if inst.kind() == _ALT { + alt := inst.(*_Alt); + alt.left = unNop(alt.left); + } + } +} + +func (re *Regexp) dump() { + for i := 0; i < re.inst.Len(); i++ { + inst := re.inst.At(i).(instr); + print(inst.index(), ": "); + inst.print(); + if inst.kind() != _END { + print(" -> ", inst.next().index()) + } + print("\n"); + } +} + +func (re *Regexp) doParse() { + p := newParser(re); + start := new(_Start); + re.add(start); + s, e := p.regexp(); + start.setNext(s); + re.start = start; + e.setNext(re.add(new(_End))); + + if debug { + re.dump(); + println(); + } + + re.eliminateNops(); + + if debug { + re.dump(); + println(); + } +} + + +func compiler(str string, ch chan *Regexp) { + re := new(Regexp); + re.expr = str; + re.inst = vector.New(0); + re.ch = ch; + re.doParse(); + ch <- re; +} + +// Compile parses a regular expression and returns, if successful, a Regexp +// object that can be used to match against text. +func Compile(str string) (regexp *Regexp, error os.Error) { + // Compile in a separate goroutine and wait for the result. + ch := make(chan *Regexp); + go compiler(str, ch); + re := <-ch; + return re, re.error +} + +type state struct { + inst instr; // next instruction to execute + match []int; // pairs of bracketing submatches. 0th is start,end +} + +// Append new state to to-do list. Leftmost-longest wins so avoid +// adding a state that's already active. +func addState(s []state, inst instr, match []int) []state { + index := inst.index(); + l := len(s); + pos := match[0]; + // TODO: Once the state is a vector and we can do insert, have inputs always + // go in order correctly and this "earlier" test is never necessary, + for i := 0; i < l; i++ { + if s[i].inst.index() == index && // same instruction + s[i].match[0] < pos { // earlier match already going; lefmost wins + return s + } + } + if l == cap(s) { + s1 := make([]state, 2*l)[0:l]; + for i := 0; i < l; i++ { + s1[i] = s[i]; + } + s = s1; + } + s = s[0:l+1]; + s[l].inst = inst; + s[l].match = match; + return s; +} + +func (re *Regexp) doExecute(str string, pos int) []int { + var s [2][]state; // TODO: use a vector when state values (not ptrs) can be vector elements + s[0] = make([]state, 10)[0:0]; + s[1] = make([]state, 10)[0:0]; + in, out := 0, 1; + var final state; + found := false; + for pos <= len(str) { + if !found { + // prime the pump if we haven't seen a match yet + match := make([]int, 2*(re.nbra+1)); + for i := 0; i < len(match); i++ { + match[i] = -1; // no match seen; catches cases like "a(b)?c" on "ac" + } + match[0] = pos; + s[out] = addState(s[out], re.start.next(), match); + } + in, out = out, in; // old out state is new in state + s[out] = s[out][0:0]; // clear out state + if len(s[in]) == 0 { + // machine has completed + break; + } + charwidth := 1; + c := endOfFile; + if pos < len(str) { + c, charwidth = utf8.DecodeRuneInString(str[pos:len(str)]); + } + for i := 0; i < len(s[in]); i++ { + st := s[in][i]; + switch s[in][i].inst.kind() { + case _BOT: + if pos == 0 { + s[in] = addState(s[in], st.inst.next(), st.match) + } + case _EOT: + if pos == len(str) { + s[in] = addState(s[in], st.inst.next(), st.match) + } + case _CHAR: + if c == st.inst.(*_Char).char { + s[out] = addState(s[out], st.inst.next(), st.match) + } + case _CHARCLASS: + if st.inst.(*_CharClass).matches(c) { + s[out] = addState(s[out], st.inst.next(), st.match) + } + case _ANY: + if c != endOfFile { + s[out] = addState(s[out], st.inst.next(), st.match) + } + case _BRA: + n := st.inst.(*_Bra).n; + st.match[2*n] = pos; + s[in] = addState(s[in], st.inst.next(), st.match); + case _EBRA: + n := st.inst.(*_Ebra).n; + st.match[2*n+1] = pos; + s[in] = addState(s[in], st.inst.next(), st.match); + case _ALT: + s[in] = addState(s[in], st.inst.(*_Alt).left, st.match); + // give other branch a copy of this match vector + s1 := make([]int, 2*(re.nbra+1)); + for i := 0; i < len(s1); i++ { + s1[i] = st.match[i] + } + s[in] = addState(s[in], st.inst.next(), s1); + case _END: + // choose leftmost longest + if !found || // first + st.match[0] < final.match[0] || // leftmost + (st.match[0] == final.match[0] && pos > final.match[1]) { // longest + final = st; + final.match[1] = pos; + } + found = true; + default: + st.inst.print(); + panic("unknown instruction in execute"); + } + } + pos += charwidth; + } + return final.match; +} + + +// Execute matches the Regexp against the string s. +// The return value is an array of integers, in pairs, identifying the positions of +// substrings matched by the expression. +// s[a[0]:a[1]] is the substring matched by the entire expression. +// s[a[2*i]:a[2*i+1]] for i > 0 is the substring matched by the ith parenthesized subexpression. +// A negative value means the subexpression did not match any element of the string. +// An empty array means "no match". +func (re *Regexp) Execute(s string) (a []int) { + return re.doExecute(s, 0) +} + + +// Match returns whether the Regexp matches the string s. +// The return value is a boolean: true for match, false for no match. +func (re *Regexp) Match(s string) bool { + return len(re.doExecute(s, 0)) > 0 +} + + +// MatchStrings matches the Regexp against the string s. +// The return value is an array of strings matched by the expression. +// a[0] is the substring matched by the entire expression. +// a[i] for i > 0 is the substring matched by the ith parenthesized subexpression. +// An empty array means ``no match''. +func (re *Regexp) MatchStrings(s string) (a []string) { + r := re.doExecute(s, 0); + if r == nil { + return nil + } + a = make([]string, len(r)/2); + for i := 0; i < len(r); i += 2 { + if r[i] != -1 { // -1 means no match for this subexpression + a[i/2] = s[r[i] : r[i+1]] + } + } + return +} + +// Match checks whether a textual regular expression +// matches a substring. More complicated queries need +// to use Compile and the full Regexp interface. +func Match(pattern string, s string) (matched bool, error os.Error) { + re, err := Compile(pattern); + if err != nil { + return false, err + } + return re.Match(s), nil +} diff --git a/src/pkg/runtime/386/asm.s b/src/pkg/runtime/386/asm.s new file mode 100644 index 000000000..5d3c4261a --- /dev/null +++ b/src/pkg/runtime/386/asm.s @@ -0,0 +1,217 @@ +// Copyright 2009 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. + +TEXT _rt0_386(SB),7,$0 + // copy arguments forward on an even stack + MOVL 0(SP), AX // argc + LEAL 4(SP), BX // argv + SUBL $128, SP // plenty of scratch + ANDL $~7, SP + MOVL AX, 120(SP) // save argc, argv away + MOVL BX, 124(SP) + +/* + // write "go386\n" + PUSHL $6 + PUSHL $hello(SB) + PUSHL $1 + CALL sys·write(SB) + POPL AX + POPL AX + POPL AX +*/ + + CALL ldt0setup(SB) + + // set up %fs to refer to that ldt entry + MOVL $(7*8+7), AX + MOVW AX, FS + + // store through it, to make sure it works + MOVL $0x123, 0(FS) + MOVL tls0(SB), AX + CMPL AX, $0x123 + JEQ ok + MOVL AX, 0 +ok: + + // set up m and g "registers" + // g is 0(FS), m is 4(FS) + LEAL g0(SB), CX + MOVL CX, 0(FS) + LEAL m0(SB), AX + MOVL AX, 4(FS) + + // save m->g0 = g0 + MOVL CX, 0(AX) + + // create istack out of the OS stack + LEAL (-8192+104)(SP), AX // TODO: 104? + MOVL AX, 0(CX) // 8(g) is stack limit (w 104b guard) + MOVL SP, 4(CX) // 12(g) is base + CALL emptyfunc(SB) // fault if stack check is wrong + + // convention is D is always cleared + CLD + + CALL check(SB) + + // saved argc, argv + MOVL 120(SP), AX + MOVL AX, 0(SP) + MOVL 124(SP), AX + MOVL AX, 4(SP) + CALL args(SB) + CALL osinit(SB) + CALL schedinit(SB) + + // create a new goroutine to start program + PUSHL $mainstart(SB) // entry + PUSHL $8 // arg size + CALL sys·newproc(SB) + POPL AX + POPL AX + + // start this M + CALL mstart(SB) + + INT $3 + RET + +TEXT mainstart(SB),7,$0 + CALL main·init(SB) + CALL initdone(SB) + CALL main·main(SB) + PUSHL $0 + CALL exit(SB) + POPL AX + INT $3 + RET + +TEXT breakpoint(SB),7,$0 + BYTE $0xcc + RET + +// go-routine +TEXT gogo(SB), 7, $0 + MOVL 4(SP), AX // gobuf + MOVL 0(AX), SP // restore SP + MOVL 4(AX), AX + MOVL AX, 0(SP) // put PC on the stack + MOVL $1, AX + RET + +TEXT gosave(SB), 7, $0 + MOVL 4(SP), AX // gobuf + MOVL SP, 0(AX) // save SP + MOVL 0(SP), BX + MOVL BX, 4(AX) // save PC + MOVL $0, AX // return 0 + RET + +// support for morestack + +// return point when leaving new stack. +// save AX, jmp to lesstack to switch back +TEXT retfromnewstack(SB),7,$0 + MOVL 4(FS), BX // m + MOVL AX, 12(BX) // save AX in m->cret + JMP lessstack(SB) + +// gogo, returning 2nd arg instead of 1 +TEXT gogoret(SB), 7, $0 + MOVL 8(SP), AX // return 2nd arg + MOVL 4(SP), BX // gobuf + MOVL 0(BX), SP // restore SP + MOVL 4(BX), BX + MOVL BX, 0(SP) // put PC on the stack + RET + +TEXT setspgoto(SB), 7, $0 + MOVL 4(SP), AX // SP + MOVL 8(SP), BX // fn to call + MOVL 12(SP), CX // fn to return + MOVL AX, SP + PUSHL CX + JMP BX + POPL AX // not reached + RET + +// bool cas(int32 *val, int32 old, int32 new) +// Atomically: +// if(*val == old){ +// *val = new; +// return 1; +// }else +// return 0; +TEXT cas(SB), 7, $0 + MOVL 4(SP), BX + MOVL 8(SP), AX + MOVL 12(SP), CX + LOCK + CMPXCHGL CX, 0(BX) + JZ 3(PC) + MOVL $0, AX + RET + MOVL $1, AX + RET + +// void jmpdefer(fn, sp); +// called from deferreturn. +// 1. pop the caller +// 2. sub 5 bytes from the callers return +// 3. jmp to the argument +TEXT jmpdefer(SB), 7, $0 + MOVL 4(SP), AX // fn + MOVL 8(SP), BX // caller sp + LEAL -4(BX), SP // caller sp after CALL + SUBL $5, (SP) // return to CALL again + JMP AX // but first run the deferred function + +TEXT sys·memclr(SB),7,$0 + MOVL 4(SP), DI // arg 1 addr + MOVL 8(SP), CX // arg 2 count + ADDL $3, CX + SHRL $2, CX + MOVL $0, AX + CLD + REP + STOSL + RET + +TEXT sys·getcallerpc+0(SB),7,$0 + MOVL x+0(FP),AX // addr of first arg + MOVL -4(AX),AX // get calling pc + RET + +TEXT sys·setcallerpc+0(SB),7,$0 + MOVL x+0(FP),AX // addr of first arg + MOVL x+4(FP), BX + MOVL BX, -4(AX) // set calling pc + RET + +TEXT ldt0setup(SB),7,$16 + // set up ldt 7 to point at tls0 + // ldt 1 would be fine on Linux, but on OS X, 7 is as low as we can go. + MOVL $7, 0(SP) + LEAL tls0(SB), AX + MOVL AX, 4(SP) + MOVL $32, 8(SP) // sizeof(tls array) + CALL setldt(SB) + RET + +GLOBL m0+0(SB), $1024 +GLOBL g0+0(SB), $1024 + +GLOBL tls0+0(SB), $32 + +TEXT emptyfunc(SB),0,$0 + RET + +TEXT abort(SB),7,$0 + INT $0x3 + +DATA hello+0(SB)/8, $"go386\n\z\z" +GLOBL hello+0(SB), $8 + diff --git a/src/pkg/runtime/386/closure.c b/src/pkg/runtime/386/closure.c new file mode 100644 index 000000000..6ccbe3b8b --- /dev/null +++ b/src/pkg/runtime/386/closure.c @@ -0,0 +1,104 @@ +// Copyright 2009 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. + +#include "runtime.h" + +#pragma textflag 7 +// func closure(siz int32, +// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, +// arg0, arg1, arg2 *ptr) (func(xxx) yyy) +void +sys·closure(int32 siz, byte *fn, byte *arg0) +{ + byte *p, *q, **ret; + int32 i, n; + int32 pcrel; + + if(siz < 0 || siz%4 != 0) + throw("bad closure size"); + + ret = (byte**)((byte*)&arg0 + siz); + + if(siz > 100) { + // TODO(rsc): implement stack growth preamble? + throw("closure too big"); + } + + // compute size of new fn. + // must match code laid out below. + n = 6+5+2+1; // SUBL MOVL MOVL CLD + if(siz <= 4*4) + n += 1*siz/4; // MOVSL MOVSL... + else + n += 6+2; // MOVL REP MOVSL + n += 5; // CALL + n += 6+1; // ADDL RET + + // store args aligned after code, so gc can find them. + n += siz; + if(n%4) + n += 4 - n%4; + + p = mal(n); + *ret = p; + q = p + n - siz; + mcpy(q, (byte*)&arg0, siz); + + // SUBL $siz, SP + *p++ = 0x81; + *p++ = 0xec; + *(uint32*)p = siz; + p += 4; + + // MOVL $q, SI + *p++ = 0xbe; + *(byte**)p = q; + p += 4; + + // MOVL SP, DI + *p++ = 0x89; + *p++ = 0xe7; + + // CLD + *p++ = 0xfc; + + if(siz <= 4*4) { + for(i=0; i<siz; i+=4) { + // MOVSL + *p++ = 0xa5; + } + } else { + // MOVL $(siz/4), CX [32-bit immediate siz/4] + *p++ = 0xc7; + *p++ = 0xc1; + *(uint32*)p = siz/4; + p += 4; + + // REP; MOVSL + *p++ = 0xf3; + *p++ = 0xa5; + } + + // call fn + pcrel = fn - (p+5); + // direct call with pc-relative offset + // CALL fn + *p++ = 0xe8; + *(int32*)p = pcrel; + p += 4; + + // ADDL $siz, SP + *p++ = 0x81; + *p++ = 0xc4; + *(uint32*)p = siz; + p += 4; + + // RET + *p++ = 0xc3; + + if(p > q) + throw("bad math in sys.closure"); +} + + diff --git a/src/pkg/runtime/386/traceback.c b/src/pkg/runtime/386/traceback.c new file mode 100644 index 000000000..05724d9ac --- /dev/null +++ b/src/pkg/runtime/386/traceback.c @@ -0,0 +1,148 @@ +// Copyright 2009 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. + +#include "runtime.h" + +// TODO(rsc): Move this into portable code, with calls to a +// machine-dependent isclosure() function. + +void +traceback(byte *pc0, byte *sp, G *g) +{ + Stktop *stk; + uintptr pc; + int32 i, n; + Func *f; + byte *p; + + pc = (uintptr)pc0; + + // If the PC is zero, it's likely a nil function call. + // Start in the caller's frame. + if(pc == 0) { + pc = *(uintptr*)sp; + sp += sizeof(uintptr); + } + + stk = (Stktop*)g->stackbase; + for(n=0; n<100; n++) { + while(pc == (uintptr)retfromnewstack) { + // pop to earlier stack block + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *(uintptr*)(sp+sizeof(uintptr)); + sp += 2*sizeof(uintptr); // two irrelevant calls on stack: morestack plus its call + } + f = findfunc(pc); + if(f == nil) { + // dangerous, but poke around to see if it is a closure + p = (byte*)pc; + // ADDL $xxx, SP; RET + if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { + sp += *(uint32*)(p+2) + 8; + pc = *(uintptr*)(sp - 8); + if(pc <= 0x1000) + return; + continue; + } + printf("%p unknown pc\n", pc); + return; + } + if(f->frame < sizeof(uintptr)) // assembly funcs say 0 but lie + sp += sizeof(uintptr); + else + sp += f->frame; + + // print this frame + // main+0xf /home/rsc/go/src/runtime/x.go:23 + // main(0x1, 0x2, 0x3) + printf("%S", f->name); + if(pc > f->entry) + printf("+%p", (uintptr)(pc - f->entry)); + printf(" %S:%d\n", f->src, funcline(f, pc-1)); // -1 to get to CALL instr. + printf("\t%S(", f->name); + for(i = 0; i < f->args; i++) { + if(i != 0) + prints(", "); + sys·printhex(((uint32*)sp)[i]); + if(i >= 4) { + prints(", ..."); + break; + } + } + prints(")\n"); + + pc = *(uintptr*)(sp-sizeof(uintptr)); + if(pc <= 0x1000) + return; + } + prints("...\n"); +} + +// func caller(n int) (pc uintptr, file string, line int, ok bool) +void +runtime·Caller(int32 n, uintptr retpc, String retfile, int32 retline, bool retbool) +{ + uintptr pc; + byte *sp; + byte *p; + Stktop *stk; + Func *f; + + // our caller's pc, sp. + sp = (byte*)&n; + pc = *((uintptr*)sp - 1); + if((f = findfunc(pc)) == nil) { + error: + retpc = 0; + retline = 0; + retfile = emptystring; + retbool = false; + FLUSH(&retpc); + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); + return; + } + + // now unwind n levels + stk = (Stktop*)g->stackbase; + while(n-- > 0) { + while(pc == (uintptr)retfromnewstack) { + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *((uintptr*)sp + 1); + sp += 2*sizeof(uintptr); + } + + if(f->frame < sizeof(uintptr)) // assembly functions lie + sp += sizeof(uintptr); + else + sp += f->frame; + + loop: + pc = *((uintptr*)sp - 1); + if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { + // dangerous, but let's try this. + // see if it is a closure. + p = (byte*)pc; + // ADDL $xxx, SP; RET + if(p[0] == 0x81 && p[1] == 0xc4 && p[6] == 0xc3) { + sp += *(uint32*)(p+2) + sizeof(uintptr); + goto loop; + } + goto error; + } + } + + retpc = pc; + retfile = f->src; + retline = funcline(f, pc-1); + retbool = true; + FLUSH(&retpc); + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); +} + diff --git a/src/pkg/runtime/386/vlop.s b/src/pkg/runtime/386/vlop.s new file mode 100755 index 000000000..803276ce2 --- /dev/null +++ b/src/pkg/runtime/386/vlop.s @@ -0,0 +1,48 @@ +// Inferno's libkern/vlop-386.s +// http://code.google.com/p/inferno-os/source/browse/libkern/vlop-386.s +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. +// Portions Copyright 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* + * C runtime for 64-bit divide. + */ + +TEXT _mul64by32(SB), 7, $0 + MOVL r+0(FP), CX + MOVL a+4(FP), AX + MULL b+12(FP) + MOVL AX, 0(CX) + MOVL DX, BX + MOVL a+8(FP), AX + MULL b+12(FP) + ADDL AX, BX + MOVL BX, 4(CX) + RET + +TEXT _div64by32(SB), 7, $0 + MOVL r+12(FP), CX + MOVL a+0(FP), AX + MOVL a+4(FP), DX + DIVL b+8(FP) + MOVL DX, 0(CX) + RET diff --git a/src/pkg/runtime/386/vlrt.c b/src/pkg/runtime/386/vlrt.c new file mode 100755 index 000000000..093cca70d --- /dev/null +++ b/src/pkg/runtime/386/vlrt.c @@ -0,0 +1,815 @@ +// Inferno's libkern/vlrt-386.c +// http://code.google.com/p/inferno-os/source/browse/libkern/vlrt-386.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved. +// Portions Copyright 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* + * C runtime for 64-bit divide, others. + * + * TODO(rsc): The simple functions are dregs--8c knows how + * to generate the code directly now. Find and remove. + */ + +typedef unsigned long ulong; +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef signed char schar; + +#define SIGN(n) (1UL<<(n-1)) + +typedef struct Vlong Vlong; +struct Vlong +{ + union + { + long long v; + struct + { + ulong lo; + ulong hi; + }; + struct + { + ushort lols; + ushort loms; + ushort hils; + ushort hims; + }; + }; +}; + +void abort(void); + +void +_d2v(Vlong *y, double d) +{ + union { double d; struct Vlong; } x; + ulong xhi, xlo, ylo, yhi; + int sh; + + x.d = d; + + xhi = (x.hi & 0xfffff) | 0x100000; + xlo = x.lo; + sh = 1075 - ((x.hi >> 20) & 0x7ff); + + ylo = 0; + yhi = 0; + if(sh >= 0) { + /* v = (hi||lo) >> sh */ + if(sh < 32) { + if(sh == 0) { + ylo = xlo; + yhi = xhi; + } else { + ylo = (xlo >> sh) | (xhi << (32-sh)); + yhi = xhi >> sh; + } + } else { + if(sh == 32) { + ylo = xhi; + } else + if(sh < 64) { + ylo = xhi >> (sh-32); + } + } + } else { + /* v = (hi||lo) << -sh */ + sh = -sh; + if(sh <= 10) { + ylo = xlo << sh; + yhi = (xhi << sh) | (xlo >> (32-sh)); + } else { + /* overflow */ + yhi = d; /* causes something awful */ + } + } + if(x.hi & SIGN(32)) { + if(ylo != 0) { + ylo = -ylo; + yhi = ~yhi; + } else + yhi = -yhi; + } + + y->hi = yhi; + y->lo = ylo; +} + +void +_f2v(Vlong *y, float f) +{ + + _d2v(y, f); +} + +double +_v2d(Vlong x) +{ + if(x.hi & SIGN(32)) { + if(x.lo) { + x.lo = -x.lo; + x.hi = ~x.hi; + } else + x.hi = -x.hi; + return -((long)x.hi*4294967296. + x.lo); + } + return (long)x.hi*4294967296. + x.lo; +} + +float +_v2f(Vlong x) +{ + return _v2d(x); +} + +ulong _div64by32(Vlong, ulong, ulong*); +void _mul64by32(Vlong*, Vlong, ulong); + +static void +slowdodiv(Vlong num, Vlong den, Vlong *q, Vlong *r) +{ + ulong numlo, numhi, denhi, denlo, quohi, quolo, t; + int i; + + numhi = num.hi; + numlo = num.lo; + denhi = den.hi; + denlo = den.lo; + + /* + * get a divide by zero + */ + if(denlo==0 && denhi==0) { + numlo = numlo / denlo; + } + + /* + * set up the divisor and find the number of iterations needed + */ + if(numhi >= SIGN(32)) { + quohi = SIGN(32); + quolo = 0; + } else { + quohi = numhi; + quolo = numlo; + } + i = 0; + while(denhi < quohi || (denhi == quohi && denlo < quolo)) { + denhi = (denhi<<1) | (denlo>>31); + denlo <<= 1; + i++; + } + + quohi = 0; + quolo = 0; + for(; i >= 0; i--) { + quohi = (quohi<<1) | (quolo>>31); + quolo <<= 1; + if(numhi > denhi || (numhi == denhi && numlo >= denlo)) { + t = numlo; + numlo -= denlo; + if(numlo > t) + numhi--; + numhi -= denhi; + quolo |= 1; + } + denlo = (denlo>>1) | (denhi<<31); + denhi >>= 1; + } + + if(q) { + q->lo = quolo; + q->hi = quohi; + } + if(r) { + r->lo = numlo; + r->hi = numhi; + } +} + +static void +dodiv(Vlong num, Vlong den, Vlong *qp, Vlong *rp) +{ + ulong n; + Vlong x, q, r; + + if(den.hi > num.hi || (den.hi == num.hi && den.lo > num.lo)){ + if(qp) { + qp->hi = 0; + qp->lo = 0; + } + if(rp) { + rp->hi = num.hi; + rp->lo = num.lo; + } + return; + } + + if(den.hi != 0){ + q.hi = 0; + n = num.hi/den.hi; + _mul64by32(&x, den, n); + if(x.hi > num.hi || (x.hi == num.hi && x.lo > num.lo)) + slowdodiv(num, den, &q, &r); + else { + q.lo = n; + r.v = num.v - x.v; + } + } else { + if(num.hi >= den.lo){ + q.hi = n = num.hi/den.lo; + num.hi -= den.lo*n; + } else { + q.hi = 0; + } + q.lo = _div64by32(num, den.lo, &r.lo); + r.hi = 0; + } + if(qp) { + qp->lo = q.lo; + qp->hi = q.hi; + } + if(rp) { + rp->lo = r.lo; + rp->hi = r.hi; + } +} + +void +_divvu(Vlong *q, Vlong n, Vlong d) +{ + + if(n.hi == 0 && d.hi == 0) { + q->hi = 0; + q->lo = n.lo / d.lo; + return; + } + dodiv(n, d, q, 0); +} + +void +sys·uint64div(Vlong n, Vlong d, Vlong q) +{ + _divvu(&q, n, d); +} + +void +_modvu(Vlong *r, Vlong n, Vlong d) +{ + + if(n.hi == 0 && d.hi == 0) { + r->hi = 0; + r->lo = n.lo % d.lo; + return; + } + dodiv(n, d, 0, r); +} + +void +sys·uint64mod(Vlong n, Vlong d, Vlong q) +{ + _modvu(&q, n, d); +} + +static void +vneg(Vlong *v) +{ + + if(v->lo == 0) { + v->hi = -v->hi; + return; + } + v->lo = -v->lo; + v->hi = ~v->hi; +} + +void +_divv(Vlong *q, Vlong n, Vlong d) +{ + long nneg, dneg; + + if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { + if((long)n.lo == -0x80000000 && (long)d.lo == -1) { + // special case: 32-bit -0x80000000 / -1 causes divide error, + // but it's okay in this 64-bit context. + q->lo = 0x80000000; + q->hi = 0; + return; + } + q->lo = (long)n.lo / (long)d.lo; + q->hi = ((long)q->lo) >> 31; + return; + } + nneg = n.hi >> 31; + if(nneg) + vneg(&n); + dneg = d.hi >> 31; + if(dneg) + vneg(&d); + dodiv(n, d, q, 0); + if(nneg != dneg) + vneg(q); +} + +void +sys·int64div(Vlong n, Vlong d, Vlong q) +{ + _divv(&q, n, d); +} + +void +_modv(Vlong *r, Vlong n, Vlong d) +{ + long nneg, dneg; + + if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { + if((long)n.lo == -0x80000000 && (long)d.lo == -1) { + // special case: 32-bit -0x80000000 % -1 causes divide error, + // but it's okay in this 64-bit context. + r->lo = 0; + r->hi = 0; + return; + } + r->lo = (long)n.lo % (long)d.lo; + r->hi = ((long)r->lo) >> 31; + return; + } + nneg = n.hi >> 31; + if(nneg) + vneg(&n); + dneg = d.hi >> 31; + if(dneg) + vneg(&d); + dodiv(n, d, 0, r); + if(nneg) + vneg(r); +} + +void +sys·int64mod(Vlong n, Vlong d, Vlong q) +{ + _modv(&q, n, d); +} + +void +_rshav(Vlong *r, Vlong a, int b) +{ + long t; + + t = a.hi; + if(b >= 32) { + r->hi = t>>31; + if(b >= 64) { + /* this is illegal re C standard */ + r->lo = t>>31; + return; + } + r->lo = t >> (b-32); + return; + } + if(b <= 0) { + r->hi = t; + r->lo = a.lo; + return; + } + r->hi = t >> b; + r->lo = (t << (32-b)) | (a.lo >> b); +} + +void +_rshlv(Vlong *r, Vlong a, int b) +{ + ulong t; + + t = a.hi; + if(b >= 32) { + r->hi = 0; + if(b >= 64) { + /* this is illegal re C standard */ + r->lo = 0; + return; + } + r->lo = t >> (b-32); + return; + } + if(b <= 0) { + r->hi = t; + r->lo = a.lo; + return; + } + r->hi = t >> b; + r->lo = (t << (32-b)) | (a.lo >> b); +} + +void +_lshv(Vlong *r, Vlong a, int b) +{ + ulong t; + + t = a.lo; + if(b >= 32) { + r->lo = 0; + if(b >= 64) { + /* this is illegal re C standard */ + r->hi = 0; + return; + } + r->hi = t << (b-32); + return; + } + if(b <= 0) { + r->lo = t; + r->hi = a.hi; + return; + } + r->lo = t << b; + r->hi = (t >> (32-b)) | (a.hi << b); +} + +void +_andv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi & b.hi; + r->lo = a.lo & b.lo; +} + +void +_orv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi | b.hi; + r->lo = a.lo | b.lo; +} + +void +_xorv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi ^ b.hi; + r->lo = a.lo ^ b.lo; +} + +void +_vpp(Vlong *l, Vlong *r) +{ + + l->hi = r->hi; + l->lo = r->lo; + r->lo++; + if(r->lo == 0) + r->hi++; +} + +void +_vmm(Vlong *l, Vlong *r) +{ + + l->hi = r->hi; + l->lo = r->lo; + if(r->lo == 0) + r->hi--; + r->lo--; +} + +void +_ppv(Vlong *l, Vlong *r) +{ + + r->lo++; + if(r->lo == 0) + r->hi++; + l->hi = r->hi; + l->lo = r->lo; +} + +void +_mmv(Vlong *l, Vlong *r) +{ + + if(r->lo == 0) + r->hi--; + r->lo--; + l->hi = r->hi; + l->lo = r->lo; +} + +void +_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) +{ + Vlong t, u; + + u.lo = 0; + u.hi = 0; + switch(type) { + default: + abort(); + break; + + case 1: /* schar */ + t.lo = *(schar*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(schar*)lv = u.lo; + break; + + case 2: /* uchar */ + t.lo = *(uchar*)lv; + t.hi = 0; + fn(&u, t, rv); + *(uchar*)lv = u.lo; + break; + + case 3: /* short */ + t.lo = *(short*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(short*)lv = u.lo; + break; + + case 4: /* ushort */ + t.lo = *(ushort*)lv; + t.hi = 0; + fn(&u, t, rv); + *(ushort*)lv = u.lo; + break; + + case 9: /* int */ + t.lo = *(int*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(int*)lv = u.lo; + break; + + case 10: /* uint */ + t.lo = *(uint*)lv; + t.hi = 0; + fn(&u, t, rv); + *(uint*)lv = u.lo; + break; + + case 5: /* long */ + t.lo = *(long*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(long*)lv = u.lo; + break; + + case 6: /* ulong */ + t.lo = *(ulong*)lv; + t.hi = 0; + fn(&u, t, rv); + *(ulong*)lv = u.lo; + break; + + case 7: /* vlong */ + case 8: /* uvlong */ + fn(&u, *(Vlong*)lv, rv); + *(Vlong*)lv = u; + break; + } + *ret = u; +} + +void +_p2v(Vlong *ret, void *p) +{ + long t; + + t = (ulong)p; + ret->lo = t; + ret->hi = 0; +} + +void +_sl2v(Vlong *ret, long sl) +{ + long t; + + t = sl; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_ul2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul; + ret->lo = t; + ret->hi = 0; +} + +void +_si2v(Vlong *ret, int si) +{ + long t; + + t = si; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_ui2v(Vlong *ret, uint ui) +{ + long t; + + t = ui; + ret->lo = t; + ret->hi = 0; +} + +void +_sh2v(Vlong *ret, long sh) +{ + long t; + + t = (sh << 16) >> 16; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_uh2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul & 0xffff; + ret->lo = t; + ret->hi = 0; +} + +void +_sc2v(Vlong *ret, long uc) +{ + long t; + + t = (uc << 24) >> 24; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_uc2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul & 0xff; + ret->lo = t; + ret->hi = 0; +} + +long +_v2sc(Vlong rv) +{ + long t; + + t = rv.lo & 0xff; + return (t << 24) >> 24; +} + +long +_v2uc(Vlong rv) +{ + + return rv.lo & 0xff; +} + +long +_v2sh(Vlong rv) +{ + long t; + + t = rv.lo & 0xffff; + return (t << 16) >> 16; +} + +long +_v2uh(Vlong rv) +{ + + return rv.lo & 0xffff; +} + +long +_v2sl(Vlong rv) +{ + + return rv.lo; +} + +long +_v2ul(Vlong rv) +{ + + return rv.lo; +} + +long +_v2si(Vlong rv) +{ + + return rv.lo; +} + +long +_v2ui(Vlong rv) +{ + + return rv.lo; +} + +int +_testv(Vlong rv) +{ + return rv.lo || rv.hi; +} + +int +_eqv(Vlong lv, Vlong rv) +{ + return lv.lo == rv.lo && lv.hi == rv.hi; +} + +int +_nev(Vlong lv, Vlong rv) +{ + return lv.lo != rv.lo || lv.hi != rv.hi; +} + +int +_ltv(Vlong lv, Vlong rv) +{ + return (long)lv.hi < (long)rv.hi || + (lv.hi == rv.hi && lv.lo < rv.lo); +} + +int +_lev(Vlong lv, Vlong rv) +{ + return (long)lv.hi < (long)rv.hi || + (lv.hi == rv.hi && lv.lo <= rv.lo); +} + +int +_gtv(Vlong lv, Vlong rv) +{ + return (long)lv.hi > (long)rv.hi || + (lv.hi == rv.hi && lv.lo > rv.lo); +} + +int +_gev(Vlong lv, Vlong rv) +{ + return (long)lv.hi > (long)rv.hi || + (lv.hi == rv.hi && lv.lo >= rv.lo); +} + +int +_lov(Vlong lv, Vlong rv) +{ + return lv.hi < rv.hi || + (lv.hi == rv.hi && lv.lo < rv.lo); +} + +int +_lsv(Vlong lv, Vlong rv) +{ + return lv.hi < rv.hi || + (lv.hi == rv.hi && lv.lo <= rv.lo); +} + +int +_hiv(Vlong lv, Vlong rv) +{ + return lv.hi > rv.hi || + (lv.hi == rv.hi && lv.lo > rv.lo); +} + +int +_hsv(Vlong lv, Vlong rv) +{ + return lv.hi > rv.hi || + (lv.hi == rv.hi && lv.lo >= rv.lo); +} diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile new file mode 100644 index 000000000..5a5ace9c5 --- /dev/null +++ b/src/pkg/runtime/Makefile @@ -0,0 +1,125 @@ +# Copyright 2009 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. + +# Set SIZE to 32 or 64. +SIZE_386=32 +SIZE_amd64=64 +SIZE_arm=32 +SIZE=$(SIZE_$(GOARCH)) + +# Setup CFLAGS. Add -D_64BIT on 64-bit platforms (sorry). +CFLAGS_64=-D_64BIT +CFLAGS=-I$(GOOS) -I$(GOOS)/$(GOARCH) -wF $(CFLAGS_$(SIZE)) + +# Set O to right letter. +O_386=8 +O_amd64=6 +O_arm=5 +O=$(O_$(GOARCH)) + +# Tools +CC=$(O)c +GC=$(O)g +AS=$(O)a +AR=gopack + +LIB=runtime.a + +# 386-specific object files +OFILES_386=\ + vlop.$O\ + vlrt.$O\ + +OFILES=\ + array.$O\ + asm.$O\ + chan.$O\ + closure.$O\ + extern.$O\ + float.$O\ + float_go.$O\ + hashmap.$O\ + iface.$O\ + malloc.$O\ + malloc_go.$O\ + mcache.$O\ + mcentral.$O\ + mem.$O\ + mfixalloc.$O\ + mgc0.$O\ + mheap.$O\ + mheapmap$(SIZE).$O\ + msize.$O\ + print.$O\ + proc.$O\ + rune.$O\ + runtime.$O\ + rt0.$O\ + sema.$O\ + sema_go.$O\ + signal.$O\ + string.$O\ + symtab.$O\ + sys.$O\ + thread.$O\ + traceback.$O\ + $(OFILES_$(GOARCH))\ + +HFILES=\ + runtime.h\ + hashmap.h\ + malloc.h\ + $(GOOS)/os.h\ + $(GOOS)/$(GOARCH)/defs.h\ + +install: $(LIB) runtime.acid + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH) + cp $(LIB) $(GOROOT)/pkg/$(GOOS)_$(GOARCH)/$(LIB) + cp runtime.acid $(GOROOT)/acid/runtime.acid + +$(LIB): $(OFILES) + $(AR) grc $(LIB) $(OFILES) + +$(OFILES): $(HFILES) + +nuke: + rm -f *.[568] *.a $(GOROOT)/lib/$(LIB) + +clean: + rm -f *.[568] *.a runtime.acid cgo2c + +%.$O: %.go + $(GC) $< + +%.$O: %.c + $(CC) $(CFLAGS) $< + +%.$O: $(GOARCH)/%.c + $(CC) $(CFLAGS) $< + +%.$O: $(GOOS)/%.c + $(CC) $(CFLAGS) $< + +%.$O: $(GOOS)/$(GOARCH)/%.c + $(CC) $(CFLAGS) $< + +%.$O: $(GOARCH)/%.s + $(AS) $< + +%.$O: $(GOOS)/$(GOARCH)/%.s + $(AS) $< + +cgo2c: cgo2c.c + quietgcc -o $@ $< + +%.c: %.cgo cgo2c + ./cgo2c $< > $@.tmp + mv -f $@.tmp $@ + +runtime.acid: runtime.h proc.c + $(CC) -a proc.c >runtime.acid + +chan.acid: runtime.h chan.c + $(CC) -a chan.c >chan.acid + diff --git a/src/pkg/runtime/amd64/asm.s b/src/pkg/runtime/amd64/asm.s new file mode 100644 index 000000000..6fc01bbc9 --- /dev/null +++ b/src/pkg/runtime/amd64/asm.s @@ -0,0 +1,207 @@ +// Copyright 2009 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. + + +TEXT _rt0_amd64(SB),7,$-8 + + // copy arguments forward on an even stack + + MOVQ 0(SP), AX // argc + LEAQ 8(SP), BX // argv + SUBQ $(4*8+7), SP // 2args 2auto + ANDQ $~7, SP + MOVQ AX, 16(SP) + MOVQ BX, 24(SP) + + // set the per-goroutine and per-mach registers + + LEAQ m0(SB), R14 // dedicated m. register + LEAQ g0(SB), R15 // dedicated g. register + MOVQ R15, 0(R14) // m has pointer to its g0 + + // create istack out of the given (operating system) stack + + LEAQ (-8192+104)(SP), AX + MOVQ AX, 0(R15) // 0(R15) is stack limit (w 104b guard) + MOVQ SP, 8(R15) // 8(R15) is base + + CLD // convention is D is always left cleared + CALL check(SB) + + MOVL 16(SP), AX // copy argc + MOVL AX, 0(SP) + MOVQ 24(SP), AX // copy argv + MOVQ AX, 8(SP) + CALL args(SB) + CALL osinit(SB) + CALL schedinit(SB) + + // create a new goroutine to start program + PUSHQ $mainstart(SB) // entry + PUSHQ $16 // arg size + CALL sys·newproc(SB) + POPQ AX + POPQ AX + + // start this M + CALL mstart(SB) + + CALL notok(SB) // never returns + RET + +TEXT mainstart(SB),7,$0 + CALL main·init(SB) + CALL initdone(SB) + CALL main·main(SB) + PUSHQ $0 + CALL exit(SB) + POPQ AX + CALL notok(SB) + RET + +TEXT breakpoint(SB),7,$0 + BYTE $0xcc + RET + +/* + * go-routine + */ +TEXT gogo(SB), 7, $0 + MOVQ 8(SP), AX // gobuf + MOVQ 0(AX), SP // restore SP + MOVQ 8(AX), AX + MOVQ AX, 0(SP) // put PC on the stack + MOVL $1, AX // return 1 + RET + +TEXT gosave(SB), 7, $0 + MOVQ 8(SP), AX // gobuf + MOVQ SP, 0(AX) // save SP + MOVQ 0(SP), BX + MOVQ BX, 8(AX) // save PC + MOVL $0, AX // return 0 + RET + +/* + * support for morestack + */ + +// morestack trampolines +TEXT sys·morestack00+0(SB),7,$0 + MOVQ $0, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestack01+0(SB),7,$0 + SHLQ $32, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestack10+0(SB),7,$0 + MOVLQZX AX, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestack11+0(SB),7,$0 + MOVQ AX, 8(R14) + MOVQ $sys·morestack+0(SB), AX + JMP AX + +TEXT sys·morestackx(SB),7,$0 + POPQ AX + SHLQ $35, AX + MOVQ AX, 8(R14) + MOVQ $sys·morestack(SB), AX + JMP AX + +// subcases of morestack01 +// with const of 8,16,...48 +TEXT sys·morestack8(SB),7,$0 + PUSHQ $1 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack16(SB),7,$0 + PUSHQ $2 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack24(SB),7,$0 + PUSHQ $3 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack32(SB),7,$0 + PUSHQ $4 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack40(SB),7,$0 + PUSHQ $5 + MOVQ $sys·morestackx(SB), AX + JMP AX + +TEXT sys·morestack48(SB),7,$0 + PUSHQ $6 + MOVQ $sys·morestackx(SB), AX + JMP AX + +// return point when leaving new stack. save AX, jmp to lessstack to switch back +TEXT retfromnewstack(SB), 7, $0 + MOVQ AX, 16(R14) // save AX in m->cret + MOVQ $lessstack(SB), AX + JMP AX + +// gogo, returning 2nd arg instead of 1 +TEXT gogoret(SB), 7, $0 + MOVQ 16(SP), AX // return 2nd arg + MOVQ 8(SP), BX // gobuf + MOVQ 0(BX), SP // restore SP + MOVQ 8(BX), BX + MOVQ BX, 0(SP) // put PC on the stack + RET + +TEXT setspgoto(SB), 7, $0 + MOVQ 8(SP), AX // SP + MOVQ 16(SP), BX // fn to call + MOVQ 24(SP), CX // fn to return + MOVQ AX, SP + PUSHQ CX + JMP BX + POPQ AX // not reached + RET + +// bool cas(int32 *val, int32 old, int32 new) +// Atomically: +// if(*val == old){ +// *val = new; +// return 1; +// } else +// return 0; +TEXT cas(SB), 7, $0 + MOVQ 8(SP), BX + MOVL 16(SP), AX + MOVL 20(SP), CX + LOCK + CMPXCHGL CX, 0(BX) + JZ 3(PC) + MOVL $0, AX + RET + MOVL $1, AX + RET + +// void jmpdefer(fn, sp); +// called from deferreturn. +// 1. pop the caller +// 2. sub 5 bytes from the callers return +// 3. jmp to the argument +TEXT jmpdefer(SB), 7, $0 + MOVQ 8(SP), AX // fn + MOVQ 16(SP), BX // caller sp + LEAQ -8(BX), SP // caller sp after CALL + SUBQ $5, (SP) // return to CALL again + JMP AX // but first run the deferred function diff --git a/src/pkg/runtime/amd64/closure.c b/src/pkg/runtime/amd64/closure.c new file mode 100644 index 000000000..5717d3c5e --- /dev/null +++ b/src/pkg/runtime/amd64/closure.c @@ -0,0 +1,121 @@ +// Copyright 2009 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. + +#include "runtime.h" + +#pragma textflag 7 +// func closure(siz int32, +// fn func(arg0, arg1, arg2 *ptr, callerpc uintptr, xxx) yyy, +// arg0, arg1, arg2 *ptr) (func(xxx) yyy) +void +sys·closure(int32 siz, byte *fn, byte *arg0) +{ + byte *p, *q, **ret; + int32 i, n; + int64 pcrel; + + if(siz < 0 || siz%8 != 0) + throw("bad closure size"); + + ret = (byte**)((byte*)&arg0 + siz); + + if(siz > 100) { + // TODO(rsc): implement stack growth preamble? + throw("closure too big"); + } + + // compute size of new fn. + // must match code laid out below. + n = 7+10+3; // SUBQ MOVQ MOVQ + if(siz <= 4*8) + n += 2*siz/8; // MOVSQ MOVSQ... + else + n += 7+3; // MOVQ REP MOVSQ + n += 12; // CALL worst case; sometimes only 5 + n += 7+1; // ADDQ RET + + // store args aligned after code, so gc can find them. + n += siz; + if(n%8) + n += 8 - n%8; + + p = mal(n); + *ret = p; + q = p + n - siz; + mcpy(q, (byte*)&arg0, siz); + + // SUBQ $siz, SP + *p++ = 0x48; + *p++ = 0x81; + *p++ = 0xec; + *(uint32*)p = siz; + p += 4; + + // MOVQ $q, SI + *p++ = 0x48; + *p++ = 0xbe; + *(byte**)p = q; + p += 8; + + // MOVQ SP, DI + *p++ = 0x48; + *p++ = 0x89; + *p++ = 0xe7; + + if(siz <= 4*8) { + for(i=0; i<siz; i+=8) { + // MOVSQ + *p++ = 0x48; + *p++ = 0xa5; + } + } else { + // MOVQ $(siz/8), CX [32-bit immediate siz/8] + *p++ = 0x48; + *p++ = 0xc7; + *p++ = 0xc1; + *(uint32*)p = siz/8; + p += 4; + + // REP; MOVSQ + *p++ = 0xf3; + *p++ = 0x48; + *p++ = 0xa5; + } + + + // call fn + pcrel = fn - (p+5); + if((int32)pcrel == pcrel) { + // can use direct call with pc-relative offset + // CALL fn + *p++ = 0xe8; + *(int32*)p = pcrel; + p += 4; + } else { + // MOVQ $fn, CX [64-bit immediate fn] + *p++ = 0x48; + *p++ = 0xb9; + *(byte**)p = fn; + p += 8; + + // CALL *CX + *p++ = 0xff; + *p++ = 0xd1; + } + + // ADDQ $siz, SP + *p++ = 0x48; + *p++ = 0x81; + *p++ = 0xc4; + *(uint32*)p = siz; + p += 4; + + // RET + *p++ = 0xc3; + + if(p > q) + throw("bad math in sys.closure"); +} + + diff --git a/src/pkg/runtime/amd64/traceback.c b/src/pkg/runtime/amd64/traceback.c new file mode 100644 index 000000000..16d7bed72 --- /dev/null +++ b/src/pkg/runtime/amd64/traceback.c @@ -0,0 +1,146 @@ +// Copyright 2009 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. + +#include "runtime.h" + +void +traceback(byte *pc0, byte *sp, G *g) +{ + Stktop *stk; + uint64 pc; + int32 i, n; + Func *f; + byte *p; + + pc = (uint64)pc0; + + // If the PC is zero, it's likely a nil function call. + // Start in the caller's frame. + if(pc == 0) { + pc = *(uint64*)sp; + sp += 8; + } + + stk = (Stktop*)g->stackbase; + for(n=0; n<100; n++) { + while(pc == (uint64)retfromnewstack) { + // pop to earlier stack block + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *(uint64*)(sp+8); + sp += 16; // two irrelevant calls on stack: morestack plus its call + } + f = findfunc(pc); + if(f == nil) { + // dangerous, but poke around to see if it is a closure + p = (byte*)pc; + // ADDQ $xxx, SP; RET + if(p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) { + sp += *(uint32*)(p+3) + 8; + pc = *(uint64*)(sp - 8); + if(pc <= 0x1000) + return; + continue; + } + printf("%p unknown pc\n", pc); + return; + } + if(f->frame < 8) // assembly funcs say 0 but lie + sp += 8; + else + sp += f->frame; + + // print this frame + // main+0xf /home/rsc/go/src/runtime/x.go:23 + // main(0x1, 0x2, 0x3) + printf("%S", f->name); + if(pc > f->entry) + printf("+%X", pc - f->entry); + printf(" %S:%d\n", f->src, funcline(f, pc-1)); // -1 to get to CALL instr. + printf("\t%S(", f->name); + for(i = 0; i < f->args; i++) { + if(i != 0) + prints(", "); + sys·printhex(((uint32*)sp)[i]); + if(i >= 4) { + prints(", ..."); + break; + } + } + prints(")\n"); + + pc = *(uint64*)(sp-8); + if(pc <= 0x1000) + return; + } + prints("...\n"); +} + +// func caller(n int) (pc uint64, file string, line int, ok bool) +void +runtime·Caller(int32 n, uint64 retpc, String retfile, int32 retline, bool retbool) +{ + uint64 pc; + byte *sp; + byte *p; + Stktop *stk; + Func *f; + + // our caller's pc, sp. + sp = (byte*)&n; + pc = *(uint64*)(sp-8); + if((f = findfunc(pc)) == nil) { + error: + retpc = 0; + retline = 0; + retfile = emptystring; + retbool = false; + FLUSH(&retpc); + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); + return; + } + + // now unwind n levels + stk = (Stktop*)g->stackbase; + while(n-- > 0) { + while(pc == (uint64)retfromnewstack) { + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + pc = *(uint64*)(sp+8); + sp += 16; + } + + if(f->frame < 8) // assembly functions lie + sp += 8; + else + sp += f->frame; + + loop: + pc = *(uint64*)(sp-8); + if(pc <= 0x1000 || (f = findfunc(pc)) == nil) { + // dangerous, but let's try this. + // see if it is a closure. + p = (byte*)pc; + // ADDQ $xxx, SP; RET + if(p[0] == 0x48 && p[1] == 0x81 && p[2] == 0xc4 && p[7] == 0xc3) { + sp += *(uint32*)(p+3) + 8; + goto loop; + } + goto error; + } + } + + retpc = pc; + retfile = f->src; + retline = funcline(f, pc-1); + retbool = true; + FLUSH(&retpc); + FLUSH(&retfile); + FLUSH(&retline); + FLUSH(&retbool); +} + + diff --git a/src/pkg/runtime/arm/asm.s b/src/pkg/runtime/arm/asm.s new file mode 100644 index 000000000..232ab4ddf --- /dev/null +++ b/src/pkg/runtime/arm/asm.s @@ -0,0 +1,83 @@ +// Copyright 2009 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. + +TEXT _rt0_arm(SB),7,$0 + // copy arguments forward on an even stack + // MOVW $0(SP), R0 + // MOVL 0(SP), R1 // argc +// LEAL 4(SP), R1 // argv +// SUBL $128, SP // plenty of scratch +// ANDL $~7, SP +// MOVL AX, 120(SP) // save argc, argv away +// MOVL BX, 124(SP) + + +// // write "go386\n" +// PUSHL $6 +// PUSHL $hello(SB) +// PUSHL $1 +// CALL sys·write(SB) +// POPL AX +// POPL AX +// POPL AX + + +// CALL ldt0setup(SB) + + // set up %fs to refer to that ldt entry +// MOVL $(7*8+7), AX +// MOVW AX, FS + +// // store through it, to make sure it works +// MOVL $0x123, 0(FS) +// MOVL tls0(SB), AX +// CMPL AX, $0x123 +// JEQ ok +// MOVL AX, 0 +// ok: + +// // set up m and g "registers" +// // g is 0(FS), m is 4(FS) +// LEAL g0(SB), CX +// MOVL CX, 0(FS) +// LEAL m0(SB), AX +// MOVL AX, 4(FS) + +// // save m->g0 = g0 +// MOVL CX, 0(AX) + +// // create istack out of the OS stack +// LEAL (-8192+104)(SP), AX // TODO: 104? +// MOVL AX, 0(CX) // 8(g) is stack limit (w 104b guard) +// MOVL SP, 4(CX) // 12(g) is base +// CALL emptyfunc(SB) // fault if stack check is wrong + +// // convention is D is always cleared +// CLD + +// CALL check(SB) + +// // saved argc, argv +// MOVL 120(SP), AX +// MOVL AX, 0(SP) +// MOVL 124(SP), AX +// MOVL AX, 4(SP) +// CALL args(SB) +// CALL osinit(SB) +// CALL schedinit(SB) + +// // create a new goroutine to start program +// PUSHL $mainstart(SB) // entry +// PUSHL $8 // arg size +// CALL sys·newproc(SB) +// POPL AX +// POPL AX + +// // start this M +// CALL mstart(SB) + + BL main�main(SB) + MOVW $99, R0 + SWI $0x00900001 + diff --git a/src/pkg/runtime/arm/closure.c b/src/pkg/runtime/arm/closure.c new file mode 100644 index 000000000..bfa9df67f --- /dev/null +++ b/src/pkg/runtime/arm/closure.c @@ -0,0 +1,3 @@ +// Copyright 2009 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. diff --git a/src/pkg/runtime/arm/traceback.s b/src/pkg/runtime/arm/traceback.s new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/pkg/runtime/arm/traceback.s diff --git a/src/pkg/runtime/array.c b/src/pkg/runtime/array.c new file mode 100644 index 000000000..bbd57b03e --- /dev/null +++ b/src/pkg/runtime/array.c @@ -0,0 +1,175 @@ +// Copyright 2009 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. + +#include "runtime.h" + +static int32 debug = 0; + +// newarray(nel int, cap int, width int) (ary []any); +void +sys·newarray(uint32 nel, uint32 cap, uint32 width, Array ret) +{ + uint64 size; + + if(cap < nel) + cap = nel; + size = cap*width; + + ret.nel = nel; + ret.cap = cap; + ret.array = mal(size); + + FLUSH(&ret); + + if(debug) { + prints("newarray: nel="); + sys·printint(nel); + prints("; cap="); + sys·printint(cap); + prints("; width="); + sys·printint(width); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +static void +throwslice(uint32 lb, uint32 hb, uint32 n) +{ + prints("slice["); + sys·printint(lb); + prints(":"); + sys·printint(hb); + prints("] of ["); + sys·printint(n); + prints("] array\n"); + throw("array slice"); +} + +// arraysliced(old []any, lb int, hb int, width int) (ary []any); +void +sys·arraysliced(Array old, uint32 lb, uint32 hb, uint32 width, Array ret) +{ + + if(hb > old.cap || lb > hb) { + if(debug) { + prints("sys·arraysliced: old="); + sys·printarray(old); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("\n"); + + prints("oldarray: nel="); + sys·printint(old.nel); + prints("; cap="); + sys·printint(old.cap); + prints("\n"); + } + throwslice(lb, hb, old.cap); + } + + // new array is inside old array + ret.nel = hb-lb; + ret.cap = old.cap - lb; + ret.array = old.array + lb*width; + + FLUSH(&ret); + + if(debug) { + prints("sys·arraysliced: old="); + sys·printarray(old); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +// arrayslices(old *any, nel int, lb int, hb int, width int) (ary []any); +void +sys·arrayslices(byte* old, uint32 nel, uint32 lb, uint32 hb, uint32 width, Array ret) +{ + + if(hb > nel || lb > hb) { + if(debug) { + prints("sys·arrayslices: old="); + sys·printpointer(old); + prints("; nel="); + sys·printint(nel); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("\n"); + } + throwslice(lb, hb, nel); + } + + // new array is inside old array + ret.nel = hb-lb; + ret.cap = nel-lb; + ret.array = old + lb*width; + + FLUSH(&ret); + + if(debug) { + prints("sys·arrayslices: old="); + sys·printpointer(old); + prints("; nel="); + sys·printint(nel); + prints("; lb="); + sys·printint(lb); + prints("; hb="); + sys·printint(hb); + prints("; width="); + sys·printint(width); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +// arrays2d(old *any, nel int) (ary []any) +void +sys·arrays2d(byte* old, uint32 nel, Array ret) +{ + + // new dope to old array + ret.nel = nel; + ret.cap = nel; + ret.array = old; + + FLUSH(&ret); + + if(debug) { + prints("sys·arrays2d: old="); + sys·printpointer(old); + prints("; ret="); + sys·printarray(ret); + prints("\n"); + } +} + +void +sys·printarray(Array a) +{ + prints("["); + sys·printint(a.nel); + prints("/"); + sys·printint(a.cap); + prints("]"); + sys·printpointer(a.array); +} diff --git a/src/pkg/runtime/cgo2c.c b/src/pkg/runtime/cgo2c.c new file mode 100644 index 000000000..3905f7e6d --- /dev/null +++ b/src/pkg/runtime/cgo2c.c @@ -0,0 +1,602 @@ +// Copyright 2009 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. + +/* Translate a .cgo file into a .c file. A .cgo file is a combination + of a limited form of Go with C. */ + +/* + package PACKAGENAME + {# line} + func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{ + C code with proper brace nesting + \} +*/ + +/* We generate C code which implements the function such that it can + be called from Go and executes the C code. */ + +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/* Whether we're emitting for gcc */ +static int gcc; + +/* File and line number */ +static const char *file; +static unsigned int lineno; + +/* List of names and types. */ +struct params { + struct params *next; + char *name; + char *type; +}; + +/* Unexpected EOF. */ +static void +bad_eof(void) +{ + fprintf(stderr, "%s:%u: unexpected EOF\n", file, lineno); + exit(1); +} + +/* Out of memory. */ +static void +bad_mem(void) +{ + fprintf(stderr, "%s:%u: out of memory\n", file, lineno); + exit(1); +} + +/* Allocate memory without fail. */ +static void * +xmalloc(unsigned int size) +{ + void *ret = malloc(size); + if (ret == NULL) + bad_mem(); + return ret; +} + +/* Reallocate memory without fail. */ +static void* +xrealloc(void *buf, unsigned int size) +{ + void *ret = realloc(buf, size); + if (ret == NULL) + bad_mem(); + return ret; +} + +/* Free a list of parameters. */ +static void +free_params(struct params *p) +{ + while (p != NULL) { + struct params *next; + + next = p->next; + free(p->name); + free(p->type); + free(p); + p = next; + } +} + +/* Read a character, tracking lineno. */ +static int +getchar_update_lineno(void) +{ + int c; + + c = getchar(); + if (c == '\n') + ++lineno; + return c; +} + +/* Read a character, giving an error on EOF, tracking lineno. */ +static int +getchar_no_eof(void) +{ + int c; + + c = getchar_update_lineno(); + if (c == EOF) + bad_eof(); + return c; +} + +/* Read a character, skipping comments. */ +static int +getchar_skipping_comments(void) +{ + int c; + + while (1) { + c = getchar_update_lineno(); + if (c != '/') + return c; + + c = getchar(); + if (c == '/') { + do { + c = getchar_update_lineno(); + } while (c != EOF && c != '\n'); + return c; + } else if (c == '*') { + while (1) { + c = getchar_update_lineno(); + if (c == EOF) + return EOF; + if (c == '*') { + do { + c = getchar_update_lineno(); + } while (c == '*'); + if (c == '/') + break; + } + } + } else { + ungetc(c, stdin); + return '/'; + } + } +} + +/* Read and return a token. Tokens are delimited by whitespace or by + [(),{}]. The latter are all returned as single characters. */ +static char * +read_token(void) +{ + int c; + char *buf; + unsigned int alc, off; + const char* delims = "(),{}"; + + while (1) { + c = getchar_skipping_comments(); + if (c == EOF) + return NULL; + if (!isspace(c)) + break; + } + alc = 16; + buf = xmalloc(alc + 1); + off = 0; + if (strchr(delims, c) != NULL) { + buf[off] = c; + ++off; + } else { + while (1) { + if (off >= alc) { + alc *= 2; + buf = xrealloc(buf, alc + 1); + } + buf[off] = c; + ++off; + c = getchar_skipping_comments(); + if (c == EOF) + break; + if (isspace(c) || strchr(delims, c) != NULL) { + ungetc(c, stdin); + break; + } + } + } + buf[off] = '\0'; + return buf; +} + +/* Read a token, giving an error on EOF. */ +static char * +read_token_no_eof(void) +{ + char *token = read_token(); + if (token == NULL) + bad_eof(); + return token; +} + +/* Read the package clause, and return the package name. */ +static char * +read_package(void) +{ + char *token; + + token = read_token_no_eof(); + if (strcmp(token, "package") != 0) { + fprintf(stderr, + "%s:%u: expected \"package\", got \"%s\"\n", + file, lineno, token); + exit(1); + } + return read_token_no_eof(); +} + +/* Read and copy preprocessor lines. */ +static void +read_preprocessor_lines(void) +{ + while (1) { + int c; + + do { + c = getchar_skipping_comments(); + } while (isspace(c)); + if (c != '#') { + ungetc(c, stdin); + return; + } + putchar(c); + do { + c = getchar_update_lineno(); + putchar(c); + } while (c != '\n'); + } +} + +/* Read a type in Go syntax and return a type in C syntax. We only + permit basic types and pointers. */ +static char * +read_type(void) +{ + char *p, *op, *q; + int pointer_count; + unsigned int len; + + p = read_token_no_eof(); + if (*p != '*') + return p; + op = p; + pointer_count = 0; + while (*p == '*') { + ++pointer_count; + ++p; + } + len = strlen(p); + q = xmalloc(len + pointer_count + 1); + memcpy(q, p, len); + while (pointer_count > 0) { + q[len] = '*'; + ++len; + --pointer_count; + } + q[len] = '\0'; + free(op); + return q; +} + +/* Read a list of parameters. Each parameter is a name and a type. + The list ends with a ')'. We have already read the '('. */ +static struct params * +read_params(void) +{ + char *token; + struct params *ret, **pp; + + ret = NULL; + pp = &ret; + token = read_token_no_eof(); + if (strcmp(token, ")") != 0) { + while (1) { + *pp = xmalloc(sizeof(struct params)); + (*pp)->name = token; + (*pp)->type = read_type(); + pp = &(*pp)->next; + *pp = NULL; + + token = read_token_no_eof(); + if (strcmp(token, ",") != 0) + break; + token = read_token_no_eof(); + } + } + if (strcmp(token, ")") != 0) { + fprintf(stderr, "%s:%u: expected '('\n", + file, lineno); + exit(1); + } + return ret; +} + +/* Read a function header. This reads up to and including the initial + '{' character. Returns 1 if it read a header, 0 at EOF. */ +static int +read_func_header(char **name, struct params **params, struct params **rets) +{ + char *token; + + token = read_token(); + if (token == NULL) + return 0; + if (strcmp(token, "func") != 0) { + fprintf(stderr, "%s:%u: expected \"func\"\n", + file, lineno); + exit(1); + } + *name = read_token_no_eof(); + + token = read_token(); + if (token == NULL || strcmp(token, "(") != 0) { + fprintf(stderr, "%s:%u: expected \"(\"\n", + file, lineno); + exit(1); + } + *params = read_params(); + + token = read_token(); + if (token == NULL || strcmp(token, "(") != 0) + *rets = NULL; + else { + *rets = read_params(); + token = read_token(); + } + if (token == NULL || strcmp(token, "{") != 0) { + fprintf(stderr, "%s:%u: expected \"{\"\n", + file, lineno); + exit(1); + } + return 1; +} + +/* Write out parameters. */ +static void +write_params(struct params *params, int *first) +{ + struct params *p; + + for (p = params; p != NULL; p = p->next) { + if (*first) + *first = 0; + else + printf(", "); + printf("%s %s", p->type, p->name); + } +} + +/* Write a 6g function header. */ +static void +write_6g_func_header(char *package, char *name, struct params *params, + struct params *rets) +{ + int first; + + printf("void\n%s·%s(", package, name); + first = 1; + write_params(params, &first); + write_params(rets, &first); + printf(")\n{\n"); +} + +/* Write a 6g function trailer. */ +static void +write_6g_func_trailer(struct params *rets) +{ + struct params *p; + + for (p = rets; p != NULL; p = p->next) + printf("\tFLUSH(&%s);\n", p->name); + printf("}\n"); +} + +/* Define the gcc function return type if necessary. */ +static void +define_gcc_return_type(char *package, char *name, struct params *rets) +{ + struct params *p; + + if (rets == NULL || rets->next == NULL) + return; + printf("struct %s_%s_ret {\n", package, name); + for (p = rets; p != NULL; p = p->next) + printf(" %s %s;\n", p->type, p->name); + printf("};\n"); +} + +/* Write out the gcc function return type. */ +static void +write_gcc_return_type(char *package, char *name, struct params *rets) +{ + if (rets == NULL) + printf("void"); + else if (rets->next == NULL) + printf("%s", rets->type); + else + printf("struct %s_%s_ret", package, name); +} + +/* Write out a gcc function header. */ +static void +write_gcc_func_header(char *package, char *name, struct params *params, + struct params *rets) +{ + int first; + struct params *p; + + define_gcc_return_type(package, name, rets); + write_gcc_return_type(package, name, rets); + printf(" %s_%s(", package, name); + first = 1; + write_params(params, &first); + printf(") asm (\"%s.%s\");\n", package, name); + write_gcc_return_type(package, name, rets); + printf(" %s_%s(", package, name); + first = 1; + write_params(params, &first); + printf(")\n{\n"); + for (p = rets; p != NULL; p = p->next) + printf(" %s %s;\n", p->type, p->name); +} + +/* Write out a gcc function trailer. */ +static void +write_gcc_func_trailer(char *package, char *name, struct params *rets) +{ + if (rets == NULL) + ; + else if (rets->next == NULL) + printf("return %s;\n", rets->name); + else { + struct params *p; + + printf(" {\n struct %s_%s_ret __ret;\n", package, name); + for (p = rets; p != NULL; p = p->next) + printf(" __ret.%s = %s;\n", p->name, p->name); + printf(" return __ret;\n }\n"); + } + printf("}\n"); +} + +/* Write out a function header. */ +static void +write_func_header(char *package, char *name, + struct params *params, struct params *rets) +{ + if (gcc) + write_gcc_func_header(package, name, params, rets); + else + write_6g_func_header(package, name, params, rets); + printf("#line %d \"%s\"\n", lineno, file); +} + +/* Write out a function trailer. */ +static void +write_func_trailer(char *package, char *name, + struct params *rets) +{ + if (gcc) + write_gcc_func_trailer(package, name, rets); + else + write_6g_func_trailer(rets); +} + +/* Read and write the body of the function, ending in an unnested } + (which is read but not written). */ +static void +copy_body(void) +{ + int nesting = 0; + while (1) { + int c; + + c = getchar_no_eof(); + if (c == '}' && nesting == 0) + return; + putchar(c); + switch (c) { + default: + break; + case '{': + ++nesting; + break; + case '}': + --nesting; + break; + case '/': + c = getchar_update_lineno(); + putchar(c); + if (c == '/') { + do { + c = getchar_no_eof(); + putchar(c); + } while (c != '\n'); + } else if (c == '*') { + while (1) { + c = getchar_no_eof(); + putchar(c); + if (c == '*') { + do { + c = getchar_no_eof(); + putchar(c); + } while (c == '*'); + if (c == '/') + break; + } + } + } + break; + case '"': + case '\'': + { + int delim = c; + do { + c = getchar_no_eof(); + putchar(c); + if (c == '\\') { + c = getchar_no_eof(); + putchar(c); + c = '\0'; + } + } while (c != delim); + } + break; + } + } +} + +/* Process the entire file. */ +static void +process_file(void) +{ + char *package, *name; + struct params *params, *rets; + + package = read_package(); + read_preprocessor_lines(); + while (read_func_header(&name, ¶ms, &rets)) { + write_func_header(package, name, params, rets); + copy_body(); + write_func_trailer(package, name, rets); + free(name); + free_params(params); + free_params(rets); + } + free(package); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: cgo2c [--6g | --gc] [file]\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + while(argc > 1 && argv[1][0] == '-') { + if(strcmp(argv[1], "-") == 0) + break; + if(strcmp(argv[1], "--6g") == 0) + gcc = 0; + else if(strcmp(argv[1], "--gcc") == 0) + gcc = 1; + else + usage(); + argc--; + argv++; + } + + if(argc <= 1 || strcmp(argv[1], "-") == 0) { + file = "<stdin>"; + process_file(); + return 0; + } + + if(argc > 2) + usage(); + + file = argv[1]; + if(freopen(file, "r", stdin) == 0) { + fprintf(stderr, "open %s: %s\n", file, strerror(errno)); + exit(1); + } + process_file(); + return 0; +} diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c new file mode 100644 index 000000000..be65bcbc1 --- /dev/null +++ b/src/pkg/runtime/chan.c @@ -0,0 +1,1024 @@ +// Copyright 2009 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. + +#include "runtime.h" + +static int32 debug = 0; +static Lock chanlock; + +enum +{ + Wclosed = 0x0001, // writer has closed + Rclosed = 0x0002, // reader has seen close + Eincr = 0x0004, // increment errors + Emax = 0x0800, // error limit before throw +}; + +typedef struct Hchan Hchan; +typedef struct Link Link; +typedef struct WaitQ WaitQ; +typedef struct SudoG SudoG; +typedef struct Select Select; +typedef struct Scase Scase; + +struct SudoG +{ + G* g; // g and selgen constitute + int32 selgen; // a weak pointer to g + int16 offset; // offset of case number + int8 isfree; // offset of case number + SudoG* link; + byte elem[8]; // synch data element (+ more) +}; + +struct WaitQ +{ + SudoG* first; + SudoG* last; +}; + +struct Hchan +{ + uint16 elemsize; + uint16 closed; // Wclosed Rclosed errorcount + uint32 dataqsiz; // size of the circular q + uint32 qcount; // total data in the q + Alg* elemalg; // interface for element type + Link* senddataq; // pointer for sender + Link* recvdataq; // pointer for receiver + WaitQ recvq; // list of recv waiters + WaitQ sendq; // list of send waiters + SudoG* free; // freelist +}; + +struct Link +{ + Link* link; // asynch queue circular linked list + byte elem[8]; // asynch queue data element (+ more) +}; + +struct Scase +{ + Hchan* chan; // chan + byte* pc; // return pc + uint16 send; // 0-recv 1-send 2-default + uint16 so; // vararg of selected bool + union { + byte elem[8]; // element (send) + byte* elemp; // pointer to element (recv) + } u; +}; + +struct Select +{ + uint16 tcase; // total count of scase[] + uint16 ncase; // currently filled scase[] + Select* link; // for freelist + Scase* scase[1]; // one per case +}; + +static Select* selfree[20]; + +static SudoG* dequeue(WaitQ*, Hchan*); +static void enqueue(WaitQ*, SudoG*); +static SudoG* allocsg(Hchan*); +static void freesg(Hchan*, SudoG*); +static uint32 gcd(uint32, uint32); +static uint32 fastrand1(void); +static uint32 fastrand2(void); + +// newchan(elemsize uint32, elemalg uint32, hint uint32) (hchan *chan any); +void +sys·newchan(uint32 elemsize, uint32 elemalg, uint32 hint, + Hchan* ret) +{ + Hchan *c; + int32 i; + + if(elemalg >= nelem(algarray)) { + printf("chan(alg=%d)\n", elemalg); + throw("sys·newchan: unsupported elem type"); + } + + c = mal(sizeof(*c)); + + c->elemsize = elemsize; + c->elemalg = &algarray[elemalg]; + + if(hint > 0) { + Link *d, *b, *e; + + // make a circular q + b = nil; + e = nil; + for(i=0; i<hint; i++) { + d = mal(sizeof(*d) + c->elemsize - sizeof(d->elem)); + if(e == nil) + e = d; + d->link = b; + b = d; + } + e->link = b; + c->recvdataq = b; + c->senddataq = b; + c->qcount = 0; + c->dataqsiz = hint; + } + + ret = c; + FLUSH(&ret); + + if(debug) { + prints("newchan: chan="); + sys·printpointer(c); + prints("; elemsize="); + sys·printint(elemsize); + prints("; elemalg="); + sys·printint(elemalg); + prints("; dataqsiz="); + sys·printint(c->dataqsiz); + prints("\n"); + } +} + +static void +incerr(Hchan* c) +{ + c->closed += Eincr; + if(c->closed & Emax) { + unlock(&chanlock); + throw("too many operations on a closed channel"); + } +} + +/* + * generic single channel send/recv + * if the bool pointer is nil, + * then the full exchange will + * occur. if pres is not nil, + * then the protocol will not + * sleep but return if it could + * not complete + */ +void +sendchan(Hchan *c, byte *ep, bool *pres) +{ + SudoG *sg; + G* gp; + + if(debug) { + prints("chansend: chan="); + sys·printpointer(c); + prints("; elem="); + c->elemalg->print(c->elemsize, ep); + prints("\n"); + } + + lock(&chanlock); +loop: + if(c->closed & Wclosed) + goto closed; + + if(c->dataqsiz > 0) + goto asynch; + + sg = dequeue(&c->recvq, c); + if(sg != nil) { + if(ep != nil) + c->elemalg->copy(c->elemsize, sg->elem, ep); + + gp = sg->g; + gp->param = sg; + unlock(&chanlock); + ready(gp); + + if(pres != nil) + *pres = true; + return; + } + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + + sg = allocsg(c); + if(ep != nil) + c->elemalg->copy(c->elemsize, sg->elem, ep); + g->param = nil; + g->status = Gwaiting; + enqueue(&c->sendq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + freesg(c, sg); + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +asynch: + if(c->closed & Wclosed) + goto closed; + + if(c->qcount >= c->dataqsiz) { + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + sg = allocsg(c); + g->status = Gwaiting; + enqueue(&c->sendq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + goto asynch; + } + if(ep != nil) + c->elemalg->copy(c->elemsize, c->senddataq->elem, ep); + c->senddataq = c->senddataq->link; + c->qcount++; + + sg = dequeue(&c->recvq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + unlock(&chanlock); + ready(gp); + } else + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +closed: + incerr(c); + if(pres != nil) + *pres = true; + unlock(&chanlock); +} + +static void +chanrecv(Hchan* c, byte *ep, bool* pres) +{ + SudoG *sg; + G *gp; + + if(debug) { + prints("chanrecv: chan="); + sys·printpointer(c); + prints("\n"); + } + + lock(&chanlock); +loop: + if(c->dataqsiz > 0) + goto asynch; + + if(c->closed & Wclosed) + goto closed; + + sg = dequeue(&c->sendq, c); + if(sg != nil) { + c->elemalg->copy(c->elemsize, ep, sg->elem); + + gp = sg->g; + gp->param = sg; + unlock(&chanlock); + ready(gp); + + if(pres != nil) + *pres = true; + return; + } + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + + sg = allocsg(c); + g->param = nil; + g->status = Gwaiting; + enqueue(&c->recvq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + + c->elemalg->copy(c->elemsize, ep, sg->elem); + freesg(c, sg); + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +asynch: + if(c->qcount <= 0) { + if(c->closed & Wclosed) + goto closed; + + if(pres != nil) { + unlock(&chanlock); + *pres = false; + return; + } + sg = allocsg(c); + g->status = Gwaiting; + enqueue(&c->recvq, sg); + unlock(&chanlock); + gosched(); + + lock(&chanlock); + goto asynch; + } + c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem); + c->recvdataq = c->recvdataq->link; + c->qcount--; + sg = dequeue(&c->sendq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + unlock(&chanlock); + ready(gp); + if(pres != nil) + *pres = true; + return; + } + + unlock(&chanlock); + if(pres != nil) + *pres = true; + return; + +closed: + c->elemalg->copy(c->elemsize, ep, nil); + c->closed |= Rclosed; + incerr(c); + if(pres != nil) + *pres = true; + unlock(&chanlock); +} + +// chansend1(hchan *chan any, elem any); +void +sys·chansend1(Hchan* c, ...) +{ + int32 o; + byte *ae; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + sendchan(c, ae, nil); +} + +// chansend2(hchan *chan any, elem any) (pres bool); +void +sys·chansend2(Hchan* c, ...) +{ + int32 o; + byte *ae, *ap; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + o = rnd(o+c->elemsize, 1); + ap = (byte*)&c + o; + + sendchan(c, ae, ap); +} + +// chanrecv1(hchan *chan any) (elem any); +void +sys·chanrecv1(Hchan* c, ...) +{ + int32 o; + byte *ae; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + + chanrecv(c, ae, nil); +} + +// chanrecv2(hchan *chan any) (elem any, pres bool); +void +sys·chanrecv2(Hchan* c, ...) +{ + int32 o; + byte *ae, *ap; + + o = rnd(sizeof(c), c->elemsize); + ae = (byte*)&c + o; + o = rnd(o+c->elemsize, 1); + ap = (byte*)&c + o; + + chanrecv(c, ae, ap); +} + +// chanrecv3(hchan *chan any, elem *any) (pres bool); +void +sys·chanrecv3(Hchan* c, byte* ep, byte pres) +{ + chanrecv(c, ep, &pres); +} + +// newselect(size uint32) (sel *byte); +void +sys·newselect(int32 size, Select *sel) +{ + int32 n; + + n = 0; + if(size > 1) + n = size-1; + + lock(&chanlock); + sel = nil; + if(size >= 1 && size < nelem(selfree)) { + sel = selfree[size]; + if(sel != nil) + selfree[size] = sel->link; + } + unlock(&chanlock); + if(sel == nil) + sel = mal(sizeof(*sel) + n*sizeof(sel->scase[0])); + + sel->tcase = size; + sel->ncase = 0; + FLUSH(&sel); + if(debug) { + prints("newselect s="); + sys·printpointer(sel); + prints(" size="); + sys·printint(size); + prints("\n"); + } +} + +// selectsend(sel *byte, hchan *chan any, elem any) (selected bool); +void +sys·selectsend(Select *sel, Hchan *c, ...) +{ + int32 i, eo; + Scase *cas; + byte *ae; + + // nil cases do not compete + if(c == nil) + return; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectsend: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas + c->elemsize - sizeof(cas->u.elem)); + sel->scase[i] = cas; + } + + cas->pc = sys·getcallerpc(&sel); + cas->chan = c; + + eo = rnd(sizeof(sel), sizeof(c)); + eo = rnd(eo+sizeof(c), c->elemsize); + cas->so = rnd(eo+c->elemsize, 1); + cas->send = 1; + + ae = (byte*)&sel + eo; + c->elemalg->copy(c->elemsize, cas->u.elem, ae); + + if(debug) { + prints("selectsend s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" chan="); + sys·printpointer(cas->chan); + prints(" po="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + +// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); +void +sys·selectrecv(Select *sel, Hchan *c, ...) +{ + int32 i, eo; + Scase *cas; + + // nil cases do not compete + if(c == nil) + return; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectrecv: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas); + sel->scase[i] = cas; + } + cas->pc = sys·getcallerpc(&sel); + cas->chan = c; + + eo = rnd(sizeof(sel), sizeof(c)); + eo = rnd(eo+sizeof(c), sizeof(byte*)); + cas->so = rnd(eo+sizeof(byte*), 1); + cas->send = 0; + cas->u.elemp = *(byte**)((byte*)&sel + eo); + + if(debug) { + prints("selectrecv s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" chan="); + sys·printpointer(cas->chan); + prints(" so="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + + +// selectdefaul(sel *byte) (selected bool); +void +sys·selectdefault(Select *sel, ...) +{ + int32 i; + Scase *cas; + + i = sel->ncase; + if(i >= sel->tcase) + throw("selectdefault: too many cases"); + sel->ncase = i+1; + cas = sel->scase[i]; + if(cas == nil) { + cas = mal(sizeof *cas); + sel->scase[i] = cas; + } + cas->pc = sys·getcallerpc(&sel); + cas->chan = nil; + + cas->so = rnd(sizeof(sel), 1); + cas->send = 2; + cas->u.elemp = nil; + + if(debug) { + prints("selectdefault s="); + sys·printpointer(sel); + prints(" pc="); + sys·printpointer(cas->pc); + prints(" so="); + sys·printint(cas->so); + prints(" send="); + sys·printint(cas->send); + prints("\n"); + } +} + +// selectgo(sel *byte); +void +sys·selectgo(Select *sel) +{ + uint32 p, o, i; + Scase *cas, *dfl; + Hchan *c; + SudoG *sg; + G *gp; + byte *as; + + if(debug) { + prints("selectgo: sel="); + sys·printpointer(sel); + prints("\n"); + } + + if(sel->ncase < 2) { + if(sel->ncase < 1) + throw("selectgo: no cases"); + // make special case of one. + } + + // select a (relative) prime + for(i=0;; i++) { + p = fastrand1(); + if(gcd(p, sel->ncase) == 1) + break; + if(i > 1000) { + throw("selectgo: failed to select prime"); + } + } + + // select an initial offset + o = fastrand2(); + + p %= sel->ncase; + o %= sel->ncase; + + lock(&chanlock); + +loop: + // pass 1 - look for something already waiting + dfl = nil; + for(i=0; i<sel->ncase; i++) { + cas = sel->scase[o]; + + if(cas->send == 2) { // default + dfl = cas; + goto next1; + } + + c = cas->chan; + if(c->dataqsiz > 0) { + if(cas->send) { + if(c->closed & Wclosed) + goto sclose; + if(c->qcount < c->dataqsiz) + goto asyns; + goto next1; + } + if(c->qcount > 0) + goto asynr; + if(c->closed & Wclosed) + goto rclose; + goto next1; + } + + if(cas->send) { + if(c->closed & Wclosed) + goto sclose; + sg = dequeue(&c->recvq, c); + if(sg != nil) + goto gots; + goto next1; + } + sg = dequeue(&c->sendq, c); + if(sg != nil) + goto gotr; + if(c->closed & Wclosed) + goto rclose; + + next1: + o += p; + if(o >= sel->ncase) + o -= sel->ncase; + } + + if(dfl != nil) { + cas = dfl; + goto retc; + } + + + // pass 2 - enqueue on all chans + for(i=0; i<sel->ncase; i++) { + cas = sel->scase[o]; + c = cas->chan; + + if(c->dataqsiz > 0) { + if(cas->send) { + if(c->qcount < c->dataqsiz) { + prints("selectgo: pass 2 async send\n"); + goto asyns; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->sendq, sg); + goto next2; + } + if(c->qcount > 0) { + prints("selectgo: pass 2 async recv\n"); + goto asynr; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->recvq, sg); + goto next2; + } + + if(cas->send) { + sg = dequeue(&c->recvq, c); + if(sg != nil) { + prints("selectgo: pass 2 sync send\n"); + g->selgen++; + goto gots; + } + sg = allocsg(c); + sg->offset = o; + c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem); + enqueue(&c->sendq, sg); + goto next2; + } + sg = dequeue(&c->sendq, c); + if(sg != nil) { + prints("selectgo: pass 2 sync recv\n"); + g->selgen++; + goto gotr; + } + sg = allocsg(c); + sg->offset = o; + enqueue(&c->recvq, sg); + + next2: + o += p; + if(o >= sel->ncase) + o -= sel->ncase; + } + + g->param = nil; + g->status = Gwaiting; + unlock(&chanlock); + gosched(); + + lock(&chanlock); + sg = g->param; + if(sg == nil) + goto loop; + + o = sg->offset; + cas = sel->scase[o]; + c = cas->chan; + + if(c->dataqsiz > 0) { +// prints("shouldnt happen\n"); + goto loop; + } + + if(debug) { + prints("wait-return: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" cas="); + sys·printpointer(cas); + prints(" send="); + sys·printint(cas->send); + prints(" o="); + sys·printint(o); + prints("\n"); + } + + if(!cas->send) { + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem); + } + + freesg(c, sg); + goto retc; + +asynr: + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, c->recvdataq->elem); + c->recvdataq = c->recvdataq->link; + c->qcount--; + sg = dequeue(&c->sendq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + ready(gp); + } + goto retc; + +asyns: + if(cas->u.elem != nil) + c->elemalg->copy(c->elemsize, c->senddataq->elem, cas->u.elem); + c->senddataq = c->senddataq->link; + c->qcount++; + sg = dequeue(&c->recvq, c); + if(sg != nil) { + gp = sg->g; + freesg(c, sg); + ready(gp); + } + goto retc; + +gotr: + // recv path to wakeup the sender (sg) + if(debug) { + prints("gotr: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" o="); + sys·printint(o); + prints("\n"); + } + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem); + gp = sg->g; + gp->param = sg; + ready(gp); + goto retc; + +rclose: + if(cas->u.elemp != nil) + c->elemalg->copy(c->elemsize, cas->u.elemp, nil); + c->closed |= Rclosed; + incerr(c); + goto retc; + +gots: + // send path to wakeup the receiver (sg) + if(debug) { + prints("gots: sel="); + sys·printpointer(sel); + prints(" c="); + sys·printpointer(c); + prints(" o="); + sys·printint(o); + prints("\n"); + } + if(c->closed & Wclosed) + goto sclose; + c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem); + gp = sg->g; + gp->param = sg; + ready(gp); + goto retc; + +sclose: + incerr(c); + goto retc; + +retc: + if(sel->ncase >= 1 && sel->ncase < nelem(selfree)) { + sel->link = selfree[sel->ncase]; + selfree[sel->ncase] = sel; + } + unlock(&chanlock); + + sys·setcallerpc(&sel, cas->pc); + as = (byte*)&sel + cas->so; + *as = true; +} + +// closechan(sel *byte); +void +sys·closechan(Hchan *c) +{ + SudoG *sg; + G* gp; + + lock(&chanlock); + incerr(c); + c->closed |= Wclosed; + + // release all readers + for(;;) { + sg = dequeue(&c->recvq, c); + if(sg == nil) + break; + gp = sg->g; + gp->param = nil; + freesg(c, sg); + ready(gp); + } + + // release all writers + for(;;) { + sg = dequeue(&c->sendq, c); + if(sg == nil) + break; + gp = sg->g; + gp->param = nil; + freesg(c, sg); + ready(gp); + } + + unlock(&chanlock); +} + +// closedchan(sel *byte) bool; +void +sys·closedchan(Hchan *c, bool closed) +{ + // test Rclosed + closed = 0; + if(c->closed & Rclosed) + closed = 1; + FLUSH(&closed); +} + +static SudoG* +dequeue(WaitQ *q, Hchan *c) +{ + SudoG *sgp; + +loop: + sgp = q->first; + if(sgp == nil) + return nil; + q->first = sgp->link; + + // if sgp is stale, ignore it + if(sgp->selgen != sgp->g->selgen) { + //prints("INVALID PSEUDOG POINTER\n"); + freesg(c, sgp); + goto loop; + } + + // invalidate any others + sgp->g->selgen++; + return sgp; +} + +static void +enqueue(WaitQ *q, SudoG *sgp) +{ + sgp->link = nil; + if(q->first == nil) { + q->first = sgp; + q->last = sgp; + return; + } + q->last->link = sgp; + q->last = sgp; +} + +static SudoG* +allocsg(Hchan *c) +{ + SudoG* sg; + + sg = c->free; + if(sg != nil) { + c->free = sg->link; + } else + sg = mal(sizeof(*sg) + c->elemsize - sizeof(sg->elem)); + sg->selgen = g->selgen; + sg->g = g; + sg->offset = 0; + sg->isfree = 0; + + return sg; +} + +static void +freesg(Hchan *c, SudoG *sg) +{ + if(sg != nil) { + if(sg->isfree) + throw("chan.freesg: already free"); + sg->isfree = 1; + sg->link = c->free; + c->free = sg; + } +} + +static uint32 +gcd(uint32 u, uint32 v) +{ + for(;;) { + if(u > v) { + if(v == 0) + return u; + u = u%v; + continue; + } + if(u == 0) + return v; + v = v%u; + } +} + +static uint32 +fastrand1(void) +{ + static uint32 x = 0x49f6428aUL; + + x += x; + if(x & 0x80000000L) + x ^= 0x88888eefUL; + return x; +} + +static uint32 +fastrand2(void) +{ + static uint32 x = 0x49f6428aUL; + + x += x; + if(x & 0x80000000L) + x ^= 0xfafd871bUL; + return x; +} diff --git a/src/pkg/runtime/darwin/386/defs.h b/src/pkg/runtime/darwin/386/defs.h new file mode 100644 index 000000000..b66a5d8b4 --- /dev/null +++ b/src/pkg/runtime/darwin/386/defs.h @@ -0,0 +1,229 @@ +// godefs -f -m32 defs.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MACH_MSG_TYPE_MOVE_RECEIVE = 0x10, + MACH_MSG_TYPE_MOVE_SEND = 0x11, + MACH_MSG_TYPE_MOVE_SEND_ONCE = 0x12, + MACH_MSG_TYPE_COPY_SEND = 0x13, + MACH_MSG_TYPE_MAKE_SEND = 0x14, + MACH_MSG_TYPE_MAKE_SEND_ONCE = 0x15, + MACH_MSG_TYPE_COPY_RECEIVE = 0x16, + MACH_MSG_PORT_DESCRIPTOR = 0, + MACH_MSG_OOL_DESCRIPTOR = 0x1, + MACH_MSG_OOL_PORTS_DESCRIPTOR = 0x2, + MACH_MSG_OOL_VOLATILE_DESCRIPTOR = 0x3, + MACH_MSGH_BITS_COMPLEX = 0x80000000, + MACH_SEND_MSG = 0x1, + MACH_RCV_MSG = 0x2, + MACH_RCV_LARGE = 0x4, + MACH_SEND_TIMEOUT = 0x10, + MACH_SEND_INTERRUPT = 0x40, + MACH_SEND_CANCEL = 0x80, + MACH_SEND_ALWAYS = 0x10000, + MACH_SEND_TRAILER = 0x20000, + MACH_RCV_TIMEOUT = 0x100, + MACH_RCV_NOTIFY = 0x200, + MACH_RCV_INTERRUPT = 0x400, + MACH_RCV_OVERWRITE = 0x1000, + NDR_PROTOCOL_2_0 = 0, + NDR_INT_BIG_ENDIAN = 0, + NDR_INT_LITTLE_ENDIAN = 0x1, + NDR_FLOAT_IEEE = 0, + NDR_CHAR_ASCII = 0, + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + SA_USERTRAMP = 0x100, + SA_64REGSET = 0x200, +}; + +// Types +#pragma pack on + +typedef struct MachBody MachBody; +struct MachBody { + uint32 msgh_descriptor_count; +}; + +typedef struct MachHeader MachHeader; +struct MachHeader { + uint32 msgh_bits; + uint32 msgh_size; + uint32 msgh_remote_port; + uint32 msgh_local_port; + uint32 msgh_reserved; + int32 msgh_id; +}; + +typedef struct MachNDR MachNDR; +struct MachNDR { + uint8 mig_vers; + uint8 if_vers; + uint8 reserved1; + uint8 mig_encoding; + uint8 int_rep; + uint8 char_rep; + uint8 float_rep; + uint8 reserved2; +}; + +typedef struct MachPort MachPort; +struct MachPort { + uint32 name; + uint32 pad1; + uint16 pad2; + uint8 disposition; + uint8 type; +}; + +typedef struct StackT StackT; +struct StackT { + void *ss_sp; + uint32 ss_size; + int32 ss_flags; +}; + +typedef union Sighandler Sighandler; +union Sighandler { + void *__sa_handler; + void *__sa_sigaction; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + Sighandler __sigaction_u; + void *sa_tramp; + uint32 sa_mask; + int32 sa_flags; +}; + +typedef union Sigval Sigval; +union Sigval { + int32 sival_int; + void *sival_ptr; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + int32 si_pid; + uint32 si_uid; + int32 si_status; + void *si_addr; + Sigval si_value; + int32 si_band; + uint32 __pad[7]; +}; + +typedef struct FPControl FPControl; +struct FPControl { + byte pad0[2]; +}; + +typedef struct FPStatus FPStatus; +struct FPStatus { + byte pad0[2]; +}; + +typedef struct RegMMST RegMMST; +struct RegMMST { + int8 mmst_reg[10]; + int8 mmst_rsrv[6]; +}; + +typedef struct RegXMM RegXMM; +struct RegXMM { + int8 xmm_reg[16]; +}; + +typedef struct Regs Regs; +struct Regs { + uint32 eax; + uint32 ebx; + uint32 ecx; + uint32 edx; + uint32 edi; + uint32 esi; + uint32 ebp; + uint32 esp; + uint32 ss; + uint32 eflags; + uint32 eip; + uint32 cs; + uint32 ds; + uint32 es; + uint32 fs; + uint32 gs; +}; + +typedef struct FloatState FloatState; +struct FloatState { + int32 fpu_reserved[2]; + FPControl fpu_fcw; + FPStatus fpu_fsw; + uint8 fpu_ftw; + uint8 fpu_rsrv1; + uint16 fpu_fop; + uint32 fpu_ip; + uint16 fpu_cs; + uint16 fpu_rsrv2; + uint32 fpu_dp; + uint16 fpu_ds; + uint16 fpu_rsrv3; + uint32 fpu_mxcsr; + uint32 fpu_mxcsrmask; + RegMMST fpu_stmm0; + RegMMST fpu_stmm1; + RegMMST fpu_stmm2; + RegMMST fpu_stmm3; + RegMMST fpu_stmm4; + RegMMST fpu_stmm5; + RegMMST fpu_stmm6; + RegMMST fpu_stmm7; + RegXMM fpu_xmm0; + RegXMM fpu_xmm1; + RegXMM fpu_xmm2; + RegXMM fpu_xmm3; + RegXMM fpu_xmm4; + RegXMM fpu_xmm5; + RegXMM fpu_xmm6; + RegXMM fpu_xmm7; + int8 fpu_rsrv4[224]; + int32 fpu_reserved1; +}; + +typedef struct ExceptionState ExceptionState; +struct ExceptionState { + uint32 trapno; + uint32 err; + uint32 faultvaddr; +}; + +typedef struct Mcontext Mcontext; +struct Mcontext { + ExceptionState es; + Regs ss; + FloatState fs; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + int32 uc_onstack; + uint32 uc_sigmask; + StackT uc_stack; + Ucontext *uc_link; + uint32 uc_mcsize; + Mcontext *uc_mcontext; +}; +#pragma pack off diff --git a/src/pkg/runtime/darwin/386/rt0.s b/src/pkg/runtime/darwin/386/rt0.s new file mode 100755 index 000000000..5b52e912c --- /dev/null +++ b/src/pkg/runtime/darwin/386/rt0.s @@ -0,0 +1,8 @@ +// Copyright 2009 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_386_darwin(SB),7,$0 + JMP _rt0_386(SB) diff --git a/src/pkg/runtime/darwin/386/signal.c b/src/pkg/runtime/darwin/386/signal.c new file mode 100644 index 000000000..3a63c4b38 --- /dev/null +++ b/src/pkg/runtime/darwin/386/signal.c @@ -0,0 +1,103 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs.h" +#include "os.h" +#include "signals.h" + +void +dumpregs(Regs *r) +{ + printf("eax %x\n", r->eax); + printf("ebx %x\n", r->ebx); + printf("ecx %x\n", r->ecx); + printf("edx %x\n", r->edx); + printf("edi %x\n", r->edi); + printf("esi %x\n", r->esi); + printf("ebp %x\n", r->ebp); + printf("esp %x\n", r->esp); + printf("eip %x\n", r->eip); + printf("eflags %x\n", r->eflags); + printf("cs %x\n", r->cs); + printf("fs %x\n", r->fs); + printf("gs %x\n", r->gs); +} + +void +sighandler(int32 sig, Siginfo *info, void *context) +{ + Ucontext *uc; + Mcontext *mc; + Regs *r; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + if(sig < 0 || sig >= NSIG){ + printf("Signal %d\n", sig); + }else{ + printf("%s\n", sigtab[sig].name); + } + + uc = context; + mc = uc->uc_mcontext; + r = &mc->ss; + + printf("Faulting address: %p\n", info->si_addr); + printf("pc: %x\n", r->eip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)r->eip, (void*)r->esp, m->curg); + tracebackothers(m->curg); + dumpregs(r); + } + + breakpoint(); + exit(2); +} + +void +sigignore(int32, Siginfo*, void*) +{ +} + +void +signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + int32 i; + static Sigaction sa; + + sa.sa_flags |= SA_SIGINFO|SA_ONSTACK; + sa.sa_mask = 0; // 0xFFFFFFFFU; + sa.sa_tramp = sigtramp; // sigtramp's job is to call into real handler + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) { + sa.__sigaction_u.__sa_sigaction = sighandler; + } else { + sa.__sigaction_u.__sa_sigaction = sigignore; + } + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + sigaction(i, &sa, nil); + } + } +} + diff --git a/src/pkg/runtime/darwin/386/sys.s b/src/pkg/runtime/darwin/386/sys.s new file mode 100644 index 000000000..bbcb622d5 --- /dev/null +++ b/src/pkg/runtime/darwin/386/sys.s @@ -0,0 +1,278 @@ +// Copyright 2009 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. + +// System calls and other sys.stuff for 386, Darwin +// See http://fxr.watson.org/fxr/source/bsd/kern/syscalls.c?v=xnu-1228 +// or /usr/include/sys/syscall.h (on a Mac) for system call numbers. + +TEXT notok(SB),7,$0 + MOVL $0xf1, 0xf1 + RET + +// Exit the entire program (like C exit) +TEXT exit(SB),7,$0 + MOVL $1, AX + INT $0x80 + CALL notok(SB) + RET + +// Exit this OS thread (like pthread_exit, which eventually +// calls __bsdthread_terminate). +TEXT exit1(SB),7,$0 + MOVL $361, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +TEXT sys·write(SB),7,$0 + MOVL $4, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +TEXT sys·mmap(SB),7,$0 + MOVL $197, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +TEXT sigaction(SB),7,$0 + MOVL $46, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// Sigtramp's job is to call the actual signal handler. +// It is called with the following arguments on the stack: +// 0(FP) "return address" - ignored +// 4(FP) actual handler +// 8(FP) siginfo style - ignored +// 12(FP) signal number +// 16(FP) siginfo +// 20(FP) context +TEXT sigtramp(SB),7,$40 + MOVL 4(FS), BP // m + MOVL 28(BP), BP // m->gsignal + MOVL BP, 0(FS) // g = m->gsignal + + MOVL handler+4(FP), DI + MOVL signo+12(FP), AX + MOVL siginfo+16(FP), BX + MOVL context+20(FP), CX + + MOVL AX, 0(SP) + MOVL BX, 4(SP) + MOVL CX, 8(SP) + CALL DI + + MOVL context+20(FP), CX + MOVL style+8(FP), BX + + MOVL $0, 0(SP) // "caller PC" - ignored + MOVL CX, 4(SP) + MOVL BX, 8(SP) + MOVL $184, AX // sigreturn(ucontext, infostyle) + INT $0x80 + CALL notok(SB) + RET + +TEXT sigaltstack(SB),7,$0 + MOVL $53, AX + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void)) +// System call args are: func arg stack pthread flags. +TEXT bsdthread_create(SB),7,$32 + MOVL $360, AX + // 0(SP) is where the caller PC would be; kernel skips it + MOVL func+12(FP), BX + MOVL BX, 4(SP) // func + MOVL m+4(FP), BX + MOVL BX, 8(SP) // arg + MOVL stk+0(FP), BX + MOVL BX, 12(SP) // stack + MOVL g+8(FP), BX + MOVL BX, 16(SP) // pthread + MOVL $0x1000000, 20(SP) // flags = PTHREAD_START_CUSTOM + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// The thread that bsdthread_create creates starts executing here, +// because we registered this function using bsdthread_register +// at startup. +// AX = "pthread" (= g) +// BX = mach thread port +// CX = "func" (= fn) +// DX = "arg" (= m) +// DI = stack top +// SI = flags (= 0x1000000) +// SP = stack - C_32_STK_ALIGN +TEXT bsdthread_start(SB),7,$0 + // set up ldt 7+id to point at m->tls. + // m->tls is at m+40. newosproc left + // the m->id in tls[0]. + LEAL 40(DX), BP + MOVL 0(BP), DI + ADDL $7, DI // m0 is LDT#7. count up. + // setldt(tls#, &tls, sizeof tls) + PUSHAL // save registers + PUSHL $32 // sizeof tls + PUSHL BP // &tls + PUSHL DI // tls # + CALL setldt(SB) + POPL AX + POPL AX + POPL AX + POPAL + SHLL $3, DI // segment# is ldt*8 + 7. + ADDL $7, DI + MOVW DI, FS + + // Now segment is established. Initialize m, g. + MOVL AX, 0(FS) // g + MOVL DX, 4(FS) // m + MOVL BX, 20(DX) // m->procid = thread port (for debuggers) + CALL CX // fn() + CALL exit1(SB) + RET + +// void bsdthread_register(void) +// registers callbacks for threadstart (see bsdthread_create above +// and wqthread and pthsize (not used). returns 0 on success. +TEXT bsdthread_register(SB),7,$40 + MOVL $366, AX + // 0(SP) is where kernel expects caller PC; ignored + MOVL $bsdthread_start(SB), 4(SP) // threadstart + MOVL $0, 8(SP) // wqthread, not used by us + MOVL $0, 12(SP) // pthsize, not used by us + MOVL $0, 16(SP) // paranoia + MOVL $0, 20(SP) + MOVL $0, 24(SP) + INT $0x80 + JAE 2(PC) + CALL notok(SB) + RET + +// Invoke Mach system call. +// Assumes system call number in AX, +// caller PC on stack, caller's caller PC next, +// and then the system call arguments. +// +// Can be used for BSD too, but we don't, +// because if you use this interface the BSD +// system call numbers need an extra field +// in the high 16 bits that seems to be the +// argument count in bytes but is not always. +// INT $0x80 works fine for those. +TEXT sysenter(SB),7,$0 + POPL DX + MOVL SP, CX + BYTE $0x0F; BYTE $0x34; // SYSENTER + // returns to DX with SP set to CX + +TEXT mach_msg_trap(SB),7,$0 + MOVL $-31, AX + CALL sysenter(SB) + RET + +TEXT mach_reply_port(SB),7,$0 + MOVL $-26, AX + CALL sysenter(SB) + RET + +TEXT mach_task_self(SB),7,$0 + MOVL $-28, AX + CALL sysenter(SB) + RET + +// Mach provides trap versions of the semaphore ops, +// instead of requiring the use of RPC. + +// uint32 mach_semaphore_wait(uint32) +TEXT mach_semaphore_wait(SB),7,$0 + MOVL $-36, AX + CALL sysenter(SB) + RET + +// uint32 mach_semaphore_timedwait(uint32, uint32, uint32) +TEXT mach_semaphore_timedwait(SB),7,$0 + MOVL $-38, AX + CALL sysenter(SB) + RET + +// uint32 mach_semaphore_signal(uint32) +TEXT mach_semaphore_signal(SB),7,$0 + MOVL $-33, AX + CALL sysenter(SB) + RET + +// uint32 mach_semaphore_signal_all(uint32) +TEXT mach_semaphore_signal_all(SB),7,$0 + MOVL $-34, AX + CALL sysenter(SB) + RET + +/* +descriptor entry format for system call +is the native machine format, ugly as it is: + + 2-byte limit + 3-byte base + 1-byte: 0x80=present, 0x60=dpl<<5, 0x1F=type + 1-byte: 0x80=limit is *4k, 0x40=32-bit operand size, + 0x0F=4 more bits of limit + 1 byte: 8 more bits of base + +int i386_get_ldt(int, union ldt_entry *, int); +int i386_set_ldt(int, const union ldt_entry *, int); + +*/ + +// setldt(int entry, int address, int limit) +TEXT setldt(SB),7,$32 + // set up data_desc + LEAL 16(SP), AX // struct data_desc + MOVL $0, 0(AX) + MOVL $0, 4(AX) + + MOVL address+4(FP), BX // aka base + MOVW BX, 2(AX) + SHRL $16, BX + MOVB BX, 4(AX) + SHRL $8, BX + MOVB BX, 7(AX) + + MOVL limit+8(FP), BX + MOVW BX, 0(AX) + SHRL $16, BX + ANDL $0x0F, BX + ORL $0x40, BX // 32-bit operand size + MOVB BX, 6(AX) + + MOVL $0xF2, 5(AX) // r/w data descriptor, dpl=3, present + + // call i386_set_ldt(entry, desc, 1) + MOVL entry+0(FP), BX + MOVL BX, 0(SP) + MOVL AX, 4(SP) + MOVL $1, 8(SP) + CALL i386_set_ldt(SB) + RET + +TEXT i386_set_ldt(SB),7,$0 + MOVL $5, AX + INT $0x82 // sic + JAE 2(PC) + CALL notok(SB) + RET + diff --git a/src/pkg/runtime/darwin/amd64/defs.h b/src/pkg/runtime/darwin/amd64/defs.h new file mode 100644 index 000000000..1076e4c10 --- /dev/null +++ b/src/pkg/runtime/darwin/amd64/defs.h @@ -0,0 +1,244 @@ +// godefs -f -m64 defs.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x1000, + MAP_PRIVATE = 0x2, + MACH_MSG_TYPE_MOVE_RECEIVE = 0x10, + MACH_MSG_TYPE_MOVE_SEND = 0x11, + MACH_MSG_TYPE_MOVE_SEND_ONCE = 0x12, + MACH_MSG_TYPE_COPY_SEND = 0x13, + MACH_MSG_TYPE_MAKE_SEND = 0x14, + MACH_MSG_TYPE_MAKE_SEND_ONCE = 0x15, + MACH_MSG_TYPE_COPY_RECEIVE = 0x16, + MACH_MSG_PORT_DESCRIPTOR = 0, + MACH_MSG_OOL_DESCRIPTOR = 0x1, + MACH_MSG_OOL_PORTS_DESCRIPTOR = 0x2, + MACH_MSG_OOL_VOLATILE_DESCRIPTOR = 0x3, + MACH_MSGH_BITS_COMPLEX = 0x80000000, + MACH_SEND_MSG = 0x1, + MACH_RCV_MSG = 0x2, + MACH_RCV_LARGE = 0x4, + MACH_SEND_TIMEOUT = 0x10, + MACH_SEND_INTERRUPT = 0x40, + MACH_SEND_CANCEL = 0x80, + MACH_SEND_ALWAYS = 0x10000, + MACH_SEND_TRAILER = 0x20000, + MACH_RCV_TIMEOUT = 0x100, + MACH_RCV_NOTIFY = 0x200, + MACH_RCV_INTERRUPT = 0x400, + MACH_RCV_OVERWRITE = 0x1000, + NDR_PROTOCOL_2_0 = 0, + NDR_INT_BIG_ENDIAN = 0, + NDR_INT_LITTLE_ENDIAN = 0x1, + NDR_FLOAT_IEEE = 0, + NDR_CHAR_ASCII = 0, + SA_SIGINFO = 0x40, + SA_RESTART = 0x2, + SA_ONSTACK = 0x1, + SA_USERTRAMP = 0x100, + SA_64REGSET = 0x200, +}; + +// Types +#pragma pack on + +typedef struct MachBody MachBody; +struct MachBody { + uint32 msgh_descriptor_count; +}; + +typedef struct MachHeader MachHeader; +struct MachHeader { + uint32 msgh_bits; + uint32 msgh_size; + uint32 msgh_remote_port; + uint32 msgh_local_port; + uint32 msgh_reserved; + int32 msgh_id; +}; + +typedef struct MachNDR MachNDR; +struct MachNDR { + uint8 mig_vers; + uint8 if_vers; + uint8 reserved1; + uint8 mig_encoding; + uint8 int_rep; + uint8 char_rep; + uint8 float_rep; + uint8 reserved2; +}; + +typedef struct MachPort MachPort; +struct MachPort { + uint32 name; + uint32 pad1; + uint16 pad2; + uint8 disposition; + uint8 type; +}; + +typedef struct StackT StackT; +struct StackT { + void *ss_sp; + uint64 ss_size; + int32 ss_flags; + byte pad0[4]; +}; + +typedef union Sighandler Sighandler; +union Sighandler { + void *__sa_handler; + void *__sa_sigaction; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + Sighandler __sigaction_u; + void *sa_tramp; + uint32 sa_mask; + int32 sa_flags; +}; + +typedef union Sigval Sigval; +union Sigval { + int32 sival_int; + void *sival_ptr; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + int32 si_pid; + uint32 si_uid; + int32 si_status; + void *si_addr; + Sigval si_value; + int64 si_band; + uint64 __pad[7]; +}; + +typedef struct FPControl FPControl; +struct FPControl { + byte pad0[2]; +}; + +typedef struct FPStatus FPStatus; +struct FPStatus { + byte pad0[2]; +}; + +typedef struct RegMMST RegMMST; +struct RegMMST { + int8 mmst_reg[10]; + int8 mmst_rsrv[6]; +}; + +typedef struct RegXMM RegXMM; +struct RegXMM { + int8 xmm_reg[16]; +}; + +typedef struct Regs Regs; +struct Regs { + uint64 rax; + uint64 rbx; + uint64 rcx; + uint64 rdx; + uint64 rdi; + uint64 rsi; + uint64 rbp; + uint64 rsp; + uint64 r8; + uint64 r9; + uint64 r10; + uint64 r11; + uint64 r12; + uint64 r13; + uint64 r14; + uint64 r15; + uint64 rip; + uint64 rflags; + uint64 cs; + uint64 fs; + uint64 gs; +}; + +typedef struct FloatState FloatState; +struct FloatState { + int32 fpu_reserved[2]; + FPControl fpu_fcw; + FPStatus fpu_fsw; + uint8 fpu_ftw; + uint8 fpu_rsrv1; + uint16 fpu_fop; + uint32 fpu_ip; + uint16 fpu_cs; + uint16 fpu_rsrv2; + uint32 fpu_dp; + uint16 fpu_ds; + uint16 fpu_rsrv3; + uint32 fpu_mxcsr; + uint32 fpu_mxcsrmask; + RegMMST fpu_stmm0; + RegMMST fpu_stmm1; + RegMMST fpu_stmm2; + RegMMST fpu_stmm3; + RegMMST fpu_stmm4; + RegMMST fpu_stmm5; + RegMMST fpu_stmm6; + RegMMST fpu_stmm7; + RegXMM fpu_xmm0; + RegXMM fpu_xmm1; + RegXMM fpu_xmm2; + RegXMM fpu_xmm3; + RegXMM fpu_xmm4; + RegXMM fpu_xmm5; + RegXMM fpu_xmm6; + RegXMM fpu_xmm7; + RegXMM fpu_xmm8; + RegXMM fpu_xmm9; + RegXMM fpu_xmm10; + RegXMM fpu_xmm11; + RegXMM fpu_xmm12; + RegXMM fpu_xmm13; + RegXMM fpu_xmm14; + RegXMM fpu_xmm15; + int8 fpu_rsrv4[96]; + int32 fpu_reserved1; +}; + +typedef struct ExceptionState ExceptionState; +struct ExceptionState { + uint32 trapno; + uint32 err; + uint64 faultvaddr; +}; + +typedef struct Mcontext Mcontext; +struct Mcontext { + ExceptionState es; + Regs ss; + FloatState fs; + byte pad0[4]; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + int32 uc_onstack; + uint32 uc_sigmask; + StackT uc_stack; + Ucontext *uc_link; + uint64 uc_mcsize; + Mcontext *uc_mcontext; +}; +#pragma pack off diff --git a/src/pkg/runtime/darwin/amd64/rt0.s b/src/pkg/runtime/darwin/amd64/rt0.s new file mode 100644 index 000000000..0a0011781 --- /dev/null +++ b/src/pkg/runtime/darwin/amd64/rt0.s @@ -0,0 +1,9 @@ +// Copyright 2009 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_amd64_darwin(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + JMP AX diff --git a/src/pkg/runtime/darwin/amd64/signal.c b/src/pkg/runtime/darwin/amd64/signal.c new file mode 100644 index 000000000..45e5e8d47 --- /dev/null +++ b/src/pkg/runtime/darwin/amd64/signal.c @@ -0,0 +1,111 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs.h" +#include "os.h" +#include "signals.h" + +void +dumpregs(Regs *r) +{ + printf("rax %X\n", r->rax); + printf("rbx %X\n", r->rbx); + printf("rcx %X\n", r->rcx); + printf("rdx %X\n", r->rdx); + printf("rdi %X\n", r->rdi); + printf("rsi %X\n", r->rsi); + printf("rbp %X\n", r->rbp); + printf("rsp %X\n", r->rsp); + printf("r8 %X\n", r->r8 ); + printf("r9 %X\n", r->r9 ); + printf("r10 %X\n", r->r10); + printf("r11 %X\n", r->r11); + printf("r12 %X\n", r->r12); + printf("r13 %X\n", r->r13); + printf("r14 %X\n", r->r14); + printf("r15 %X\n", r->r15); + printf("rip %X\n", r->rip); + printf("rflags %X\n", r->rflags); + printf("cs %X\n", r->cs); + printf("fs %X\n", r->fs); + printf("gs %X\n", r->gs); +} + +void +sighandler(int32 sig, Siginfo *info, void *context) +{ + Ucontext *uc; + Mcontext *mc; + Regs *r; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + if(sig < 0 || sig >= NSIG){ + printf("Signal %d\n", sig); + }else{ + printf("%s\n", sigtab[sig].name); + } + + uc = context; + mc = uc->uc_mcontext; + r = &mc->ss; + + printf("Faulting address: %p\n", info->si_addr); + printf("pc: %X\n", r->rip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)r->rip, (void*)r->rsp, (void*)r->r15); + tracebackothers((void*)r->r15); + dumpregs(r); + } + + breakpoint(); + exit(2); +} + +void +sigignore(int32, Siginfo*, void*) +{ +} + +void +signalstack(byte *p, int32 n) +{ + StackT st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + int32 i; + static Sigaction sa; + + sa.sa_flags |= SA_SIGINFO|SA_ONSTACK; + sa.sa_mask = 0; // 0xFFFFFFFFU; + sa.sa_tramp = sigtramp; // sigtramp's job is to call into real handler + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) { + sa.__sigaction_u.__sa_sigaction = sighandler; + } else { + sa.__sigaction_u.__sa_sigaction = sigignore; + } + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + sigaction(i, &sa, nil); + } + } +} + diff --git a/src/pkg/runtime/darwin/amd64/sys.s b/src/pkg/runtime/darwin/amd64/sys.s new file mode 100644 index 000000000..b46c823ae --- /dev/null +++ b/src/pkg/runtime/darwin/amd64/sys.s @@ -0,0 +1,263 @@ +// Copyright 2009 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. + +// +// System calls and other sys.stuff for AMD64, Darwin +// See http://fxr.watson.org/fxr/source/bsd/kern/syscalls.c?v=xnu-1228 +// or /usr/include/sys/syscall.h (on a Mac) for system call numbers. +// + +// Exit the entire program (like C exit) +TEXT exit(SB),7,$-8 + MOVL 8(SP), DI // arg 1 exit status + MOVL $(0x2000000+1), AX // syscall entry + SYSCALL + CALL notok(SB) + RET + +// Exit this OS thread (like pthread_exit, which eventually +// calls __bsdthread_terminate). +TEXT exit1(SB),7,$-8 + MOVL 8(SP), DI // arg 1 exit status + MOVL $(0x2000000+361), AX // syscall entry + SYSCALL + CALL notok(SB) + RET + +TEXT sys·write(SB),7,$-8 + MOVL 8(SP), DI // arg 1 fid + MOVQ 16(SP), SI // arg 2 buf + MOVL 24(SP), DX // arg 3 count + MOVL $(0x2000000+4), AX // syscall entry + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +TEXT open(SB),7,$-8 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVQ $0, R10 + MOVL $(0x2000000+5), AX // syscall entry + SYSCALL + RET + +TEXT close(SB),7,$-8 + MOVL 8(SP), DI + MOVL $(0x2000000+6), AX // syscall entry + SYSCALL + RET + +TEXT fstat(SB),7,$-8 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL $(0x2000000+339), AX // syscall entry; really fstat64 + SYSCALL + RET + +TEXT read(SB),7,$-8 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $(0x2000000+3), AX // syscall entry + SYSCALL + RET + +TEXT write(SB),7,$-8 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $(0x2000000+4), AX // syscall entry + SYSCALL + RET + +TEXT sigaction(SB),7,$-8 + MOVL 8(SP), DI // arg 1 sig + MOVQ 16(SP), SI // arg 2 act + MOVQ 24(SP), DX // arg 3 oact + MOVQ 24(SP), CX // arg 3 oact + MOVQ 24(SP), R10 // arg 3 oact + MOVL $(0x2000000+46), AX // syscall entry + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +TEXT sigtramp(SB),7,$40 + MOVQ 32(R14), R15 // g = m->gsignal + MOVL DX,0(SP) + MOVQ CX,8(SP) + MOVQ R8,16(SP) + MOVQ R8, 24(SP) // save ucontext + MOVQ SI, 32(SP) // save infostyle + CALL DI + MOVL $(0x2000000+184), AX // sigreturn(ucontext, infostyle) + MOVQ 24(SP), DI // saved ucontext + MOVQ 32(SP), SI // saved infostyle + SYSCALL + INT $3 // not reached + +TEXT sys·mmap(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 addr + MOVL 16(SP), SI // arg 2 len + MOVL 20(SP), DX // arg 3 prot + MOVL 24(SP), R10 // arg 4 flags + MOVL 28(SP), R8 // arg 5 fid + MOVL 32(SP), R9 // arg 6 offset + MOVL $(0x2000000+197), AX // syscall entry + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +TEXT notok(SB),7,$-8 + MOVL $0xf1, BP + MOVQ BP, (BP) + RET + +TEXT sys·memclr(SB),7,$-8 + MOVQ 8(SP), DI // arg 1 addr + MOVL 16(SP), CX // arg 2 count + ADDL $7, CX + SHRL $3, CX + MOVQ $0, AX + CLD + REP + STOSQ + RET + +TEXT sys·getcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ -8(AX),AX // get calling pc + RET + +TEXT sys·setcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ x+8(FP), BX + MOVQ BX, -8(AX) // set calling pc + RET + +TEXT sigaltstack(SB),7,$-8 + MOVQ new+8(SP), DI + MOVQ old+16(SP), SI + MOVQ $(0x2000000+53), AX + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void)) +TEXT bsdthread_create(SB),7,$-8 + // Set up arguments to bsdthread_create system call. + // The ones in quotes pass through to the thread callback + // uninterpreted, so we can put whatever we want there. + MOVQ fn+32(SP), DI // "func" + MOVQ m+16(SP), SI // "arg" + MOVQ stk+8(SP), DX // stack + MOVQ g+24(SP), R10 // "pthread" +// TODO(rsc): why do we get away with 0 flags here but not on 386? + MOVQ $0, R8 // flags + MOVQ $(0x2000000+360), AX // bsdthread_create + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +// The thread that bsdthread_create creates starts executing here, +// because we registered this function using bsdthread_register +// at startup. +// DI = "pthread" (= g) +// SI = mach thread port +// DX = "func" (= fn) +// CX = "arg" (= m) +// R8 = stack +// R9 = flags (= 0) +// SP = stack - C_64_REDZONE_LEN (= stack - 128) +TEXT bsdthread_start(SB),7,$-8 + MOVQ CX, R14 // m + MOVQ DI, R15 // g + MOVQ SI, 24(R14) // thread port is m->procid + CALL DX // fn + CALL exit1(SB) + RET + +// void bsdthread_register(void) +// registers callbacks for threadstart (see bsdthread_create above +// and wqthread and pthsize (not used). returns 0 on success. +TEXT bsdthread_register(SB),7,$-8 + MOVQ $bsdthread_start(SB), DI // threadstart + MOVQ $0, SI // wqthread, not used by us + MOVQ $0, DX // pthsize, not used by us + MOVQ $(0x2000000+366), AX // bsdthread_register + SYSCALL + JCC 2(PC) + CALL notok(SB) + RET + +// Mach system calls use 0x1000000 instead of the BSD's 0x2000000. + +// uint32 mach_msg_trap(void*, uint32, uint32, uint32, uint32, uint32, uint32) +TEXT mach_msg_trap(SB),7,$0 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVL 24(SP), R10 + MOVL 28(SP), R8 + MOVL 32(SP), R9 + MOVL 36(SP), R11 + PUSHQ R11 // seventh arg, on stack + MOVL $(0x1000000+31), AX // mach_msg_trap + SYSCALL + POPQ R11 + RET + +TEXT mach_task_self(SB),7,$0 + MOVL $(0x1000000+28), AX // task_self_trap + SYSCALL + RET + +TEXT mach_thread_self(SB),7,$0 + MOVL $(0x1000000+27), AX // thread_self_trap + SYSCALL + RET + +TEXT mach_reply_port(SB),7,$0 + MOVL $(0x1000000+26), AX // mach_reply_port + SYSCALL + RET + +// Mach provides trap versions of the semaphore ops, +// instead of requiring the use of RPC. + +// uint32 mach_semaphore_wait(uint32) +TEXT mach_semaphore_wait(SB),7,$0 + MOVL 8(SP), DI + MOVL $(0x1000000+36), AX // semaphore_wait_trap + SYSCALL + RET + +// uint32 mach_semaphore_timedwait(uint32, uint32, uint32) +TEXT mach_semaphore_timedwait(SB),7,$0 + MOVL 8(SP), DI + MOVL 12(SP), SI + MOVL 16(SP), DX + MOVL $(0x1000000+38), AX // semaphore_timedwait_trap + SYSCALL + RET + +// uint32 mach_semaphore_signal(uint32) +TEXT mach_semaphore_signal(SB),7,$0 + MOVL 8(SP), DI + MOVL $(0x1000000+33), AX // semaphore_signal_trap + SYSCALL + RET + +// uint32 mach_semaphore_signal_all(uint32) +TEXT mach_semaphore_signal_all(SB),7,$0 + MOVL 8(SP), DI + MOVL $(0x1000000+34), AX // semaphore_signal_all_trap + SYSCALL + RET + diff --git a/src/pkg/runtime/darwin/defs.c b/src/pkg/runtime/darwin/defs.c new file mode 100644 index 000000000..1ed662957 --- /dev/null +++ b/src/pkg/runtime/darwin/defs.c @@ -0,0 +1,104 @@ +// Copyright 2009 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. + +/* + * Input to godefs. + * + godefs -f -m64 defs.c >amd64/defs.h + godefs defs.c >386/defs.h + */ + +#define __DARWIN_UNIX03 0 + +#include <mach/mach.h> +#include <mach/message.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> +#include <sys/mman.h> + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANON, + $MAP_PRIVATE = MAP_PRIVATE, + + $MACH_MSG_TYPE_MOVE_RECEIVE = MACH_MSG_TYPE_MOVE_RECEIVE, + $MACH_MSG_TYPE_MOVE_SEND = MACH_MSG_TYPE_MOVE_SEND, + $MACH_MSG_TYPE_MOVE_SEND_ONCE = MACH_MSG_TYPE_MOVE_SEND_ONCE, + $MACH_MSG_TYPE_COPY_SEND = MACH_MSG_TYPE_COPY_SEND, + $MACH_MSG_TYPE_MAKE_SEND = MACH_MSG_TYPE_MAKE_SEND, + $MACH_MSG_TYPE_MAKE_SEND_ONCE = MACH_MSG_TYPE_MAKE_SEND_ONCE, + $MACH_MSG_TYPE_COPY_RECEIVE = MACH_MSG_TYPE_COPY_RECEIVE, + + $MACH_MSG_PORT_DESCRIPTOR = MACH_MSG_PORT_DESCRIPTOR, + $MACH_MSG_OOL_DESCRIPTOR = MACH_MSG_OOL_DESCRIPTOR, + $MACH_MSG_OOL_PORTS_DESCRIPTOR = MACH_MSG_OOL_PORTS_DESCRIPTOR, + $MACH_MSG_OOL_VOLATILE_DESCRIPTOR = MACH_MSG_OOL_VOLATILE_DESCRIPTOR, + + $MACH_MSGH_BITS_COMPLEX = MACH_MSGH_BITS_COMPLEX, + + $MACH_SEND_MSG = MACH_SEND_MSG, + $MACH_RCV_MSG = MACH_RCV_MSG, + $MACH_RCV_LARGE = MACH_RCV_LARGE, + + $MACH_SEND_TIMEOUT = MACH_SEND_TIMEOUT, + $MACH_SEND_INTERRUPT = MACH_SEND_INTERRUPT, + $MACH_SEND_CANCEL = MACH_SEND_CANCEL, + $MACH_SEND_ALWAYS = MACH_SEND_ALWAYS, + $MACH_SEND_TRAILER = MACH_SEND_TRAILER, + $MACH_RCV_TIMEOUT = MACH_RCV_TIMEOUT, + $MACH_RCV_NOTIFY = MACH_RCV_NOTIFY, + $MACH_RCV_INTERRUPT = MACH_RCV_INTERRUPT, + $MACH_RCV_OVERWRITE = MACH_RCV_OVERWRITE, + + $NDR_PROTOCOL_2_0 = NDR_PROTOCOL_2_0, + $NDR_INT_BIG_ENDIAN = NDR_INT_BIG_ENDIAN, + $NDR_INT_LITTLE_ENDIAN = NDR_INT_LITTLE_ENDIAN, + $NDR_FLOAT_IEEE = NDR_FLOAT_IEEE, + $NDR_CHAR_ASCII = NDR_CHAR_ASCII, + + $SA_SIGINFO = SA_SIGINFO, + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_USERTRAMP = SA_USERTRAMP, + $SA_64REGSET = SA_64REGSET, +}; + +typedef mach_msg_body_t $MachBody; +typedef mach_msg_header_t $MachHeader; +typedef NDR_record_t $MachNDR; +typedef mach_msg_port_descriptor_t $MachPort; + +typedef stack_t $StackT; +typedef union __sigaction_u $Sighandler; + +typedef struct __sigaction $Sigaction; // used in syscalls +// typedef struct sigaction $Sigaction; // used by the C library +typedef union sigval $Sigval; +typedef siginfo_t $Siginfo; + +typedef struct fp_control $FPControl; +typedef struct fp_status $FPStatus; +typedef struct mmst_reg $RegMMST; +typedef struct xmm_reg $RegXMM; + +#ifdef __LP64__ +// amd64 +typedef x86_thread_state64_t $Regs; +typedef x86_float_state64_t $FloatState; +typedef x86_exception_state64_t $ExceptionState; +typedef struct mcontext64 $Mcontext; +#else +// 386 +typedef x86_thread_state32_t $Regs; +typedef x86_float_state32_t $FloatState; +typedef x86_exception_state32_t $ExceptionState; +typedef struct mcontext32 $Mcontext; +#endif + +typedef ucontext_t $Ucontext; diff --git a/src/pkg/runtime/darwin/os.h b/src/pkg/runtime/darwin/os.h new file mode 100644 index 000000000..2a3ca87bd --- /dev/null +++ b/src/pkg/runtime/darwin/os.h @@ -0,0 +1,24 @@ +// Copyright 2009 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. + +void bsdthread_create(void*, M*, G*, void(*)(void)); +void bsdthread_register(void); +int32 mach_msg_trap(MachHeader*, int32, uint32, uint32, uint32, uint32, uint32); +uint32 mach_reply_port(void); +void mach_semacquire(uint32); +uint32 mach_semcreate(void); +void mach_semdestroy(uint32); +void mach_semrelease(uint32); +void mach_semreset(uint32); +uint32 mach_task_self(void); +uint32 mach_task_self(void); +uint32 mach_thread_self(void); +uint32 mach_thread_self(void); + +struct Sigaction; +void sigaction(int64, struct Sigaction*, struct Sigaction*); + +struct StackT; +void sigaltstack(struct StackT*, struct StackT*); +void sigtramp(void); diff --git a/src/pkg/runtime/darwin/signals.h b/src/pkg/runtime/darwin/signals.h new file mode 100644 index 000000000..8cca361f7 --- /dev/null +++ b/src/pkg/runtime/darwin/signals.h @@ -0,0 +1,47 @@ +// Copyright 2009 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. + +#define C SigCatch +#define I SigIgnore +#define R SigRestart + +static SigTab sigtab[] = { + /* 0 */ 0, "SIGNONE: no trap", + /* 1 */ 0, "SIGHUP: terminal line hangup", + /* 2 */ 0, "SIGINT: interrupt", + /* 3 */ C, "SIGQUIT: quit", + /* 4 */ C, "SIGILL: illegal instruction", + /* 5 */ C, "SIGTRAP: trace trap", /* used by panic and array out of bounds, etc. */ + /* 6 */ C, "SIGABRT: abort", + /* 7 */ C, "SIGEMT: emulate instruction executed", + /* 8 */ C, "SIGFPE: floating-point exception", + /* 9 */ 0, "SIGKILL: kill", + /* 10 */ C, "SIGBUS: bus error", + /* 11 */ C, "SIGSEGV: segmentation violation", + /* 12 */ C, "SIGSYS: bad system call", + /* 13 */ I, "SIGPIPE: write to broken pipe", + /* 14 */ 0, "SIGALRM: alarm clock", + /* 15 */ 0, "SIGTERM: termination", + /* 16 */ 0, "SIGURG: urgent condition on socket", + /* 17 */ 0, "SIGSTOP: stop", + /* 18 */ 0, "SIGTSTP: keyboard stop", + /* 19 */ 0, "SIGCONT: continue after stop", + /* 20 */ I+R, "SIGCHLD: child status has changed", + /* 21 */ 0, "SIGTTIN: background read from tty", + /* 22 */ 0, "SIGTTOU: background write to tty", + /* 23 */ 0, "SIGIO: i/o now possible", + /* 24 */ 0, "SIGXCPU: cpu limit exceeded", + /* 25 */ 0, "SIGXFSZ: file size limit exceeded", + /* 26 */ 0, "SIGVTALRM: virtual alarm clock", + /* 27 */ 0, "SIGPROF: profiling alarm clock", + /* 28 */ I+R, "SIGWINCH: window size change", + /* 29 */ 0, "SIGINFO: status request from keyboard", + /* 30 */ 0, "SIGUSR1: user-defined signal 1", + /* 31 */ 0, "SIGUSR2: user-defined signal 2", +}; +#undef C +#undef I +#undef R + +#define NSIG 32 diff --git a/src/pkg/runtime/darwin/thread.c b/src/pkg/runtime/darwin/thread.c new file mode 100644 index 000000000..3a982471a --- /dev/null +++ b/src/pkg/runtime/darwin/thread.c @@ -0,0 +1,441 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs.h" +#include "os.h" + +static void +unimplemented(int8 *name) +{ + prints(name); + prints(" not implemented\n"); + *(int32*)1231 = 1231; +} + +// Thread-safe allocation of a semaphore. +// Psema points at a kernel semaphore key. +// It starts out zero, meaning no semaphore. +// Fill it in, being careful of others calling initsema +// simultaneously. +static void +initsema(uint32 *psema) +{ + uint32 sema; + + if(*psema != 0) // already have one + return; + + sema = mach_semcreate(); + if(!cas(psema, 0, sema)){ + // Someone else filled it in. Use theirs. + mach_semdestroy(sema); + return; + } +} + + +// Atomic add and return new value. +static uint32 +xadd(uint32 volatile *val, int32 delta) +{ + uint32 oval, nval; + + for(;;){ + oval = *val; + nval = oval + delta; + if(cas(val, oval, nval)) + return nval; + } +} + + +// Blocking locks. + +// Implement Locks, using semaphores. +// l->key is the number of threads who want the lock. +// In a race, one thread increments l->key from 0 to 1 +// and the others increment it from >0 to >1. The thread +// who does the 0->1 increment gets the lock, and the +// others wait on the semaphore. When the 0->1 thread +// releases the lock by decrementing l->key, l->key will +// be >0, so it will increment the semaphore to wake up +// one of the others. This is the same algorithm used +// in Plan 9's user-level locks. +// +// Note that semaphores are never destroyed (the kernel +// will clean up when the process exits). We assume for now +// that Locks are only used for long-lived structures like M and G. + +void +lock(Lock *l) +{ + if(m->locks < 0) + throw("lock count"); + m->locks++; + + // Allocate semaphore if needed. + if(l->sema == 0) + initsema(&l->sema); + + if(xadd(&l->key, 1) > 1) // someone else has it; wait + mach_semacquire(l->sema); +} + +void +unlock(Lock *l) +{ + m->locks--; + if(m->locks < 0) + throw("lock count"); + + if(xadd(&l->key, -1) > 0) // someone else is waiting + mach_semrelease(l->sema); +} + + +// User-level semaphore implementation: +// try to do the operations in user space on u, +// but when it's time to block, fall back on the kernel semaphore k. +// This is the same algorithm used in Plan 9. +void +usemacquire(Usema *s) +{ + if((int32)xadd(&s->u, -1) < 0) + mach_semacquire(s->k); +} + +void +usemrelease(Usema *s) +{ + if((int32)xadd(&s->u, 1) <= 0) + mach_semrelease(s->k); +} + + +// Event notifications. +void +noteclear(Note *n) +{ + n->wakeup = 0; +} + +void +notesleep(Note *n) +{ + if(n->sema.k == 0) + initsema(&n->sema.k); + while(!n->wakeup) + usemacquire(&n->sema); +} + +void +notewakeup(Note *n) +{ + if(n->sema.k == 0) + initsema(&n->sema.k); + n->wakeup = 1; + usemrelease(&n->sema); +} + + +// BSD interface for threading. +void +osinit(void) +{ + // Register our thread-creation callback (see {amd64,386}/sys.s). + bsdthread_register(); +} + +void +newosproc(M *m, G *g, void *stk, void (*fn)(void)) +{ + // printf("newosproc m=%p g=%p stk=%p fn=%p\n", m, g, stk, fn); + m->tls[0] = m->id; // so 386 asm can find it + bsdthread_create(stk, m, g, fn); +} + +// Called to initialize a new m (including the bootstrap m). +void +minit(void) +{ + // Initialize signal handling. + m->gsignal = malg(32*1024); // OS X wants >=8K, Linux >=2K + signalstack(m->gsignal->stackguard, 32*1024); +} + +// Mach IPC, to get at semaphores +// Definitions are in /usr/include/mach on a Mac. + +static void +macherror(int32 r, int8 *fn) +{ + printf("mach error %s: %d\n", fn, r); + throw("mach error"); +} + +enum +{ + DebugMach = 0 +}; + +static MachNDR zerondr; + +#define MACH_MSGH_BITS(a, b) ((a) | ((b)<<8)) + +static int32 +mach_msg(MachHeader *h, + int32 op, + uint32 send_size, + uint32 rcv_size, + uint32 rcv_name, + uint32 timeout, + uint32 notify) +{ + // TODO: Loop on interrupt. + return mach_msg_trap(h, op, send_size, rcv_size, rcv_name, timeout, notify); +} + +// Mach RPC (MIG) + +enum +{ + MinMachMsg = 48, + Reply = 100, +}; + +#pragma pack on +typedef struct CodeMsg CodeMsg; +struct CodeMsg +{ + MachHeader h; + MachNDR NDR; + int32 code; +}; +#pragma pack off + +static int32 +machcall(MachHeader *h, int32 maxsize, int32 rxsize) +{ + uint32 *p; + int32 i, ret, id; + uint32 port; + CodeMsg *c; + + if((port = m->machport) == 0){ + port = mach_reply_port(); + m->machport = port; + } + + h->msgh_bits |= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + h->msgh_local_port = port; + h->msgh_reserved = 0; + id = h->msgh_id; + + if(DebugMach){ + p = (uint32*)h; + prints("send:\t"); + for(i=0; i<h->msgh_size/sizeof(p[0]); i++){ + prints(" "); + sys·printpointer((void*)p[i]); + if(i%8 == 7) + prints("\n\t"); + } + if(i%8) + prints("\n"); + } + + ret = mach_msg(h, MACH_SEND_MSG|MACH_RCV_MSG, + h->msgh_size, maxsize, port, 0, 0); + if(ret != 0){ + if(DebugMach){ + prints("mach_msg error "); + sys·printint(ret); + prints("\n"); + } + return ret; + } + + if(DebugMach){ + p = (uint32*)h; + prints("recv:\t"); + for(i=0; i<h->msgh_size/sizeof(p[0]); i++){ + prints(" "); + sys·printpointer((void*)p[i]); + if(i%8 == 7) + prints("\n\t"); + } + if(i%8) + prints("\n"); + } + + if(h->msgh_id != id+Reply){ + if(DebugMach){ + prints("mach_msg reply id mismatch "); + sys·printint(h->msgh_id); + prints(" != "); + sys·printint(id+Reply); + prints("\n"); + } + return -303; // MIG_REPLY_MISMATCH + } + + // Look for a response giving the return value. + // Any call can send this back with an error, + // and some calls only have return values so they + // send it back on success too. I don't quite see how + // you know it's one of these and not the full response + // format, so just look if the message is right. + c = (CodeMsg*)h; + if(h->msgh_size == sizeof(CodeMsg) + && !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)){ + if(DebugMach){ + prints("mig result "); + sys·printint(c->code); + prints("\n"); + } + return c->code; + } + + if(h->msgh_size != rxsize){ + if(DebugMach){ + prints("mach_msg reply size mismatch "); + sys·printint(h->msgh_size); + prints(" != "); + sys·printint(rxsize); + prints("\n"); + } + return -307; // MIG_ARRAY_TOO_LARGE + } + + return 0; +} + + +// Semaphores! + +enum +{ + Tmach_semcreate = 3418, + Rmach_semcreate = Tmach_semcreate + Reply, + + Tmach_semdestroy = 3419, + Rmach_semdestroy = Tmach_semdestroy + Reply, + + // Mach calls that get interrupted by Unix signals + // return this error code. We retry them. + KERN_ABORTED = 14, +}; + +typedef struct Tmach_semcreateMsg Tmach_semcreateMsg; +typedef struct Rmach_semcreateMsg Rmach_semcreateMsg; +typedef struct Tmach_semdestroyMsg Tmach_semdestroyMsg; +// Rmach_semdestroyMsg = CodeMsg + +#pragma pack on +struct Tmach_semcreateMsg +{ + MachHeader h; + MachNDR ndr; + int32 policy; + int32 value; +}; + +struct Rmach_semcreateMsg +{ + MachHeader h; + MachBody body; + MachPort semaphore; +}; + +struct Tmach_semdestroyMsg +{ + MachHeader h; + MachBody body; + MachPort semaphore; +}; +#pragma pack off + +uint32 +mach_semcreate(void) +{ + union { + Tmach_semcreateMsg tx; + Rmach_semcreateMsg rx; + uint8 pad[MinMachMsg]; + } m; + int32 r; + + m.tx.h.msgh_bits = 0; + m.tx.h.msgh_size = sizeof(m.tx); + m.tx.h.msgh_remote_port = mach_task_self(); + m.tx.h.msgh_id = Tmach_semcreate; + m.tx.ndr = zerondr; + + m.tx.policy = 0; // 0 = SYNC_POLICY_FIFO + m.tx.value = 0; + + while((r = machcall(&m.tx.h, sizeof m, sizeof(m.rx))) != 0){ + if(r == KERN_ABORTED) // interrupted + continue; + macherror(r, "semaphore_create"); + } + if(m.rx.body.msgh_descriptor_count != 1) + unimplemented("mach_semcreate desc count"); + return m.rx.semaphore.name; +} + +void +mach_semdestroy(uint32 sem) +{ + union { + Tmach_semdestroyMsg tx; + uint8 pad[MinMachMsg]; + } m; + int32 r; + + m.tx.h.msgh_bits = MACH_MSGH_BITS_COMPLEX; + m.tx.h.msgh_size = sizeof(m.tx); + m.tx.h.msgh_remote_port = mach_task_self(); + m.tx.h.msgh_id = Tmach_semdestroy; + m.tx.body.msgh_descriptor_count = 1; + m.tx.semaphore.name = sem; + m.tx.semaphore.disposition = MACH_MSG_TYPE_MOVE_SEND; + m.tx.semaphore.type = 0; + + while((r = machcall(&m.tx.h, sizeof m, 0)) != 0){ + macherror(r, "semaphore_destroy"); + } +} + +// The other calls have simple system call traps in sys.s +int32 mach_semaphore_wait(uint32 sema); +int32 mach_semaphore_timedwait(uint32 sema, uint32 sec, uint32 nsec); +int32 mach_semaphore_signal(uint32 sema); +int32 mach_semaphore_signal_all(uint32 sema); + +void +mach_semacquire(uint32 sem) +{ + int32 r; + + while((r = mach_semaphore_wait(sem)) != 0) { + if(r == KERN_ABORTED) // interrupted + continue; + macherror(r, "semaphore_wait"); + } +} + +void +mach_semrelease(uint32 sem) +{ + int32 r; + + while((r = mach_semaphore_signal(sem)) != 0) { + if(r == KERN_ABORTED) // interrupted + continue; + macherror(r, "semaphore_signal"); + } +} + diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go new file mode 100644 index 000000000..6fb5756d6 --- /dev/null +++ b/src/pkg/runtime/extern.go @@ -0,0 +1,28 @@ +// Copyright 2009 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. + +/* + The runtime package contains operations that interact with Go's runtime system, + such as functions to control goroutines. + */ +package runtime + +// These functions are implemented in the base runtime library, ../../runtime/. + +// Gosched yields the processor, allowing other goroutines to run. It does not +// suspend the current goroutine, so execution resumes automatically. +func Gosched() + +// Goexit terminates the goroutine that calls it. No other goroutine is affected. +func Goexit() + +// Breakpoint() executes a breakpoint trap. +func Breakpoint() + +// Caller reports file and line number information about function invocations on +// the calling goroutine's stack. The argument is the number of stack frames to +// ascend, with 1 identifying the the caller of Caller. The return values report the +// program counter, file name, and line number within the file of the corresponding +// call. The boolean ok is false if it was not possible to recover the information. +func Caller(n int) (pc uintptr, file string, line int, ok bool) diff --git a/src/pkg/runtime/float.c b/src/pkg/runtime/float.c new file mode 100644 index 000000000..5122f359a --- /dev/null +++ b/src/pkg/runtime/float.c @@ -0,0 +1,173 @@ +// Copyright 2009 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. + +#include "runtime.h" + +static uint64 uvnan = 0x7FF0000000000001ULL; +static uint64 uvinf = 0x7FF0000000000000ULL; +static uint64 uvneginf = 0xFFF0000000000000ULL; + +uint32 +float32tobits(float32 f) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float32 f; + uint32 i; + } u; + u.f = f; + return u.i; +} + +uint64 +float64tobits(float64 f) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float64 f; + uint64 i; + } u; + u.f = f; + return u.i; +} + +float64 +float64frombits(uint64 i) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float64 f; + uint64 i; + } u; + u.i = i; + return u.f; +} + +float32 +float32frombits(uint32 i) +{ + // The obvious cast-and-pointer code is technically + // not valid, and gcc miscompiles it. Use a union instead. + union { + float32 f; + uint32 i; + } u; + u.i = i; + return u.f; +} + +bool +isInf(float64 f, int32 sign) +{ + uint64 x; + + x = float64tobits(f); + if(sign == 0) + return x == uvinf || x == uvneginf; + if(sign > 0) + return x == uvinf; + return x == uvneginf; +} + +float64 +NaN(void) +{ + return float64frombits(uvnan); +} + +bool +isNaN(float64 f) +{ + uint64 x; + + x = float64tobits(f); + return ((uint32)(x>>52) & 0x7FF) == 0x7FF && !isInf(f, 0); +} + +float64 +Inf(int32 sign) +{ + if(sign >= 0) + return float64frombits(uvinf); + else + return float64frombits(uvneginf); +} + +enum +{ + MASK = 0x7ffL, + SHIFT = 64-11-1, + BIAS = 1022L, +}; + +float64 +frexp(float64 d, int32 *ep) +{ + uint64 x; + + if(d == 0) { + *ep = 0; + return 0; + } + x = float64tobits(d); + *ep = (int32)((x >> SHIFT) & MASK) - BIAS; + x &= ~((uint64)MASK << SHIFT); + x |= (uint64)BIAS << SHIFT; + return float64frombits(x); +} + +float64 +ldexp(float64 d, int32 e) +{ + uint64 x; + + if(d == 0) + return 0; + x = float64tobits(d); + e += (int32)(x >> SHIFT) & MASK; + if(e <= 0) + return 0; /* underflow */ + if(e >= MASK){ /* overflow */ + if(d < 0) + return Inf(-1); + return Inf(1); + } + x &= ~((uint64)MASK << SHIFT); + x |= (uint64)e << SHIFT; + return float64frombits(x); +} + +float64 +modf(float64 d, float64 *ip) +{ + float64 dd; + uint64 x; + int32 e; + + if(d < 1) { + if(d < 0) { + d = modf(-d, ip); + *ip = -*ip; + return -d; + } + *ip = 0; + return d; + } + + x = float64tobits(d); + e = (int32)((x >> SHIFT) & MASK) - BIAS; + + /* + * Keep the top 11+e bits; clear the rest. + */ + if(e <= 64-11) + x &= ~(((uint64)1 << (64LL-11LL-e))-1); + dd = float64frombits(x); + *ip = dd; + return d - dd; +} + diff --git a/src/pkg/runtime/float_go.cgo b/src/pkg/runtime/float_go.cgo new file mode 100644 index 000000000..518d55950 --- /dev/null +++ b/src/pkg/runtime/float_go.cgo @@ -0,0 +1,52 @@ +// Copyright 2009 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 math + +#include "runtime.h" + +func Frexp(f float64) (frac float64, exp int32) { + frac = frexp(f, &exp); +} + +func Ldexp(frac float64, exp int32) (f float64) { + f = ldexp(frac, exp); +} + +func Modf(f float64) (integer float64, frac float64) { + frac = modf(f, &integer); +} + +func IsInf(f float64, sign int32) (is bool) { + is = isInf(f, sign); +} + +func IsNaN(f float64) (is bool) { + is = isNaN(f); +} + +func Inf(sign int32) (f float64) { + f = Inf(sign); +} + +func NaN() (f float64) { + f = NaN(); +} + +func Float32bits(f float32) (b uint32) { + b = float32tobits(f); +} + +func Float64bits(f float64) (b uint64) { + b = float64tobits(f); +} + +func Float32frombits(b uint32) (f float32) { + f = float32frombits(b); +} + +func Float64frombits(b uint64) (f float64) { + f = float64frombits(b); +} + diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c new file mode 100644 index 000000000..b3022ca14 --- /dev/null +++ b/src/pkg/runtime/hashmap.c @@ -0,0 +1,954 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "hashmap.h" + +/* Return a pointer to the struct/union of type "type" + whose "field" field is addressed by pointer "p". */ + + +struct hash { /* a hash table; initialize with hash_init() */ + uint32 count; /* elements in table - must be first */ + + uint8 datasize; /* amount of data to store in entry */ + uint8 max_power; /* max power of 2 to create sub-tables */ + uint8 max_probes; /* max entries to probe before rehashing */ + int32 changes; /* inc'ed whenever a subtable is created/grown */ + hash_hash_t (*data_hash) (uint32, void *a); /* return hash of *a */ + uint32 (*data_eq) (uint32, void *a, void *b); /* return whether *a == *b */ + void (*data_del) (uint32, void *arg, void *data); /* invoked on deletion */ + struct hash_subtable *st; /* first-level table */ + + uint32 keysize; + uint32 valsize; + uint32 datavo; + uint32 ko; + uint32 vo; + uint32 po; + Alg* keyalg; + Alg* valalg; +}; + +struct hash_entry { + hash_hash_t hash; /* hash value of data */ + byte data[1]; /* user data has "datasize" bytes */ +}; + +struct hash_subtable { + uint8 power; /* bits used to index this table */ + uint8 used; /* bits in hash used before reaching this table */ + uint8 datasize; /* bytes of client data in an entry */ + uint8 max_probes; /* max number of probes when searching */ + int16 limit_bytes; /* max_probes * (datasize+sizeof (hash_hash_t)) */ + struct hash_entry *end; /* points just past end of entry[] */ + struct hash_entry entry[1]; /* 2**power+max_probes-1 elements of elemsize bytes */ +}; + +#define HASH_DATA_EQ(h,x,y) ((*h->data_eq) (h->keysize, (x), (y))) + +#define HASH_REHASH 0x2 /* an internal flag */ +/* the number of bits used is stored in the flags word too */ +#define HASH_USED(x) ((x) >> 2) +#define HASH_MAKE_USED(x) ((x) << 2) + +#define HASH_LOW 6 +#define HASH_ONE (((hash_hash_t)1) << HASH_LOW) +#define HASH_MASK (HASH_ONE - 1) +#define HASH_ADJUST(x) (((x) < HASH_ONE) << HASH_LOW) + +#define HASH_BITS (sizeof (hash_hash_t) * 8) + +#define HASH_SUBHASH HASH_MASK +#define HASH_NIL 0 +#define HASH_NIL_MEMSET 0 + +#define HASH_OFFSET(base, byte_offset) \ + ((struct hash_entry *) (((byte *) (base)) + (byte_offset))) + + +/* return a hash layer with 2**power empty entries */ +static struct hash_subtable * +hash_subtable_new (struct hash *h, int32 power, int32 used) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + int32 bytes = elemsize << power; + struct hash_subtable *st; + int32 limit_bytes = h->max_probes * elemsize; + int32 max_probes = h->max_probes; + + if (bytes < limit_bytes) { + limit_bytes = bytes; + max_probes = 1 << power; + } + bytes += limit_bytes - elemsize; + st = malloc (offsetof (struct hash_subtable, entry[0]) + bytes); + st->power = power; + st->used = used; + st->datasize = h->datasize; + st->max_probes = max_probes; + st->limit_bytes = limit_bytes; + st->end = HASH_OFFSET (st->entry, bytes); + memset (st->entry, HASH_NIL_MEMSET, bytes); + return (st); +} + +static void +init_sizes (int64 hint, int32 *init_power, int32 *max_power) +{ + int32 log = 0; + int32 i; + + for (i = 32; i != 0; i >>= 1) { + if ((hint >> (log + i)) != 0) { + log += i; + } + } + log += 1 + (((hint << 3) >> log) >= 11); /* round up for utilization */ + if (log <= 14) { + *init_power = log; + } else { + *init_power = 12; + } + *max_power = 12; +} + +static void +hash_init (struct hash *h, + int32 datasize, + hash_hash_t (*data_hash) (uint32, void *), + uint32 (*data_eq) (uint32, void *, void *), + void (*data_del) (uint32, void *, void *), + int64 hint) +{ + int32 init_power; + int32 max_power; + + if(datasize < sizeof (void *)) + datasize = sizeof (void *); + datasize = rnd(datasize, sizeof (void *)); + init_sizes (hint, &init_power, &max_power); + h->datasize = datasize; + h->max_power = max_power; + h->max_probes = 15; + assert (h->datasize == datasize); + assert (h->max_power == max_power); + assert (sizeof (void *) <= h->datasize || h->max_power == 255); + h->count = 0; + h->changes = 0; + h->data_hash = data_hash; + h->data_eq = data_eq; + h->data_del = data_del; + h->st = hash_subtable_new (h, init_power, 0); +} + +static void +hash_remove_n (struct hash_subtable *st, struct hash_entry *dst_e, int32 n) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *src_e = HASH_OFFSET (dst_e, n * elemsize); + struct hash_entry *end_e = st->end; + int32 shift = HASH_BITS - (st->power + st->used); + int32 index_mask = (((hash_hash_t)1) << st->power) - 1; + int32 dst_i = (((byte *) dst_e) - ((byte *) st->entry)) / elemsize; + int32 src_i = dst_i + n; + hash_hash_t hash; + int32 skip; + int32 bytes; + + while (dst_e != src_e) { + if (src_e != end_e) { + struct hash_entry *cp_e = src_e; + int32 save_dst_i = dst_i; + while (cp_e != end_e && (hash = cp_e->hash) != HASH_NIL && + ((hash >> shift) & index_mask) <= dst_i) { + cp_e = HASH_OFFSET (cp_e, elemsize); + dst_i++; + } + bytes = ((byte *) cp_e) - (byte *) src_e; + memmove (dst_e, src_e, bytes); + dst_e = HASH_OFFSET (dst_e, bytes); + src_e = cp_e; + src_i += dst_i - save_dst_i; + if (src_e != end_e && (hash = src_e->hash) != HASH_NIL) { + skip = ((hash >> shift) & index_mask) - dst_i; + } else { + skip = src_i - dst_i; + } + } else { + skip = src_i - dst_i; + } + bytes = skip * elemsize; + memset (dst_e, HASH_NIL_MEMSET, bytes); + dst_e = HASH_OFFSET (dst_e, bytes); + dst_i += skip; + } +} + +static int32 +hash_insert_internal (struct hash_subtable **pst, int32 flags, hash_hash_t hash, + struct hash *h, void *data, void **pres); + +static void +hash_conv (struct hash *h, + struct hash_subtable *st, int32 flags, + hash_hash_t hash, + struct hash_entry *e) +{ + int32 new_flags = (flags + HASH_MAKE_USED (st->power)) | HASH_REHASH; + int32 shift = HASH_BITS - HASH_USED (new_flags); + hash_hash_t prefix_mask = (-(hash_hash_t)1) << shift; + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + void *dummy_result; + struct hash_entry *de; + int32 index_mask = (1 << st->power) - 1; + hash_hash_t e_hash; + struct hash_entry *pe = HASH_OFFSET (e, -elemsize); + + while (e != st->entry && (e_hash = pe->hash) != HASH_NIL && (e_hash & HASH_MASK) != HASH_SUBHASH) { + e = pe; + pe = HASH_OFFSET (pe, -elemsize); + } + + de = e; + while (e != st->end && + (e_hash = e->hash) != HASH_NIL && + (e_hash & HASH_MASK) != HASH_SUBHASH) { + struct hash_entry *target_e = HASH_OFFSET (st->entry, ((e_hash >> shift) & index_mask) * elemsize); + struct hash_entry *ne = HASH_OFFSET (e, elemsize); + hash_hash_t current = e_hash & prefix_mask; + if (de < target_e) { + memset (de, HASH_NIL_MEMSET, ((byte *) target_e) - (byte *) de); + de = target_e; + } + if ((hash & prefix_mask) == current || + (ne != st->end && (e_hash = ne->hash) != HASH_NIL && + (e_hash & prefix_mask) == current)) { + struct hash_subtable *new_st = hash_subtable_new (h, 1, HASH_USED (new_flags)); + int32 rc = hash_insert_internal (&new_st, new_flags, e->hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + e = ne; + while (e != st->end && (e_hash = e->hash) != HASH_NIL && (e_hash & prefix_mask) == current) { + assert ((e_hash & HASH_MASK) != HASH_SUBHASH); + rc = hash_insert_internal (&new_st, new_flags, e_hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + e = HASH_OFFSET (e, elemsize); + } + memset (de->data, HASH_NIL_MEMSET, h->datasize); + *(struct hash_subtable **)de->data = new_st; + de->hash = current | HASH_SUBHASH; + } else { + if (e != de) { + memcpy (de, e, elemsize); + } + e = HASH_OFFSET (e, elemsize); + } + de = HASH_OFFSET (de, elemsize); + } + if (e != de) { + hash_remove_n (st, de, (((byte *) e) - (byte *) de) / elemsize); + } +} + +static void +hash_grow (struct hash *h, struct hash_subtable **pst, int32 flags) +{ + struct hash_subtable *old_st = *pst; + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + *pst = hash_subtable_new (h, old_st->power + 1, HASH_USED (flags)); + struct hash_entry *end_e = old_st->end; + struct hash_entry *e; + void *dummy_result; + int32 used = 0; + + flags |= HASH_REHASH; + for (e = old_st->entry; e != end_e; e = HASH_OFFSET (e, elemsize)) { + hash_hash_t hash = e->hash; + if (hash != HASH_NIL) { + int32 rc = hash_insert_internal (pst, flags, e->hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + used++; + } + } + free (old_st); +} + +int32 +hash_lookup (struct hash *h, void *data, void **pres) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + hash_hash_t hash = (*h->data_hash) (h->keysize, data) & ~HASH_MASK; + struct hash_subtable *st = h->st; + int32 used = 0; + hash_hash_t e_hash; + struct hash_entry *e; + struct hash_entry *end_e; + + hash += HASH_ADJUST (hash); + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + + e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ + e_hash = e->hash; + if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ + break; + } + used += st->power; + st = *(struct hash_subtable **)e->data; + } + end_e = HASH_OFFSET (e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + } + while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, e->data)) { /* a match */ + *pres = e->data; + return (1); + } + e = HASH_OFFSET (e, elemsize); + } + USED(e_hash); + *pres = 0; + return (0); +} + +int32 +hash_remove (struct hash *h, void *data, void *arg) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + hash_hash_t hash = (*h->data_hash) (h->keysize, data) & ~HASH_MASK; + struct hash_subtable *st = h->st; + int32 used = 0; + hash_hash_t e_hash; + struct hash_entry *e; + struct hash_entry *end_e; + + hash += HASH_ADJUST (hash); + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + + e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ + e_hash = e->hash; + if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ + break; + } + used += st->power; + st = *(struct hash_subtable **)e->data; + } + end_e = HASH_OFFSET (e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + } + while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, e->data)) { /* a match */ + (*h->data_del) (h->keysize, arg, e->data); + hash_remove_n (st, e, 1); + h->count--; + return (1); + } + e = HASH_OFFSET (e, elemsize); + } + USED(e_hash); + return (0); +} + +static int32 +hash_insert_internal (struct hash_subtable **pst, int32 flags, hash_hash_t hash, + struct hash *h, void *data, void **pres) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + + if ((flags & HASH_REHASH) == 0) { + hash += HASH_ADJUST (hash); + hash &= ~HASH_MASK; + } + for (;;) { + struct hash_subtable *st = *pst; + int32 shift = HASH_BITS - (st->power + HASH_USED (flags)); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + struct hash_entry *start_e = + HASH_OFFSET (st->entry, i * elemsize); /* start_e is the pointer to element i */ + struct hash_entry *e = start_e; /* e is going to range over [start_e, end_e) */ + struct hash_entry *end_e; + hash_hash_t e_hash = e->hash; + + if ((e_hash & HASH_MASK) == HASH_SUBHASH) { /* a subtable */ + pst = (struct hash_subtable **) e->data; + flags += HASH_MAKE_USED (st->power); + continue; + } + end_e = HASH_OFFSET (start_e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + i++; + } + if (e != end_e && e_hash != HASH_NIL) { + /* ins_e ranges over the elements that may match */ + struct hash_entry *ins_e = e; + int32 ins_i = i; + hash_hash_t ins_e_hash; + while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, ins_e->data)) { /* a match */ + *pres = ins_e->data; + return (1); + } + assert (e_hash != hash || (flags & HASH_REHASH) == 0); + hash += (e_hash == hash); /* adjust hash if it collides */ + ins_e = HASH_OFFSET (ins_e, elemsize); + ins_i++; + if (e_hash <= hash) { /* set e to insertion point */ + e = ins_e; + i = ins_i; + } + } + /* set ins_e to the insertion point for the new element */ + ins_e = e; + ins_i = i; + ins_e_hash = 0; + /* move ins_e to point at the end of the contiguous block, but + stop if any element can't be moved by one up */ + while (ins_e != st->end && (ins_e_hash = ins_e->hash) != HASH_NIL && + ins_i + 1 - ((ins_e_hash >> shift) & index_mask) < st->max_probes && + (ins_e_hash & HASH_MASK) != HASH_SUBHASH) { + ins_e = HASH_OFFSET (ins_e, elemsize); + ins_i++; + } + if (e == end_e || ins_e == st->end || ins_e_hash != HASH_NIL) { + e = end_e; /* can't insert; must grow or convert to subtable */ + } else { /* make space for element */ + memmove (HASH_OFFSET (e, elemsize), e, ((byte *) ins_e) - (byte *) e); + } + } + if (e != end_e) { + e->hash = hash; + *pres = e->data; + return (0); + } + h->changes++; + if (st->power < h->max_power) { + hash_grow (h, pst, flags); + } else { + hash_conv (h, st, flags, hash, start_e); + } + } +} + +int32 +hash_insert (struct hash *h, void *data, void **pres) +{ + int32 rc = hash_insert_internal (&h->st, 0, (*h->data_hash) (h->keysize, data), h, data, pres); + + h->count += (rc == 0); /* increment count if element didn't previously exist */ + return (rc); +} + +uint32 +hash_count (struct hash *h) +{ + return (h->count); +} + +static void +iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used) +{ + int32 elemsize = it->elemsize; + hash_hash_t last_hash = it->last_hash; + struct hash_entry *e; + hash_hash_t e_hash; + struct hash_iter_sub *sub = &it->subtable_state[it->i]; + struct hash_entry *end; + + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (last_hash >> shift) & index_mask; + + end = st->end; + e = HASH_OFFSET (st->entry, i * elemsize); + sub->start = st->entry; + sub->end = end; + + if ((e->hash & HASH_MASK) != HASH_SUBHASH) { + break; + } + sub->e = HASH_OFFSET (e, elemsize); + sub = &it->subtable_state[++(it->i)]; + used += st->power; + st = *(struct hash_subtable **)e->data; + } + while (e != end && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { + e = HASH_OFFSET (e, elemsize); + } + sub->e = e; +} + +void * +hash_next (struct hash_iter *it) +{ + int32 elemsize = it->elemsize; + struct hash_iter_sub *sub = &it->subtable_state[it->i]; + struct hash_entry *e = sub->e; + struct hash_entry *end = sub->end; + hash_hash_t e_hash = 0; + + if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */ + it->changes = it->h->changes; + it->i = 0; + iter_restart (it, it->h->st, 0); + sub = &it->subtable_state[it->i]; + e = sub->e; + end = sub->end; + } + if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) { + struct hash_entry *start = HASH_OFFSET (e, -(elemsize * it->h->max_probes)); + struct hash_entry *pe = HASH_OFFSET (e, -elemsize); + hash_hash_t last_hash = it->last_hash; + if (start < sub->start) { + start = sub->start; + } + while (e != start && ((e_hash = pe->hash) == HASH_NIL || last_hash < e_hash)) { + e = pe; + pe = HASH_OFFSET (pe, -elemsize); + } + while (e != end && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { + e = HASH_OFFSET (e, elemsize); + } + } + + for (;;) { + while (e != end && (e_hash = e->hash) == HASH_NIL) { + e = HASH_OFFSET (e, elemsize); + } + if (e == end) { + if (it->i == 0) { + it->last_hash = HASH_OFFSET (e, -elemsize)->hash; + sub->e = e; + return (0); + } else { + it->i--; + sub = &it->subtable_state[it->i]; + e = sub->e; + end = sub->end; + } + } else if ((e_hash & HASH_MASK) != HASH_SUBHASH) { + it->last_hash = e->hash; + sub->e = HASH_OFFSET (e, elemsize); + return (e->data); + } else { + struct hash_subtable *st = + *(struct hash_subtable **)e->data; + sub->e = HASH_OFFSET (e, elemsize); + it->i++; + assert (it->i < sizeof (it->subtable_state) / + sizeof (it->subtable_state[0])); + sub = &it->subtable_state[it->i]; + sub->e = e = st->entry; + sub->start = st->entry; + sub->end = end = st->end; + } + } +} + +void +hash_iter_init (struct hash *h, struct hash_iter *it) +{ + it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + it->changes = h->changes; + it->i = 0; + it->h = h; + it->last_hash = 0; + it->subtable_state[0].e = h->st->entry; + it->subtable_state[0].start = h->st->entry; + it->subtable_state[0].end = h->st->end; +} + +static void +clean_st (struct hash_subtable *st, int32 *slots, int32 *used) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *e = st->entry; + struct hash_entry *end = st->end; + int32 lslots = (((byte *) end) - (byte *) e) / elemsize; + int32 lused = 0; + + while (e != end) { + hash_hash_t hash = e->hash; + if ((hash & HASH_MASK) == HASH_SUBHASH) { + clean_st (*(struct hash_subtable **)e->data, slots, used); + } else { + lused += (hash != HASH_NIL); + } + e = HASH_OFFSET (e, elemsize); + } + free (st); + *slots += lslots; + *used += lused; +} + +void +hash_destroy (struct hash *h) +{ + int32 slots = 0; + int32 used = 0; + + clean_st (h->st, &slots, &used); + free (h); +} + +static void +hash_visit_internal (struct hash_subtable *st, + int32 used, int32 level, + void (*data_visit) (void *arg, int32 level, void *data), + void *arg) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *e = st->entry; + int32 shift = HASH_BITS - (used + st->power); + int32 i = 0; + + while (e != st->end) { + int32 index = ((e->hash >> (shift - 1)) >> 1) & ((1 << st->power) - 1); + if ((e->hash & HASH_MASK) == HASH_SUBHASH) { + (*data_visit) (arg, level, e->data); + hash_visit_internal (*(struct hash_subtable **)e->data, + used + st->power, level + 1, data_visit, arg); + } else { + (*data_visit) (arg, level, e->data); + } + if (e->hash != HASH_NIL) { + assert (i < index + st->max_probes); + assert (index <= i); + } + e = HASH_OFFSET (e, elemsize); + i++; + } +} + +void +hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg) +{ + hash_visit_internal (h->st, 0, 0, data_visit, arg); +} + +// +/// interfaces to go runtime +// + +static void +donothing(uint32 s, void *a, void *b) +{ + USED(s); + USED(a); + USED(b); +} + +typedef struct hash Hmap; +static int32 debug = 0; + +// newmap(keysize uint32, valsize uint32, +// keyalg uint32, valalg uint32, +// hint uint32) (hmap *map[any]any); +void +sys·newmap(uint32 keysize, uint32 valsize, + uint32 keyalg, uint32 valalg, uint32 hint, + Hmap* ret) +{ + Hmap *h; + + if(keyalg >= nelem(algarray) || algarray[keyalg].hash == nohash) { + printf("map(keyalg=%d)\n", keyalg); + throw("sys·newmap: unsupported map key type"); + } + + if(valalg >= nelem(algarray)) { + printf("map(valalg=%d)\n", valalg); + throw("sys·newmap: unsupported map value type"); + } + + h = mal(sizeof(*h)); + + // align value inside data so that mark-sweep gc can find it. + // might remove in the future and just assume datavo == keysize. + h->datavo = keysize; + if(valsize >= sizeof(void*)) + h->datavo = rnd(keysize, sizeof(void*)); + + hash_init(h, h->datavo+valsize, + algarray[keyalg].hash, + algarray[keyalg].equal, + donothing, + hint); + + h->keysize = keysize; + h->valsize = valsize; + h->keyalg = &algarray[keyalg]; + h->valalg = &algarray[valalg]; + + // these calculations are compiler dependent. + // figure out offsets of map call arguments. + h->ko = rnd(sizeof(h), keysize); + h->vo = rnd(h->ko+keysize, valsize); + h->po = rnd(h->vo+valsize, 1); + + ret = h; + FLUSH(&ret); + + if(debug) { + prints("newmap: map="); + sys·printpointer(h); + prints("; keysize="); + sys·printint(keysize); + prints("; valsize="); + sys·printint(valsize); + prints("; keyalg="); + sys·printint(keyalg); + prints("; valalg="); + sys·printint(valalg); + prints("; ko="); + sys·printint(h->ko); + prints("; vo="); + sys·printint(h->vo); + prints("; po="); + sys·printint(h->po); + prints("\n"); + } +} + +// mapaccess1(hmap *map[any]any, key any) (val any); +void +sys·mapaccess1(Hmap *h, ...) +{ + byte *ak, *av; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + + res = nil; + hit = hash_lookup(h, ak, (void**)&res); + if(!hit) + throw("sys·mapaccess1: key not in map"); + h->valalg->copy(h->valsize, av, res+h->datavo); + + if(debug) { + prints("sys·mapaccess1: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapaccess2(hmap *map[any]any, key any) (val any, pres bool); +void +sys·mapaccess2(Hmap *h, ...) +{ + byte *ak, *av, *ap; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + ap = (byte*)&h + h->po; + + res = nil; + hit = hash_lookup(h, ak, (void**)&res); + if(!hit) { + *ap = false; + h->valalg->copy(h->valsize, av, nil); + } else { + *ap = true; + h->valalg->copy(h->valsize, av, res+h->datavo); + } + + if(debug) { + prints("sys·mapaccess2: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("; pres="); + sys·printbool(*ap); + prints("\n"); + } +} + +static void +mapassign(Hmap *h, byte *ak, byte *av) +{ + byte *res; + int32 hit; + + res = nil; + hit = hash_insert(h, ak, (void**)&res); + h->keyalg->copy(h->keysize, res, ak); + h->valalg->copy(h->valsize, res+h->datavo, av); + + if(debug) { + prints("mapassign: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapassign1(hmap *map[any]any, key any, val any); +void +sys·mapassign1(Hmap *h, ...) +{ + byte *ak, *av; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + + mapassign(h, ak, av); +} + +// mapassign2(hmap *map[any]any, key any, val any, pres bool); +void +sys·mapassign2(Hmap *h, ...) +{ + byte *ak, *av, *ap; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + ap = (byte*)&h + h->po; + + if(*ap == true) { + // assign + mapassign(h, ak, av); + return; + } + + // delete + hit = hash_remove(h, ak, (void**)&res); + + if(debug) { + prints("mapassign2: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapiterinit(hmap *map[any]any, hiter *any); +void +sys·mapiterinit(Hmap *h, struct hash_iter *it) +{ + if(h == nil) { + it->data = nil; + return; + } + hash_iter_init(h, it); + it->data = hash_next(it); + if(debug) { + prints("sys·mapiterinit: map="); + sys·printpointer(h); + prints("; iter="); + sys·printpointer(it); + prints("; data="); + sys·printpointer(it->data); + prints("\n"); + } +} + +// mapiternext(hiter *any); +void +sys·mapiternext(struct hash_iter *it) +{ + it->data = hash_next(it); + if(debug) { + prints("sys·mapiternext: iter="); + sys·printpointer(it); + prints("; data="); + sys·printpointer(it->data); + prints("\n"); + } +} + +// mapiter1(hiter *any) (key any); +void +sys·mapiter1(struct hash_iter *it, ...) +{ + Hmap *h; + byte *ak, *res; + + h = it->h; + ak = (byte*)&it + h->ko; + + res = it->data; + if(res == nil) + throw("sys·mapiter2: key:val nil pointer"); + + h->keyalg->copy(h->keysize, ak, res); + + if(debug) { + prints("mapiter2: iter="); + sys·printpointer(it); + prints("; map="); + sys·printpointer(h); + prints("\n"); + } +} + +// mapiter2(hiter *any) (key any, val any); +void +sys·mapiter2(struct hash_iter *it, ...) +{ + Hmap *h; + byte *ak, *av, *res; + + h = it->h; + ak = (byte*)&it + h->ko; + av = (byte*)&it + h->vo; + + res = it->data; + if(res == nil) + throw("sys·mapiter2: key:val nil pointer"); + + h->keyalg->copy(h->keysize, ak, res); + h->valalg->copy(h->valsize, av, res+h->datavo); + + if(debug) { + prints("mapiter2: iter="); + sys·printpointer(it); + prints("; map="); + sys·printpointer(h); + prints("\n"); + } +} diff --git a/src/pkg/runtime/hashmap.h b/src/pkg/runtime/hashmap.h new file mode 100644 index 000000000..e8bcfab29 --- /dev/null +++ b/src/pkg/runtime/hashmap.h @@ -0,0 +1,160 @@ +// Copyright 2009 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. + +/* A hash table. + Example, hashing nul-terminated char*s: + hash_hash_t str_hash (void *v) { + char *s; + hash_hash_t hash = 0; + for (s = *(char **)v; *s != 0; s++) { + hash = (hash ^ *s) * 2654435769U; + } + return (hash); + } + int str_eq (void *a, void *b) { + return (strcmp (*(char **)a, *(char **)b) == 0); + } + void str_del (void *arg, void *data) { + *(char **)arg = *(char **)data; + } + + struct hash *h = hash_new (sizeof (char *), &str_hash, &str_eq, &str_del, 3, 12, 15); + ... 3=> 2**3 entries initial size + ... 12=> 2**12 entries before sprouting sub-tables + ... 15=> number of adjacent probes to attempt before growing + + Example lookup: + char *key = "foobar"; + char **result_ptr; + if (hash_lookup (h, &key, (void **) &result_ptr)) { + printf ("found in table: %s\n", *result_ptr); + } else { + printf ("not found in table\n"); + } + + Example insertion: + char *key = strdup ("foobar"); + char **result_ptr; + if (hash_lookup (h, &key, (void **) &result_ptr)) { + printf ("found in table: %s\n", *result_ptr); + printf ("to overwrite, do *result_ptr = key\n"); + } else { + printf ("not found in table; inserted as %s\n", *result_ptr); + assert (*result_ptr == key); + } + + Example deletion: + char *key = "foobar"; + char *result; + if (hash_remove (h, &key, &result)) { + printf ("key found and deleted from table\n"); + printf ("called str_del (&result, data) to copy data to result: %s\n", result); + } else { + printf ("not found in table\n"); + } + + Example iteration over the elements of *h: + char **data; + struct hash_iter it; + hash_iter_init (h, &it); + for (data = hash_next (&it); data != 0; data = hash_next (&it)) { + printf ("%s\n", *data); + } + */ + +#define malloc mal +#define free(a) USED(a) +#define offsetof(s,m) (uint32)(&(((s*)0)->m)) +#define memset(a,b,c) sys·memclr((byte*)(a), (uint32)(c)) +#define memmove(a,b,c) mmov((byte*)(a),(byte*)(b),(uint32)(c)) +#define memcpy(a,b,c) mcpy((byte*)(a),(byte*)(b),(uint32)(c)) +#define assert(a) if(!(a)) throw("assert") + +struct hash; /* opaque */ +struct hash_subtable; /* opaque */ +struct hash_entry; /* opaque */ + +typedef uintptr uintptr_t; +typedef uintptr_t hash_hash_t; + +struct hash_iter { + uint8* data; /* returned from next */ + int32 elemsize; /* size of elements in table */ + int32 changes; /* number of changes observed last time */ + int32 i; /* stack pointer in subtable_state */ + hash_hash_t last_hash; /* last hash value returned */ + struct hash *h; /* the hash table */ + struct hash_iter_sub { + struct hash_entry *e; /* pointer into subtable */ + struct hash_entry *start; /* start of subtable */ + struct hash_entry *end; /* end of subtable */ + } subtable_state[4]; /* Should be large enough unless the hashing is + so bad that many distinct data values hash + to the same hash value. */ +}; + +/* Return a hashtable h 2**init_power empty entries, each with + "datasize" data bytes. + (*data_hash)(a) should return the hash value of data element *a. + (*data_eq)(a,b) should return whether the data at "a" and the data at "b" + are equal. + (*data_del)(arg, a) will be invoked when data element *a is about to be removed + from the table. "arg" is the argument passed to "hash_remove()". + + Growing is accomplished by resizing if the current tables size is less than + a threshold, and by adding subtables otherwise. hint should be set + the expected maximum size of the table. + "datasize" should be in [sizeof (void*), ..., 255]. If you need a + bigger "datasize", store a pointer to another piece of memory. */ + +//struct hash *hash_new (int32 datasize, +// hash_hash_t (*data_hash) (void *), +// int32 (*data_eq) (void *, void *), +// void (*data_del) (void *, void *), +// int64 hint); + +/* Lookup *data in *h. If the data is found, return 1 and place a pointer to + the found element in *pres. Otherwise return 0 and place 0 in *pres. */ +int32 hash_lookup (struct hash *h, void *data, void **pres); + +/* Lookup *data in *h. If the data is found, execute (*data_del) (arg, p) + where p points to the data in the table, then remove it from *h and return + 1. Otherwise return 0. */ +int32 hash_remove (struct hash *h, void *data, void *arg); + +/* Lookup *data in *h. If the data is found, return 1, and place a pointer + to the found element in *pres. Otherwise, return 0, allocate a region + for the data to be inserted, and place a pointer to the inserted element + in *pres; it is the caller's responsibility to copy the data to be + inserted to the pointer returned in *pres in this case. + + If using garbage collection, it is the caller's responsibility to + add references for **pres if HASH_ADDED is returned. */ +int32 hash_insert (struct hash *h, void *data, void **pres); + +/* Return the number of elements in the table. */ +uint32 hash_count (struct hash *h); + +/* The following call is useful only if not using garbage collection on the + table. + Remove all sub-tables associated with *h. + This undoes the effects of hash_init(). + If other memory pointed to by user data must be freed, the caller is + responsible for doiing do by iterating over *h first; see + hash_iter_init()/hash_next(). */ +void hash_destroy (struct hash *h); + +/*----- iteration -----*/ + +/* Initialize *it from *h. */ +void hash_iter_init (struct hash *h, struct hash_iter *it); + +/* Return the next used entry in the table which which *it was initialized. */ +void *hash_next (struct hash_iter *it); + +/*---- test interface ----*/ +/* Call (*data_visit) (arg, level, data) for every data entry in the table, + whether used or not. "level" is the subtable level, 0 means first level. */ +/* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ +void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg); diff --git a/src/pkg/runtime/iface.c b/src/pkg/runtime/iface.c new file mode 100644 index 000000000..6c933b1b2 --- /dev/null +++ b/src/pkg/runtime/iface.c @@ -0,0 +1,906 @@ +// Copyright 2009 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. + +#include "runtime.h" + +int32 iface_debug = 0; + +typedef struct Sigt Sigt; +typedef struct Sigi Sigi; +typedef struct Itype Itype; + +/* + * the layout of Iface, Sigt and Sigi are known to the compiler + */ +struct Sigt +{ + byte* name; // name of basic type + Sigt* link; // for linking into hash tables + uint32 thash; // hash of type + uint32 mhash; // hash of methods + uint16 width; // width of base type in bytes + uint16 alg; // algorithm + // note: on amd64 there is a 32-bit pad here. + struct { + byte* fname; + uint32 fhash; // hash of type + uint32 offset; // offset of substruct + void (*fun)(void); + } meth[1]; // one or more - last name is nil +}; + +struct Sigi +{ + byte* name; + uint32 hash; + uint32 size; // number of methods + struct { + byte* fname; + uint32 fhash; + uint32 perm; // location of fun in Sigt + } meth[1]; // [size+1] - last name is nil +}; + +struct Itype +{ + Sigi* sigi; + Sigt* sigt; + Itype* link; + int32 bad; + int32 unused; + void (*fun[])(void); +}; + +static Iface niliface; +static Eface nileface; + +static Itype* hash[1009]; +static Lock ifacelock; + +Sigi sigi·empty[2] = { (byte*)"interface { }" }; + +static void +printsigi(Sigi *si) +{ + int32 i; + byte *name; + + sys·printpointer(si); + prints("{"); + prints((int8*)si->name); + prints(":"); + for(i=0;; i++) { + name = si->meth[i].fname; + if(name == nil) + break; + prints("["); + sys·printint(i); + prints("]\""); + prints((int8*)name); + prints("\""); + sys·printint(si->meth[i].fhash%999); + prints("/"); + sys·printint(si->meth[i].perm); + } + prints("}"); +} + +static void +printsigt(Sigt *st) +{ + int32 i; + byte *name; + + sys·printpointer(st); + prints("{"); + prints((int8*)st->name); + prints(":"); + sys·printint(st->thash%999); // type hash + prints(","); + sys·printint(st->mhash%999); // method hash + prints(","); + sys·printint(st->width); // width + prints(","); + sys·printint(st->alg); // algorithm + for(i=0;; i++) { + name = st->meth[i].fname; + if(name == nil) + break; + prints("["); + sys·printint(i); + prints("]\""); + prints((int8*)name); + prints("\""); + sys·printint(st->meth[i].fhash%999); + prints("/"); + sys·printint(st->meth[i].offset); + prints("/"); + sys·printpointer(st->meth[i].fun); + } + prints("}"); +} + +static void +printiface(Iface i) +{ + prints("("); + sys·printpointer(i.type); + prints(","); + sys·printpointer(i.data); + prints(")"); +} + +static void +printeface(Eface e) +{ + prints("("); + sys·printpointer(e.type); + prints(","); + sys·printpointer(e.data); + prints(")"); +} + +static Itype* +itype(Sigi *si, Sigt *st, int32 canfail) +{ + int32 locked; + int32 nt, ni; + uint32 ihash, h; + byte *sname, *iname; + Itype *m; + + if(si->size == 0) + throw("internal error - misuse of itype"); + + // easy case + if(st->meth[0].fname == nil) { + if(canfail) + return nil; + iname = si->meth[0].fname; + goto throw1; + } + + // compiler has provided some good hash codes for us. + h = 0; + if(si) + h += si->hash; + if(st) { + h += st->thash; + h += st->mhash; + } + + h %= nelem(hash); + + // look twice - once without lock, once with. + // common case will be no lock contention. + for(locked=0; locked<2; locked++) { + if(locked) + lock(&ifacelock); + for(m=hash[h]; m!=nil; m=m->link) { + if(m->sigi == si && m->sigt == st) { + if(m->bad) { + m = nil; + if(!canfail) { + // this can only happen if the conversion + // was already done once using the , ok form + // and we have a cached negative result. + // the cached result doesn't record which + // interface function was missing, so jump + // down to the interface check, which will + // give a better error. + goto throw; + } + } + if(locked) + unlock(&ifacelock); + return m; + } + } + } + + ni = si->size; + m = malloc(sizeof(*m) + ni*sizeof(m->fun[0])); + m->sigi = si; + m->sigt = st; + +throw: + nt = 0; + for(ni=0;; ni++) { + iname = si->meth[ni].fname; + if(iname == nil) + break; + + // pick up next name from + // interface signature + ihash = si->meth[ni].fhash; + + for(;; nt++) { + // pick up and compare next name + // from structure signature + sname = st->meth[nt].fname; + if(sname == nil) { + if(!canfail) { + throw1: + printf("cannot convert type %s to interface %s: missing method %s\n", + st->name, si->name, iname); + if(iface_debug) { + prints("interface"); + printsigi(si); + prints("\ntype"); + printsigt(st); + prints("\n"); + } + throw("interface conversion"); + return nil; // not reached + } + m->bad = 1; + m->link = hash[h]; + hash[h] = m; + if(locked) + unlock(&ifacelock); + return nil; + } + if(ihash == st->meth[nt].fhash && strcmp(sname, iname) == 0) + break; + } + m->fun[si->meth[ni].perm] = st->meth[nt].fun; + } + m->link = hash[h]; + hash[h] = m; + if(locked) + unlock(&ifacelock); + + return m; +} + +static void +copyin(Sigt *st, void *src, void **dst) +{ + int32 wid, alg; + void *p; + + wid = st->width; + alg = st->alg; + + if(wid <= sizeof(*dst)) + algarray[alg].copy(wid, dst, src); + else { + p = mal(wid); + algarray[alg].copy(wid, p, src); + *dst = p; + } +} + +static void +copyout(Sigt *st, void **src, void *dst) +{ + int32 wid, alg; + + wid = st->width; + alg = st->alg; + + if(wid <= sizeof(*src)) + algarray[alg].copy(wid, dst, src); + else + algarray[alg].copy(wid, dst, *src); +} + +// ifaceT2I(sigi *byte, sigt *byte, elem any) (ret any); +#pragma textflag 7 +void +sys·ifaceT2I(Sigi *si, Sigt *st, ...) +{ + byte *elem; + Iface *ret; + int32 wid; + + elem = (byte*)(&st+1); + wid = st->width; + ret = (Iface*)(elem + rnd(wid, sizeof(uintptr))); + + ret->type = itype(si, st, 0); + copyin(st, elem, &ret->data); +} + +// ifaceT2E(sigt *byte, elem any) (ret any); +#pragma textflag 7 +void +sys·ifaceT2E(Sigt *st, ...) +{ + byte *elem; + Eface *ret; + int32 wid; + + elem = (byte*)(&st+1); + wid = st->width; + ret = (Eface*)(elem + rnd(wid, sizeof(uintptr))); + + ret->type = st; + copyin(st, elem, &ret->data); +} + +// ifaceI2T(sigt *byte, iface any) (ret any); +#pragma textflag 7 +void +sys·ifaceI2T(Sigt *st, Iface i, ...) +{ + Itype *im; + byte *ret; + + ret = (byte*)(&i+1); + + im = i.type; + if(im == nil) { + printf("interface is nil, not %s\n", st->name); + throw("interface conversion"); + } + if(im->sigt != st) { + printf("%s is %s, not %s\n", im->sigi->name, im->sigt->name, st->name); + throw("interface conversion"); + } + copyout(st, &i.data, ret); +} + +// ifaceI2T2(sigt *byte, iface any) (ret any, ok bool); +#pragma textflag 7 +void +sys·ifaceI2T2(Sigt *st, Iface i, ...) +{ + byte *ret; + bool *ok; + Itype *im; + int32 wid; + + ret = (byte*)(&i+1); + wid = st->width; + ok = (bool*)(ret+rnd(wid, 1)); + + im = i.type; + if(im == nil || im->sigt != st) { + *ok = false; + sys·memclr(ret, wid); + return; + } + + *ok = true; + copyout(st, &i.data, ret); +} + +// ifaceE2T(sigt *byte, iface any) (ret any); +#pragma textflag 7 +void +sys·ifaceE2T(Sigt *st, Eface e, ...) +{ + Sigt *t; + byte *ret; + + ret = (byte*)(&e+1); + + t = e.type; + if(t == nil) { + printf("interface is nil, not %s\n", st->name); + throw("interface conversion"); + } + if(t != st) { + printf("interface is %s, not %s\n", t->name, st->name); + throw("interface conversion"); + } + copyout(st, &e.data, ret); +} + +// ifaceE2T2(sigt *byte, iface any) (ret any, ok bool); +#pragma textflag 7 +void +sys·ifaceE2T2(Sigt *st, Eface e, ...) +{ + byte *ret; + bool *ok; + Sigt *t; + int32 wid; + + ret = (byte*)(&e+1); + wid = st->width; + ok = (bool*)(ret+rnd(wid, 1)); + + t = e.type; + if(t != st) { + *ok = false; + sys·memclr(ret, wid); + return; + } + + *ok = true; + copyout(st, &e.data, ret); +} + +// ifaceI2E(sigi *byte, iface any) (ret any); +// TODO(rsc): Move to back end, throw away function. +void +sys·ifaceI2E(Iface i, Eface ret) +{ + Itype *im; + + ret.data = i.data; + im = i.type; + if(im == nil) + ret.type = nil; + else + ret.type = im->sigt; + FLUSH(&ret); +} + +// ifaceI2I(sigi *byte, iface any) (ret any); +// called only for implicit (no type assertion) conversions +void +sys·ifaceI2I(Sigi *si, Iface i, Iface ret) +{ + Itype *im; + + im = i.type; + if(im == nil) { + // If incoming interface is uninitialized (zeroed) + // make the outgoing interface zeroed as well. + ret = niliface; + } else { + ret = i; + if(im->sigi != si) + ret.type = itype(si, im->sigt, 0); + } + + FLUSH(&ret); +} + +// ifaceI2Ix(sigi *byte, iface any) (ret any); +// called only for explicit conversions (with type assertion). +void +sys·ifaceI2Ix(Sigi *si, Iface i, Iface ret) +{ + Itype *im; + + im = i.type; + if(im == nil) { + // explicit conversions require non-nil interface value. + printf("interface is nil, not %s\n", si->name); + throw("interface conversion"); + } else { + ret = i; + if(im->sigi != si) + ret.type = itype(si, im->sigt, 0); + } + + FLUSH(&ret); +} + +// ifaceI2I2(sigi *byte, iface any) (ret any, ok bool); +void +sys·ifaceI2I2(Sigi *si, Iface i, Iface ret, bool ok) +{ + Itype *im; + + im = i.type; + if(im == nil) { + // If incoming interface is nil, the conversion fails. + ret = niliface; + ok = false; + } else { + ret = i; + ok = true; + if(im->sigi != si) { + ret.type = itype(si, im->sigt, 1); + if(ret.type == nil) { + ret = niliface; + ok = false; + } + } + } + + FLUSH(&ret); + FLUSH(&ok); +} + +// ifaceE2I(sigi *byte, iface any) (ret any); +// Called only for explicit conversions (with type assertion). +void +sys·ifaceE2I(Sigi *si, Eface e, Iface ret) +{ + Sigt *t; + + t = e.type; + if(t == nil) { + // explicit conversions require non-nil interface value. + printf("interface is nil, not %s\n", si->name); + throw("interface conversion"); + } else { + ret.data = e.data; + ret.type = itype(si, t, 0); + } + FLUSH(&ret); +} + +// ifaceE2I2(sigi *byte, iface any) (ret any, ok bool); +void +sys·ifaceE2I2(Sigi *si, Eface e, Iface ret, bool ok) +{ + Sigt *t; + + t = e.type; + ok = true; + if(t == nil) { + // If incoming interface is nil, the conversion fails. + ret = niliface; + ok = false; + } else { + ret.data = e.data; + ret.type = itype(si, t, 1); + if(ret.type == nil) { + ret = niliface; + ok = false; + } + } + FLUSH(&ret); + FLUSH(&ok); +} + +static uintptr +ifacehash1(void *data, Sigt *sigt) +{ + int32 alg, wid; + + if(sigt == nil) + return 0; + + alg = sigt->alg; + wid = sigt->width; + if(algarray[alg].hash == nohash) { + // calling nohash will throw too, + // but we can print a better error. + printf("hash of unhashable type %s\n", sigt->name); + if(alg == AFAKE) + throw("fake interface hash"); + throw("interface hash"); + } + if(wid <= sizeof(data)) + return algarray[alg].hash(wid, &data); + return algarray[alg].hash(wid, data); +} + +uintptr +ifacehash(Iface a) +{ + if(a.type == nil) + return 0; + return ifacehash1(a.data, a.type->sigt); +} + +uintptr +efacehash(Eface a) +{ + return ifacehash1(a.data, a.type); +} + +static bool +ifaceeq1(void *data1, void *data2, Sigt *sigt) +{ + int32 alg, wid; + + alg = sigt->alg; + wid = sigt->width; + + if(algarray[alg].equal == noequal) { + // calling noequal will throw too, + // but we can print a better error. + printf("comparing uncomparable type %s\n", sigt->name); + if(alg == AFAKE) + throw("fake interface compare"); + throw("interface compare"); + } + + if(wid <= sizeof(data1)) + return algarray[alg].equal(wid, &data1, &data2); + return algarray[alg].equal(wid, data1, data2); +} + +bool +ifaceeq(Iface i1, Iface i2) +{ + if(i1.type != i2.type) + return false; + if(i1.type == nil) + return true; + return ifaceeq1(i1.data, i2.data, i1.type->sigt); +} + +bool +efaceeq(Eface e1, Eface e2) +{ + if(e1.type != e2.type) + return false; + if(e1.type == nil) + return true; + return ifaceeq1(e1.data, e2.data, e1.type); +} + +// ifaceeq(i1 any, i2 any) (ret bool); +void +sys·ifaceeq(Iface i1, Iface i2, bool ret) +{ + ret = ifaceeq(i1, i2); + FLUSH(&ret); +} + +// efaceeq(i1 any, i2 any) (ret bool) +void +sys·efaceeq(Eface e1, Eface e2, bool ret) +{ + ret = efaceeq(e1, e2); + FLUSH(&ret); +} + +// ifacethash(i1 any) (ret uint32); +void +sys·ifacethash(Iface i1, uint32 ret) +{ + Itype *im; + Sigt *st; + + ret = 0; + im = i1.type; + if(im != nil) { + st = im->sigt; + if(st != nil) + ret = st->thash; + } + FLUSH(&ret); +} + +// efacethash(e1 any) (ret uint32) +void +sys·efacethash(Eface e1, uint32 ret) +{ + Sigt *st; + + ret = 0; + st = e1.type; + if(st != nil) + ret = st->thash; + FLUSH(&ret); +} + +void +sys·printiface(Iface i) +{ + printiface(i); +} + +void +sys·printeface(Eface e) +{ + printeface(e); +} + +void +unsafe·Reflect(Eface i, uint64 retit, String rettype, bool retindir) +{ + int32 wid; + + if(i.type == nil) { + retit = 0; + rettype = emptystring; + retindir = false; + } else { + retit = (uint64)i.data; + rettype = gostring(i.type->name); + wid = i.type->width; + retindir = wid > sizeof(i.data); + } + FLUSH(&retit); + FLUSH(&rettype); + FLUSH(&retindir); +} + +extern Sigt *gotypesigs[]; +extern int32 ngotypesigs; + + +// The reflection library can ask to unreflect on a type +// that has never been used, so we don't have a signature for it. +// For concreteness, suppose a program does +// +// type T struct{ x []int } +// var t T; +// v := reflect.NewValue(v); +// vv := v.Field(0); +// if s, ok := vv.Interface().(string) { +// print("first field is string"); +// } +// +// vv.Interface() returns the result of sys.Unreflect with +// a typestring of "[]int". If []int is not used with interfaces +// in the rest of the program, there will be no signature in gotypesigs +// for "[]int", so we have to invent one. The requirements +// on the fake signature are: +// +// (1) any interface conversion using the signature will fail +// (2) calling unsafe.Reflect() returns the args to unreflect +// (3) the right algorithm type is used, for == and map insertion +// +// (1) is ensured by the fact that we allocate a new Sigt, +// so it will necessarily be != any Sigt in gotypesigs. +// (2) is ensured by storing the type string in the signature +// and setting the width to force the correct value of the bool indir. +// (3) is ensured by sniffing the type string. +// +// Note that (1) is correct behavior: if the program had tested +// for .([]int) instead of .(string) above, then there would be a +// signature with type string "[]int" in gotypesigs, and unreflect +// wouldn't call fakesigt. + +static Sigt* fake[1009]; +static int32 nfake; + +enum +{ + SizeofInt = 4, + SizeofFloat = 4, +}; + +// Table of prefixes of names of comparable types. +static struct { + int8 *s; + int8 n; + int8 alg; + int8 w; +} cmp[] = +{ + // basic types + "int", 3+1, AMEM, SizeofInt, // +1 is NUL + "uint", 4+1, AMEM, SizeofInt, + "int8", 4+1, AMEM, 1, + "uint8", 5+1, AMEM, 1, + "int16", 5+1, AMEM, 2, + "uint16", 6+1, AMEM, 2, + "int32", 5+1, AMEM, 4, + "uint32", 6+1, AMEM, 4, + "int64", 5+1, AMEM, 8, + "uint64", 6+1, AMEM, 8, + "uintptr", 7+1, AMEM, sizeof(uintptr), + "float", 5+1, AMEM, SizeofFloat, + "float32", 7+1, AMEM, 4, + "float64", 7+1, AMEM, 8, + "bool", 4+1, AMEM, sizeof(bool), + + // string compare is special + "string", 6+1, ASTRING, sizeof(String), + + // generic types, identified by prefix + "*", 1, AMEM, sizeof(uintptr), + "chan ", 5, AMEM, sizeof(uintptr), + "func(", 5, AMEM, sizeof(uintptr), + "map[", 4, AMEM, sizeof(uintptr), +}; + +static Sigt* +fakesigt(String type, bool indir) +{ + Sigt *sigt; + uint32 h; + int32 i, locked; + + h = 0; + for(i=0; i<type.len; i++) + h = h*37 + type.str[i]; + h += indir; + h %= nelem(fake); + + for(locked=0; locked<2; locked++) { + if(locked) + lock(&ifacelock); + for(sigt = fake[h]; sigt != nil; sigt = sigt->link) { + // don't need to compare indir. + // same type string but different indir will have + // different hashes. + if(mcmp(sigt->name, type.str, type.len) == 0) + if(sigt->name[type.len] == '\0') { + if(locked) + unlock(&ifacelock); + return sigt; + } + } + } + + sigt = malloc(sizeof(*sigt)); + sigt->name = malloc(type.len + 1); + mcpy(sigt->name, type.str, type.len); + + sigt->alg = AFAKE; + sigt->width = 1; // small width + if(indir) + sigt->width = 2*sizeof(niliface.data); // big width + + // AFAKE is like ANOEQ; check whether the type + // should have a more capable algorithm. + for(i=0; i<nelem(cmp); i++) { + if(mcmp((byte*)sigt->name, (byte*)cmp[i].s, cmp[i].n) == 0) { + sigt->alg = cmp[i].alg; + sigt->width = cmp[i].w; + break; + } + } + + sigt->link = fake[h]; + fake[h] = sigt; + + unlock(&ifacelock); + return sigt; +} + +static int32 +cmpstringchars(String a, uint8 *b) +{ + int32 i; + byte c1, c2; + + for(i=0;; i++) { + c1 = 0; + if(i < a.len) + c1 = a.str[i]; + c2 = b[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + if(c1 == 0) + return 0; + } +} + +static Sigt* +findtype(String type, bool indir) +{ + int32 i, lo, hi, m; + + lo = 0; + hi = ngotypesigs; + while(lo < hi) { + m = lo + (hi - lo)/2; + i = cmpstringchars(type, gotypesigs[m]->name); + if(i == 0) + return gotypesigs[m]; + if(i < 0) + hi = m; + else + lo = m+1; + } + return fakesigt(type, indir); +} + + +void +unsafe·Unreflect(uint64 it, String type, bool indir, Eface ret) +{ + Sigt *sigt; + + ret = nileface; + + if(cmpstring(type, emptystring) == 0) + goto out; + + if(type.len > 10 && mcmp(type.str, (byte*)"interface ", 10) == 0) { + printf("unsafe.Unreflect: cannot put %S in interface\n", type); + throw("unsafe.Unreflect"); + } + + // if we think the type should be indirect + // and caller does not, play it safe, return nil. + sigt = findtype(type, indir); + if(indir != (sigt->width > sizeof(ret.data))) + goto out; + + ret.type = sigt; + ret.data = (void*)it; + +out: + FLUSH(&ret); +} + diff --git a/src/pkg/runtime/linux/386/defs.h b/src/pkg/runtime/linux/386/defs.h new file mode 100755 index 000000000..112fc7b09 --- /dev/null +++ b/src/pkg/runtime/linux/386/defs.h @@ -0,0 +1,136 @@ +// godefs -f -m32 -f -I/home/rsc/pub/linux-2.6/arch/x86/include -f -I/home/rsc/pub/linux-2.6/include defs2.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x20, + MAP_PRIVATE = 0x2, + SA_RESTART = 0x10000000, + SA_ONSTACK = 0x8000000, + SA_RESTORER = 0x4000000, + SA_SIGINFO = 0x4, +}; + +// Types +#pragma pack on + +typedef struct Fpreg Fpreg; +struct Fpreg { + uint16 significand[4]; + uint16 exponent; +}; + +typedef struct Fpxreg Fpxreg; +struct Fpxreg { + uint16 significand[4]; + uint16 exponent; + uint16 padding[3]; +}; + +typedef struct Xmmreg Xmmreg; +struct Xmmreg { + uint32 element[4]; +}; + +typedef struct Fpstate Fpstate; +struct Fpstate { + uint32 cw; + uint32 sw; + uint32 tag; + uint32 ipoff; + uint32 cssel; + uint32 dataoff; + uint32 datasel; + Fpreg _st[8]; + uint16 status; + uint16 magic; + uint32 _fxsr_env[6]; + uint32 mxcsr; + uint32 reserved; + Fpxreg _fxsr_st[8]; + Xmmreg _xmm[8]; + uint32 padding1[44]; + byte _anon_[48]; +}; + +typedef struct Timespec Timespec; +struct Timespec { + int32 tv_sec; + int32 tv_nsec; +}; + +typedef struct Timeval Timeval; +struct Timeval { + int32 tv_sec; + int32 tv_usec; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + byte _u[4]; + uint32 sa_mask; + uint32 sa_flags; + void *sa_restorer; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + byte _sifields[116]; +}; + +typedef struct Sigaltstack Sigaltstack; +struct Sigaltstack { + void *ss_sp; + int32 ss_flags; + uint32 ss_size; +}; + +typedef struct Sigcontext Sigcontext; +struct Sigcontext { + uint16 gs; + uint16 __gsh; + uint16 fs; + uint16 __fsh; + uint16 es; + uint16 __esh; + uint16 ds; + uint16 __dsh; + uint32 edi; + uint32 esi; + uint32 ebp; + uint32 esp; + uint32 ebx; + uint32 edx; + uint32 ecx; + uint32 eax; + uint32 trapno; + uint32 err; + uint32 eip; + uint16 cs; + uint16 __csh; + uint32 eflags; + uint32 esp_at_signal; + uint16 ss; + uint16 __ssh; + Fpstate *fpstate; + uint32 oldmask; + uint32 cr2; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + uint32 uc_flags; + Ucontext *uc_link; + Sigaltstack uc_stack; + Sigcontext uc_mcontext; + uint32 uc_sigmask; +}; +#pragma pack off diff --git a/src/pkg/runtime/linux/386/rt0.s b/src/pkg/runtime/linux/386/rt0.s new file mode 100755 index 000000000..7717c37e8 --- /dev/null +++ b/src/pkg/runtime/linux/386/rt0.s @@ -0,0 +1,8 @@ +// Copyright 2009 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_386_linux(SB),7,$0 + JMP _rt0_386(SB) diff --git a/src/pkg/runtime/linux/386/signal.c b/src/pkg/runtime/linux/386/signal.c new file mode 100644 index 000000000..7dfca6bb4 --- /dev/null +++ b/src/pkg/runtime/linux/386/signal.c @@ -0,0 +1,102 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs.h" +#include "signals.h" +#include "os.h" + +void +dumpregs(Sigcontext *r) +{ + printf("eax %X\n", r->eax); + printf("ebx %X\n", r->ebx); + printf("ecx %X\n", r->ecx); + printf("edx %X\n", r->edx); + printf("edi %X\n", r->edi); + printf("esi %X\n", r->esi); + printf("ebp %X\n", r->ebp); + printf("esp %X\n", r->esp); + printf("eip %X\n", r->eip); + printf("eflags %X\n", r->eflags); + printf("cs %X\n", r->cs); + printf("fs %X\n", r->fs); + printf("gs %X\n", r->gs); +} + +/* + * This assembler routine takes the args from registers, puts them on the stack, + * and calls sighandler(). + */ +extern void sigtramp(void); +extern void sigignore(void); // just returns +extern void sigreturn(void); // calls sigreturn + +void +sighandler(int32 sig, Siginfo* info, void* context) +{ + Ucontext *uc; + Sigcontext *sc; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + uc = context; + sc = &uc->uc_mcontext; + + if(sig < 0 || sig >= NSIG) + printf("Signal %d\n", sig); + else + printf("%s\n", sigtab[sig].name); + + printf("Faulting address: %p\n", *(void**)info->_sifields); + printf("pc=%X\n", sc->eip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)sc->eip, (void*)sc->esp, m->curg); + tracebackothers(m->curg); + dumpregs(sc); + } + + breakpoint(); + exit(2); +} + +void +signalstack(byte *p, int32 n) +{ + Sigaltstack st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + static Sigaction sa; + + int32 i; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; + sa.sa_mask = 0xFFFFFFFFFFFFFFFFULL; + sa.sa_restorer = (void*)sigreturn; + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) + *(void**)sa._u = (void*)sigtramp; // handler + else + *(void**)sa._u = (void*)sigignore; // handler + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + rt_sigaction(i, &sa, nil, 8); + } + } +} + diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s new file mode 100755 index 000000000..419973a5c --- /dev/null +++ b/src/pkg/runtime/linux/386/sys.s @@ -0,0 +1,222 @@ +// Copyright 2009 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. + +// +// System calls and other sys.stuff for 386, Linux +// + +TEXT syscall(SB),7,$0 + MOVL 4(SP), AX // syscall number + MOVL 8(SP), BX // arg1 + MOVL 12(SP), CX // arg2 + MOVL 16(SP), DX // arg3 + MOVL 20(SP), SI // arg4 + MOVL 24(SP), DI // arg5 + MOVL 28(SP), BP // arg6 + INT $0x80 + CMPL AX, $0xfffff001 + JLS 2(PC) + INT $3 // not reached + RET + +TEXT exit(SB),7,$0 + MOVL $252, AX // syscall number + MOVL 4(SP), BX + INT $0x80 + INT $3 // not reached + RET + +TEXT exit1(SB),7,$0 + MOVL $1, AX // exit - exit the current os thread + MOVL 4(SP), BX + INT $0x80 + INT $3 // not reached + RET + +TEXT write(SB),7,$0 + MOVL $4, AX // syscall - write + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + INT $0x80 + RET + +TEXT getpid(SB),7,$0 + MOVL $20, AX + INT $0x80 + RET + +TEXT kill(SB),7,$0 + MOVL $37, AX + MOVL 4(SP), BX + MOVL 8(SP), CX + INT $0x80 + RET + +TEXT sys·write(SB),7,$0 + MOVL $4, AX // syscall - write + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + INT $0x80 + RET + +TEXT rt_sigaction(SB),7,$0 + MOVL $174, AX // syscall - rt_sigaction + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + INT $0x80 + RET + +TEXT sigtramp(SB),7,$0 + MOVL 4(FS), BP // m + MOVL 20(BP), AX // m->gsignal + MOVL AX, 0(FS) // g = m->gsignal + JMP sighandler(SB) + +TEXT sigignore(SB),7,$0 + RET + +TEXT sigreturn(SB),7,$0 + MOVL 4(FS), BP // m + MOVL 32(BP), BP // m->curg + MOVL BP, 0(FS) // g = m->curg + MOVL $173, AX // rt_sigreturn + INT $0x80 + INT $3 // not reached + RET + +TEXT sys·mmap(SB),7,$0 + MOVL $192, AX // mmap2 + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + MOVL 20(SP), DI + MOVL 24(SP), BP + SHRL $12, BP + INT $0x80 + CMPL AX, $0xfffff001 + JLS 2(PC) + INT $3 + RET + +// int64 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT futex(SB),7,$0 + MOVL $240, AX // futex + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + MOVL 16(SP), SI + MOVL 20(SP), DI + MOVL 24(SP), BP + INT $0x80 + RET + +// int64 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); +TEXT clone(SB),7,$0 + MOVL $120, AX // clone + MOVL flags+4(SP), BX + MOVL stack+8(SP), CX + + // Copy m, g, fn off parent stack for use by child. + SUBL $12, CX + MOVL m+12(SP), DX + MOVL DX, 0(CX) + MOVL g+16(SP), DX + MOVL DX, 4(CX) + MOVL fn+20(SP), DX + MOVL DX, 8(CX) + + MOVL $120, AX + INT $0x80 + + // In parent, return. + CMPL AX, $0 + JEQ 2(PC) + RET + + // In child, set up new stack, etc. + MOVL 0(CX), BX // m + MOVL 12(AX), AX // fs (= m->cret) + MOVW AX, FS + MOVL 8(CX), DX // fn + ADDL $12, CX + MOVL CX, SP + + // fn is now on top of stack. + + // initialize m->procid to Linux tid + MOVL $224, AX + INT $0x80 + MOVL AX, 20(BX) + + // call fn + CALL DX + + // It shouldn't return; if it does, exit. + MOVL $111, DI + MOVL $1, AX + INT $0x80 + JMP -3(PC) // keep exiting + +TEXT sigaltstack(SB),7,$-8 + MOVL $186, AX // sigaltstack + MOVL new+4(SP), BX + MOVL old+8(SP), CX + INT $0x80 + CMPL AX, $0xfffff001 + JLS 2(PC) + INT $3 + RET + +// // fake the per-goroutine and per-mach registers +// LEAL m0(SB), + +// TODO(rsc): move to linux.s +// <asm-i386/ldt.h> +// struct user_desc { +// unsigned int entry_number; +// unsigned long base_addr; +// unsigned int limit; +// unsigned int seg_32bit:1; +// unsigned int contents:2; +// unsigned int read_exec_only:1; +// unsigned int limit_in_pages:1; +// unsigned int seg_not_present:1; +// unsigned int useable:1; +// }; +#define SEG_32BIT 0x01 +// contents are the 2 bits 0x02 and 0x04. +#define CONTENTS_DATA 0x00 +#define CONTENTS_STACK 0x02 +#define CONTENTS_CODE 0x04 +#define READ_EXEC_ONLY 0x08 +#define LIMIT_IN_PAGES 0x10 +#define SEG_NOT_PRESENT 0x20 +#define USEABLE 0x40 + +// setldt(int entry, int address, int limit) +TEXT setldt(SB),7,$32 + // set up user_desc + LEAL 16(SP), AX // struct user_desc + MOVL entry+0(FP), BX // entry + MOVL BX, 0(AX) + MOVL address+4(FP), BX // base address + MOVL BX, 4(AX) + MOVL limit+8(FP), BX // limit + MOVL BX, 8(AX) + MOVL $(SEG_32BIT|USEABLE|CONTENTS_DATA), 12(AX) // flag bits + + // call modify_ldt + MOVL $123, 0(SP) // syscall - modify_ldt + MOVL $1, 4(SP) // func = 1 (write) + MOVL AX, 8(SP) // user_desc + MOVL $16, 12(SP) // sizeof(user_desc) + CALL syscall(SB) + RET + diff --git a/src/pkg/runtime/linux/amd64/defs.h b/src/pkg/runtime/linux/amd64/defs.h new file mode 100644 index 000000000..43b047523 --- /dev/null +++ b/src/pkg/runtime/linux/amd64/defs.h @@ -0,0 +1,175 @@ +// godefs -f -m64 defs.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x20, + MAP_PRIVATE = 0x2, + SA_RESTART = 0x10000000, + SA_ONSTACK = 0x8000000, + SA_RESTORER = 0x4000000, + SA_SIGINFO = 0x4, +}; + +// Types +#pragma pack on + +typedef struct Timespec Timespec; +struct Timespec { + int64 tv_sec; + int64 tv_nsec; +}; + +typedef struct Timeval Timeval; +struct Timeval { + int64 tv_sec; + int64 tv_usec; +}; + +typedef struct Sigaction Sigaction; +struct Sigaction { + void *sa_handler; + uint64 sa_flags; + void *sa_restorer; + uint64 sa_mask; +}; + +typedef struct Siginfo Siginfo; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + byte pad0[4]; + byte _sifields[112]; +}; +#pragma pack off +// godefs -f -m64 defs1.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants + +// Types +#pragma pack on + +typedef struct Usigset Usigset; +struct Usigset { + uint64 __val[16]; +}; + +typedef struct Fpxreg Fpxreg; +struct Fpxreg { + uint16 significand[4]; + uint16 exponent; + uint16 padding[3]; +}; + +typedef struct Xmmreg Xmmreg; +struct Xmmreg { + uint32 element[4]; +}; + +typedef struct Fpstate Fpstate; +struct Fpstate { + uint16 cwd; + uint16 swd; + uint16 ftw; + uint16 fop; + uint64 rip; + uint64 rdp; + uint32 mxcsr; + uint32 mxcr_mask; + Fpxreg _st[8]; + Xmmreg _xmm[16]; + uint32 padding[24]; +}; + +typedef struct Fpxreg1 Fpxreg1; +struct Fpxreg1 { + uint16 significand[4]; + uint16 exponent; + uint16 padding[3]; +}; + +typedef struct Xmmreg1 Xmmreg1; +struct Xmmreg1 { + uint32 element[4]; +}; + +typedef struct Fpstate1 Fpstate1; +struct Fpstate1 { + uint16 cwd; + uint16 swd; + uint16 ftw; + uint16 fop; + uint64 rip; + uint64 rdp; + uint32 mxcsr; + uint32 mxcr_mask; + Fpxreg1 _st[8]; + Xmmreg1 _xmm[16]; + uint32 padding[24]; +}; + +typedef struct Sigaltstack Sigaltstack; +struct Sigaltstack { + void *ss_sp; + int32 ss_flags; + byte pad0[4]; + uint64 ss_size; +}; + +typedef struct Mcontext Mcontext; +struct Mcontext { + int64 gregs[23]; + Fpstate *fpregs; + uint64 __reserved1[8]; +}; + +typedef struct Ucontext Ucontext; +struct Ucontext { + uint64 uc_flags; + Ucontext *uc_link; + Sigaltstack uc_stack; + Mcontext uc_mcontext; + Usigset uc_sigmask; + Fpstate __fpregs_mem; +}; + +typedef struct Sigcontext Sigcontext; +struct Sigcontext { + uint64 r8; + uint64 r9; + uint64 r10; + uint64 r11; + uint64 r12; + uint64 r13; + uint64 r14; + uint64 r15; + uint64 rdi; + uint64 rsi; + uint64 rbp; + uint64 rbx; + uint64 rdx; + uint64 rax; + uint64 rcx; + uint64 rsp; + uint64 rip; + uint64 eflags; + uint16 cs; + uint16 gs; + uint16 fs; + uint16 __pad0; + uint64 err; + uint64 trapno; + uint64 oldmask; + uint64 cr2; + Fpstate1 *fpstate; + uint64 __reserved1[8]; +}; +#pragma pack off diff --git a/src/pkg/runtime/linux/amd64/rt0.s b/src/pkg/runtime/linux/amd64/rt0.s new file mode 100644 index 000000000..55be5bcee --- /dev/null +++ b/src/pkg/runtime/linux/amd64/rt0.s @@ -0,0 +1,9 @@ +// Copyright 2009 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. + +// Darwin and Linux use the same linkage to main + +TEXT _rt0_amd64_linux(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + JMP AX diff --git a/src/pkg/runtime/linux/amd64/signal.c b/src/pkg/runtime/linux/amd64/signal.c new file mode 100644 index 000000000..55215176d --- /dev/null +++ b/src/pkg/runtime/linux/amd64/signal.c @@ -0,0 +1,112 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs.h" +#include "signals.h" +#include "os.h" + +void +dumpregs(Sigcontext *r) +{ + printf("rax %X\n", r->rax); + printf("rbx %X\n", r->rbx); + printf("rcx %X\n", r->rcx); + printf("rdx %X\n", r->rdx); + printf("rdi %X\n", r->rdi); + printf("rsi %X\n", r->rsi); + printf("rbp %X\n", r->rbp); + printf("rsp %X\n", r->rsp); + printf("r8 %X\n", r->r8 ); + printf("r9 %X\n", r->r9 ); + printf("r10 %X\n", r->r10); + printf("r11 %X\n", r->r11); + printf("r12 %X\n", r->r12); + printf("r13 %X\n", r->r13); + printf("r14 %X\n", r->r14); + printf("r15 %X\n", r->r15); + printf("rip %X\n", r->rip); + printf("rflags %X\n", r->eflags); + printf("cs %X\n", (uint64)r->cs); + printf("fs %X\n", (uint64)r->fs); + printf("gs %X\n", (uint64)r->gs); +} + +/* + * This assembler routine takes the args from registers, puts them on the stack, + * and calls sighandler(). + */ +extern void sigtramp(void); +extern void sigignore(void); // just returns +extern void sigreturn(void); // calls sigreturn + +void +sighandler(int32 sig, Siginfo* info, void* context) +{ + Ucontext *uc; + Mcontext *mc; + Sigcontext *sc; + + if(panicking) // traceback already printed + exit(2); + panicking = 1; + + uc = context; + mc = &uc->uc_mcontext; + sc = (Sigcontext*)mc; // same layout, more conveient names + + if(sig < 0 || sig >= NSIG) + printf("Signal %d\n", sig); + else + printf("%s\n", sigtab[sig].name); + + printf("Faulting address: %p\n", *(void**)info->_sifields); + printf("PC=%X\n", sc->rip); + printf("\n"); + + if(gotraceback()){ + traceback((void*)sc->rip, (void*)sc->rsp, (void*)sc->r15); + tracebackothers((void*)sc->r15); + dumpregs(sc); + } + + breakpoint(); + exit(2); +} + +void +signalstack(byte *p, int32 n) +{ + Sigaltstack st; + + st.ss_sp = p; + st.ss_size = n; + st.ss_flags = 0; + sigaltstack(&st, nil); +} + +void +initsig(void) +{ + static Sigaction sa; + + int32 i; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESTORER; + sa.sa_mask = 0xFFFFFFFFFFFFFFFFULL; + sa.sa_restorer = (void*)sigreturn; + for(i = 0; i<NSIG; i++) { + if(sigtab[i].flags) { + if(sigtab[i].flags & SigCatch) + sa.sa_handler = (void*)sigtramp; + else + sa.sa_handler = (void*)sigignore; + if(sigtab[i].flags & SigRestart) + sa.sa_flags |= SA_RESTART; + else + sa.sa_flags &= ~SA_RESTART; + rt_sigaction(i, &sa, nil, 8); + } + } +} + diff --git a/src/pkg/runtime/linux/amd64/sys.s b/src/pkg/runtime/linux/amd64/sys.s new file mode 100644 index 000000000..f90c704fa --- /dev/null +++ b/src/pkg/runtime/linux/amd64/sys.s @@ -0,0 +1,193 @@ +// Copyright 2009 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. + +// +// System calls and other sys.stuff for AMD64, Linux +// + +TEXT exit(SB),7,$0-8 + MOVL 8(SP), DI + MOVL $231, AX // exitgroup - force all os threads to exi + SYSCALL + RET + +TEXT exit1(SB),7,$0-8 + MOVL 8(SP), DI + MOVL $60, AX // exit - exit the current os thread + SYSCALL + RET + +TEXT open(SB),7,$0-16 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVL $2, AX // syscall entry + SYSCALL + RET + +TEXT close(SB),7,$0-8 + MOVL 8(SP), DI + MOVL $3, AX // syscall entry + SYSCALL + RET + +TEXT fstat(SB),7,$0-16 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL $5, AX // syscall entry + SYSCALL + RET + +TEXT read(SB),7,$0-24 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $0, AX // syscall entry + SYSCALL + RET + +TEXT write(SB),7,$0-24 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $1, AX // syscall entry + SYSCALL + RET + +TEXT sys·write(SB),7,$0-24 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVL 24(SP), DX + MOVL $1, AX // syscall entry + SYSCALL + RET + +TEXT rt_sigaction(SB),7,$0-32 + MOVL 8(SP), DI + MOVQ 16(SP), SI + MOVQ 24(SP), DX + MOVQ 32(SP), R10 + MOVL $13, AX // syscall entry + SYSCALL + RET + +TEXT sigtramp(SB),7,$24-16 + MOVQ 32(R14), R15 // g = m->gsignal + MOVQ DI,0(SP) + MOVQ SI,8(SP) + MOVQ DX,16(SP) + CALL sighandler(SB) + RET + +TEXT sigignore(SB),7,$0 + RET + +TEXT sigreturn(SB),7,$0 + MOVL $15, AX // rt_sigreturn + SYSCALL + INT $3 // not reached + +TEXT sys·mmap(SB),7,$0-32 + MOVQ 8(SP), DI + MOVQ $0, SI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVL 24(SP), R10 + MOVL 28(SP), R8 + MOVL 32(SP), R9 + + MOVL $9, AX // syscall entry + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS 2(PC) + CALL notok(SB) + RET + +TEXT notok(SB),7,$0 + MOVQ $0xf1, BP + MOVQ BP, (BP) + RET + +TEXT sys·memclr(SB),7,$0-16 + MOVQ 8(SP), DI // arg 1 addr + MOVL 16(SP), CX // arg 2 count (cannot be zero) + ADDL $7, CX + SHRL $3, CX + MOVQ $0, AX + CLD + REP + STOSQ + RET + +TEXT sys·getcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ -8(AX),AX // get calling pc + RET + +TEXT sys·setcallerpc+0(SB),7,$0 + MOVQ x+0(FP),AX // addr of first arg + MOVQ x+8(FP), BX + MOVQ BX, -8(AX) // set calling pc + RET + +// int64 futex(int32 *uaddr, int32 op, int32 val, +// struct timespec *timeout, int32 *uaddr2, int32 val2); +TEXT futex(SB),7,$0 + MOVQ 8(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), DX + MOVQ 24(SP), R10 + MOVQ 32(SP), R8 + MOVL 40(SP), R9 + MOVL $202, AX + SYSCALL + RET + +// int64 clone(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); +TEXT clone(SB),7,$0 + MOVL flags+8(SP), DI + MOVQ stack+16(SP), SI + + // Copy m, g, fn off parent stack for use by child. + // Careful: Linux system call clobbers CX and R11. + MOVQ m+24(SP), R8 + MOVQ g+32(SP), R9 + MOVQ fn+40(SP), R12 + + MOVL $56, AX + SYSCALL + + // In parent, return. + CMPQ AX, $0 + JEQ 2(PC) + RET + + // In child, set up new stack + MOVQ SI, SP + MOVQ R8, R14 // m + MOVQ R9, R15 // g + + // Initialize m->procid to Linux tid + MOVL $186, AX // gettid + SYSCALL + MOVQ AX, 24(R14) + + // Call fn + CALL R12 + + // It shouldn't return. If it does, exi + MOVL $111, DI + MOVL $60, AX + SYSCALL + JMP -3(PC) // keep exiting + +TEXT sigaltstack(SB),7,$-8 + MOVQ new+8(SP), DI + MOVQ old+16(SP), SI + MOVQ $131, AX + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS 2(PC) + CALL notok(SB) + RET diff --git a/src/pkg/runtime/linux/arm/defs.h b/src/pkg/runtime/linux/arm/defs.h new file mode 100644 index 000000000..caad66989 --- /dev/null +++ b/src/pkg/runtime/linux/arm/defs.h @@ -0,0 +1,27 @@ +// godefs -carm-gcc -f -I/usr/local/google/src/linux-2.6.28/arch/arm/include -f -I/usr/local/google/src/linux-2.6.28/include defs_arm.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x20, + MAP_PRIVATE = 0x2, + SA_RESTART = 0x10000000, + SA_ONSTACK = 0x8000000, + SA_RESTORER = 0x4000000, + SA_SIGINFO = 0x4, +}; + +// Types +#pragma pack on + +typedef struct Timespec Timespec; +struct Timespec { + int32 tv_sec; + int32 tv_nsec; +}; +#pragma pack off diff --git a/src/pkg/runtime/linux/arm/rt0.s b/src/pkg/runtime/linux/arm/rt0.s new file mode 100644 index 000000000..024547ddd --- /dev/null +++ b/src/pkg/runtime/linux/arm/rt0.s @@ -0,0 +1,6 @@ +// Copyright 2009 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. + +TEXT _rt0_arm_linux(SB),7,$0 + B _rt0_arm(SB) diff --git a/src/pkg/runtime/linux/arm/signal.c b/src/pkg/runtime/linux/arm/signal.c new file mode 100644 index 000000000..024018d5a --- /dev/null +++ b/src/pkg/runtime/linux/arm/signal.c @@ -0,0 +1,4 @@ +// Copyright 2009 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. + diff --git a/src/pkg/runtime/linux/arm/sys.s b/src/pkg/runtime/linux/arm/sys.s new file mode 100644 index 000000000..f5db32305 --- /dev/null +++ b/src/pkg/runtime/linux/arm/sys.s @@ -0,0 +1,15 @@ +// Copyright 2009 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. + +// +// System calls and other sys.stuff for arm, Linux +// + +TEXT write(SB),7,$0 + MOVW 4(SP), R0 + MOVW 8(SP), R1 + MOVW 12(SP), R2 + SWI $0x00900004 // syscall write + RET + diff --git a/src/pkg/runtime/linux/defs.c b/src/pkg/runtime/linux/defs.c new file mode 100644 index 000000000..35fa02953 --- /dev/null +++ b/src/pkg/runtime/linux/defs.c @@ -0,0 +1,40 @@ +// Copyright 2009 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. + +/* + * Input to godefs + godefs -f -m64 defs.c >amd64/defs.h + godefs -f -m64 defs1.c >>amd64/defs.h + */ + +// Linux glibc and Linux kernel define different and conflicting +// definitions for struct sigaction, struct timespec, etc. +// We want the kernel ones, which are in the asm/* headers. +// But then we'd get conflicts when we include the system +// headers for things like ucontext_t, so that happens in +// a separate file, defs1.c. + +#include <asm/signal.h> +#include <asm/siginfo.h> +#include <asm/mman.h> + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANONYMOUS, + $MAP_PRIVATE = MAP_PRIVATE, + + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_RESTORER = SA_RESTORER, + $SA_SIGINFO = SA_SIGINFO, +}; + +typedef struct timespec $Timespec; +typedef struct timeval $Timeval; +typedef struct sigaction $Sigaction; +typedef siginfo_t $Siginfo; diff --git a/src/pkg/runtime/linux/defs1.c b/src/pkg/runtime/linux/defs1.c new file mode 100644 index 000000000..0fe3506ad --- /dev/null +++ b/src/pkg/runtime/linux/defs1.c @@ -0,0 +1,25 @@ +// Copyright 2009 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. + +/* + * Input to godefs + godefs -f -m64 defs.c >amd64/defs.h + godefs -f -m64 defs1.c >>amd64/defs.h + */ + +#include <ucontext.h> + +typedef __sigset_t $Usigset; +typedef struct _libc_fpxreg $Fpxreg; +typedef struct _libc_xmmreg $Xmmreg; +typedef struct _libc_fpstate $Fpstate; +typedef struct _libc_fpreg $Fpreg; +typedef struct _fpxreg $Fpxreg1; +typedef struct _xmmreg $Xmmreg1; +typedef struct _fpstate $Fpstate1; +typedef struct _fpreg $Fpreg1; +typedef struct sigaltstack $Sigaltstack; +typedef mcontext_t $Mcontext; +typedef ucontext_t $Ucontext; +typedef struct sigcontext $Sigcontext; diff --git a/src/pkg/runtime/linux/defs2.c b/src/pkg/runtime/linux/defs2.c new file mode 100644 index 000000000..aa0331a37 --- /dev/null +++ b/src/pkg/runtime/linux/defs2.c @@ -0,0 +1,51 @@ +// Copyright 2009 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. + +/* + * Input to godefs + godefs -f -m32 -f -I/home/rsc/pub/linux-2.6/arch/x86/include -f -I/home/rsc/pub/linux-2.6/include defs2.c >386/defs.h + + * The asm header tricks we have to use for Linux on amd64 + * (see defs.c and defs1.c) don't work here, so this is yet another + * file. Sigh. + */ + +#include <asm/signal.h> +#include <asm/mman.h> +#include <asm/sigframe.h> +#include <asm/ucontext.h> + +/* +#include <sys/signal.h> +#include <sys/mman.h> +#include <ucontext.h> +*/ + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANONYMOUS, + $MAP_PRIVATE = MAP_PRIVATE, + + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_RESTORER = SA_RESTORER, + $SA_SIGINFO = SA_SIGINFO, +}; + +typedef struct _fpreg $Fpreg; +typedef struct _fpxreg $Fpxreg; +typedef struct _xmmreg $Xmmreg; +typedef struct _fpstate $Fpstate; +typedef struct timespec $Timespec; +typedef struct timeval $Timeval; +typedef struct sigaction $Sigaction; +typedef siginfo_t $Siginfo; +typedef struct sigaltstack $Sigaltstack; +typedef struct sigcontext $Sigcontext; +typedef struct ucontext $Ucontext; + diff --git a/src/pkg/runtime/linux/defs_arm.c b/src/pkg/runtime/linux/defs_arm.c new file mode 100644 index 000000000..eaec05154 --- /dev/null +++ b/src/pkg/runtime/linux/defs_arm.c @@ -0,0 +1,54 @@ +// Copyright 2009 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. + +/* + * Input to godefs + godefs -carm-gcc -f -I/usr/local/google/src/linux-2.6.28/arch/arm/include -f + -I/usr/local/google/src/linux-2.6.28/include defs_arm.c >arm/defs.h + + * Another input file for ARM defs.h + */ + +#include <asm/signal.h> +#include <asm/mman.h> +#include <asm/sigcontext.h> +#include <asm/ucontext.h> + +/* +#include <sys/signal.h> +#include <sys/mman.h> +#include <ucontext.h> +*/ + +#include <time.h> + +enum { + $PROT_NONE = PROT_NONE, + $PROT_READ = PROT_READ, + $PROT_WRITE = PROT_WRITE, + $PROT_EXEC = PROT_EXEC, + + $MAP_ANON = MAP_ANONYMOUS, + $MAP_PRIVATE = MAP_PRIVATE, + + $SA_RESTART = SA_RESTART, + $SA_ONSTACK = SA_ONSTACK, + $SA_RESTORER = SA_RESTORER, + $SA_SIGINFO = SA_SIGINFO +}; + + + + +//typedef struct _fpreg $Fpreg; +//typedef struct _fpxreg $Fpxreg; +//typedef struct _xmmreg $Xmmreg; +//typedef struct _fpstate $Fpstate; +typedef struct timespec $Timespec; +//typedef struct timeval $Timeval; +// typedef struct sigaction $Sigaction; +// typedef siginfo_t $Siginfo; +// typedef struct sigaltstack $Sigaltstack; +// typedef struct sigcontext $Sigcontext; +// typedef struct ucontext $Ucontext; diff --git a/src/pkg/runtime/linux/os.h b/src/pkg/runtime/linux/os.h new file mode 100644 index 000000000..c61619367 --- /dev/null +++ b/src/pkg/runtime/linux/os.h @@ -0,0 +1,10 @@ +// Copyright 2009 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. + +// Linux-specific system calls +int64 futex(uint32*, int32, uint32, Timespec*, uint32*, uint32); +int64 clone(int32, void*, M*, G*, void(*)(void)); + +struct Sigaction; +void rt_sigaction(int64, struct Sigaction*, void*, uint64); diff --git a/src/pkg/runtime/linux/signals.h b/src/pkg/runtime/linux/signals.h new file mode 100644 index 000000000..8f1112b99 --- /dev/null +++ b/src/pkg/runtime/linux/signals.h @@ -0,0 +1,47 @@ +// Copyright 2009 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. + +#define C SigCatch +#define I SigIgnore +#define R SigRestart + +static SigTab sigtab[] = { + /* 0 */ 0, "SIGNONE: no trap", + /* 1 */ 0, "SIGHUP: terminal line hangup", + /* 2 */ 0, "SIGINT: interrupt", + /* 3 */ C, "SIGQUIT: quit", + /* 4 */ C, "SIGILL: illegal instruction", + /* 5 */ C, "SIGTRAP: trace trap", + /* 6 */ C, "SIGABRT: abort", + /* 7 */ C, "SIGBUS: bus error", + /* 8 */ C, "SIGFPE: floating-point exception", + /* 9 */ 0, "SIGKILL: kill", + /* 10 */ 0, "SIGUSR1: user-defined signal 1", + /* 11 */ C, "SIGSEGV: segmentation violation", + /* 12 */ 0, "SIGUSR2: user-defined signal 2", + /* 13 */ I, "SIGPIPE: write to broken pipe", + /* 14 */ 0, "SIGALRM: alarm clock", + /* 15 */ 0, "SIGTERM: termination", + /* 16 */ 0, "SIGSTKFLT: stack fault", + /* 17 */ I+R, "SIGCHLD: child status has changed", + /* 18 */ 0, "SIGCONT: continue", + /* 19 */ 0, "SIGSTOP: stop, unblockable", + /* 20 */ 0, "SIGTSTP: keyboard stop", + /* 21 */ 0, "SIGTTIN: background read from tty", + /* 22 */ 0, "SIGTTOU: background write to tty", + /* 23 */ 0, "SIGURG: urgent condition on socket", + /* 24 */ 0, "SIGXCPU: cpu limit exceeded", + /* 25 */ 0, "SIGXFSZ: file size limit exceeded", + /* 26 */ 0, "SIGVTALRM: virtual alarm clock", + /* 27 */ 0, "SIGPROF: profiling alarm clock", + /* 28 */ I+R, "SIGWINCH: window size change", + /* 29 */ 0, "SIGIO: i/o now possible", + /* 30 */ 0, "SIGPWR: power failure restart", + /* 31 */ C, "SIGSYS: bad system call", +}; +#undef C +#undef I +#undef R + +#define NSIG 32 diff --git a/src/pkg/runtime/linux/thread.c b/src/pkg/runtime/linux/thread.c new file mode 100644 index 000000000..cc9ba161b --- /dev/null +++ b/src/pkg/runtime/linux/thread.c @@ -0,0 +1,282 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs.h" +#include "signals.h" +#include "os.h" + +// Linux futex. +// +// futexsleep(uint32 *addr, uint32 val) +// futexwakeup(uint32 *addr) +// +// Futexsleep atomically checks if *addr == val and if so, sleeps on addr. +// Futexwakeup wakes up one thread sleeping on addr. +// Futexsleep is allowed to wake up spuriously. + +enum +{ + FUTEX_WAIT = 0, + FUTEX_WAKE = 1, + + EINTR = 4, + EAGAIN = 11, +}; + +// TODO(rsc): I tried using 1<<40 here but futex woke up (-ETIMEDOUT). +// I wonder if the timespec that gets to the kernel +// actually has two 32-bit numbers in it, so that +// a 64-bit 1<<40 ends up being 0 seconds, +// 1<<8 nanoseconds. +static Timespec longtime = +{ + 1<<30, // 34 years + 0 +}; + +// Atomically, +// if(*addr == val) sleep +// Might be woken up spuriously; that's allowed. +static void +futexsleep(uint32 *addr, uint32 val) +{ + int64 ret; + + ret = futex(addr, FUTEX_WAIT, val, &longtime, nil, 0); + if(ret >= 0 || ret == -EAGAIN || ret == -EINTR) + return; + + prints("futexsleep addr="); + sys·printpointer(addr); + prints(" val="); + sys·printint(val); + prints(" returned "); + sys·printint(ret); + prints("\n"); + *(int32*)0x1005 = 0x1005; +} + +// If any procs are sleeping on addr, wake up at least one. +static void +futexwakeup(uint32 *addr) +{ + int64 ret; + + ret = futex(addr, FUTEX_WAKE, 1, nil, nil, 0); + + if(ret >= 0) + return; + + // I don't know that futex wakeup can return + // EAGAIN or EINTR, but if it does, it would be + // safe to loop and call futex again. + + prints("futexwakeup addr="); + sys·printpointer(addr); + prints(" returned "); + sys·printint(ret); + prints("\n"); + *(int32*)0x1006 = 0x1006; +} + + +// Lock and unlock. +// +// The lock state is a single 32-bit word that holds +// a 31-bit count of threads waiting for the lock +// and a single bit (the low bit) saying whether the lock is held. +// The uncontended case runs entirely in user space. +// When contention is detected, we defer to the kernel (futex). +// +// A reminder: compare-and-swap cas(addr, old, new) does +// if(*addr == old) { *addr = new; return 1; } +// else return 0; +// but atomically. + +static void +futexlock(Lock *l) +{ + uint32 v; + +again: + v = l->key; + if((v&1) == 0){ + if(cas(&l->key, v, v|1)){ + // Lock wasn't held; we grabbed it. + return; + } + goto again; + } + + // Lock was held; try to add ourselves to the waiter count. + if(!cas(&l->key, v, v+2)) + goto again; + + // We're accounted for, now sleep in the kernel. + // + // We avoid the obvious lock/unlock race because + // the kernel won't put us to sleep if l->key has + // changed underfoot and is no longer v+2. + // + // We only really care that (v&1) == 1 (the lock is held), + // and in fact there is a futex variant that could + // accomodate that check, but let's not get carried away.) + futexsleep(&l->key, v+2); + + // We're awake: remove ourselves from the count. + for(;;){ + v = l->key; + if(v < 2) + throw("bad lock key"); + if(cas(&l->key, v, v-2)) + break; + } + + // Try for the lock again. + goto again; +} + +static void +futexunlock(Lock *l) +{ + uint32 v; + + // Atomically get value and clear lock bit. +again: + v = l->key; + if((v&1) == 0) + throw("unlock of unlocked lock"); + if(!cas(&l->key, v, v&~1)) + goto again; + + // If there were waiters, wake one. + if(v & ~1) + futexwakeup(&l->key); +} + +void +lock(Lock *l) +{ + if(m->locks < 0) + throw("lock count"); + m->locks++; + futexlock(l); +} + +void +unlock(Lock *l) +{ + m->locks--; + if(m->locks < 0) + throw("lock count"); + futexunlock(l); +} + + +// One-time notifications. +// +// Since the lock/unlock implementation already +// takes care of sleeping in the kernel, we just reuse it. +// (But it's a weird use, so it gets its own interface.) +// +// We use a lock to represent the event: +// unlocked == event has happened. +// Thus the lock starts out locked, and to wait for the +// event you try to lock the lock. To signal the event, +// you unlock the lock. + +void +noteclear(Note *n) +{ + n->lock.key = 0; // memset(n, 0, sizeof *n) + futexlock(&n->lock); +} + +void +notewakeup(Note *n) +{ + futexunlock(&n->lock); +} + +void +notesleep(Note *n) +{ + futexlock(&n->lock); + futexunlock(&n->lock); // Let other sleepers find out too. +} + + +// Clone, the Linux rfork. +enum +{ + CLONE_VM = 0x100, + CLONE_FS = 0x200, + CLONE_FILES = 0x400, + CLONE_SIGHAND = 0x800, + CLONE_PTRACE = 0x2000, + CLONE_VFORK = 0x4000, + CLONE_PARENT = 0x8000, + CLONE_THREAD = 0x10000, + CLONE_NEWNS = 0x20000, + CLONE_SYSVSEM = 0x40000, + CLONE_SETTLS = 0x80000, + CLONE_PARENT_SETTID = 0x100000, + CLONE_CHILD_CLEARTID = 0x200000, + CLONE_UNTRACED = 0x800000, + CLONE_CHILD_SETTID = 0x1000000, + CLONE_STOPPED = 0x2000000, + CLONE_NEWUTS = 0x4000000, + CLONE_NEWIPC = 0x8000000, +}; + +void +newosproc(M *m, G *g, void *stk, void (*fn)(void)) +{ + int64 ret; + int32 flags; + + /* + * note: strace gets confused if we use CLONE_PTRACE here. + */ + flags = CLONE_PARENT /* getppid doesn't change in child */ + | CLONE_VM /* share memory */ + | CLONE_FS /* share cwd, etc */ + | CLONE_FILES /* share fd table */ + | CLONE_SIGHAND /* share sig handler table */ + | CLONE_THREAD /* revisit - okay for now */ + ; + + if(0){ + prints("newosproc stk="); + sys·printpointer(stk); + prints(" m="); + sys·printpointer(m); + prints(" g="); + sys·printpointer(g); + prints(" fn="); + sys·printpointer(fn); + prints(" clone="); + sys·printpointer(clone); + prints("\n"); + } + + ret = clone(flags, stk, m, g, fn); + if(ret < 0) + *(int32*)123 = 123; +} + +void +osinit(void) +{ +} + +// Called to initialize a new m (including the bootstrap m). +void +minit(void) +{ + // Initialize signal handling. + m->gsignal = malg(32*1024); // OS X wants >=8K, Linux >=2K + signalstack(m->gsignal->stackguard, 32*1024); +} diff --git a/src/pkg/runtime/malloc.c b/src/pkg/runtime/malloc.c new file mode 100644 index 000000000..81cdfb300 --- /dev/null +++ b/src/pkg/runtime/malloc.c @@ -0,0 +1,308 @@ +// Copyright 2009 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. + +// See malloc.h for overview. +// +// TODO(rsc): double-check stats. +// TODO(rsc): solve "stack overflow during malloc" problem. + +#include "runtime.h" +#include "malloc.h" +#include "defs.h" + +MHeap mheap; +MStats mstats; + +// Allocate an object of at least size bytes. +// Small objects are allocated from the per-thread cache's free lists. +// Large objects (> 32 kB) are allocated straight from the heap. +void* +malloc(uintptr size) +{ + int32 sizeclass; + MCache *c; + uintptr npages; + MSpan *s; + void *v; + uint32 *ref; + + if(m->mallocing) + throw("malloc/free - deadlock"); + m->mallocing = 1; + + if(size == 0) + size = 1; + + if(size <= MaxSmallSize) { + // Allocate from mcache free lists. + sizeclass = SizeToClass(size); + size = class_to_size[sizeclass]; + c = m->mcache; + v = MCache_Alloc(c, sizeclass, size); + if(v == nil) + throw("out of memory"); + mstats.alloc += size; + } else { + // TODO(rsc): Report tracebacks for very large allocations. + + // Allocate directly from heap. + npages = size >> PageShift; + if((size & PageMask) != 0) + npages++; + s = MHeap_Alloc(&mheap, npages, 0); + if(s == nil) + throw("out of memory"); + mstats.alloc += npages<<PageShift; + v = (void*)(s->start << PageShift); + } + + // setup for mark sweep + if(!mlookup(v, nil, nil, &ref)) { + printf("malloc %D; mlookup failed\n", (uint64)size); + throw("malloc mlookup"); + } + *ref = RefNone; + + m->mallocing = 0; + return v; +} + +void* +mallocgc(uintptr size) +{ + void *v; + + v = malloc(size); + if(mstats.inuse_pages > mstats.next_gc) + gc(0); + return v; +} + +// Free the object whose base pointer is v. +void +free(void *v) +{ + int32 sizeclass, size; + uintptr page, tmp; + MSpan *s; + MCache *c; + uint32 *ref; + + if(v == nil) + return; + + if(m->mallocing) + throw("malloc/free - deadlock"); + m->mallocing = 1; + + if(!mlookup(v, nil, nil, &ref)) + throw("free mlookup"); + *ref = RefFree; + + // Find size class for v. + page = (uintptr)v >> PageShift; + sizeclass = MHeapMapCache_GET(&mheap.mapcache, page, tmp); + if(sizeclass == 0) { + // Missed in cache. + s = MHeap_Lookup(&mheap, page); + if(s == nil) + throw("free - invalid pointer"); + sizeclass = s->sizeclass; + if(sizeclass == 0) { + // Large object. + mstats.alloc -= s->npages<<PageShift; + sys_memclr(v, s->npages<<PageShift); + MHeap_Free(&mheap, s); + goto out; + } + MHeapMapCache_SET(&mheap.mapcache, page, sizeclass); + } + + // Small object. + c = m->mcache; + size = class_to_size[sizeclass]; + sys_memclr(v, size); + mstats.alloc -= size; + MCache_Free(c, v, sizeclass, size); + +out: + m->mallocing = 0; +} + +int32 +mlookup(void *v, byte **base, uintptr *size, uint32 **ref) +{ + uintptr n, nobj, i; + byte *p; + MSpan *s; + + s = MHeap_LookupMaybe(&mheap, (uintptr)v>>PageShift); + if(s == nil) { + if(base) + *base = nil; + if(size) + *size = 0; + if(ref) + *ref = 0; + return 0; + } + + p = (byte*)((uintptr)s->start<<PageShift); + if(s->sizeclass == 0) { + // Large object. + if(base) + *base = p; + if(size) + *size = s->npages<<PageShift; + if(ref) + *ref = &s->gcref0; + return 1; + } + + if((byte*)v >= (byte*)s->gcref) { + // pointers into the gc ref counts + // do not count as pointers. + return 0; + } + + n = class_to_size[s->sizeclass]; + i = ((byte*)v - p)/n; + if(base) + *base = p + i*n; + if(size) + *size = n; + nobj = (s->npages << PageShift) / (n + RefcountOverhead); + if((byte*)s->gcref < p || (byte*)(s->gcref+nobj) > p+(s->npages<<PageShift)) { + printf("odd span state=%d span=%p base=%p sizeclass=%d n=%D size=%D npages=%D\n", + s->state, s, p, s->sizeclass, (uint64)nobj, (uint64)n, (uint64)s->npages); + printf("s->base sizeclass %d v=%p base=%p gcref=%p blocksize=%D nobj=%D size=%D end=%p end=%p\n", + s->sizeclass, v, p, s->gcref, (uint64)s->npages<<PageShift, + (uint64)nobj, (uint64)n, s->gcref + nobj, p+(s->npages<<PageShift)); + throw("bad gcref"); + } + if(ref) + *ref = &s->gcref[i]; + + return 1; +} + +MCache* +allocmcache(void) +{ + return FixAlloc_Alloc(&mheap.cachealloc); +} + +void +mallocinit(void) +{ + InitSizes(); + MHeap_Init(&mheap, SysAlloc); + m->mcache = allocmcache(); + + // See if it works. + free(malloc(1)); +} + +void* +SysAlloc(uintptr n) +{ + mstats.sys += n; + return sys_mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); +} + +void +SysUnused(void *v, uintptr n) +{ + USED(v); + USED(n); + // TODO(rsc): call madvise MADV_DONTNEED +} + +void +SysFree(void *v, uintptr n) +{ + USED(v); + USED(n); + // TODO(rsc): call munmap +} + +// Runtime stubs. + +extern void *oldmal(uint32); + +void* +mal(uint32 n) +{ +//return oldmal(n); + void *v; + + v = mallocgc(n); + + if(0) { + byte *p; + uint32 i; + p = v; + for(i=0; i<n; i++) { + if(p[i] != 0) { + printf("mal %d => %p: byte %d is non-zero\n", n, v, i); + throw("mal"); + } + } + } + +//printf("mal %d %p\n", n, v); // |checkmal to check for overlapping returns. + return v; +} + +// Stack allocator uses malloc/free most of the time, +// but if we're in the middle of malloc and need stack, +// we have to do something else to avoid deadlock. +// In that case, we fall back on a fixed-size free-list +// allocator, assuming that inside malloc all the stack +// frames are small, so that all the stack allocations +// will be a single size, the minimum (right now, 5k). +struct { + Lock; + FixAlloc; +} stacks; + +void* +stackalloc(uint32 n) +{ + void *v; + uint32 *ref; + +//return oldmal(n); + if(m->mallocing) { + lock(&stacks); + if(stacks.size == 0) + FixAlloc_Init(&stacks, n, SysAlloc, nil, nil); + if(stacks.size != n) { + printf("stackalloc: in malloc, size=%D want %d", (uint64)stacks.size, n); + throw("stackalloc"); + } + v = FixAlloc_Alloc(&stacks); + unlock(&stacks); + return v; + } + v = malloc(n); + if(!mlookup(v, nil, nil, &ref)) + throw("stackalloc mlookup"); + *ref = RefStack; + return v; +} + +void +stackfree(void *v) +{ +//return; + + if(m->mallocing) { + lock(&stacks); + FixAlloc_Free(&stacks, v); + unlock(&stacks); + return; + } + free(v); +} diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h new file mode 100644 index 000000000..5b657a495 --- /dev/null +++ b/src/pkg/runtime/malloc.h @@ -0,0 +1,308 @@ +// Copyright 2009 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. + +// Memory allocator, based on tcmalloc. +// http://goog-perftools.sourceforge.net/doc/tcmalloc.html + +// The main allocator works in runs of pages. +// Small allocation sizes (up to and including 32 kB) are +// rounded to one of about 100 size classes, each of which +// has its own free list of objects of exactly that size. +// Any free page of memory can be split into a set of objects +// of one size class, which are then managed using free list +// allocators. +// +// The allocator's data structures are: +// +// FixAlloc: a free-list allocator for fixed-size objects, +// used to manage storage used by the allocator. +// MHeap: the malloc heap, managed at page (4096-byte) granularity. +// MSpan: a run of pages managed by the MHeap. +// MHeapMap: a mapping from page IDs to MSpans. +// MHeapMapCache: a small cache of MHeapMap mapping page IDs +// to size classes for pages used for small objects. +// MCentral: a shared free list for a given size class. +// MCache: a per-thread (in Go, per-M) cache for small objects. +// MStats: allocation statistics. +// +// Allocating a small object proceeds up a hierarchy of caches: +// +// 1. Round the size up to one of the small size classes +// and look in the corresponding MCache free list. +// If the list is not empty, allocate an object from it. +// This can all be done without acquiring a lock. +// +// 2. If the MCache free list is empty, replenish it by +// taking a bunch of objects from the MCentral free list. +// Moving a bunch amortizes the cost of acquiring the MCentral lock. +// +// 3. If the MCentral free list is empty, replenish it by +// allocating a run of pages from the MHeap and then +// chopping that memory into a objects of the given size. +// Allocating many objects amortizes the cost of locking +// the heap. +// +// 4. If the MHeap is empty or has no page runs large enough, +// allocate a new group of pages (at least 1MB) from the +// operating system. Allocating a large run of pages +// amortizes the cost of talking to the operating system. +// +// Freeing a small object proceeds up the same hierarchy: +// +// 1. Look up the size class for the object and add it to +// the MCache free list. +// +// 2. If the MCache free list is too long or the MCache has +// too much memory, return some to the MCentral free lists. +// +// 3. If all the objects in a given span have returned to +// the MCentral list, return that span to the page heap. +// +// 4. If the heap has too much memory, return some to the +// operating system. +// +// TODO(rsc): Step 4 is not implemented. +// +// Allocating and freeing a large object uses the page heap +// directly, bypassing the MCache and MCentral free lists. +// +// This C code was written with an eye toward translating to Go +// in the future. Methods have the form Type_Method(Type *t, ...). + + +typedef struct FixAlloc FixAlloc; +typedef struct MCentral MCentral; +typedef struct MHeap MHeap; +typedef struct MHeapMap MHeapMap; +typedef struct MHeapMapCache MHeapMapCache; +typedef struct MSpan MSpan; +typedef struct MStats MStats; +typedef struct MLink MLink; + +enum +{ + PageShift = 12, + PageSize = 1<<PageShift, + PageMask = PageSize - 1, +}; +typedef uintptr PageID; // address >> PageShift + +enum +{ + // Tunable constants. + NumSizeClasses = 67, // Number of size classes (must match msize.c) + MaxSmallSize = 32<<10, + + FixAllocChunk = 128<<10, // Chunk size for FixAlloc + MaxMCacheListLen = 256, // Maximum objects on MCacheList + MaxMCacheSize = 2<<20, // Maximum bytes in one MCache + MaxMHeapList = 1<<(20 - PageShift), // Maximum page length for fixed-size list in MHeap. + HeapAllocChunk = 1<<20, // Chunk size for heap growth +}; + +#ifdef _64BIT +#include "mheapmap64.h" +#else +#include "mheapmap32.h" +#endif + +// A generic linked list of blocks. (Typically the block is bigger than sizeof(MLink).) +struct MLink +{ + MLink *next; +}; + +// SysAlloc obtains a large chunk of memory from the operating system, +// typically on the order of a hundred kilobytes or a megabyte. +// +// SysUnused notifies the operating system that the contents +// of the memory region are no longer needed and can be reused +// for other purposes. The program reserves the right to start +// accessing those pages in the future. +// +// SysFree returns it unconditionally; this is only used if +// an out-of-memory error has been detected midway through +// an allocation. It is okay if SysFree is a no-op. + +void* SysAlloc(uintptr nbytes); +void SysFree(void *v, uintptr nbytes); +void SysUnused(void *v, uintptr nbytes); + + +// FixAlloc is a simple free-list allocator for fixed size objects. +// Malloc uses a FixAlloc wrapped around SysAlloc to manages its +// MCache and MSpan objects. +// +// Memory returned by FixAlloc_Alloc is not zeroed. +// The caller is responsible for locking around FixAlloc calls. +// Callers can keep state in the object but the first word is +// smashed by freeing and reallocating. +struct FixAlloc +{ + uintptr size; + void *(*alloc)(uintptr); + void (*first)(void *arg, byte *p); // called first time p is returned + void *arg; + MLink *list; + byte *chunk; + uint32 nchunk; +}; + +void FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg); +void* FixAlloc_Alloc(FixAlloc *f); +void FixAlloc_Free(FixAlloc *f, void *p); + + +// Statistics. +// Shared with Go: if you edit this structure, also edit ../malloc/malloc.go. +struct MStats +{ + uint64 alloc; + uint64 sys; + uint64 stacks; + uint64 inuse_pages; // protected by mheap.Lock + uint64 next_gc; // protected by mheap.Lock + bool enablegc; +}; +extern MStats mstats; + + +// Size classes. Computed and initialized by InitSizes. +// +// SizeToClass(0 <= n <= MaxSmallSize) returns the size class, +// 1 <= sizeclass < NumSizeClasses, for n. +// Size class 0 is reserved to mean "not small". +// +// class_to_size[i] = largest size in class i +// class_to_allocnpages[i] = number of pages to allocate when +// making new objects in class i +// class_to_transfercount[i] = number of objects to move when +// taking a bunch of objects out of the central lists +// and putting them in the thread free list. + +int32 SizeToClass(int32); +extern int32 class_to_size[NumSizeClasses]; +extern int32 class_to_allocnpages[NumSizeClasses]; +extern int32 class_to_transfercount[NumSizeClasses]; +extern void InitSizes(void); + + +// Per-thread (in Go, per-M) cache for small objects. +// No locking needed because it is per-thread (per-M). +typedef struct MCacheList MCacheList; +struct MCacheList +{ + MLink *list; + uint32 nlist; + uint32 nlistmin; +}; + +struct MCache +{ + MCacheList list[NumSizeClasses]; + uint64 size; +}; + +void* MCache_Alloc(MCache *c, int32 sizeclass, uintptr size); +void MCache_Free(MCache *c, void *p, int32 sizeclass, uintptr size); + + +// An MSpan is a run of pages. +enum +{ + MSpanInUse = 0, + MSpanFree, + MSpanListHead, + MSpanDead, +}; +struct MSpan +{ + MSpan *next; // in a span linked list + MSpan *prev; // in a span linked list + MSpan *allnext; // in the list of all spans + PageID start; // starting page number + uintptr npages; // number of pages in span + MLink *freelist; // list of free objects + uint32 ref; // number of allocated objects in this span + uint32 sizeclass; // size class + uint32 state; // MSpanInUse etc + union { + uint32 *gcref; // sizeclass > 0 + uint32 gcref0; // sizeclass == 0 + }; +}; + +void MSpan_Init(MSpan *span, PageID start, uintptr npages); + +// Every MSpan is in one doubly-linked list, +// either one of the MHeap's free lists or one of the +// MCentral's span lists. We use empty MSpan structures as list heads. +void MSpanList_Init(MSpan *list); +bool MSpanList_IsEmpty(MSpan *list); +void MSpanList_Insert(MSpan *list, MSpan *span); +void MSpanList_Remove(MSpan *span); // from whatever list it is in + + +// Central list of free objects of a given size. +struct MCentral +{ + Lock; + int32 sizeclass; + MSpan nonempty; + MSpan empty; + int32 nfree; +}; + +void MCentral_Init(MCentral *c, int32 sizeclass); +int32 MCentral_AllocList(MCentral *c, int32 n, MLink **first); +void MCentral_FreeList(MCentral *c, int32 n, MLink *first); + +// Main malloc heap. +// The heap itself is the "free[]" and "large" arrays, +// but all the other global data is here too. +struct MHeap +{ + Lock; + MSpan free[MaxMHeapList]; // free lists of given length + MSpan large; // free lists length >= MaxMHeapList + MSpan *allspans; + + // span lookup + MHeapMap map; + MHeapMapCache mapcache; + + // central free lists for small size classes. + // the union makes sure that the MCentrals are + // spaced 64 bytes apart, so that each MCentral.Lock + // gets its own cache line. + union { + MCentral; + byte pad[64]; + } central[NumSizeClasses]; + + FixAlloc spanalloc; // allocator for Span* + FixAlloc cachealloc; // allocator for MCache* +}; +extern MHeap mheap; + +void MHeap_Init(MHeap *h, void *(*allocator)(uintptr)); +MSpan* MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass); +void MHeap_Free(MHeap *h, MSpan *s); +MSpan* MHeap_Lookup(MHeap *h, PageID p); +MSpan* MHeap_LookupMaybe(MHeap *h, PageID p); + +int32 mlookup(void *v, byte **base, uintptr *size, uint32 **ref); +void gc(int32 force); + +enum +{ + RefcountOverhead = 4, // one uint32 per object + + RefFree = 0, // must be zero + RefManual, // manual allocation - don't free + RefStack, // stack segment - don't free and don't scan for pointers + RefNone, // no references + RefSome, // some references +}; + diff --git a/src/pkg/runtime/malloc_go.cgo b/src/pkg/runtime/malloc_go.cgo new file mode 100644 index 000000000..6dcdaece2 --- /dev/null +++ b/src/pkg/runtime/malloc_go.cgo @@ -0,0 +1,28 @@ +// Copyright 2009 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 malloc +#include "runtime.h" +#include "malloc.h" + +func Alloc(n uintptr) (p *byte) { + p = malloc(n); +} + +func Free(p *byte) { + free(p); +} + +func Lookup(p *byte) (base *byte, size uintptr) { + mlookup(p, &base, &size, nil); +} + +func GetStats() (s *MStats) { + s = &mstats; +} + +func GC() { + gc(1); +} + diff --git a/src/pkg/runtime/mcache.c b/src/pkg/runtime/mcache.c new file mode 100644 index 000000000..ae2594023 --- /dev/null +++ b/src/pkg/runtime/mcache.c @@ -0,0 +1,105 @@ +// Copyright 2009 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. + +// Per-thread (in Go, per-M) malloc cache for small objects. +// +// See malloc.h for an overview. + +#include "runtime.h" +#include "malloc.h" + +void* +MCache_Alloc(MCache *c, int32 sizeclass, uintptr size) +{ + MCacheList *l; + MLink *first, *v; + int32 n; + + // Allocate from list. + l = &c->list[sizeclass]; + if(l->list == nil) { + // Replenish using central lists. + n = MCentral_AllocList(&mheap.central[sizeclass], + class_to_transfercount[sizeclass], &first); + l->list = first; + l->nlist = n; + c->size += n*size; + } + v = l->list; + l->list = v->next; + l->nlist--; + if(l->nlist < l->nlistmin) + l->nlistmin = l->nlist; + c->size -= size; + + // v is zeroed except for the link pointer + // that we used above; zero that. + v->next = nil; + return v; +} + +// Take n elements off l and return them to the central free list. +static void +ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass) +{ + MLink *first, **lp; + int32 i; + + // Cut off first n elements. + first = l->list; + lp = &l->list; + for(i=0; i<n; i++) + lp = &(*lp)->next; + l->list = *lp; + *lp = nil; + l->nlist -= n; + if(l->nlist < l->nlistmin) + l->nlistmin = l->nlist; + c->size -= n*class_to_size[sizeclass]; + + // Return them to central free list. + MCentral_FreeList(&mheap.central[sizeclass], n, first); +} + +void +MCache_Free(MCache *c, void *v, int32 sizeclass, uintptr size) +{ + int32 i, n; + MCacheList *l; + MLink *p; + + // Put back on list. + l = &c->list[sizeclass]; + p = v; + p->next = l->list; + l->list = p; + l->nlist++; + c->size += size; + + if(l->nlist >= MaxMCacheListLen) { + // Release a chunk back. + ReleaseN(c, l, class_to_transfercount[sizeclass], sizeclass); + } + + if(c->size >= MaxMCacheSize) { + // Scavenge. + for(i=0; i<NumSizeClasses; i++) { + l = &c->list[i]; + n = l->nlistmin; + + // n is the minimum number of elements we've seen on + // the list since the last scavenge. If n > 0, it means that + // we could have gotten by with n fewer elements + // without needing to consult the central free list. + // Move toward that situation by releasing n/2 of them. + if(n > 0) { + if(n > 1) + n /= 2; + ReleaseN(c, l, n, i); + } + l->nlistmin = l->nlist; + } + } +} + diff --git a/src/pkg/runtime/mcentral.c b/src/pkg/runtime/mcentral.c new file mode 100644 index 000000000..5c9f720c0 --- /dev/null +++ b/src/pkg/runtime/mcentral.c @@ -0,0 +1,192 @@ +// Copyright 2009 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. + +// Central free lists. +// +// See malloc.h for an overview. +// +// The MCentral doesn't actually contain the list of free objects; the MSpan does. +// Each MCentral is two lists of MSpans: those with free objects (c->nonempty) +// and those that are completely allocated (c->empty). +// +// TODO(rsc): tcmalloc uses a "transfer cache" to split the list +// into sections of class_to_transfercount[sizeclass] objects +// so that it is faster to move those lists between MCaches and MCentrals. + +#include "runtime.h" +#include "malloc.h" + +static bool MCentral_Grow(MCentral *c); +static void* MCentral_Alloc(MCentral *c); +static void MCentral_Free(MCentral *c, void *v); + +// Initialize a single central free list. +void +MCentral_Init(MCentral *c, int32 sizeclass) +{ + c->sizeclass = sizeclass; + MSpanList_Init(&c->nonempty); + MSpanList_Init(&c->empty); +} + +// Allocate up to n objects from the central free list. +// Return the number of objects allocated. +// The objects are linked together by their first words. +// On return, *pstart points at the first object and *pend at the last. +int32 +MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst) +{ + MLink *first, *last, *v; + int32 i; + + + lock(c); + // Replenish central list if empty. + if(MSpanList_IsEmpty(&c->nonempty)) { + if(!MCentral_Grow(c)) { + unlock(c); + *pfirst = nil; + return 0; + } + } + + // Copy from list, up to n. + // First one is guaranteed to work, because we just grew the list. + first = MCentral_Alloc(c); + last = first; + for(i=1; i<n && (v = MCentral_Alloc(c)) != nil; i++) { + last->next = v; + last = v; + } + last->next = nil; + c->nfree -= i; + + unlock(c); + *pfirst = first; + return i; +} + +// Helper: allocate one object from the central free list. +static void* +MCentral_Alloc(MCentral *c) +{ + MSpan *s; + MLink *v; + + if(MSpanList_IsEmpty(&c->nonempty)) + return nil; + s = c->nonempty.next; + s->ref++; + v = s->freelist; + s->freelist = v->next; + if(s->freelist == nil) { + MSpanList_Remove(s); + MSpanList_Insert(&c->empty, s); + } + return v; +} + +// Free n objects back into the central free list. +// Return the number of objects allocated. +// The objects are linked together by their first words. +// On return, *pstart points at the first object and *pend at the last. +void +MCentral_FreeList(MCentral *c, int32 n, MLink *start) +{ + MLink *v, *next; + + // Assume next == nil marks end of list. + // n and end would be useful if we implemented + // the transfer cache optimization in the TODO above. + USED(n); + + lock(c); + for(v=start; v; v=next) { + next = v->next; + MCentral_Free(c, v); + } + unlock(c); +} + +// Helper: free one object back into the central free list. +static void +MCentral_Free(MCentral *c, void *v) +{ + MSpan *s; + PageID page; + MLink *p, *next; + + // Find span for v. + page = (uintptr)v >> PageShift; + s = MHeap_Lookup(&mheap, page); + if(s == nil || s->ref == 0) + throw("invalid free"); + + // Move to nonempty if necessary. + if(s->freelist == nil) { + MSpanList_Remove(s); + MSpanList_Insert(&c->nonempty, s); + } + + // Add v back to s's free list. + p = v; + p->next = s->freelist; + s->freelist = p; + c->nfree++; + + // If s is completely freed, return it to the heap. + if(--s->ref == 0) { + MSpanList_Remove(s); + // Freed blocks are zeroed except for the link pointer. + // Zero the link pointers so that the page is all zero. + for(p=s->freelist; p; p=next) { + next = p->next; + p->next = nil; + } + s->freelist = nil; + c->nfree -= (s->npages << PageShift) / class_to_size[c->sizeclass]; + unlock(c); + MHeap_Free(&mheap, s); + lock(c); + } +} + +// Fetch a new span from the heap and +// carve into objects for the free list. +static bool +MCentral_Grow(MCentral *c) +{ + int32 i, n, npages, size; + MLink **tailp, *v; + byte *p; + MSpan *s; + + unlock(c); + npages = class_to_allocnpages[c->sizeclass]; + s = MHeap_Alloc(&mheap, npages, c->sizeclass); + if(s == nil) { + // TODO(rsc): Log out of memory + lock(c); + return false; + } + + // Carve span into sequence of blocks. + tailp = &s->freelist; + p = (byte*)(s->start << PageShift); + size = class_to_size[c->sizeclass]; + n = (npages << PageShift) / (size + RefcountOverhead); + s->gcref = (uint32*)(p + size*n); + for(i=0; i<n; i++) { + v = (MLink*)p; + *tailp = v; + tailp = &v->next; + p += size; + } + *tailp = nil; + + lock(c); + c->nfree += n; + MSpanList_Insert(&c->nonempty, s); + return true; +} diff --git a/src/pkg/runtime/mem.c b/src/pkg/runtime/mem.c new file mode 100644 index 000000000..7ed299eb0 --- /dev/null +++ b/src/pkg/runtime/mem.c @@ -0,0 +1,75 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "defs.h" + +// Stubs for memory management. +// In a separate file so they can be overridden during testing of gc. + +enum +{ + NHUNK = 20<<20, +}; + +// Convenient wrapper around mmap. +static void* +brk(uint32 n) +{ + byte *v; + + v = sys_mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); + m->mem.nmmap += n; + return v; +} + +// Allocate n bytes of memory. Note that this gets used +// to allocate new stack segments, so at each call to a function +// you have to ask yourself "would it be okay to call mal recursively +// right here?" The answer is yes unless we're in the middle of +// editing the malloc state in m->mem. +void* +oldmal(uint32 n) +{ + byte* v; + + // round to keep everything 64-bit aligned + n = rnd(n, 8); + + // be careful. calling any function might invoke + // mal to allocate more stack. + if(n > NHUNK) { + v = brk(n); + } else { + // allocate a new hunk if this one is too small + if(n > m->mem.nhunk) { + // here we're in the middle of editing m->mem + // (we're about to overwrite m->mem.hunk), + // so we can't call brk - it might call mal to grow the + // stack, and the recursive call would allocate a new + // hunk, and then once brk returned we'd immediately + // overwrite that hunk with our own. + // (the net result would be a memory leak, not a crash.) + // so we have to call sys_mmap directly - it is written + // in assembly and tagged not to grow the stack. + m->mem.hunk = + sys_mmap(nil, NHUNK, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_ANON|MAP_PRIVATE, 0, 0); + m->mem.nhunk = NHUNK; + m->mem.nmmap += NHUNK; + } + v = m->mem.hunk; + m->mem.hunk += n; + m->mem.nhunk -= n; + } + m->mem.nmal += n; + return v; +} + +void +sys_mal(uint32 n, uint8 *ret) +{ + ret = mal(n); + FLUSH(&ret); +} diff --git a/src/pkg/runtime/mfixalloc.c b/src/pkg/runtime/mfixalloc.c new file mode 100644 index 000000000..dd4f3f251 --- /dev/null +++ b/src/pkg/runtime/mfixalloc.c @@ -0,0 +1,56 @@ +// Copyright 2009 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. + +// Fixed-size object allocator. Returned memory is not zeroed. +// +// See malloc.h for overview. + +#include "runtime.h" +#include "malloc.h" + +// Initialize f to allocate objects of the given size, +// using the allocator to obtain chunks of memory. +void +FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg) +{ + f->size = size; + f->alloc = alloc; + f->first = first; + f->arg = arg; + f->list = nil; + f->chunk = nil; + f->nchunk = 0; +} + +void* +FixAlloc_Alloc(FixAlloc *f) +{ + void *v; + + if(f->list) { + v = f->list; + f->list = *(void**)f->list; + return v; + } + if(f->nchunk < f->size) { + f->chunk = f->alloc(FixAllocChunk); + if(f->chunk == nil) + throw("out of memory (FixAlloc)"); + f->nchunk = FixAllocChunk; + } + v = f->chunk; + if(f->first) + f->first(f->arg, v); + f->chunk += f->size; + f->nchunk -= f->size; + return v; +} + +void +FixAlloc_Free(FixAlloc *f, void *p) +{ + *(void**)p = f->list; + f->list = p; +} + diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c new file mode 100644 index 000000000..d58d6ce44 --- /dev/null +++ b/src/pkg/runtime/mgc0.c @@ -0,0 +1,231 @@ +// Copyright 2009 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. + +// Garbage collector -- step 0. +// +// Stop the world, mark and sweep garbage collector. +// NOT INTENDED FOR PRODUCTION USE. +// +// A mark and sweep collector provides a way to exercise +// and test the memory allocator and the stack walking machinery +// without also needing to get reference counting +// exactly right. + +#include "runtime.h" +#include "malloc.h" + +enum { + Debug = 0 +}; + +extern byte etext[]; +extern byte end[]; + +enum { + PtrSize = sizeof(void*) +}; + +static void +scanblock(int32 depth, byte *b, int64 n) +{ + int32 off; + void *obj; + uintptr size; + uint32 *ref; + void **vp; + int64 i; + + if(Debug) + printf("%d scanblock %p %D\n", depth, b, n); + off = (uint32)(uintptr)b & (PtrSize-1); + if(off) { + b += PtrSize - off; + n -= PtrSize - off; + } + + vp = (void**)b; + n /= PtrSize; + for(i=0; i<n; i++) { + if(mlookup(vp[i], &obj, &size, &ref)) { + if(*ref == RefFree || *ref == RefStack) + continue; + if(*ref == RefNone) { + if(Debug) + printf("%d found at %p: ", depth, &vp[i]); + *ref = RefSome; + scanblock(depth+1, obj, size); + } + } + } +} + +static void +scanstack(G *g) +{ + Stktop *stk; + byte *sp; + + sp = g->sched.SP; + stk = (Stktop*)g->stackbase; + while(stk) { + scanblock(0, sp, (byte*)stk - sp); + sp = stk->oldsp; + stk = (Stktop*)stk->oldbase; + } +} + +static void +mark(void) +{ + G *gp; + + // mark data+bss + scanblock(0, etext, end - etext); + + // mark stacks + for(gp=allg; gp!=nil; gp=gp->alllink) { + switch(gp->status){ + default: + printf("unexpected G.status %d\n", gp->status); + throw("mark - bad status"); + case Gdead: + break; + case Grunning: + if(gp != g) + throw("mark - world not stopped"); + scanstack(gp); + break; + case Grunnable: + case Gsyscall: + case Gwaiting: + scanstack(gp); + break; + } + } +} + +static void +sweepspan(MSpan *s) +{ + int32 i, n, npages, size; + byte *p; + + if(s->state != MSpanInUse) + return; + + p = (byte*)(s->start << PageShift); + if(s->sizeclass == 0) { + // Large block. + switch(s->gcref0) { + default: + throw("bad 'ref count'"); + case RefFree: + case RefManual: + case RefStack: + break; + case RefNone: + if(Debug) + printf("free %D at %p\n", (uint64)s->npages<<PageShift, p); + free(p); + break; + case RefSome: + s->gcref0 = RefNone; // set up for next mark phase + break; + } + return; + } + + // Chunk full of small blocks. + // Must match computation in MCentral_Grow. + size = class_to_size[s->sizeclass]; + npages = class_to_allocnpages[s->sizeclass]; + n = (npages << PageShift) / (size + RefcountOverhead); + for(i=0; i<n; i++) { + switch(s->gcref[i]) { + default: + throw("bad 'ref count'"); + case RefFree: + case RefManual: + case RefStack: + break; + case RefNone: + if(Debug) + printf("free %d at %p\n", size, p+i*size); + free(p + i*size); + break; + case RefSome: + s->gcref[i] = RefNone; // set up for next mark phase + break; + } + } +} + +static void +sweep(void) +{ + MSpan *s; + + // Sweep all the spans. + for(s = mheap.allspans; s != nil; s = s->allnext) + sweepspan(s); +} + +// Semaphore, not Lock, so that the goroutine +// reschedules when there is contention rather +// than spinning. +static uint32 gcsema = 1; + +// Initialized from $GOGC. GOGC=off means no gc. +// +// Next gc is after we've allocated an extra amount of +// memory proportional to the amount already in use. +// If gcpercent=100 and we're using 4M, we'll gc again +// when we get to 8M. This keeps the gc cost in linear +// proportion to the allocation cost. Adjusting gcpercent +// just changes the linear constant (and also the amount of +// extra memory used). +static int32 gcpercent = -2; + +void +gc(int32 force) +{ + byte *p; + + // The gc is turned off (via enablegc) until + // the bootstrap has completed. + // Also, malloc gets called in the guts + // of a number of libraries that might be + // holding locks. To avoid priority inversion + // problems, don't bother trying to run gc + // while holding a lock. The next mallocgc + // without a lock will do the gc instead. + if(!mstats.enablegc || m->locks > 0 || panicking) + return; + + if(gcpercent == -2) { // first time through + p = getenv("GOGC"); + if(p == nil || p[0] == '\0') + gcpercent = 100; + else if(strcmp(p, (byte*)"off") == 0) + gcpercent = -1; + else + gcpercent = atoi(p); + } + if(gcpercent < 0) + return; + + semacquire(&gcsema); + gosave(&g->sched); // update g's stack pointer for scanstack + stoptheworld(); + if(mheap.Lock.key != 0) + throw("mheap locked during gc"); + if(force || mstats.inuse_pages >= mstats.next_gc) { + mark(); + sweep(); + mstats.next_gc = mstats.inuse_pages+mstats.inuse_pages*gcpercent/100; + } + starttheworld(); + gosave(&g->sched); // update g's stack pointer for debugging + semrelease(&gcsema); +} diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c new file mode 100644 index 000000000..d0cf2237b --- /dev/null +++ b/src/pkg/runtime/mheap.c @@ -0,0 +1,333 @@ +// Copyright 2009 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. + +// Page heap. +// +// See malloc.h for overview. +// +// When a MSpan is in the heap free list, state == MSpanFree +// and heapmap(s->start) == span, heapmap(s->start+s->npages-1) == span. +// +// When a MSpan is allocated, state == MSpanInUse +// and heapmap(i) == span for all s->start <= i < s->start+s->npages. + +#include "runtime.h" +#include "malloc.h" + +static MSpan *MHeap_AllocLocked(MHeap*, uintptr, int32); +static bool MHeap_Grow(MHeap*, uintptr); +static void MHeap_FreeLocked(MHeap*, MSpan*); +static MSpan *MHeap_AllocLarge(MHeap*, uintptr); +static MSpan *BestFit(MSpan*, uintptr, MSpan*); + +static void +RecordSpan(void *vh, byte *p) +{ + MHeap *h; + MSpan *s; + + h = vh; + s = (MSpan*)p; + s->allnext = h->allspans; + h->allspans = s; +} + +// Initialize the heap; fetch memory using alloc. +void +MHeap_Init(MHeap *h, void *(*alloc)(uintptr)) +{ + uint32 i; + + FixAlloc_Init(&h->spanalloc, sizeof(MSpan), alloc, RecordSpan, h); + FixAlloc_Init(&h->cachealloc, sizeof(MCache), alloc, nil, nil); + MHeapMap_Init(&h->map, alloc); + // h->mapcache needs no init + for(i=0; i<nelem(h->free); i++) + MSpanList_Init(&h->free[i]); + MSpanList_Init(&h->large); + for(i=0; i<nelem(h->central); i++) + MCentral_Init(&h->central[i], i); +} + +// Allocate a new span of npage pages from the heap +// and record its size class in the HeapMap and HeapMapCache. +MSpan* +MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass) +{ + MSpan *s; + + lock(h); + s = MHeap_AllocLocked(h, npage, sizeclass); + if(s != nil) + mstats.inuse_pages += npage; + unlock(h); + return s; +} + +static MSpan* +MHeap_AllocLocked(MHeap *h, uintptr npage, int32 sizeclass) +{ + uintptr n; + MSpan *s, *t; + + // Try in fixed-size lists up to max. + for(n=npage; n < nelem(h->free); n++) { + if(!MSpanList_IsEmpty(&h->free[n])) { + s = h->free[n].next; + goto HaveSpan; + } + } + + // Best fit in list of large spans. + if((s = MHeap_AllocLarge(h, npage)) == nil) { + if(!MHeap_Grow(h, npage)) + return nil; + if((s = MHeap_AllocLarge(h, npage)) == nil) + return nil; + } + +HaveSpan: + // Mark span in use. + if(s->state != MSpanFree) + throw("MHeap_AllocLocked - MSpan not free"); + if(s->npages < npage) + throw("MHeap_AllocLocked - bad npages"); + MSpanList_Remove(s); + s->state = MSpanInUse; + + if(s->npages > npage) { + // Trim extra and put it back in the heap. + t = FixAlloc_Alloc(&h->spanalloc); + MSpan_Init(t, s->start + npage, s->npages - npage); + s->npages = npage; + MHeapMap_Set(&h->map, t->start - 1, s); + MHeapMap_Set(&h->map, t->start, t); + MHeapMap_Set(&h->map, t->start + t->npages - 1, t); + t->state = MSpanInUse; + MHeap_FreeLocked(h, t); + } + + // If span is being used for small objects, cache size class. + // No matter what, cache span info, because gc needs to be + // able to map interior pointer to containing span. + s->sizeclass = sizeclass; + for(n=0; n<npage; n++) + MHeapMap_Set(&h->map, s->start+n, s); + if(sizeclass == 0) { + uintptr tmp; + + // If there are entries for this span, invalidate them, + // but don't blow out cache entries about other spans. + for(n=0; n<npage; n++) + if(MHeapMapCache_GET(&h->mapcache, s->start+n, tmp) != 0) + MHeapMapCache_SET(&h->mapcache, s->start+n, 0); + } else { + // Save cache entries for this span. + // If there's a size class, there aren't that many pages. + for(n=0; n<npage; n++) + MHeapMapCache_SET(&h->mapcache, s->start+n, sizeclass); + } + + return s; +} + +// Allocate a span of exactly npage pages from the list of large spans. +static MSpan* +MHeap_AllocLarge(MHeap *h, uintptr npage) +{ + return BestFit(&h->large, npage, nil); +} + +// Search list for smallest span with >= npage pages. +// If there are multiple smallest spans, take the one +// with the earliest starting address. +static MSpan* +BestFit(MSpan *list, uintptr npage, MSpan *best) +{ + MSpan *s; + + for(s=list->next; s != list; s=s->next) { + if(s->npages < npage) + continue; + if(best == nil + || s->npages < best->npages + || (s->npages == best->npages && s->start < best->start)) + best = s; + } + return best; +} + +// Try to add at least npage pages of memory to the heap, +// returning whether it worked. +static bool +MHeap_Grow(MHeap *h, uintptr npage) +{ + uintptr ask; + void *v; + MSpan *s; + + // Ask for a big chunk, to reduce the number of mappings + // the operating system needs to track; also amortizes + // the overhead of an operating system mapping. + ask = npage<<PageShift; + if(ask < HeapAllocChunk) + ask = HeapAllocChunk; + + v = SysAlloc(ask); + if(v == nil) { + if(ask > (npage<<PageShift)) { + ask = npage<<PageShift; + v = SysAlloc(ask); + } + if(v == nil) + return false; + } + + // NOTE(rsc): In tcmalloc, if we've accumulated enough + // system allocations, the heap map gets entirely allocated + // in 32-bit mode. (In 64-bit mode that's not practical.) + + if(!MHeapMap_Preallocate(&h->map, ((uintptr)v>>PageShift) - 1, (ask>>PageShift) + 2)) { + SysFree(v, ask); + return false; + } + + // Create a fake "in use" span and free it, so that the + // right coalescing happens. + s = FixAlloc_Alloc(&h->spanalloc); + MSpan_Init(s, (uintptr)v>>PageShift, ask>>PageShift); + MHeapMap_Set(&h->map, s->start, s); + MHeapMap_Set(&h->map, s->start + s->npages - 1, s); + s->state = MSpanInUse; + MHeap_FreeLocked(h, s); + return true; +} + +// Look up the span at the given page number. +// Page number is guaranteed to be in map +// and is guaranteed to be start or end of span. +MSpan* +MHeap_Lookup(MHeap *h, PageID p) +{ + return MHeapMap_Get(&h->map, p); +} + +// Look up the span at the given page number. +// Page number is *not* guaranteed to be in map +// and may be anywhere in the span. +// Map entries for the middle of a span are only +// valid for allocated spans. Free spans may have +// other garbage in their middles, so we have to +// check for that. +MSpan* +MHeap_LookupMaybe(MHeap *h, PageID p) +{ + MSpan *s; + + s = MHeapMap_GetMaybe(&h->map, p); + if(s == nil || p < s->start || p - s->start >= s->npages) + return nil; + if(s->state != MSpanInUse) + return nil; + return s; +} + +// Free the span back into the heap. +void +MHeap_Free(MHeap *h, MSpan *s) +{ + lock(h); + mstats.inuse_pages -= s->npages; + MHeap_FreeLocked(h, s); + unlock(h); +} + +static void +MHeap_FreeLocked(MHeap *h, MSpan *s) +{ + MSpan *t; + + if(s->state != MSpanInUse || s->ref != 0) { + printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d\n", s, s->start<<PageShift, s->state, s->ref); + throw("MHeap_FreeLocked - invalid free"); + } + s->state = MSpanFree; + MSpanList_Remove(s); + + // Coalesce with earlier, later spans. + if((t = MHeapMap_Get(&h->map, s->start - 1)) != nil && t->state != MSpanInUse) { + s->start = t->start; + s->npages += t->npages; + MHeapMap_Set(&h->map, s->start, s); + MSpanList_Remove(t); + t->state = MSpanDead; + FixAlloc_Free(&h->spanalloc, t); + } + if((t = MHeapMap_Get(&h->map, s->start + s->npages)) != nil && t->state != MSpanInUse) { + s->npages += t->npages; + MHeapMap_Set(&h->map, s->start + s->npages - 1, s); + MSpanList_Remove(t); + t->state = MSpanDead; + FixAlloc_Free(&h->spanalloc, t); + } + + // Insert s into appropriate list. + if(s->npages < nelem(h->free)) + MSpanList_Insert(&h->free[s->npages], s); + else + MSpanList_Insert(&h->large, s); + + // TODO(rsc): IncrementalScavenge() to return memory to OS. +} + +// Initialize a new span with the given start and npages. +void +MSpan_Init(MSpan *span, PageID start, uintptr npages) +{ + span->next = nil; + span->prev = nil; + span->start = start; + span->npages = npages; + span->freelist = nil; + span->ref = 0; + span->sizeclass = 0; + span->state = 0; +} + +// Initialize an empty doubly-linked list. +void +MSpanList_Init(MSpan *list) +{ + list->state = MSpanListHead; + list->next = list; + list->prev = list; +} + +void +MSpanList_Remove(MSpan *span) +{ + if(span->prev == nil && span->next == nil) + return; + span->prev->next = span->next; + span->next->prev = span->prev; + span->prev = nil; + span->next = nil; +} + +bool +MSpanList_IsEmpty(MSpan *list) +{ + return list->next == list; +} + +void +MSpanList_Insert(MSpan *list, MSpan *span) +{ + if(span->next != nil || span->prev != nil) + throw("MSpanList_Insert"); + span->next = list->next; + span->prev = list; + span->next->prev = span; + span->prev->next = span; +} diff --git a/src/pkg/runtime/mheapmap32.c b/src/pkg/runtime/mheapmap32.c new file mode 100644 index 000000000..420ca2d83 --- /dev/null +++ b/src/pkg/runtime/mheapmap32.c @@ -0,0 +1,96 @@ +// Copyright 2009 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. + +// Heap map, 32-bit version +// See malloc.h and mheap.c for overview. + +#include "runtime.h" +#include "malloc.h" + +// 3-level radix tree mapping page ids to Span*. +void +MHeapMap_Init(MHeapMap *m, void *(*allocator)(size_t)) +{ + m->allocator = allocator; +} + +MSpan* +MHeapMap_Get(MHeapMap *m, PageID k) +{ + int32 i1, i2; + + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + return m->p[i1]->s[i2]; +} + +MSpan* +MHeapMap_GetMaybe(MHeapMap *m, PageID k) +{ + int32 i1, i2; + MHeapMapNode2 *p2; + + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + p2 = m->p[i1]; + if(p2 == nil) + return nil; + return p2->s[i2]; +} + +void +MHeapMap_Set(MHeapMap *m, PageID k, MSpan *s) +{ + int32 i1, i2; + + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Set"); + + m->p[i1]->s[i2] = s; +} + +// Allocate the storage required for entries [k, k+1, ..., k+len-1] +// so that Get and Set calls need not check for nil pointers. +bool +MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr len) +{ + uintptr end; + int32 i1; + MHeapMapNode2 *p2; + + end = k+len; + while(k < end) { + if((k >> MHeapMap_TotalBits) != 0) + return false; + i1 = (k >> MHeapMap_Level2Bits) & MHeapMap_Level1Mask; + + // first-level pointer + if(m->p[i1] == nil) { + p2 = m->allocator(sizeof *p2); + if(p2 == nil) + return false; + sys_memclr((byte*)p2, sizeof *p2); + m->p[i1] = p2; + } + + // advance key past this leaf node + k = ((k >> MHeapMap_Level2Bits) + 1) << MHeapMap_Level2Bits; + } + return true; +} + diff --git a/src/pkg/runtime/mheapmap32.h b/src/pkg/runtime/mheapmap32.h new file mode 100644 index 000000000..0a16ccd10 --- /dev/null +++ b/src/pkg/runtime/mheapmap32.h @@ -0,0 +1,76 @@ +// Copyright 2009 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. + +// Free(v) must be able to determine the MSpan containing v. +// The MHeapMap is a 2-level radix tree mapping page numbers to MSpans. + +typedef struct MHeapMapNode2 MHeapMapNode2; + +enum +{ + // 32 bit address - 12 bit page size = 20 bits to map + MHeapMap_Level1Bits = 10, + MHeapMap_Level2Bits = 10, + + MHeapMap_TotalBits = + MHeapMap_Level1Bits + + MHeapMap_Level2Bits, + + MHeapMap_Level1Mask = (1<<MHeapMap_Level1Bits) - 1, + MHeapMap_Level2Mask = (1<<MHeapMap_Level2Bits) - 1, +}; + +struct MHeapMap +{ + void *(*allocator)(uintptr); + MHeapMapNode2 *p[1<<MHeapMap_Level1Bits]; +}; + +struct MHeapMapNode2 +{ + MSpan *s[1<<MHeapMap_Level2Bits]; +}; + +void MHeapMap_Init(MHeapMap *m, void *(*allocator)(uintptr)); +bool MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr npages); +MSpan* MHeapMap_Get(MHeapMap *m, PageID k); +MSpan* MHeapMap_GetMaybe(MHeapMap *m, PageID k); +void MHeapMap_Set(MHeapMap *m, PageID k, MSpan *v); + + +// Much of the time, free(v) needs to know only the size class for v, +// not which span it came from. The MHeapMap finds the size class +// by looking up the span. +// +// An MHeapMapCache is a simple direct-mapped cache translating +// page numbers to size classes. It avoids the expensive MHeapMap +// lookup for hot pages. +// +// The cache entries are 32 bits, with the page number in the low part +// and the value at the top. +// +// NOTE(rsc): On a machine with 32-bit addresses (= 20-bit page numbers), +// we can use a 16-bit cache entry by not storing the redundant 12 bits +// of the key that are used as the entry index. For now, keep it simple. +enum +{ + MHeapMapCache_HashBits = 12 +}; + +struct MHeapMapCache +{ + uint32 array[1<<MHeapMapCache_HashBits]; +}; + +// All macros for speed (sorry). +#define HMASK ((1<<MHeapMapCache_HashBits)-1) +#define KBITS MHeapMap_TotalBits +#define KMASK ((1LL<<KBITS)-1) + +#define MHeapMapCache_SET(cache, key, value) \ + ((cache)->array[(key) & HMASK] = (key) | ((uintptr)(value) << KBITS)) + +#define MHeapMapCache_GET(cache, key, tmp) \ + (tmp = (cache)->array[(key) & HMASK], \ + (tmp & KMASK) == (key) ? (tmp >> KBITS) : 0) diff --git a/src/pkg/runtime/mheapmap64.c b/src/pkg/runtime/mheapmap64.c new file mode 100644 index 000000000..1886ba529 --- /dev/null +++ b/src/pkg/runtime/mheapmap64.c @@ -0,0 +1,117 @@ +// Copyright 2009 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. + +// Heap map, 64-bit version +// See malloc.h and mheap.c for overview. + +#include "runtime.h" +#include "malloc.h" + +// 3-level radix tree mapping page ids to Span*. +void +MHeapMap_Init(MHeapMap *m, void *(*allocator)(size_t)) +{ + m->allocator = allocator; +} + +MSpan* +MHeapMap_Get(MHeapMap *m, PageID k) +{ + int32 i1, i2, i3; + + i3 = k & MHeapMap_Level3Mask; + k >>= MHeapMap_Level3Bits; + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + return m->p[i1]->p[i2]->s[i3]; +} + +MSpan* +MHeapMap_GetMaybe(MHeapMap *m, PageID k) +{ + int32 i1, i2, i3; + MHeapMapNode2 *p2; + MHeapMapNode3 *p3; + + i3 = k & MHeapMap_Level3Mask; + k >>= MHeapMap_Level3Bits; + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Get"); + + p2 = m->p[i1]; + if(p2 == nil) + return nil; + p3 = p2->p[i2]; + if(p3 == nil) + return nil; + return p3->s[i3]; +} + +void +MHeapMap_Set(MHeapMap *m, PageID k, MSpan *s) +{ + int32 i1, i2, i3; + + i3 = k & MHeapMap_Level3Mask; + k >>= MHeapMap_Level3Bits; + i2 = k & MHeapMap_Level2Mask; + k >>= MHeapMap_Level2Bits; + i1 = k & MHeapMap_Level1Mask; + k >>= MHeapMap_Level1Bits; + if(k != 0) + throw("MHeapMap_Set"); + + m->p[i1]->p[i2]->s[i3] = s; +} + +// Allocate the storage required for entries [k, k+1, ..., k+len-1] +// so that Get and Set calls need not check for nil pointers. +bool +MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr len) +{ + uintptr end; + int32 i1, i2; + MHeapMapNode2 *p2; + MHeapMapNode3 *p3; + + end = k+len; + while(k < end) { + if((k >> MHeapMap_TotalBits) != 0) + return false; + i2 = (k >> MHeapMap_Level3Bits) & MHeapMap_Level2Mask; + i1 = (k >> (MHeapMap_Level3Bits + MHeapMap_Level2Bits)) & MHeapMap_Level1Mask; + + // first-level pointer + if((p2 = m->p[i1]) == nil) { + p2 = m->allocator(sizeof *p2); + if(p2 == nil) + return false; + sys_memclr((byte*)p2, sizeof *p2); + m->p[i1] = p2; + } + + // second-level pointer + if(p2->p[i2] == nil) { + p3 = m->allocator(sizeof *p3); + if(p3 == nil) + return false; + sys_memclr((byte*)p3, sizeof *p3); + p2->p[i2] = p3; + } + + // advance key past this leaf node + k = ((k >> MHeapMap_Level3Bits) + 1) << MHeapMap_Level3Bits; + } + return true; +} + diff --git a/src/pkg/runtime/mheapmap64.h b/src/pkg/runtime/mheapmap64.h new file mode 100644 index 000000000..127b773f7 --- /dev/null +++ b/src/pkg/runtime/mheapmap64.h @@ -0,0 +1,96 @@ +// Copyright 2009 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. + +// Free(v) must be able to determine the MSpan containing v. +// The MHeapMap is a 3-level radix tree mapping page numbers to MSpans. +// +// NOTE(rsc): On a 32-bit platform (= 20-bit page numbers), +// we can swap in a 2-level radix tree. +// +// NOTE(rsc): We use a 3-level tree because tcmalloc does, but +// having only three levels requires approximately 1 MB per node +// in the tree, making the minimum map footprint 3 MB. +// Using a 4-level tree would cut the minimum footprint to 256 kB. +// On the other hand, it's just virtual address space: most of +// the memory is never going to be touched, thus never paged in. + +typedef struct MHeapMapNode2 MHeapMapNode2; +typedef struct MHeapMapNode3 MHeapMapNode3; + +enum +{ + // 64 bit address - 12 bit page size = 52 bits to map + MHeapMap_Level1Bits = 18, + MHeapMap_Level2Bits = 18, + MHeapMap_Level3Bits = 16, + + MHeapMap_TotalBits = + MHeapMap_Level1Bits + + MHeapMap_Level2Bits + + MHeapMap_Level3Bits, + + MHeapMap_Level1Mask = (1<<MHeapMap_Level1Bits) - 1, + MHeapMap_Level2Mask = (1<<MHeapMap_Level2Bits) - 1, + MHeapMap_Level3Mask = (1<<MHeapMap_Level3Bits) - 1, +}; + +struct MHeapMap +{ + void *(*allocator)(uintptr); + MHeapMapNode2 *p[1<<MHeapMap_Level1Bits]; +}; + +struct MHeapMapNode2 +{ + MHeapMapNode3 *p[1<<MHeapMap_Level2Bits]; +}; + +struct MHeapMapNode3 +{ + MSpan *s[1<<MHeapMap_Level3Bits]; +}; + +void MHeapMap_Init(MHeapMap *m, void *(*allocator)(uintptr)); +bool MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr npages); +MSpan* MHeapMap_Get(MHeapMap *m, PageID k); +MSpan* MHeapMap_GetMaybe(MHeapMap *m, PageID k); +void MHeapMap_Set(MHeapMap *m, PageID k, MSpan *v); + + +// Much of the time, free(v) needs to know only the size class for v, +// not which span it came from. The MHeapMap finds the size class +// by looking up the span. +// +// An MHeapMapCache is a simple direct-mapped cache translating +// page numbers to size classes. It avoids the expensive MHeapMap +// lookup for hot pages. +// +// The cache entries are 64 bits, with the page number in the low part +// and the value at the top. +// +// NOTE(rsc): On a machine with 32-bit addresses (= 20-bit page numbers), +// we can use a 16-bit cache entry by not storing the redundant 12 bits +// of the key that are used as the entry index. Here in 64-bit land, +// that trick won't work unless the hash table has 2^28 entries. +enum +{ + MHeapMapCache_HashBits = 12 +}; + +struct MHeapMapCache +{ + uintptr array[1<<MHeapMapCache_HashBits]; +}; + +// All macros for speed (sorry). +#define HMASK ((1<<MHeapMapCache_HashBits)-1) +#define KBITS MHeapMap_TotalBits +#define KMASK ((1LL<<KBITS)-1) + +#define MHeapMapCache_SET(cache, key, value) \ + ((cache)->array[(key) & HMASK] = (key) | ((uintptr)(value) << KBITS)) + +#define MHeapMapCache_GET(cache, key, tmp) \ + (tmp = (cache)->array[(key) & HMASK], \ + (tmp & KMASK) == (key) ? (tmp >> KBITS) : 0) diff --git a/src/pkg/runtime/msize.c b/src/pkg/runtime/msize.c new file mode 100644 index 000000000..25e22637d --- /dev/null +++ b/src/pkg/runtime/msize.c @@ -0,0 +1,165 @@ +// Copyright 2009 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. + +// Malloc small size classes. +// +// See malloc.h for overview. +// +// The size classes are chosen so that rounding an allocation +// request up to the next size class wastes at most 12.5% (1.125x). +// +// Each size class has its own page count that gets allocated +// and chopped up when new objects of the size class are needed. +// That page count is chosen so that chopping up the run of +// pages into objects of the given size wastes at most 12.5% (1.125x) +// of the memory. It is not necessary that the cutoff here be +// the same as above. +// +// The two sources of waste multiply, so the worst possible case +// for the above constraints would be that allocations of some +// size might have a 26.6% (1.266x) overhead. +// In practice, only one of the wastes comes into play for a +// given size (sizes < 512 waste mainly on the round-up, +// sizes > 512 waste mainly on the page chopping). +// +// TODO(rsc): Compute max waste for any given size. + +#include "runtime.h" +#include "malloc.h" + +int32 class_to_size[NumSizeClasses]; +int32 class_to_allocnpages[NumSizeClasses]; +int32 class_to_transfercount[NumSizeClasses]; + +// The SizeToClass lookup is implemented using two arrays, +// one mapping sizes <= 1024 to their class and one mapping +// sizes >= 1024 and <= MaxSmallSize to their class. +// All objects are 8-aligned, so the first array is indexed by +// the size divided by 8 (rounded up). Objects >= 1024 bytes +// are 128-aligned, so the second array is indexed by the +// size divided by 128 (rounded up). The arrays are filled in +// by InitSizes. + +static int32 size_to_class8[1024/8 + 1]; +static int32 size_to_class128[(MaxSmallSize-1024)/128 + 1]; + +int32 +SizeToClass(int32 size) +{ + if(size > MaxSmallSize) + throw("SizeToClass - invalid size"); + if(size > 1024-8) + return size_to_class128[(size-1024+127) >> 7]; + return size_to_class8[(size+7)>>3]; +} + +void +InitSizes(void) +{ + int32 align, sizeclass, size, osize, nextsize, n; + uint32 i; + uintptr allocsize, npages; + + // Initialize the class_to_size table (and choose class sizes in the process). + class_to_size[0] = 0; + sizeclass = 1; // 0 means no class + align = 8; + for(size = align; size <= MaxSmallSize; size += align) { + if((size&(size-1)) == 0) { // bump alignment once in a while + if(size >= 2048) + align = 256; + else if(size >= 128) + align = size / 8; + else if(size >= 16) + align = 16; // required for x86 SSE instructions, if we want to use them + } + if((align&(align-1)) != 0) + throw("InitSizes - bug"); + + // Make the allocnpages big enough that + // the leftover is less than 1/8 of the total, + // so wasted space is at most 12.5%. + allocsize = PageSize; + osize = size + RefcountOverhead; + while(allocsize%osize > (allocsize/8)) + allocsize += PageSize; + npages = allocsize >> PageShift; + + // If the previous sizeclass chose the same + // allocation size and fit the same number of + // objects into the page, we might as well + // use just this size instead of having two + // different sizes. + if(sizeclass > 1 + && npages == class_to_allocnpages[sizeclass-1] + && allocsize/osize == allocsize/(class_to_size[sizeclass-1]+RefcountOverhead)) { + class_to_size[sizeclass-1] = size; + continue; + } + + class_to_allocnpages[sizeclass] = npages; + class_to_size[sizeclass] = size; + sizeclass++; + } + if(sizeclass != NumSizeClasses) { + printf("sizeclass=%d NumSizeClasses=%d\n", sizeclass, NumSizeClasses); + throw("InitSizes - bad NumSizeClasses"); + } + + // Initialize the size_to_class tables. + nextsize = 0; + for (sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) { + for(; nextsize < 1024 && nextsize <= class_to_size[sizeclass]; nextsize+=8) + size_to_class8[nextsize/8] = sizeclass; + if(nextsize >= 1024) + for(; nextsize <= class_to_size[sizeclass]; nextsize += 128) + size_to_class128[(nextsize-1024)/128] = sizeclass; + } + + // Double-check SizeToClass. + if(0) { + for(n=0; n < MaxSmallSize; n++) { + sizeclass = SizeToClass(n); + if(sizeclass < 1 || sizeclass >= NumSizeClasses || class_to_size[sizeclass] < n) { + printf("size=%d sizeclass=%d class_to_size=%d\n", n, sizeclass, class_to_size[sizeclass]); + printf("incorrect SizeToClass"); + goto dump; + } + if(sizeclass > 1 && class_to_size[sizeclass-1] >= n) { + printf("size=%d sizeclass=%d class_to_size=%d\n", n, sizeclass, class_to_size[sizeclass]); + printf("SizeToClass too big"); + goto dump; + } + } + } + + // Initialize the class_to_transfercount table. + for(sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) { + n = 64*1024 / class_to_size[sizeclass]; + if(n < 2) + n = 2; + if(n > 32) + n = 32; + class_to_transfercount[sizeclass] = n; + } + return; + +dump: + if(1){ + printf("NumSizeClasses=%d\n", NumSizeClasses); + printf("class_to_size:"); + for(sizeclass=0; sizeclass<NumSizeClasses; sizeclass++) + printf(" %d", class_to_size[sizeclass]); + printf("\n\n"); + printf("size_to_class8:"); + for(i=0; i<nelem(size_to_class8); i++) + printf(" %d=>%d(%d)\n", i*8, size_to_class8[i], class_to_size[size_to_class8[i]]); + printf("\n"); + printf("size_to_class128:"); + for(i=0; i<nelem(size_to_class128); i++) + printf(" %d=>%d(%d)\n", i*128, size_to_class128[i], class_to_size[size_to_class128[i]]); + printf("\n"); + } + throw("InitSizes failed"); +} diff --git a/src/pkg/runtime/print.c b/src/pkg/runtime/print.c new file mode 100644 index 000000000..5295e338d --- /dev/null +++ b/src/pkg/runtime/print.c @@ -0,0 +1,268 @@ +// Copyright 2009 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. + +#include "runtime.h" + + +void +dump(byte *p, int32 n) +{ + int32 i; + + for(i=0; i<n; i++) { + sys·printpointer((byte*)(p[i]>>4)); + sys·printpointer((byte*)(p[i]&0xf)); + if((i&15) == 15) + prints("\n"); + else + prints(" "); + } + if(n & 15) + prints("\n"); +} + +void +prints(int8 *s) +{ + sys·write(1, s, findnull((byte*)s)); +} + +// Very simple printf. Only for debugging prints. +// Do not add to this without checking with Rob. +void +printf(int8 *s, ...) +{ + int8 *p, *lp; + byte *arg, *narg; + + lp = p = s; + arg = (byte*)(&s+1); + for(; *p; p++) { + if(*p != '%') + continue; + if(p > lp) + sys·write(1, lp, p-lp); + p++; + narg = nil; + switch(*p) { + case 'd': // 32-bit + case 'x': + narg = arg + 4; + break; + case 'D': // 64-bit + case 'X': + if(sizeof(uintptr) == 8 && ((uint32)(uint64)arg)&4) + arg += 4; + narg = arg + 8; + break; + case 'p': // pointer-sized + case 's': + if(sizeof(uintptr) == 8 && ((uint32)(uint64)arg)&4) + arg += 4; + narg = arg + sizeof(uintptr); + break; + case 'S': // pointer-aligned but bigger + if(sizeof(uintptr) == 8 && ((uint32)(uint64)arg)&4) + arg += 4; + narg = arg + sizeof(String); + break; + } + switch(*p) { + case 'd': + sys·printint(*(int32*)arg); + break; + case 'D': + sys·printint(*(int64*)arg); + break; + case 'x': + sys·printhex(*(int32*)arg); + break; + case 'X': + sys·printhex(*(int64*)arg); + break; + case 'p': + sys·printpointer(*(void**)arg); + break; + case 's': + prints(*(int8**)arg); + break; + case 'S': + sys·printstring(*(String*)arg); + break; + } + arg = narg; + lp = p+1; + } + if(p > lp) + sys·write(1, lp, p-lp); +} + + +void +sys·printpc(void *p) +{ + prints("PC="); + sys·printhex((uint64)sys·getcallerpc(p)); +} + +void +sys·printbool(bool v) +{ + if(v) { + sys·write(1, (byte*)"true", 4); + return; + } + sys·write(1, (byte*)"false", 5); +} + +void +sys·printfloat(float64 v) +{ + byte buf[20]; + int32 e, s, i, n; + float64 h; + + if(isNaN(v)) { + sys·write(1, "NaN", 3); + return; + } + if(isInf(v, 0)) { + sys·write(1, "+Inf", 4); + return; + } + if(isInf(v, -1)) { + sys·write(1, "+Inf", 4); + return; + } + + + n = 7; // digits printed + e = 0; // exp + s = 0; // sign + if(v != 0) { + // sign + if(v < 0) { + v = -v; + s = 1; + } + + // normalize + while(v >= 10) { + e++; + v /= 10; + } + while(v < 1) { + e--; + v *= 10; + } + + // round + h = 5; + for(i=0; i<n; i++) + h /= 10; + v += h; + if(v >= 10) { + e++; + v /= 10; + } + } + + // format +d.dddd+edd + buf[0] = '+'; + if(s) + buf[0] = '-'; + for(i=0; i<n; i++) { + s = v; + buf[i+2] = s+'0'; + v -= s; + v *= 10.; + } + buf[1] = buf[2]; + buf[2] = '.'; + + buf[n+2] = 'e'; + buf[n+3] = '+'; + if(e < 0) { + e = -e; + buf[n+3] = '-'; + } + + buf[n+4] = (e/100) + '0'; + buf[n+5] = (e/10)%10 + '0'; + buf[n+6] = (e%10) + '0'; + sys·write(1, buf, n+7); +} + +void +sys·printuint(uint64 v) +{ + byte buf[100]; + int32 i; + + for(i=nelem(buf)-1; i>0; i--) { + buf[i] = v%10 + '0'; + if(v < 10) + break; + v = v/10; + } + sys·write(1, buf+i, nelem(buf)-i); +} + +void +sys·printint(int64 v) +{ + if(v < 0) { + sys·write(1, "-", 1); + v = -v; + } + sys·printuint(v); +} + +void +sys·printhex(uint64 v) +{ + static int8 *dig = "0123456789abcdef"; + byte buf[100]; + int32 i; + + i=nelem(buf); + for(; v>0; v/=16) + buf[--i] = dig[v%16]; + if(i == nelem(buf)) + buf[--i] = '0'; + buf[--i] = 'x'; + buf[--i] = '0'; + sys·write(1, buf+i, nelem(buf)-i); +} + +void +sys·printpointer(void *p) +{ + sys·printhex((uint64)p); +} + +void +sys·printstring(String v) +{ + extern int32 maxstring; + + if(v.len > maxstring) { + sys·write(1, "[invalid string]", 16); + return; + } + if(v.len > 0) + sys·write(1, v.str, v.len); +} + +void +sys·printsp(void) +{ + sys·write(1, " ", 1); +} + +void +sys·printnl(void) +{ + sys·write(1, "\n", 1); +} diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c new file mode 100644 index 000000000..1d065e6d2 --- /dev/null +++ b/src/pkg/runtime/proc.c @@ -0,0 +1,858 @@ +// Copyright 2009 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. + +#include "runtime.h" +#include "malloc.h" + +typedef struct Sched Sched; + +M m0; +G g0; // idle goroutine for m0 + +static int32 debug = 0; +static Lock debuglock; + +// Go scheduler +// +// The go scheduler's job is to match ready-to-run goroutines (`g's) +// with waiting-for-work schedulers (`m's). If there are ready gs +// and no waiting ms, ready() will start a new m running in a new +// OS thread, so that all ready gs can run simultaneously, up to a limit. +// For now, ms never go away. +// +// The default maximum number of ms is one: go runs single-threaded. +// This is because some locking details have to be worked ou +// (select in particular is not locked properly) and because the low-level +// code hasn't been written yet for OS X. Setting the environmen +// variable $gomaxprocs changes sched.mmax for now. +// +// Even a program that can run without deadlock in a single process +// might use more ms if given the chance. For example, the prime +// sieve will use as many ms as there are primes (up to sched.mmax), +// allowing different stages of the pipeline to execute in parallel. +// We could revisit this choice, only kicking off new ms for blocking +// system calls, but that would limit the amount of parallel computation +// that go would try to do. +// +// In general, one could imagine all sorts of refinements to the +// scheduler, but the goal now is just to get something working on +// Linux and OS X. + +struct Sched { + Lock; + + G *gfree; // available gs (status == Gdead) + + G *ghead; // gs waiting to run + G *gtail; + int32 gwait; // number of gs waiting to run + int32 gcount; // number of gs that are alive + + M *mhead; // ms waiting for work + int32 mwait; // number of ms waiting for work + int32 mcount; // number of ms that have been created + int32 mcpu; // number of ms executing on cpu + int32 mcpumax; // max number of ms allowed on cpu + int32 gomaxprocs; + int32 msyscall; // number of ms in system calls + + int32 predawn; // running initialization, don't run new gs. + + Note stopped; // one g can wait here for ms to stop + int32 waitstop; // after setting this flag +}; + +Sched sched; + +// Scheduling helpers. Sched must be locked. +static void gput(G*); // put/get on ghead/gtail +static G* gget(void); +static void mput(M*); // put/get on mhead +static M* mget(void); +static void gfput(G*); // put/get on gfree +static G* gfget(void); +static void matchmg(void); // match ms to gs +static void readylocked(G*); // ready, but sched is locked + +// Scheduler loop. +static void scheduler(void); + +// The bootstrap sequence is: +// +// call osinit +// call schedinit +// make & queue new G +// call mstart +// +// The new G does: +// +// call main·init_function +// call initdone +// call main·main +void +schedinit(void) +{ + int32 n; + byte *p; + + mallocinit(); + goargs(); + + // Allocate internal symbol table representation now, + // so that we don't need to call malloc when we crash. + findfunc(0); + + sched.gomaxprocs = 1; + p = getenv("GOMAXPROCS"); + if(p != nil && (n = atoi(p)) != 0) + sched.gomaxprocs = n; + sched.mcpumax = sched.gomaxprocs; + sched.mcount = 1; + sched.predawn = 1; +} + +// Called after main·init_function; main·main will be called on return. +void +initdone(void) +{ + // Let's go. + sched.predawn = 0; + mstats.enablegc = 1; + + // If main·init_function started other goroutines, + // kick off new ms to handle them, like ready + // would have, had it not been pre-dawn. + lock(&sched); + matchmg(); + unlock(&sched); +} + +void +goexit(void) +{ + if(debug > 1){ + lock(&debuglock); + printf("goexit goid=%d\n", g->goid); + unlock(&debuglock); + } + g->status = Gmoribund; + gosched(); +} + +void +tracebackothers(G *me) +{ + G *g; + + for(g = allg; g != nil; g = g->alllink) { + if(g == me || g->status == Gdead) + continue; + printf("\ngoroutine %d:\n", g->goid); + traceback(g->sched.PC, g->sched.SP+sizeof(uintptr), g); // gogo adjusts SP by one word + } +} + +// Put on `g' queue. Sched must be locked. +static void +gput(G *g) +{ + g->schedlink = nil; + if(sched.ghead == nil) + sched.ghead = g; + else + sched.gtail->schedlink = g; + sched.gtail = g; + sched.gwait++; +} + +// Get from `g' queue. Sched must be locked. +static G* +gget(void) +{ + G *g; + + g = sched.ghead; + if(g){ + sched.ghead = g->schedlink; + if(sched.ghead == nil) + sched.gtail = nil; + sched.gwait--; + } + return g; +} + +// Put on `m' list. Sched must be locked. +static void +mput(M *m) +{ + m->schedlink = sched.mhead; + sched.mhead = m; + sched.mwait++; +} + +// Get from `m' list. Sched must be locked. +static M* +mget(void) +{ + M *m; + + m = sched.mhead; + if(m){ + sched.mhead = m->schedlink; + sched.mwait--; + } + return m; +} + +// Put on gfree list. Sched must be locked. +static void +gfput(G *g) +{ + g->schedlink = sched.gfree; + sched.gfree = g; +} + +// Get from gfree list. Sched must be locked. +static G* +gfget(void) +{ + G *g; + + g = sched.gfree; + if(g) + sched.gfree = g->schedlink; + return g; +} + +// Mark g ready to run. +void +ready(G *g) +{ + lock(&sched); + readylocked(g); + unlock(&sched); +} + +// Mark g ready to run. Sched is already locked. +// G might be running already and about to stop. +// The sched lock protects g->status from changing underfoot. +static void +readylocked(G *g) +{ + if(g->m){ + // Running on another machine. + // Ready it when it stops. + g->readyonstop = 1; + return; + } + + // Mark runnable. + if(g->status == Grunnable || g->status == Grunning) + throw("bad g->status in ready"); + g->status = Grunnable; + + gput(g); + if(!sched.predawn) + matchmg(); +} + +// Get the next goroutine that m should run. +// Sched must be locked on entry, is unlocked on exit. +// Makes sure that at most $GOMAXPROCS gs are +// running on cpus (not in system calls) at any given time. +static G* +nextgandunlock(void) +{ + G *gp; + + // On startup, each m is assigned a nextg and + // has already been accounted for in mcpu. + if(m->nextg != nil) { + gp = m->nextg; + m->nextg = nil; + unlock(&sched); + if(debug > 1) { + lock(&debuglock); + printf("m%d nextg found g%d\n", m->id, gp->goid); + unlock(&debuglock); + } + return gp; + } + + // Otherwise, look for work. + if(sched.mcpu < sched.mcpumax && (gp=gget()) != nil) { + sched.mcpu++; + unlock(&sched); + if(debug > 1) { + lock(&debuglock); + printf("m%d nextg got g%d\n", m->id, gp->goid); + unlock(&debuglock); + } + return gp; + } + + // Otherwise, sleep. + mput(m); + if(sched.mcpu == 0 && sched.msyscall == 0) + throw("all goroutines are asleep - deadlock!"); + m->nextg = nil; + noteclear(&m->havenextg); + if(sched.waitstop && sched.mcpu <= sched.mcpumax) { + sched.waitstop = 0; + notewakeup(&sched.stopped); + } + unlock(&sched); + + notesleep(&m->havenextg); + if((gp = m->nextg) == nil) + throw("bad m->nextg in nextgoroutine"); + m->nextg = nil; + if(debug > 1) { + lock(&debuglock); + printf("m%d nextg woke g%d\n", m->id, gp->goid); + unlock(&debuglock); + } + return gp; +} + +// TODO(rsc): Remove. This is only temporary, +// for the mark and sweep collector. +void +stoptheworld(void) +{ + lock(&sched); + sched.mcpumax = 1; + while(sched.mcpu > 1) { + noteclear(&sched.stopped); + sched.waitstop = 1; + unlock(&sched); + notesleep(&sched.stopped); + lock(&sched); + } + unlock(&sched); +} + +// TODO(rsc): Remove. This is only temporary, +// for the mark and sweep collector. +void +starttheworld(void) +{ + lock(&sched); + sched.mcpumax = sched.gomaxprocs; + matchmg(); + unlock(&sched); +} + +// Called to start an M. +void +mstart(void) +{ + if(m->mcache == nil) + m->mcache = allocmcache(); + minit(); + scheduler(); +} + +// Kick of new ms as needed (up to mcpumax). +// There are already `other' other cpus that will +// start looking for goroutines shortly. +// Sched is locked. +static void +matchmg(void) +{ + M *m; + G *g; + + if(debug > 1 && sched.ghead != nil) { + lock(&debuglock); + printf("matchmg mcpu=%d mcpumax=%d gwait=%d\n", sched.mcpu, sched.mcpumax, sched.gwait); + unlock(&debuglock); + } + + while(sched.mcpu < sched.mcpumax && (g = gget()) != nil){ + sched.mcpu++; + if((m = mget()) != nil){ + if(debug > 1) { + lock(&debuglock); + printf("wakeup m%d g%d\n", m->id, g->goid); + unlock(&debuglock); + } + m->nextg = g; + notewakeup(&m->havenextg); + }else{ + m = malloc(sizeof(M)); + m->g0 = malg(8192); + m->nextg = g; + m->id = sched.mcount++; + if(debug) { + lock(&debuglock); + printf("alloc m%d g%d\n", m->id, g->goid); + unlock(&debuglock); + } + newosproc(m, m->g0, m->g0->stackbase, mstart); + } + } +} + +// Scheduler loop: find g to run, run it, repeat. +static void +scheduler(void) +{ + G* gp; + + lock(&sched); + if(gosave(&m->sched)){ + // Jumped here via gosave/gogo, so didn't + // execute lock(&sched) above. + lock(&sched); + + if(sched.predawn) + throw("init sleeping"); + + // Just finished running m->curg. + gp = m->curg; + gp->m = nil; + sched.mcpu--; + if(debug > 1) { + lock(&debuglock); + printf("m%d sched g%d status %d\n", m->id, gp->goid, gp->status); + unlock(&debuglock); + } + switch(gp->status){ + case Grunnable: + case Gdead: + // Shouldn't have been running! + throw("bad gp->status in sched"); + case Grunning: + gp->status = Grunnable; + gput(gp); + break; + case Gmoribund: + gp->status = Gdead; + if(--sched.gcount == 0) + exit(0); + break; + } + if(gp->readyonstop){ + gp->readyonstop = 0; + readylocked(gp); + } + } + + // Find (or wait for) g to run. Unlocks sched. + gp = nextgandunlock(); + gp->readyonstop = 0; + gp->status = Grunning; + if(debug > 1) { + lock(&debuglock); + printf("m%d run g%d at %p\n", m->id, gp->goid, gp->sched.PC); + traceback(gp->sched.PC, gp->sched.SP+8, gp); + unlock(&debuglock); + } + m->curg = gp; + gp->m = m; + g = gp; + gogo(&gp->sched); +} + +// Enter scheduler. If g->status is Grunning, +// re-queues g and runs everyone else who is waiting +// before running g again. If g->status is Gmoribund, +// kills off g. +void +gosched(void) +{ + if(g == m->g0) + throw("gosched of g0"); + if(gosave(&g->sched) == 0){ + g = m->g0; + gogo(&m->sched); + } +} + +// The goroutine g is about to enter a system call. +// Record that it's not using the cpu anymore. +// This is called only from the go syscall library, not +// from the low-level system calls used by the runtime. +// The "arguments" are syscall.Syscall's stack frame +void +sys·entersyscall(uint64 callerpc, int64 trap) +{ + USED(callerpc); + + if(debug > 1) { + lock(&debuglock); + printf("m%d g%d enter syscall %D\n", m->id, g->goid, trap); + unlock(&debuglock); + } + lock(&sched); + g->status = Gsyscall; + sched.mcpu--; + sched.msyscall++; + if(sched.gwait != 0) + matchmg(); + if(sched.waitstop && sched.mcpu <= sched.mcpumax) { + sched.waitstop = 0; + notewakeup(&sched.stopped); + } + unlock(&sched); + // leave SP around for gc and traceback + gosave(&g->sched); +} + +// The goroutine g exited its system call. +// Arrange for it to run on a cpu again. +// This is called only from the go syscall library, not +// from the low-level system calls used by the runtime. +void +sys·exitsyscall(void) +{ + if(debug > 1) { + lock(&debuglock); + printf("m%d g%d exit syscall mcpu=%d mcpumax=%d\n", m->id, g->goid, sched.mcpu, sched.mcpumax); + unlock(&debuglock); + } + + lock(&sched); + g->status = Grunning; + sched.msyscall--; + sched.mcpu++; + // Fast path - if there's room for this m, we're done. + if(sched.mcpu <= sched.mcpumax) { + unlock(&sched); + return; + } + unlock(&sched); + + // Slow path - all the cpus are taken. + // The scheduler will ready g and put this m to sleep. + // When the scheduler takes g awa from m, + // it will undo the sched.mcpu++ above. + gosched(); +} + +/* + * stack layout parameters. + * known to linkers. + * + * g->stackguard is set to point StackGuard bytes + * above the bottom of the stack. each function + * compares its stack pointer against g->stackguard + * to check for overflow. to cut one instruction from + * the check sequence for functions with tiny frames, + * the stack is allowed to protrude StackSmall bytes + * below the stack guard. functions with large frames + * don't bother with the check and always call morestack. + * the sequences are: + * + * guard = g->stackguard + * frame = function's stack frame size + * argsize = size of function arguments (call + return) + * + * stack frame size <= StackSmall: + * CMPQ guard, SP + * JHI 3(PC) + * MOVQ m->morearg, $(argsize << 32) + * CALL sys.morestack(SB) + * + * stack frame size > StackSmall but < StackBig + * LEAQ (frame-StackSmall)(SP), R0 + * CMPQ guard, R0 + * JHI 3(PC) + * MOVQ m->morearg, $(argsize << 32) + * CALL sys.morestack(SB) + * + * stack frame size >= StackBig: + * MOVQ m->morearg, $((argsize << 32) | frame) + * CALL sys.morestack(SB) + * + * the bottom StackGuard - StackSmall bytes are important: + * there has to be enough room to execute functions that + * refuse to check for stack overflow, either because they + * need to be adjacent to the actual caller's frame (sys.deferproc) + * or because they handle the imminent stack overflow (sys.morestack). + * + * for example, sys.deferproc might call malloc, + * which does one of the above checks (without allocating a full frame), + * which might trigger a call to sys.morestack. + * this sequence needs to fit in the bottom section of the stack. + * on amd64, sys.morestack's frame is 40 bytes, and + * sys.deferproc's frame is 56 bytes. that fits well within + * the StackGuard - StackSmall = 128 bytes at the bottom. + * there may be other sequences lurking or yet to be written + * that require more stack. sys.morestack checks to make sure + * the stack has not completely overflowed and should + * catch such sequences. + */ +enum +{ + // byte offset of stack guard (g->stackguard) above bottom of stack. + StackGuard = 256, + + // checked frames are allowed to protrude below the guard by + // this many bytes. this saves an instruction in the checking + // sequence when the stack frame is tiny. + StackSmall = 128, + + // extra space in the frame (beyond the function for which + // the frame is allocated) is assumed not to be much bigger + // than this amount. it may not be used efficiently if it is. + StackBig = 4096, +}; + +void +oldstack(void) +{ + Stktop *top; + uint32 args; + byte *sp; + uintptr oldsp, oldpc, oldbase, oldguard; + +// printf("oldstack m->cret=%p\n", m->cret); + + top = (Stktop*)m->curg->stackbase; + + args = (top->magic>>32) & 0xffffLL; + + sp = (byte*)top; + if(args > 0) { + args = (args+7) & ~7; + sp -= args; + mcpy(top->oldsp+2*sizeof(uintptr), sp, args); + } + + oldsp = (uintptr)top->oldsp + sizeof(uintptr); + oldpc = *(uintptr*)oldsp; + oldbase = (uintptr)top->oldbase; + oldguard = (uintptr)top->oldguard; + + stackfree((byte*)m->curg->stackguard - StackGuard); + + m->curg->stackbase = (byte*)oldbase; + m->curg->stackguard = (byte*)oldguard; + m->morestack.SP = (byte*)oldsp; + m->morestack.PC = (byte*)oldpc; + + // These two lines must happen in sequence; + // once g has been changed, must switch to g's stack + // before calling any non-assembly functions. + // TODO(rsc): Perhaps make the new g a parameter + // to gogoret and setspgoto, so that g is never + // explicitly assigned to without also setting + // the stack pointer. + g = m->curg; + gogoret(&m->morestack, m->cret); +} + +#pragma textflag 7 +void +lessstack(void) +{ + g = m->g0; + setspgoto(m->sched.SP, oldstack, nil); +} + +void +newstack(void) +{ + int32 frame, args; + Stktop *top; + byte *stk, *sp; + void (*fn)(void); + + frame = m->morearg & 0xffffffffLL; + args = (m->morearg>>32) & 0xffffLL; + +// printf("newstack frame=%d args=%d moresp=%p morepc=%p\n", frame, args, m->moresp, *(uintptr*)m->moresp); + + if(frame < StackBig) + frame = StackBig; + frame += 1024; // for more functions, Stktop. + stk = stackalloc(frame); + + top = (Stktop*)(stk+frame-sizeof(*top)); + + top->oldbase = m->curg->stackbase; + top->oldguard = m->curg->stackguard; + top->oldsp = m->moresp; + top->magic = m->morearg; + + m->curg->stackbase = (byte*)top; + m->curg->stackguard = stk + StackGuard; + + sp = (byte*)top; + + if(args > 0) { + // Copy args. There have been two function calls + // since they got pushed, so skip over those return + // addresses. + args = (args+7) & ~7; + sp -= args; + mcpy(sp, m->moresp+2*sizeof(uintptr), args); + } + + g = m->curg; + + // sys.morestack's return address + fn = (void(*)(void))(*(uintptr*)m->moresp); + +// printf("fn=%p\n", fn); + + setspgoto(sp, fn, retfromnewstack); + + *(int32*)345 = 123; // never return +} + +#pragma textflag 7 +void +sys·morestack(uintptr u) +{ + while(g == m->g0) { + // very bad news + *(int32*)0x1001 = 123; + } + + // Morestack's frame is about 0x30 bytes on amd64. + // If that the frame ends below the stack bottom, we've already + // overflowed. Stop right now. + while((byte*)&u - 0x30 < m->curg->stackguard - StackGuard) { + // very bad news + *(int32*)0x1002 = 123; + } + + g = m->g0; + m->moresp = (byte*)(&u-1); + setspgoto(m->sched.SP, newstack, nil); + + *(int32*)0x1003 = 123; // never return +} + +G* +malg(int32 stacksize) +{ + G *g; + byte *stk; + + g = malloc(sizeof(G)); + stk = stackalloc(stacksize + StackGuard); + g->stack0 = stk; + g->stackguard = stk + StackGuard; + g->stackbase = stk + StackGuard + stacksize; + return g; +} + +/* + * Newproc and deferproc need to be textflag 7 + * (no possible stack split when nearing overflow) + * because they assume that the arguments to fn + * are available sequentially beginning at &arg0. + * If a stack split happened, only the one word + * arg0 would be copied. It's okay if any functions + * they call split the stack below the newproc frame. + */ +#pragma textflag 7 +void +sys·newproc(int32 siz, byte* fn, byte* arg0) +{ + byte *stk, *sp; + G *newg; + +//printf("newproc siz=%d fn=%p", siz, fn); + + siz = (siz+7) & ~7; + if(siz > 1024) + throw("sys·newproc: too many args"); + + lock(&sched); + + if((newg = gfget()) != nil){ + newg->status = Gwaiting; + } else { + newg = malg(4096); + newg->status = Gwaiting; + newg->alllink = allg; + allg = newg; + } + stk = newg->stack0; + + newg->stackguard = stk+StackGuard; + + sp = stk + 4096 - 4*8; + newg->stackbase = sp; + + sp -= siz; + mcpy(sp, (byte*)&arg0, siz); + + sp -= sizeof(uintptr); + *(byte**)sp = (byte*)goexit; + + sp -= sizeof(uintptr); // retpc used by gogo + newg->sched.SP = sp; + newg->sched.PC = fn; + + sched.gcount++; + goidgen++; + newg->goid = goidgen; + + readylocked(newg); + unlock(&sched); + +//printf(" goid=%d\n", newg->goid); +} + +#pragma textflag 7 +void +sys·deferproc(int32 siz, byte* fn, byte* arg0) +{ + Defer *d; + + d = malloc(sizeof(*d) + siz - sizeof(d->args)); + d->fn = fn; + d->sp = (byte*)&arg0; + d->siz = siz; + mcpy(d->args, d->sp, d->siz); + + d->link = g->defer; + g->defer = d; +} + +#pragma textflag 7 +void +sys·deferreturn(uintptr arg0) +{ + Defer *d; + byte *sp, *fn; + uintptr *caller; + + d = g->defer; + if(d == nil) + return; + sp = (byte*)&arg0; + if(d->sp != sp) + return; + mcpy(d->sp, d->args, d->siz); + g->defer = d->link; + fn = d->fn; + free(d); + jmpdefer(fn, sp); + } + +void +runtime·Breakpoint(void) +{ + breakpoint(); +} + +void +runtime·Goexit(void) +{ + goexit(); +} + +void +runtime·Gosched(void) +{ + gosched(); +} + diff --git a/src/pkg/runtime/rune.c b/src/pkg/runtime/rune.c new file mode 100644 index 000000000..652ccdfa6 --- /dev/null +++ b/src/pkg/runtime/rune.c @@ -0,0 +1,224 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Portions Copyright 2009 The Go Authors. All rights reserved. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * This code is copied, with slight editing due to type differences, + * from a subset of ../lib9/utf/rune.c + */ + +#include "runtime.h" + +enum +{ + Bit1 = 7, + Bitx = 6, + Bit2 = 5, + Bit3 = 4, + Bit4 = 3, + Bit5 = 2, + + T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ + Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ + T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ + T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ + T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ + T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ + + Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */ + Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */ + Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */ + Rune4 = (1<<(Bit4+3*Bitx))-1, + /* 0001 1111 1111 1111 1111 1111 */ + + Maskx = (1<<Bitx)-1, /* 0011 1111 */ + Testx = Maskx ^ 0xFF, /* 1100 0000 */ + + Runeerror = 0xFFFD, + Runeself = 0x80, + + Bad = Runeerror, + + Runemax = 0x10FFFF, /* maximum rune value */ +}; + +/* + * Modified by Wei-Hwa Huang, Google Inc., on 2004-09-24 + * This is a slower but "safe" version of the old chartorune + * that works on strings that are not necessarily null-terminated. + * + * If you know for sure that your string is null-terminated, + * chartorune will be a bit faster. + * + * It is guaranteed not to attempt to access "length" + * past the incoming pointer. This is to avoid + * possible access violations. If the string appears to be + * well-formed but incomplete (i.e., to get the whole Rune + * we'd need to read past str+length) then we'll set the Rune + * to Bad and return 0. + * + * Note that if we have decoding problems for other + * reasons, we return 1 instead of 0. + */ +int32 +charntorune(int32 *rune, uint8 *str, int32 length) +{ + int32 c, c1, c2, c3, l; + + /* When we're not allowed to read anything */ + if(length <= 0) { + goto badlen; + } + + /* + * one character sequence (7-bit value) + * 00000-0007F => T1 + */ + c = *(uint8*)str; + if(c < Tx) { + *rune = c; + return 1; + } + + // If we can't read more than one character we must stop + if(length <= 1) { + goto badlen; + } + + /* + * two character sequence (11-bit value) + * 0080-07FF => T2 Tx + */ + c1 = *(uint8*)(str+1) ^ Tx; + if(c1 & Testx) + goto bad; + if(c < T3) { + if(c < T2) + goto bad; + l = ((c << Bitx) | c1) & Rune2; + if(l <= Rune1) + goto bad; + *rune = l; + return 2; + } + + // If we can't read more than two characters we must stop + if(length <= 2) { + goto badlen; + } + + /* + * three character sequence (16-bit value) + * 0800-FFFF => T3 Tx Tx + */ + c2 = *(uint8*)(str+2) ^ Tx; + if(c2 & Testx) + goto bad; + if(c < T4) { + l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; + if(l <= Rune2) + goto bad; + *rune = l; + return 3; + } + + if (length <= 3) + goto badlen; + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + c3 = *(uint8*)(str+3) ^ Tx; + if (c3 & Testx) + goto bad; + if (c < T5) { + l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; + if (l <= Rune3 || l > Runemax) + goto bad; + *rune = l; + return 4; + } + + // Support for 5-byte or longer UTF-8 would go here, but + // since we don't have that, we'll just fall through to bad. + + /* + * bad decoding + */ +bad: + *rune = Bad; + return 1; +badlen: + *rune = Bad; + return 0; + +} + +int32 +runetochar(byte *str, int32 rune) /* note: in original, arg2 was pointer */ +{ + /* Runes are signed, so convert to unsigned for range check. */ + uint32 c; + + /* + * one character sequence + * 00000-0007F => 00-7F + */ + c = rune; + if(c <= Rune1) { + str[0] = c; + return 1; + } + + /* + * two character sequence + * 0080-07FF => T2 Tx + */ + if(c <= Rune2) { + str[0] = T2 | (c >> 1*Bitx); + str[1] = Tx | (c & Maskx); + return 2; + } + + /* + * If the Rune is out of range, convert it to the error rune. + * Do this test here because the error rune encodes to three bytes. + * Doing it earlier would duplicate work, since an out of range + * Rune wouldn't have fit in one or two bytes. + */ + if (c > Runemax) + c = Runeerror; + + /* + * three character sequence + * 0800-FFFF => T3 Tx Tx + */ + if (c <= Rune3) { + str[0] = T3 | (c >> 2*Bitx); + str[1] = Tx | ((c >> 1*Bitx) & Maskx); + str[2] = Tx | (c & Maskx); + return 3; + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => T4 Tx Tx Tx + */ + str[0] = T4 | (c >> 3*Bitx); + str[1] = Tx | ((c >> 2*Bitx) & Maskx); + str[2] = Tx | ((c >> 1*Bitx) & Maskx); + str[3] = Tx | (c & Maskx); + return 4; +} diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c new file mode 100644 index 000000000..c5ba3e6a5 --- /dev/null +++ b/src/pkg/runtime/runtime.c @@ -0,0 +1,462 @@ +// Copyright 2009 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. + +#include "runtime.h" + +int32 panicking = 0; +int32 maxround = sizeof(uintptr); + +int32 +gotraceback(void) +{ + byte *p; + + p = getenv("GOTRACEBACK"); + if(p == nil || p[0] == '\0') + return 1; // default is on + return atoi(p); +} + +void +sys·panicl(int32 lno) +{ + uint8 *sp; + + if(panicking) { + printf("double panic\n"); + exit(3); + } + panicking++; + + printf("\npanic PC=%X\n", (uint64)(uintptr)&lno); + sp = (uint8*)&lno; + if(gotraceback()){ + traceback(sys·getcallerpc(&lno), sp, g); + tracebackothers(g); + } + breakpoint(); // so we can grab it in a debugger + exit(2); +} + +void +sys·throwindex(void) +{ + throw("index out of range"); +} + +void +sys·throwreturn(void) +{ + throw("no return at end of a typed function"); +} + +void +sys·throwinit(void) +{ + throw("recursive call during initialization"); +} + +void +throw(int8 *s) +{ + printf("throw: %s\n", s); + sys·panicl(-1); + *(int32*)0 = 0; // not reached + exit(1); // even more not reached +} + +void +mcpy(byte *t, byte *f, uint32 n) +{ + while(n > 0) { + *t = *f; + t++; + f++; + n--; + } +} + +int32 +mcmp(byte *s1, byte *s2, uint32 n) +{ + uint32 i; + byte c1, c2; + + for(i=0; i<n; i++) { + c1 = s1[i]; + c2 = s2[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + } + return 0; +} + + +void +mmov(byte *t, byte *f, uint32 n) +{ + if(t < f) { + while(n > 0) { + *t = *f; + t++; + f++; + n--; + } + } else { + t += n; + f += n; + while(n > 0) { + t--; + f--; + *t = *f; + n--; + } + } +} + +byte* +mchr(byte *p, byte c, byte *ep) +{ + for(; p < ep; p++) + if(*p == c) + return p; + return nil; +} + +uint32 +rnd(uint32 n, uint32 m) +{ + uint32 r; + + if(m > maxround) + m = maxround; + r = n % m; + if(r) + n += m-r; + return n; +} + +static int32 argc; +static uint8** argv; + +Array os·Args; +Array os·Envs; + +void +args(int32 c, uint8 **v) +{ + argc = c; + argv = v; +} + +void +goargs(void) +{ + String *gargv; + String *genvv; + int32 i, envc; + + for(envc=0; argv[argc+1+envc] != 0; envc++) + ; + + gargv = malloc(argc*sizeof gargv[0]); + genvv = malloc(envc*sizeof genvv[0]); + + for(i=0; i<argc; i++) + gargv[i] = gostring(argv[i]); + os·Args.array = (byte*)gargv; + os·Args.nel = argc; + os·Args.cap = argc; + + for(i=0; i<envc; i++) + genvv[i] = gostring(argv[argc+1+i]); + os·Envs.array = (byte*)genvv; + os·Envs.nel = envc; + os·Envs.cap = envc; +} + +byte* +getenv(int8 *s) +{ + int32 i, j, len; + byte *v, *bs; + String* envv; + int32 envc; + + bs = (byte*)s; + len = findnull(bs); + envv = (String*)os·Envs.array; + envc = os·Envs.nel; + for(i=0; i<envc; i++){ + if(envv[i].len <= len) + continue; + v = envv[i].str; + for(j=0; j<len; j++) + if(bs[j] != v[j]) + goto nomatch; + if(v[len] != '=') + goto nomatch; + return v+len+1; + nomatch:; + } + return nil; +} + + +int32 +atoi(byte *p) +{ + int32 n; + + n = 0; + while('0' <= *p && *p <= '9') + n = n*10 + *p++ - '0'; + return n; +} + +void +check(void) +{ + int8 a; + uint8 b; + int16 c; + uint16 d; + int32 e; + uint32 f; + int64 g; + uint64 h; + float32 i; + float64 j; + void* k; + uint16* l; + + if(sizeof(a) != 1) throw("bad a"); + if(sizeof(b) != 1) throw("bad b"); + if(sizeof(c) != 2) throw("bad c"); + if(sizeof(d) != 2) throw("bad d"); + if(sizeof(e) != 4) throw("bad e"); + if(sizeof(f) != 4) throw("bad f"); + if(sizeof(g) != 8) throw("bad g"); + if(sizeof(h) != 8) throw("bad h"); + if(sizeof(i) != 4) throw("bad i"); + if(sizeof(j) != 8) throw("bad j"); + if(sizeof(k) != sizeof(uintptr)) throw("bad k"); + if(sizeof(l) != sizeof(uintptr)) throw("bad l"); +// prints(1"check ok\n"); + + uint32 z; + z = 1; + if(!cas(&z, 1, 2)) + throw("cas1"); + if(z != 2) + throw("cas2"); + + z = 4; + if(cas(&z, 5, 6)) + throw("cas3"); + if(z != 4) + throw("cas4"); + + initsig(); +} + +/* + * map and chan helpers for + * dealing with unknown types + */ +static uintptr +memhash(uint32 s, void *a) +{ + byte *b; + uintptr hash; + + b = a; + if(sizeof(hash) == 4) + hash = 2860486313U; + else + hash = 33054211828000289ULL; + while(s > 0) { + if(sizeof(hash) == 4) + hash = (hash ^ *b) * 3267000013UL; + else + hash = (hash ^ *b) * 23344194077549503ULL; + b++; + s--; + } + return hash; +} + +static uint32 +memequal(uint32 s, void *a, void *b) +{ + byte *ba, *bb; + uint32 i; + + ba = a; + bb = b; + for(i=0; i<s; i++) + if(ba[i] != bb[i]) + return 0; + return 1; +} + +static void +memprint(uint32 s, void *a) +{ + uint64 v; + + v = 0xbadb00b; + switch(s) { + case 1: + v = *(uint8*)a; + break; + case 2: + v = *(uint16*)a; + break; + case 4: + v = *(uint32*)a; + break; + case 8: + v = *(uint64*)a; + break; + } + sys·printint(v); +} + +static void +memcopy(uint32 s, void *a, void *b) +{ + byte *ba, *bb; + uint32 i; + + ba = a; + bb = b; + if(bb == nil) { + for(i=0; i<s; i++) + ba[i] = 0; + return; + } + for(i=0; i<s; i++) + ba[i] = bb[i]; +} + +static uintptr +strhash(uint32 s, String *a) +{ + USED(s); + return memhash((*a).len, (*a).str); +} + +static uint32 +strequal(uint32 s, String *a, String *b) +{ + USED(s); + return cmpstring(*a, *b) == 0; +} + +static void +strprint(uint32 s, String *a) +{ + USED(s); + sys·printstring(*a); +} + +static uintptr +interhash(uint32 s, Iface *a) +{ + USED(s); + return ifacehash(*a); +} + +static void +interprint(uint32 s, Iface *a) +{ + USED(s); + sys·printiface(*a); +} + +static uint32 +interequal(uint32 s, Iface *a, Iface *b) +{ + USED(s); + return ifaceeq(*a, *b); +} + +static uintptr +nilinterhash(uint32 s, Eface *a) +{ + USED(s); + return efacehash(*a); +} + +static void +nilinterprint(uint32 s, Eface *a) +{ + USED(s); + sys·printeface(*a); +} + +static uint32 +nilinterequal(uint32 s, Eface *a, Eface *b) +{ + USED(s); + return efaceeq(*a, *b); +} + +uintptr +nohash(uint32 s, void *a) +{ + USED(s); + USED(a); + throw("hash of unhashable type"); + return 0; +} + +uint32 +noequal(uint32 s, void *a, void *b) +{ + USED(s); + USED(a); + USED(b); + throw("comparing uncomparable types"); + return 0; +} + +static void +noprint(uint32 s, void *a) +{ + USED(s); + USED(a); + throw("print of unprintable type"); +} + +static void +nocopy(uint32 s, void *a, void *b) +{ + USED(s); + USED(a); + USED(b); + throw("copy of uncopyable type"); +} + +Alg +algarray[] = +{ +[AMEM] { memhash, memequal, memprint, memcopy }, +[ANOEQ] { nohash, noequal, memprint, memcopy }, +[ASTRING] { strhash, strequal, strprint, memcopy }, +[AINTER] { interhash, interequal, interprint, memcopy }, +[ANILINTER] { nilinterhash, nilinterequal, nilinterprint, memcopy }, +[AFAKE] { nohash, noequal, noprint, nocopy }, +}; + +#pragma textflag 7 +void +FLUSH(void *v) +{ + USED(v); +} + diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h new file mode 100644 index 000000000..749364f95 --- /dev/null +++ b/src/pkg/runtime/runtime.h @@ -0,0 +1,464 @@ +// Copyright 2009 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. + +/* + * basic types + */ +typedef signed char int8; +typedef unsigned char uint8; +typedef signed short int16; +typedef unsigned short uint16; +typedef signed int int32; +typedef unsigned int uint32; +typedef signed long long int int64; +typedef unsigned long long int uint64; +typedef float float32; +typedef double float64; + +#ifdef _64BIT +typedef uint64 uintptr; +#else +typedef uint32 uintptr; +#endif + +/* + * get rid of C types + * the / / / forces a syntax error immediately, + * which will show "last name: XXunsigned". + */ +#define unsigned XXunsigned / / / +#define signed XXsigned / / / +#define char XXchar / / / +#define short XXshort / / / +#define int XXint / / / +#define long XXlong / / / +#define float XXfloat / / / +#define double XXdouble / / / + +/* + * defined types + */ +typedef uint8 bool; +typedef uint8 byte; +typedef struct Alg Alg; +typedef struct Array Array; +typedef struct Func Func; +typedef struct G G; +typedef struct Gobuf Gobuf; +typedef struct Lock Lock; +typedef struct M M; +typedef struct Mem Mem; +typedef union Note Note; +typedef struct Stktop Stktop; +typedef struct String String; +typedef struct Usema Usema; +typedef struct SigTab SigTab; +typedef struct MCache MCache; +typedef struct Iface Iface; +typedef struct Itype Itype; +typedef struct Eface Eface; +typedef struct Sigt Sigt; +typedef struct Defer Defer; + +/* + * per cpu declaration + */ +extern register G* g; // R15 +extern register M* m; // R14 + +/* + * defined constants + */ +enum +{ + // G status + Gidle, + Grunnable, + Grunning, + Gsyscall, + Gwaiting, + Gmoribund, + Gdead, +}; +enum +{ + true = 1, + false = 0, +}; + +/* + * structures + */ +struct Lock +{ + uint32 key; + uint32 sema; // for OS X +}; +struct Usema +{ + uint32 u; + uint32 k; +}; +union Note +{ + struct { // Linux + Lock lock; + }; + struct { // OS X + int32 wakeup; + Usema sema; + }; +}; +struct String +{ + byte* str; + int32 len; +}; +struct Iface +{ + Itype* type; + void* data; +}; +struct Eface +{ + Sigt* type; + void* data; +}; + +struct Array +{ // must not move anything + byte* array; // actual data + uint32 nel; // number of elements + uint32 cap; // allocated number of elements +}; +struct Gobuf +{ + byte* SP; + byte* PC; +}; +struct G +{ + byte* stackguard; // must not move + byte* stackbase; // must not move + Defer* defer; // must not move + byte* stack0; // first stack segment + Gobuf sched; + G* alllink; // on allg + void* param; // passed parameter on wakeup + int16 status; + int32 goid; + int32 selgen; // valid sudog pointer + G* schedlink; + bool readyonstop; + M* m; // for debuggers +}; +struct Mem +{ + uint8* hunk; + uint32 nhunk; + uint64 nmmap; + uint64 nmal; +}; +struct M +{ + G* g0; // g0 w interrupt stack - must not move + uint64 morearg; // arg to morestack - must not move + uint64 cret; // return value from C - must not move + uint64 procid; // for debuggers - must not move + G* gsignal; // signal-handling G - must not move + G* curg; // current running goroutine - must not move + G* lastg; // last running goroutine - to emulate fifo - must not move + uint32 tls[8]; // thread-local storage (for 386 extern register) - must not move + Gobuf sched; + Gobuf morestack; + byte* moresp; + int32 siz1; + int32 siz2; + int32 id; + int32 mallocing; + int32 locks; + Note havenextg; + G* nextg; + M* schedlink; + Mem mem; + uint32 machport; // Return address for Mach IPC (OS X) + MCache *mcache; +}; +struct Stktop +{ + uint8* oldbase; + uint8* oldsp; + uint64 magic; + uint8* oldguard; +}; +struct Alg +{ + uintptr (*hash)(uint32, void*); + uint32 (*equal)(uint32, void*, void*); + void (*print)(uint32, void*); + void (*copy)(uint32, void*, void*); +}; +struct SigTab +{ + int32 flags; + int8 *name; +}; +enum +{ + SigCatch = 1<<0, + SigIgnore = 1<<1, + SigRestart = 1<<2, +}; + +// (will be) shared with go; edit ../cmd/6g/sys.go too. +// should move out of sys.go eventually. +// also eventually, the loaded symbol table should +// be closer to this form. +struct Func +{ + String name; + String type; // go type string + String src; // src file name + uint64 entry; // entry pc + int64 frame; // stack frame size + Array pcln; // pc/ln tab for this func + int64 pc0; // starting pc, ln for table + int32 ln0; + int32 args; // number of 32-bit in/out args + int32 locals; // number of 32-bit locals +}; + +/* + * defined macros + * you need super-goru privilege + * to add this list. + */ +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define nil ((void*)0) + +/* + * known to compiler + */ +enum +{ + AMEM, + ANOEQ, + ASTRING, + AINTER, + ANILINTER, + AFAKE, + Amax +}; + +/* + * deferred subroutine calls + */ +struct Defer +{ + int32 siz; + byte* sp; + byte* fn; + Defer* link; + byte args[8]; // padded to actual size +}; + +/* + * external data + */ +extern Alg algarray[Amax]; +extern String emptystring; +G* allg; +int32 goidgen; +extern int32 gomaxprocs; +extern int32 panicking; +extern int32 maxround; + +/* + * common functions and data + */ +int32 strcmp(byte*, byte*); +int32 findnull(byte*); +void dump(byte*, int32); +int32 runetochar(byte*, int32); +int32 charntorune(int32*, uint8*, int32); + +/* + * very low level c-called + */ +int32 gogo(Gobuf*); +int32 gosave(Gobuf*); +int32 gogoret(Gobuf*, uint64); +void retfromnewstack(void); +void goargs(void); +void setspgoto(byte*, void(*)(void), void(*)(void)); +void FLUSH(void*); +void* getu(void); +void throw(int8*); +uint32 rnd(uint32, uint32); +void prints(int8*); +void printf(int8*, ...); +byte* mchr(byte*, byte, byte*); +void mcpy(byte*, byte*, uint32); +int32 mcmp(byte*, byte*, uint32); +void mmov(byte*, byte*, uint32); +void* mal(uint32); +uint32 cmpstring(String, String); +String gostring(byte*); +void initsig(void); +int32 gotraceback(void); +void traceback(uint8 *pc, uint8 *sp, G* gp); +void tracebackothers(G*); +int32 open(byte*, int32, ...); +int32 read(int32, void*, int32); +int32 write(int32, void*, int32); +void close(int32); +int32 fstat(int32, void*); +bool cas(uint32*, uint32, uint32); +void jmpdefer(byte*, void*); +void exit1(int32); +void ready(G*); +byte* getenv(int8*); +int32 atoi(byte*); +void newosproc(M *m, G *g, void *stk, void (*fn)(void)); +void sigaltstack(void*, void*); +void signalstack(byte*, int32); +G* malg(int32); +void minit(void); +Func* findfunc(uintptr); +int32 funcline(Func*, uint64); +void* stackalloc(uint32); +void stackfree(void*); +MCache* allocmcache(void); +void mallocinit(void); +bool ifaceeq(Iface, Iface); +bool efaceeq(Eface, Eface); +uintptr ifacehash(Iface); +uintptr efacehash(Eface); +uintptr nohash(uint32, void*); +uint32 noequal(uint32, void*, void*); +void* malloc(uintptr size); +void* mallocgc(uintptr size); +void free(void *v); +void exit(int32); +void breakpoint(void); +void gosched(void); +void goexit(void); + +#pragma varargck argpos printf 1 + +#pragma varargck type "d" int32 +#pragma varargck type "d" uint32 +#pragma varargck type "D" int64 +#pragma varargck type "D" uint64 +#pragma varargck type "x" int32 +#pragma varargck type "x" uint32 +#pragma varargck type "X" int64 +#pragma varargck type "X" uint64 +#pragma varargck type "p" void* +#pragma varargck type "p" uintptr +#pragma varargck type "s" int8* +#pragma varargck type "s" uint8* +#pragma varargck type "S" String + +// TODO(rsc): Remove. These are only temporary, +// for the mark and sweep collector. +void stoptheworld(void); +void starttheworld(void); + +/* + * mutual exclusion locks. in the uncontended case, + * as fast as spin locks (just a few user-level instructions), + * but on the contention path they sleep in the kernel. + * a zeroed Lock is unlocked (no need to initialize each lock). + */ +void lock(Lock*); +void unlock(Lock*); + +/* + * sleep and wakeup on one-time events. + * before any calls to notesleep or notewakeup, + * must call noteclear to initialize the Note. + * then, any number of threads can call notesleep + * and exactly one thread can call notewakeup (once). + * once notewakeup has been called, all the notesleeps + * will return. future notesleeps will return immediately. + */ +void noteclear(Note*); +void notesleep(Note*); +void notewakeup(Note*); + +/* + * Redefine methods for the benefit of gcc, which does not support + * UTF-8 characters in identifiers. + */ +#ifndef __GNUC__ +#define sys_memclr sys·memclr +#define sys_write sys·write +#define sys_catstring sys·catstring +#define sys_cmpstring sys·cmpstring +#define sys_getcallerpc sys·getcallerpc +#define sys_indexstring sys·indexstring +#define sys_intstring sys·intstring +#define sys_mal sys·mal +#define sys_mmap sys·mmap +#define sys_printarray sys·printarray +#define sys_printbool sys·printbool +#define sys_printfloat sys·printfloat +#define sys_printhex sys·printhex +#define sys_printint sys·printint +#define sys_printiface sys·printiface +#define sys_printeface sys·printeface +#define sys_printpc sys·printpc +#define sys_printpointer sys·printpointer +#define sys_printstring sys·printstring +#define sys_printuint sys·printuint +#define sys_setcallerpc sys·setcallerpc +#define sys_slicestring sys·slicestring +#endif + +/* + * low level go-called + */ +void sys_write(int32, void*, int32); +uint8* sys_mmap(byte*, uint32, int32, int32, int32, uint32); +void sys_memclr(byte*, uint32); +void sys_setcallerpc(void*, void*); +void* sys_getcallerpc(void*); + +/* + * runtime go-called + */ +void sys_printbool(bool); +void sys_printfloat(float64); +void sys_printint(int64); +void sys_printiface(Iface); +void sys_printeface(Eface); +void sys_printstring(String); +void sys_printpc(void*); +void sys_printpointer(void*); +void sys_printuint(uint64); +void sys_printhex(uint64); +void sys_printarray(Array); +void sys_catstring(String, String, String); +void sys_cmpstring(String, String, int32); +void sys_slicestring(String, int32, int32, String); +void sys_indexstring(String, int32, byte); +void sys_intstring(int64, String); + +/* + * wrapped for go users + */ +float64 Inf(int32 sign); +float64 NaN(void); +float32 float32frombits(uint32 i); +uint32 float32tobits(float32 f); +float64 float64frombits(uint64 i); +uint64 float64tobits(float64 f); +float64 frexp(float64 d, int32 *ep); +bool isInf(float64 f, int32 sign); +bool isNaN(float64 f); +float64 ldexp(float64 d, int32 e); +float64 modf(float64 d, float64 *ip); +void semacquire(uint32*); +void semrelease(uint32*); diff --git a/src/pkg/runtime/sema.c b/src/pkg/runtime/sema.c new file mode 100644 index 000000000..5e5b07aa6 --- /dev/null +++ b/src/pkg/runtime/sema.c @@ -0,0 +1,176 @@ +// Copyright 2009 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. + +// Semaphore implementation exposed to Go. +// Intended use is provide a sleep and wakeup +// primitive that can be used in the contended case +// of other synchronization primitives. +// Thus it targets the same goal as Linux's futex, +// but it has much simpler semantics. +// +// That is, don't think of these as semaphores. +// Think of them as a way to implement sleep and wakeup +// such that every sleep is paired with a single wakeup, +// even if, due to races, the wakeup happens before the sleep. +// +// See Mullender and Cox, ``Semaphores in Plan 9,'' +// http://swtch.com/semaphore.pdf + +#include "runtime.h" + +typedef struct Sema Sema; +struct Sema +{ + uint32 *addr; + G *g; + Sema *prev; + Sema *next; +}; + +// TODO: For now, a linked list; maybe a hash table of linked lists later. +static Sema *semfirst, *semlast; +static Lock semlock; + +static void +semqueue(uint32 *addr, Sema *s) +{ + s->addr = addr; + s->g = nil; + + lock(&semlock); + s->prev = semlast; + s->next = nil; + if(semlast) + semlast->next = s; + else + semfirst = s; + semlast = s; + unlock(&semlock); +} + +static void +semdequeue(Sema *s) +{ + lock(&semlock); + if(s->next) + s->next->prev = s->prev; + else + semlast = s->prev; + if(s->prev) + s->prev->next = s->next; + else + semfirst = s->next; + s->prev = nil; + s->next = nil; + unlock(&semlock); +} + +static void +semwakeup(uint32 *addr) +{ + Sema *s; + + lock(&semlock); + for(s=semfirst; s; s=s->next) { + if(s->addr == addr && s->g) { + ready(s->g); + s->g = nil; + break; + } + } + unlock(&semlock); +} + +// Step 1 of sleep: make ourselves available for wakeup. +// TODO(rsc): Maybe we can write a version without +// locks by using cas on s->g. Maybe not: I need to +// think more about whether it would be correct. +static void +semsleep1(Sema *s) +{ + lock(&semlock); + s->g = g; + unlock(&semlock); +} + +// Decided not to go through with it: undo step 1. +static void +semsleepundo1(Sema *s) +{ + lock(&semlock); + if(s->g != nil) { + s->g = nil; // back ourselves out + } else { + // If s->g == nil already, semwakeup + // already readied us. Since we never stopped + // running, readying us just set g->readyonstop. + // Clear it. + if(g->readyonstop == 0) + *(int32*)0x555 = 555; + g->readyonstop = 0; + } + unlock(&semlock); +} + +// Step 2: wait for the wakeup. +static void +semsleep2(Sema *s) +{ + USED(s); + g->status = Gwaiting; + gosched(); +} + +static int32 +cansemacquire(uint32 *addr) +{ + uint32 v; + + while((v = *addr) > 0) + if(cas(addr, v, v-1)) + return 1; + return 0; +} + +// For now has no return value. +// Might return an ok (not interrupted) bool in the future? +void +semacquire(uint32 *addr) +{ + Sema s; + + // Easy case. + if(cansemacquire(addr)) + return; + + // Harder case: + // queue + // try semacquire one more time, sleep if failed + // dequeue + // wake up one more guy to avoid races (TODO(rsc): maybe unnecessary?) + semqueue(addr, &s); + for(;;) { + semsleep1(&s); + if(cansemacquire(addr)) { + semsleepundo1(&s); + break; + } + semsleep2(&s); + } + semdequeue(&s); + semwakeup(addr); +} + +void +semrelease(uint32 *addr) +{ + uint32 v; + + for(;;) { + v = *addr; + if(cas(addr, v, v+1)) + break; + } + semwakeup(addr); +} diff --git a/src/pkg/runtime/sema_go.cgo b/src/pkg/runtime/sema_go.cgo new file mode 100644 index 000000000..eb4082a0d --- /dev/null +++ b/src/pkg/runtime/sema_go.cgo @@ -0,0 +1,15 @@ +// Copyright 2009 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 sync +#include "runtime.h" + +func semacquire(addr *uint32) { + semacquire(addr); +} + +func semrelease(addr *uint32) { + semrelease(addr); +} + diff --git a/src/pkg/runtime/string.c b/src/pkg/runtime/string.c new file mode 100644 index 000000000..5bfe8196f --- /dev/null +++ b/src/pkg/runtime/string.c @@ -0,0 +1,263 @@ +// Copyright 2009 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. + +#include "runtime.h" + +String emptystring; + +int32 +findnull(byte *s) +{ + int32 l; + + if(s == nil) + return 0; + for(l=0; s[l]!=0; l++) + ; + return l; +} + +int32 maxstring; + +String +gostringsize(int32 l) +{ + String s; + + if(l == 0) + return emptystring; + s.str = mal(l+1); // leave room for NUL for C runtime (e.g., callers of getenv) + s.len = l; + if(l > maxstring) + maxstring = l; + return s; +} + +String +gostring(byte *str) +{ + int32 l; + String s; + + l = findnull(str); + s = gostringsize(l); + mcpy(s.str, str, l); + return s; +} + +void +sys·catstring(String s1, String s2, String s3) +{ + if(s1.len == 0) { + s3 = s2; + goto out; + } + if(s2.len == 0) { + s3 = s1; + goto out; + } + + s3 = gostringsize(s1.len + s2.len); + mcpy(s3.str, s1.str, s1.len); + mcpy(s3.str+s1.len, s2.str, s2.len); + +out: + FLUSH(&s3); +} + +static void +prbounds(int8* s, int32 a, int32 b, int32 c) +{ + prints(s); + prints(" "); + sys·printint(a); + prints("<"); + sys·printint(b); + prints(">"); + sys·printint(c); + prints("\n"); + throw("string bounds"); +} + +uint32 +cmpstring(String s1, String s2) +{ + uint32 i, l; + byte c1, c2; + + l = s1.len; + if(s2.len < l) + l = s2.len; + for(i=0; i<l; i++) { + c1 = s1.str[i]; + c2 = s2.str[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + } + if(s1.len < s2.len) + return -1; + if(s1.len > s2.len) + return +1; + return 0; +} + +void +sys·cmpstring(String s1, String s2, int32 v) +{ + v = cmpstring(s1, s2); + FLUSH(&v); +} + +int32 +strcmp(byte *s1, byte *s2) +{ + uint32 i; + byte c1, c2; + + for(i=0;; i++) { + c1 = s1[i]; + c2 = s2[i]; + if(c1 < c2) + return -1; + if(c1 > c2) + return +1; + if(c1 == 0) + return 0; + } +} + +void +sys·slicestring(String si, int32 lindex, int32 hindex, String so) +{ + int32 l; + + if(lindex < 0 || lindex > si.len || + hindex < lindex || hindex > si.len) { + sys·printpc(&si); + prints(" "); + prbounds("slice", lindex, si.len, hindex); + } + + l = hindex-lindex; + so.str = si.str + lindex; + so.len = l; + +// alternate to create a new string +// so = gostringsize(l); +// mcpy(so.str, si.str+lindex, l); + + FLUSH(&so); +} + +void +sys·indexstring(String s, int32 i, byte b) +{ + if(i < 0 || i >= s.len) { + sys·printpc(&s); + prints(" "); + prbounds("index", 0, i, s.len); + } + + b = s.str[i]; + FLUSH(&b); +} + +void +sys·intstring(int64 v, String s) +{ + s = gostringsize(8); + s.len = runetochar(s.str, v); + FLUSH(&s); +} + +void +sys·arraystring(Array b, String s) +{ + s = gostringsize(b.nel); + mcpy(s.str, b.array, s.len); + FLUSH(&s); +} + +void +sys·arraystringi(Array b, String s) +{ + int32 siz1, siz2, i; + int32 *a; + byte dum[8]; + + a = (int32*)b.array; + siz1 = 0; + for(i=0; i<b.nel; i++) { + siz1 += runetochar(dum, a[i]); + } + + s = gostringsize(siz1+4); + siz2 = 0; + for(i=0; i<b.nel; i++) { + // check for race + if(siz2 >= siz1) + break; + siz2 += runetochar(s.str+siz2, a[i]); + } + s.len = siz2; + + FLUSH(&s); +} + +enum +{ + Runeself = 0x80, +}; + +// func stringiter(string, int) (retk int); +void +sys·stringiter(String s, int32 k, int32 retk) +{ + int32 l; + + if(k >= s.len) { + // retk=0 is end of iteration + retk = 0; + goto out; + } + + l = s.str[k]; + if(l < Runeself) { + retk = k+1; + goto out; + } + + // multi-char rune + retk = k + charntorune(&l, s.str+k, s.len-k); + +out: + FLUSH(&retk); +} + +// func stringiter2(string, int) (retk int, retv any); +void +sys·stringiter2(String s, int32 k, int32 retk, int32 retv) +{ + if(k >= s.len) { + // retk=0 is end of iteration + retk = 0; + retv = 0; + goto out; + } + + retv = s.str[k]; + if(retv < Runeself) { + retk = k+1; + goto out; + } + + // multi-char rune + retk = k + charntorune(&retv, s.str+k, s.len-k); + +out: + FLUSH(&retk); + FLUSH(&retv); +} diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c new file mode 100644 index 000000000..b4802715e --- /dev/null +++ b/src/pkg/runtime/symtab.c @@ -0,0 +1,377 @@ +// Copyright 2009 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. + +// Runtime symbol table access. Work in progress. +// The Plan 9 symbol table is not in a particularly convenient form. +// The routines here massage it into a more usable form; eventually +// we'll change 6l to do this for us, but it is easier to experiment +// here than to change 6l and all the other tools. +// +// The symbol table also needs to be better integrated with the type +// strings table in the future. This is just a quick way to get started +// and figure out exactly what we want. + +#include "runtime.h" + +// TODO(rsc): Move this *under* the text segment. +// Then define names for these addresses instead of hard-coding magic ones. +#ifdef _64BIT +#define SYMCOUNTS ((int32*)(0x99LL<<32)) // known to 6l +#define SYMDATA ((byte*)(0x99LL<<32) + 8) +#else +#define SYMCOUNTS ((int32*)(0x99LL<<24)) // known to 8l +#define SYMDATA ((byte*)(0x99LL<<24) + 8) +#endif + + +// Return a pointer to a byte array containing the symbol table segment. +void +sys·symdat(Array *symtab, Array *pclntab) +{ + Array *a; + int32 *v; + + v = SYMCOUNTS; + + a = mal(sizeof *a); + a->nel = v[0]; + a->cap = a->nel; + a->array = SYMDATA; + symtab = a; + FLUSH(&symtab); + + a = mal(sizeof *a); + a->nel = v[1]; + a->cap = a->nel; + a->array = SYMDATA + v[0]; + pclntab = a; + FLUSH(&pclntab); +} + +typedef struct Sym Sym; +struct Sym +{ + uintptr value; + byte symtype; + byte *name; + byte *gotype; +}; + +// Walk over symtab, calling fn(&s) for each symbol. +static void +walksymtab(void (*fn)(Sym*)) +{ + int32 *v; + byte *p, *ep, *q; + Sym s; + + v = SYMCOUNTS; + p = SYMDATA; + ep = p + v[0]; + while(p < ep) { + if(p + 7 > ep) + break; + s.value = ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]); + if(!(p[4]&0x80)) + break; + s.symtype = p[4] & ~0x80; + p += 5; + s.name = p; + if(s.symtype == 'z' || s.symtype == 'Z') { + // path reference string - skip first byte, + // then 2-byte pairs ending at two zeros. + q = p+1; + for(;;) { + if(q+2 > ep) + return; + if(q[0] == '\0' && q[1] == '\0') + break; + q += 2; + } + p = q+2; + }else{ + q = mchr(p, '\0', ep); + if(q == nil) + break; + p = q+1; + } + q = mchr(p, '\0', ep); + if(q == nil) + break; + s.gotype = p; + p = q+1; + fn(&s); + } +} + +// Symtab walker; accumulates info about functions. + +static Func *func; +static int32 nfunc; + +static byte **fname; +static int32 nfname; + +static void +dofunc(Sym *sym) +{ + Func *f; + + switch(sym->symtype) { + case 't': + case 'T': + if(strcmp(sym->name, (byte*)"etext") == 0) + break; + if(func == nil) { + nfunc++; + break; + } + f = &func[nfunc++]; + f->name = gostring(sym->name); + f->entry = sym->value; + break; + case 'm': + if(nfunc > 0 && func != nil) + func[nfunc-1].frame = sym->value; + break; + case 'p': + if(nfunc > 0 && func != nil) { + f = &func[nfunc-1]; + // args counts 32-bit words. + // sym->value is the arg's offset. + // don't know width of this arg, so assume it is 64 bits. + if(f->args < sym->value/4 + 2) + f->args = sym->value/4 + 2; + } + break; + case 'f': + if(fname == nil) { + if(sym->value >= nfname) + nfname = sym->value+1; + break; + } + fname[sym->value] = sym->name; + break; + } +} + +// put together the path name for a z entry. +// the f entries have been accumulated into fname already. +static void +makepath(byte *buf, int32 nbuf, byte *path) +{ + int32 n, len; + byte *p, *ep, *q; + + if(nbuf <= 0) + return; + + p = buf; + ep = buf + nbuf; + *p = '\0'; + for(;;) { + if(path[0] == 0 && path[1] == 0) + break; + n = (path[0]<<8) | path[1]; + path += 2; + if(n >= nfname) + break; + q = fname[n]; + len = findnull(q); + if(p+1+len >= ep) + break; + if(p > buf && p[-1] != '/') + *p++ = '/'; + mcpy(p, q, len+1); + p += len; + } +} + +// walk symtab accumulating path names for use by pc/ln table. +// don't need the full generality of the z entry history stack because +// there are no includes in go (and only sensible includes in our c). +static void +dosrcline(Sym *sym) +{ + static byte srcbuf[1000]; + static String srcstring; + static int32 lno, incstart; + static int32 nf, nhist; + Func *f; + + switch(sym->symtype) { + case 't': + case 'T': + if(strcmp(sym->name, (byte*)"etext") == 0) + break; + f = &func[nf++]; + f->src = srcstring; + f->ln0 += lno; + break; + case 'z': + if(sym->value == 1) { + // entry for main source file for a new object. + makepath(srcbuf, sizeof srcbuf, sym->name+1); + srcstring = gostring(srcbuf); + lno = 0; + nhist = 0; + } else { + // push or pop of included file. + makepath(srcbuf, sizeof srcbuf, sym->name+1); + if(srcbuf[0] != '\0') { + if(nhist++ == 0) + incstart = sym->value; + }else{ + if(--nhist == 0) + lno -= sym->value - incstart; + } + } + } +} + +enum { PcQuant = 1 }; + +// Interpret pc/ln table, saving the subpiece for each func. +static void +splitpcln(void) +{ + int32 line; + uintptr pc; + byte *p, *ep; + Func *f, *ef; + int32 *v; + + // pc/ln table bounds + v = SYMCOUNTS; + p = SYMDATA; + p += v[0]; + ep = p+v[1]; + + f = func; + ef = func + nfunc; + pc = func[0].entry; // text base + f->pcln.array = p; + f->pc0 = pc - PcQuant; + line = 0; + for(; p < ep; p++) { + if(f < ef && pc >= (f+1)->entry) { + f->pcln.nel = p - f->pcln.array; + f->pcln.cap = f->pcln.nel; + f++; + f->pcln.array = p; + f->pc0 = pc; + f->ln0 = line; + } + if(*p == 0) { + // 4 byte add to line + line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; + p += 4; + } else if(*p <= 64) { + line += *p; + } else if(*p <= 128) { + line -= *p - 64; + } else { + pc += PcQuant*(*p - 129); + } + pc += PcQuant; + } + if(f < ef) { + f->pcln.nel = p - f->pcln.array; + f->pcln.cap = f->pcln.nel; + } +} + + +// Return actual file line number for targetpc in func f. +// (Source file is f->src.) +int32 +funcline(Func *f, uint64 targetpc) +{ + byte *p, *ep; + uintptr pc; + int32 line; + + p = f->pcln.array; + ep = p + f->pcln.nel; + pc = f->pc0; + line = f->ln0; + for(; p < ep; p++) { + if(pc >= targetpc) + return line; + if(*p == 0) { + line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; + p += 4; + } else if(*p <= 64) { + line += *p; + } else if(*p <= 128) { + line -= *p - 64; + } else { + pc += PcQuant*(*p - 129); + } + pc += PcQuant; + } + return line; +} + +static void +buildfuncs(void) +{ + extern byte etext[]; + + if(func != nil) + return; + // count funcs, fnames + nfunc = 0; + nfname = 0; + walksymtab(dofunc); + + // initialize tables + func = mal((nfunc+1)*sizeof func[0]); + func[nfunc].entry = (uint64)etext; + fname = mal(nfname*sizeof fname[0]); + nfunc = 0; + walksymtab(dofunc); + + // split pc/ln table by func + splitpcln(); + + // record src file and line info for each func + walksymtab(dosrcline); +} + +Func* +findfunc(uintptr addr) +{ + Func *f; + int32 nf, n; + + if(func == nil) + buildfuncs(); + if(nfunc == 0) + return nil; + if(addr < func[0].entry || addr >= func[nfunc].entry) + return nil; + + // binary search to find func with entry <= addr. + f = func; + nf = nfunc; + while(nf > 0) { + n = nf/2; + if(f[n].entry <= addr && addr < f[n+1].entry) + return &f[n]; + else if(addr < f[n].entry) + nf = n; + else { + f += n+1; + nf -= n+1; + } + } + + // can't get here -- we already checked above + // that the address was in the table bounds. + // this can only happen if the table isn't sorted + // by address or if the binary search above is buggy. + prints("findfunc unreachable\n"); + return nil; +} diff --git a/src/pkg/sort/Makefile b/src/pkg/sort/Makefile new file mode 100644 index 000000000..4d193f859 --- /dev/null +++ b/src/pkg/sort/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + sort.$O\ + + +phases: a1 +_obj$D/sort.a: phases + +a1: $(O1) + $(AR) grc _obj$D/sort.a sort.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/sort.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/sort.a + +packages: _obj$D/sort.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/sort.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/sort.a diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go new file mode 100644 index 000000000..99ba0a0ef --- /dev/null +++ b/src/pkg/sort/sort.go @@ -0,0 +1,187 @@ +// Copyright 2009 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. + +// The sort package provides primitives for sorting arrays +// and user-defined collections. +package sort + +// SortInterface is the interface that a type, typically a collection, +// must implement for its contents to be sorted in increasing order. +// Its methods require that the elements of the collection be enumerated +// by an integer index. +type SortInterface interface { + // Len is the number of elements in the collection. + Len() int; + // Less returns whether the element with index i is should sort + // before the element with index j. + // TODO(r): should this method be renamed Before? + Less(i, j int) bool; + // Swap swaps the elements with indexes i and j. + Swap(i, j int); +} + +func min(a, b int) int { + if a < b { + return a; + } + return b; +} + +// Insertion sort +func insertionSort(data SortInterface, a, b int) { + for i := a+1; i < b; i++ { + for j := i; j > a && data.Less(j, j-1); j-- { + data.Swap(j, j-1); + } + } +} + +// Quicksort, following Bentley and McIlroy, +// ``Engineering a Sort Function,'' SP&E November 1993. + +// Move the median of the three values data[a], data[b], data[c] into data[a]. +func medianOfThree(data SortInterface, a, b, c int) { + m0 := b; + m1 := a; + m2 := c; + // bubble sort on 3 elements + if data.Less(m1, m0) { data.Swap(m1, m0); } + if data.Less(m2, m1) { data.Swap(m2, m1); } + if data.Less(m1, m0) { data.Swap(m1, m0); } + // now data[m0] <= data[m1] <= data[m2] +} + +func swapRange(data SortInterface, a, b, n int) { + for i := 0; i < n; i++ { + data.Swap(a+i, b+i); + } +} + +func doPivot(data SortInterface, lo, hi int) (midlo, midhi int) { + m := (lo+hi)/2; + if hi - lo > 40 { + // Tukey's ``Ninther,'' median of three medians of three. + s := (hi - lo) / 8; + medianOfThree(data, lo, lo+s, lo+2*s); + medianOfThree(data, m, m-s, m+s); + medianOfThree(data, hi-1, hi-1-s, hi-1-2*s); + } + medianOfThree(data, lo, m, hi-1); + + // Invariants are: + // data[lo] = pivot (set up by ChoosePivot) + // data[lo <= i < a] = pivot + // data[a <= i < b] < pivot + // data[b <= i < c] is unexamined + // data[c <= i < d] > pivot + // data[d <= i < hi] = pivot + // + // Once b meets c, can swap the "= pivot" sections + // into the middle of the array. + pivot := lo; + a, b, c, d := lo+1, lo+1, hi, hi; + for b < c { + if data.Less(b, pivot) { // data[b] < pivot + b++; + continue; + } + if !data.Less(pivot, b) { // data[b] = pivot + data.Swap(a, b); + a++; + b++; + continue; + } + if data.Less(pivot, c-1) { // data[c-1] > pivot + c--; + continue; + } + if !data.Less(c-1, pivot) { // data[c-1] = pivot + data.Swap(c-1, d-1); + c--; + d--; + continue; + } + // data[b] > pivot; data[c-1] < pivot + data.Swap(b, c-1); + b++; + c--; + } + + n := min(b-a, a-lo); + swapRange(data, lo, b-n, n); + + n = min(hi-d, d-c); + swapRange(data, c, hi-n, n); + + return lo+b-a, hi-(d-c); +} + +func quickSort(data SortInterface, a, b int) { + if b - a > 7 { + mlo, mhi := doPivot(data, a, b); + quickSort(data, a, mlo); + quickSort(data, mhi, b); + } else if b - a > 1 { + insertionSort(data, a, b); + } +} + +func Sort(data SortInterface) { + quickSort(data, 0, data.Len()); +} + + +func IsSorted(data SortInterface) bool { + n := data.Len(); + for i := n - 1; i > 0; i-- { + if data.Less(i, i - 1) { + return false; + } + } + return true; +} + + +// Convenience types for common cases + +// IntArray attaches the methods of SortInterface to []int, sorting in increasing order. +type IntArray []int + +func (p IntArray) Len() int { return len(p); } +func (p IntArray) Less(i, j int) bool { return p[i] < p[j]; } +func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } + + +// FloatArray attaches the methods of SortInterface to []float, sorting in increasing order. +type FloatArray []float + +func (p FloatArray) Len() int { return len(p); } +func (p FloatArray) Less(i, j int) bool { return p[i] < p[j]; } +func (p FloatArray) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } + + +// StringArray attaches the methods of SortInterface to []string, sorting in increasing order. +type StringArray []string + +func (p StringArray) Len() int { return len(p); } +func (p StringArray) Less(i, j int) bool { return p[i] < p[j]; } +func (p StringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i]; } + + +// Convenience wrappers for common cases + +// SortInts sorts an array of ints in increasing order. +func SortInts(a []int) { Sort(IntArray(a)); } +// SortFloats sorts an array of floats in increasing order. +func SortFloats(a []float) { Sort(FloatArray(a)); } +// SortStrings sorts an array of strings in increasing order. +func SortStrings(a []string) { Sort(StringArray(a)); } + + +// IntsAreSorted tests whether an array of ints is sorted in increasing order. +func IntsAreSorted(a []int) bool { return IsSorted(IntArray(a)); } +// FloatsAreSorted tests whether an array of floats is sorted in increasing order. +func FloatsAreSorted(a []float) bool { return IsSorted(FloatArray(a)); } +// StringsAreSorted tests whether an array of strings is sorted in increasing order. +func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)); } diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go new file mode 100644 index 000000000..1747daca6 --- /dev/null +++ b/src/pkg/sort/sort_test.go @@ -0,0 +1,229 @@ +// Copyright 2009 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 sort + +import ( + "fmt"; + "rand"; + "sort"; + "testing"; +) + + +var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586} +var floats = [...]float{74.3, 59.0, 238.2, -784.0, 2.3, 9845.768, -959.7485, 905, 7.8, 7.8} +var strings = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"} + +func TestSortIntArray(t *testing.T) { + data := ints; + a := IntArray(&data); + sort.Sort(a); + if !sort.IsSorted(a) { + t.Errorf("sorted %v", ints); + t.Errorf(" got %v", data); + } +} + +func TestSortFloatArray(t *testing.T) { + data := floats; + a := FloatArray(&data); + sort.Sort(a); + if !sort.IsSorted(a) { + t.Errorf("sorted %v", floats); + t.Errorf(" got %v", data); + } +} + +func TestSortStringArray(t *testing.T) { + data := strings; + a := StringArray(&data); + sort.Sort(a); + if !sort.IsSorted(a) { + t.Errorf("sorted %v", strings); + t.Errorf(" got %v", data); + } +} + +func TestSortInts(t *testing.T) { + data := ints; + sort.SortInts(&data); + if !sort.IntsAreSorted(&data) { + t.Errorf("sorted %v", ints); + t.Errorf(" got %v", data); + } +} + +func TestSortFloats(t *testing.T) { + data := floats; + sort.SortFloats(&data); + if !sort.FloatsAreSorted(&data) { + t.Errorf("sorted %v", floats); + t.Errorf(" got %v", data); + } +} + +func TestSortStrings(t *testing.T) { + data := strings; + sort.SortStrings(&data); + if !sort.StringsAreSorted(&data) { + t.Errorf("sorted %v", strings); + t.Errorf(" got %v", data); + } +} + +func TestSortLarge_Random(t *testing.T) { + data := make([]int, 1000000); + for i := 0; i < len(data); i++ { + data[i] = rand.Intn(100); + } + if sort.IntsAreSorted(data) { + t.Fatalf("terrible rand.rand"); + } + sort.SortInts(data); + if !sort.IntsAreSorted(data) { + t.Errorf("sort didn't sort - 1M ints"); + } +} + +const ( + _Sawtooth = iota; + _Rand; + _Stagger; + _Plateau; + _Shuffle; + _NDist; +) + +const ( + _Copy = iota; + _Reverse; + _ReverseFirstHalf; + _ReverseSecondHalf; + _Sorted; + _Dither; + _NMode; +) + +type testingData struct { + desc string; + t *testing.T; + data []int; + maxswap int; // number of swaps allowed + nswap int; +} + +func (d *testingData) Len() int { return len(d.data); } +func (d *testingData) Less(i, j int) bool { return d.data[i] < d.data[j]; } +func (d *testingData) Swap(i, j int) { + if d.nswap >= d.maxswap { + d.t.Errorf("%s: used %d swaps sorting array of %d", d.desc, d.nswap, len(d.data)); + d.t.FailNow(); + } + d.nswap++; + d.data[i], d.data[j] = d.data[j], d.data[i]; +} + +func lg(n int) int { + i := 0; + for 1<<uint(i) < n { + i++; + } + return i; +} + +func TestBentleyMcIlroy(t *testing.T) { + sizes := []int{100, 1023, 1024, 1025}; + dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"}; + modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"}; + var tmp1, tmp2 [1025]int; + for ni := 0; ni < len(sizes); ni++ { + n := sizes[ni]; + for m := 1; m < 2*n; m *= 2 { + for dist := 0; dist < _NDist; dist++ { + j := 0; + k := 1; + data := tmp1[0:n]; + for i := 0; i < n; i++ { + switch dist { + case _Sawtooth: + data[i] = i % m; + case _Rand: + data[i] = rand.Intn(m); + case _Stagger: + data[i] = (i*m + i) % n; + case _Plateau: + data[i] = min(i, m); + case _Shuffle: + if rand.Intn(m) != 0 { + j += 2; + data[i] = j; + } else { + k += 2; + data[i] = k; + } + } + } + + mdata := tmp2[0:n]; + for mode := 0; mode < _NMode; mode++ { + switch mode { + case _Copy: + for i := 0; i < n; i++ { + mdata[i] = data[i]; + } + case _Reverse: + for i := 0; i < n; i++ { + mdata[i] = data[n-i-1]; + } + case _ReverseFirstHalf: + for i := 0; i < n/2; i++ { + mdata[i] = data[n/2-i-1]; + } + for i := n/2; i < n; i++ { + mdata[i] = data[i]; + } + case _ReverseSecondHalf: + for i := 0; i < n/2; i++ { + mdata[i] = data[i]; + } + for i := n/2; i < n; i++ { + mdata[i] = data[n-(i-n/2)-1]; + } + case _Sorted: + for i := 0; i < n; i++ { + mdata[i] = data[i]; + } + // sort.SortInts is known to be correct + // because mode Sort runs after mode _Copy. + sort.SortInts(mdata); + case _Dither: + for i := 0; i < n; i++ { + mdata[i] = data[i] + i%5; + } + } + + desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode]); + d := &testingData{desc, t, mdata[0:n], n*lg(n)*12/10, 0}; + sort.Sort(d); + + // If we were testing C qsort, we'd have to make a copy + // of the array and sort it ourselves and then compare + // x against it, to ensure that qsort was only permuting + // the data, not (for example) overwriting it with zeros. + // + // In go, we don't have to be so paranoid: since the only + // mutating method sort.Sort can call is TestingData.swap, + // it suffices here just to check that the final array is sorted. + if !sort.IntsAreSorted(mdata) { + t.Errorf("%s: ints not sorted", desc); + t.Errorf("\t%v", mdata); + t.FailNow(); + } + } + } + } + } +} + diff --git a/src/pkg/strconv/Makefile b/src/pkg/strconv/Makefile new file mode 100644 index 000000000..499f8c1c1 --- /dev/null +++ b/src/pkg/strconv/Makefile @@ -0,0 +1,79 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + atoi.$O\ + decimal.$O\ + itoa.$O\ + quote.$O\ + +O2=\ + ftoa.$O\ + +O3=\ + atof.$O\ + + +phases: a1 a2 a3 +_obj$D/strconv.a: phases + +a1: $(O1) + $(AR) grc _obj$D/strconv.a atoi.$O decimal.$O itoa.$O quote.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/strconv.a ftoa.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/strconv.a atof.$O + rm -f $(O3) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/strconv.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/strconv.a + +packages: _obj$D/strconv.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/strconv.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/strconv.a diff --git a/src/pkg/strconv/atof.go b/src/pkg/strconv/atof.go new file mode 100644 index 000000000..c257b2a33 --- /dev/null +++ b/src/pkg/strconv/atof.go @@ -0,0 +1,372 @@ +// Copyright 2009 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. + +// decimal to binary floating point conversion. +// Algorithm: +// 1) Store input in multiprecision decimal. +// 2) Multiply/divide decimal by powers of two until in range [0.5, 1) +// 3) Multiply by 2^precision and round to get mantissa. + +// The strconv package implements conversions to and from +// string representations of basic data types. +package strconv + +import ( + "math"; + "os"; + "strconv"; +) + +var optimize = true // can change for testing + +// TODO(rsc): Better truncation handling. +func stringToDecimal(s string) (neg bool, d *decimal, trunc bool, ok bool) { + i := 0; + + // optional sign + if i >= len(s) { + return; + } + switch { + case s[i] == '+': + i++; + case s[i] == '-': + neg = true; + i++; + } + + // digits + b := new(decimal); + sawdot := false; + sawdigits := false; + for ; i < len(s); i++ { + switch { + case s[i] == '.': + if sawdot { + return; + } + sawdot = true; + b.dp = b.nd; + continue; + + case '0' <= s[i] && s[i] <= '9': + sawdigits = true; + if s[i] == '0' && b.nd == 0 { // ignore leading zeros + b.dp--; + continue; + } + b.d[b.nd] = s[i]; + b.nd++; + continue; + } + break; + } + if !sawdigits { + return; + } + if !sawdot { + b.dp = b.nd; + } + + // optional exponent moves decimal point. + // if we read a very large, very long number, + // just be sure to move the decimal point by + // a lot (say, 100000). it doesn't matter if it's + // not the exact number. + if i < len(s) && s[i] == 'e' { + i++; + if i >= len(s) { + return; + } + esign := 1; + if s[i] == '+' { + i++; + } else if s[i] == '-' { + i++; + esign = -1; + } + if i >= len(s) || s[i] < '0' || s[i] > '9' { + return; + } + e := 0; + for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + if e < 10000 { + e = e*10 + int(s[i]) - '0'; + } + } + b.dp += e*esign; + } + + if i != len(s) { + return; + } + + d = b; + ok = true; + return; +} + +// decimal power of ten to binary power of two. +var powtab = []int{ + 1, 3, 6, 9, 13, 16, 19, 23, 26 +} + +func decimalToFloatBits(neg bool, d *decimal, trunc bool, flt *floatInfo) (b uint64, overflow bool) { + var exp int; + var mant uint64; + + // Zero is always a special case. + if d.nd == 0 { + mant = 0; + exp = flt.bias; + goto out; + } + + // Obvious overflow/underflow. + // These bounds are for 64-bit floats. + // Will have to change if we want to support 80-bit floats in the future. + if d.dp > 310 { + goto overflow; + } + if d.dp < -330 { + // zero + mant = 0; + exp = flt.bias; + goto out; + } + + // Scale by powers of two until in range [0.5, 1.0) + exp = 0; + for d.dp > 0 { + var n int; + if d.dp >= len(powtab) { + n = 27; + } else { + n = powtab[d.dp]; + } + d.Shift(-n); + exp += n; + } + for d.dp < 0 || d.dp == 0 && d.d[0] < '5' { + var n int; + if -d.dp >= len(powtab) { + n = 27; + } else { + n = powtab[-d.dp]; + } + d.Shift(n); + exp -= n; + } + + // Our range is [0.5,1) but floating point range is [1,2). + exp--; + + // Minimum representable exponent is flt.bias+1. + // If the exponent is smaller, move it up and + // adjust d accordingly. + if exp < flt.bias+1 { + n := flt.bias+1 - exp; + d.Shift(-n); + exp += n; + } + + if exp-flt.bias >= 1<<flt.expbits - 1 { + goto overflow; + } + + // Extract 1+flt.mantbits bits. + mant = d.Shift(int(1+flt.mantbits)).RoundedInteger(); + + // Rounding might have added a bit; shift down. + if mant == 2<<flt.mantbits { + mant >>= 1; + exp++; + if exp-flt.bias >= 1<<flt.expbits - 1 { + goto overflow; + } + } + + // Denormalized? + if mant&(1<<flt.mantbits) == 0 { + exp = flt.bias; + } + goto out; + +overflow: + // ±Inf + mant = 0; + exp = 1<<flt.expbits - 1 + flt.bias; + overflow = true; + +out: + // Assemble bits. + bits := mant & (uint64(1)<<flt.mantbits - 1); + bits |= uint64((exp-flt.bias)&(1<<flt.expbits - 1)) << flt.mantbits; + if neg { + bits |= 1<<flt.mantbits<<flt.expbits; + } + return bits, overflow; +} + +// Compute exact floating-point integer from d's digits. +// Caller is responsible for avoiding overflow. +func decimalAtof64Int(neg bool, d *decimal) float64 { + f := float64(0); + for i := 0; i < d.nd; i++ { + f = f*10 + float64(d.d[i] - '0'); + } + if neg { + f *= -1; // BUG work around 6g f = -f. + } + return f; +} + +func decimalAtof32Int(neg bool, d *decimal) float32 { + f := float32(0); + for i := 0; i < d.nd; i++ { + f = f*10 + float32(d.d[i] - '0'); + } + if neg { + f *= -1; // BUG work around 6g f = -f. + } + return f; +} + +// Exact powers of 10. +var float64pow10 = []float64 { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +} +var float32pow10 = []float32 { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10 +} + +// If possible to convert decimal d to 64-bit float f exactly, +// entirely in floating-point math, do so, avoiding the expense of decimalToFloatBits. +// Three common cases: +// value is exact integer +// value is exact integer * exact power of ten +// value is exact integer / exact power of ten +// These all produce potentially inexact but correctly rounded answers. +func decimalAtof64(neg bool, d *decimal, trunc bool) (f float64, ok bool) { + // Exact integers are <= 10^15. + // Exact powers of ten are <= 10^22. + if d.nd > 15 { + return; + } + switch { + case d.dp == d.nd: // int + f := decimalAtof64Int(neg, d); + return f, true; + + case d.dp > d.nd && d.dp <= 15+22: // int * 10^k + f := decimalAtof64Int(neg, d); + k := d.dp - d.nd; + // If exponent is big but number of digits is not, + // can move a few zeros into the integer part. + if k > 22 { + f *= float64pow10[k-22]; + k = 22; + } + return f*float64pow10[k], true; + + case d.dp < d.nd && d.nd - d.dp <= 22: // int / 10^k + f := decimalAtof64Int(neg, d); + return f/float64pow10[d.nd - d.dp], true; + } + return; +} + +// If possible to convert decimal d to 32-bit float f exactly, +// entirely in floating-point math, do so, avoiding the machinery above. +func decimalAtof32(neg bool, d *decimal, trunc bool) (f float32, ok bool) { + // Exact integers are <= 10^7. + // Exact powers of ten are <= 10^10. + if d.nd > 7 { + return; + } + switch { + case d.dp == d.nd: // int + f := decimalAtof32Int(neg, d); + return f, true; + + case d.dp > d.nd && d.dp <= 7+10: // int * 10^k + f := decimalAtof32Int(neg, d); + k := d.dp - d.nd; + // If exponent is big but number of digits is not, + // can move a few zeros into the integer part. + if k > 10 { + f *= float32pow10[k-10]; + k = 10; + } + return f*float32pow10[k], true; + + case d.dp < d.nd && d.nd - d.dp <= 10: // int / 10^k + f := decimalAtof32Int(neg, d); + return f/float32pow10[d.nd - d.dp], true; + } + return; +} + +// Atof32 converts the string s to a 32-bit floating-point number. +// +// If s is well-formed and near a valid floating point number, +// Atof32 returns the nearest floating point number rounded +// using IEEE754 unbiased rounding. +// +// If s is not syntactically well-formed, Atof32 returns err = os.EINVAL. +// +// If s is syntactically well-formed but is more than 1/2 ULP +// away from the largest floating point number of the given size, +// Atof32 returns f = ±Inf, err = os.ERANGE. +func Atof32(s string) (f float32, err os.Error) { + neg, d, trunc, ok := stringToDecimal(s); + if !ok { + return 0, os.EINVAL; + } + if optimize { + if f, ok := decimalAtof32(neg, d, trunc); ok { + return f, nil; + } + } + b, ovf := decimalToFloatBits(neg, d, trunc, &float32info); + f = math.Float32frombits(uint32(b)); + if ovf { + err = os.ERANGE; + } + return f, err +} + +// Atof64 converts the string s to a 64-bit floating-point number. +// Except for the type of its result, its definition is the same as that +// of Atof32. +func Atof64(s string) (f float64, err os.Error) { + neg, d, trunc, ok := stringToDecimal(s); + if !ok { + return 0, os.EINVAL; + } + if optimize { + if f, ok := decimalAtof64(neg, d, trunc); ok { + return f, nil; + } + } + b, ovf := decimalToFloatBits(neg, d, trunc, &float64info); + f = math.Float64frombits(b); + if ovf { + err = os.ERANGE; + } + return f, err +} + +// Atof is like Atof32 or Atof64, depending on the size of float. +func Atof(s string) (f float, err os.Error) { + if FloatSize == 32 { + f1, err1 := Atof32(s); + return float(f1), err1; + } + f1, err1 := Atof64(s); + return float(f1), err1; +} + diff --git a/src/pkg/strconv/atof_test.go b/src/pkg/strconv/atof_test.go new file mode 100644 index 000000000..6782f274a --- /dev/null +++ b/src/pkg/strconv/atof_test.go @@ -0,0 +1,133 @@ +// Copyright 2009 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 strconv +import ( + "fmt"; + "os"; + "strconv"; + "testing" +) + +type atofTest struct { + in string; + out string; + err os.Error; +} + +var atoftests = []atofTest { + atofTest{ "", "0", os.EINVAL }, + atofTest{ "1", "1", nil }, + atofTest{ "+1", "1", nil }, + atofTest{ "1x", "0", os.EINVAL }, + atofTest{ "1.1.", "0", os.EINVAL }, + atofTest{ "1e23", "1e+23", nil }, + atofTest{ "100000000000000000000000", "1e+23", nil }, + atofTest{ "1e-100", "1e-100", nil }, + atofTest{ "123456700", "1.234567e+08", nil }, + atofTest{ "99999999999999974834176", "9.999999999999997e+22", nil }, + atofTest{ "100000000000000000000001", "1.0000000000000001e+23", nil }, + atofTest{ "100000000000000008388608", "1.0000000000000001e+23", nil }, + atofTest{ "100000000000000016777215", "1.0000000000000001e+23", nil }, + atofTest{ "100000000000000016777216", "1.0000000000000003e+23", nil }, + atofTest{ "-1", "-1", nil }, + atofTest{ "-0", "-0", nil }, + atofTest{ "1e-20", "1e-20", nil }, + atofTest{ "625e-3", "0.625", nil }, + + // largest float64 + atofTest{ "1.7976931348623157e308", "1.7976931348623157e+308", nil }, + atofTest{ "-1.7976931348623157e308", "-1.7976931348623157e+308", nil }, + // next float64 - too large + atofTest{ "1.7976931348623159e308", "+Inf", os.ERANGE }, + atofTest{ "-1.7976931348623159e308", "-Inf", os.ERANGE }, + // the border is ...158079 + // borderline - okay + atofTest{ "1.7976931348623158e308", "1.7976931348623157e+308", nil }, + atofTest{ "-1.7976931348623158e308", "-1.7976931348623157e+308", nil }, + // borderline - too large + atofTest{ "1.797693134862315808e308", "+Inf", os.ERANGE }, + atofTest{ "-1.797693134862315808e308", "-Inf", os.ERANGE }, + + // a little too large + atofTest{ "1e308", "1e+308", nil }, + atofTest{ "2e308", "+Inf", os.ERANGE }, + atofTest{ "1e309", "+Inf", os.ERANGE }, + + // way too large + atofTest{ "1e310", "+Inf", os.ERANGE }, + atofTest{ "-1e310", "-Inf", os.ERANGE }, + atofTest{ "1e400", "+Inf", os.ERANGE }, + atofTest{ "-1e400", "-Inf", os.ERANGE }, + atofTest{ "1e400000", "+Inf", os.ERANGE }, + atofTest{ "-1e400000", "-Inf", os.ERANGE }, + + // denormalized + atofTest{ "1e-305", "1e-305", nil }, + atofTest{ "1e-306", "1e-306", nil }, + atofTest{ "1e-307", "1e-307", nil }, + atofTest{ "1e-308", "1e-308", nil }, + atofTest{ "1e-309", "1e-309", nil }, + atofTest{ "1e-310", "1e-310", nil }, + atofTest{ "1e-322", "1e-322", nil }, + // smallest denormal + atofTest{ "5e-324", "5e-324", nil }, + // too small + atofTest{ "4e-324", "0", nil }, + // way too small + atofTest{ "1e-350", "0", nil }, + atofTest{ "1e-400000", "0", nil }, + + // try to overflow exponent + atofTest{ "1e-4294967296", "0", nil }, + atofTest{ "1e+4294967296", "+Inf", os.ERANGE }, + atofTest{ "1e-18446744073709551616", "0", nil }, + atofTest{ "1e+18446744073709551616", "+Inf", os.ERANGE }, + + // Parse errors + atofTest{ "1e", "0", os.EINVAL }, + atofTest{ "1e-", "0", os.EINVAL }, + atofTest{ ".e-1", "0", os.EINVAL }, +} + +func testAtof(t *testing.T, opt bool) { + oldopt := strconv.optimize; + strconv.optimize = opt; + for i := 0; i < len(atoftests); i++ { + test := &atoftests[i]; + out, err := strconv.Atof64(test.in); + outs := strconv.Ftoa64(out, 'g', -1); + if outs != test.out || err != test.err { + t.Errorf("strconv.Atof64(%v) = %v, %v want %v, %v\n", + test.in, out, err, test.out, test.err); + } + + if float64(float32(out)) == out { + out32, err := strconv.Atof32(test.in); + outs := strconv.Ftoa32(out32, 'g', -1); + if outs != test.out || err != test.err { + t.Errorf("strconv.Atof32(%v) = %v, %v want %v, %v # %v\n", + test.in, out32, err, test.out, test.err, out); + } + } + + if FloatSize == 64 || float64(float32(out)) == out { + outf, err := strconv.Atof(test.in); + outs := strconv.Ftoa(outf, 'g', -1); + if outs != test.out || err != test.err { + t.Errorf("strconv.Ftoa(%v) = %v, %v want %v, %v # %v\n", + test.in, outf, err, test.out, test.err, out); + } + } + } + strconv.optimize = oldopt; +} + +func TestAtof(t *testing.T) { + testAtof(t, true); +} + +func TestAtofSlow(t *testing.T) { + testAtof(t, false); +} diff --git a/src/pkg/strconv/atoi.go b/src/pkg/strconv/atoi.go new file mode 100644 index 000000000..a5d896a05 --- /dev/null +++ b/src/pkg/strconv/atoi.go @@ -0,0 +1,166 @@ +// Copyright 2009 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 strconv +import "os" + +func computeIntsize() uint { + siz := uint(8); + for 1<<siz != 0 { + siz *= 2 + } + return siz +} +var intsize = computeIntsize(); + +// Return the first number n such that n*base >= 1<<64. +func cutoff64(base int) uint64 { + if base < 2 { + return 0; + } + return (1<<64 - 1) / uint64(base) + 1; +} + +// Btoui64 interprets a string s in an arbitrary base b (2 to 36) +// and returns the corresponding value n. +// +// Btoui64 returns err == os.EINVAL if b is out of +// range or s is empty or contains invalid digits. +// It returns err == os.ERANGE if the value corresponding +// to s cannot be represented by a uint64. +func Btoui64(s string, b int) (n uint64, err os.Error) { + if b < 2 || b > 36 || len(s) < 1 { + return 0, os.EINVAL; + } + + n = 0; + cutoff := cutoff64(b); + + for i := 0; i < len(s); i++ { + var v byte; + switch { + case '0' <= s[i] && s[i] <= '9': + v = s[i] - '0'; + case 'a' <= s[i] && s[i] <= 'z': + v = s[i] - 'a' + 10; + case 'A' <= s[i] && s[i] <= 'Z': + v = s[i] - 'A' + 10; + default: + return 0, os.EINVAL; + } + if int(v) >= b { + return 0, os.EINVAL; + } + + if n >= cutoff { + // n*b overflows + return 1<<64-1, os.ERANGE; + } + n *= uint64(b); + + n1 := n+uint64(v); + if n1 < n { + // n+v overflows + return 1<<64-1, os.ERANGE; + } + n = n1; + } + + return n, nil; +} + +// Atoui64 interprets a string s as an unsigned decimal, octal, or +// hexadecimal number and returns the corresponding value n. +// The default base is decimal. Strings beginning with 0x are +// hexadecimal; strings beginning with 0 are octal. +// +// Atoui64 returns err == os.EINVAL if s is empty or contains invalid digits. +// It returns err == os.ERANGE if s cannot be represented by a uint64. +func Atoui64(s string) (n uint64, err os.Error) { + // Empty string bad. + if len(s) == 0 { + return 0, os.EINVAL + } + + // Look for octal, hex prefix. + if s[0] == '0' && len(s) > 1 { + if s[1] == 'x' || s[1] == 'X' { + // hex + return Btoui64(s[2:len(s)], 16); + } + // octal + return Btoui64(s[1:len(s)], 8); + } + // decimal + return Btoui64(s, 10); +} + + +// Atoi64 is like Atoui64 but allows signed numbers and +// returns its result in an int64. +func Atoi64(s string) (i int64, err os.Error) { + // Empty string bad. + if len(s) == 0 { + return 0, os.EINVAL + } + + // Pick off leading sign. + neg := false; + if s[0] == '+' { + s = s[1:len(s)] + } else if s[0] == '-' { + neg = true; + s = s[1:len(s)] + } + + // Convert unsigned and check range. + var un uint64; + un, err = Atoui64(s); + if err != nil && err != os.ERANGE { + return 0, err + } + if !neg && un >= 1<<63 { + return 1<<63-1, os.ERANGE + } + if neg && un > 1<<63 { + return -1<<63, os.ERANGE + } + n := int64(un); + if neg { + n = -n + } + return n, nil +} + +// Atoui is like Atoui64 but returns its result as a uint. +func Atoui(s string) (i uint, err os.Error) { + i1, e1 := Atoui64(s); + if e1 != nil && e1 != os.ERANGE { + return 0, e1 + } + i = uint(i1); + if uint64(i) != i1 { + // TODO: return uint(^0), os.ERANGE. + i1 = 1<<64-1; + return uint(i1), os.ERANGE + } + return i, nil +} + +// Atoi is like Atoi64 but returns its result as an int. +func Atoi(s string) (i int, err os.Error) { + i1, e1 := Atoi64(s); + if e1 != nil && e1 != os.ERANGE { + return 0, e1 + } + i = int(i1); + if int64(i) != i1 { + if i1 < 0 { + return -1<<(intsize-1), os.ERANGE + } + return 1<<(intsize-1) - 1, os.ERANGE + } + return i, nil +} + diff --git a/src/pkg/strconv/atoi_test.go b/src/pkg/strconv/atoi_test.go new file mode 100644 index 000000000..e4a9f955d --- /dev/null +++ b/src/pkg/strconv/atoi_test.go @@ -0,0 +1,188 @@ +// Copyright 2009 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 strconv + +import ( + "fmt"; + "os"; + "strconv"; + "testing" +) + +type atoui64Test struct { + in string; + out uint64; + err os.Error; +} + +var atoui64tests = []atoui64Test { + atoui64Test{"", 0, os.EINVAL}, + atoui64Test{"0", 0, nil}, + atoui64Test{"1", 1, nil}, + atoui64Test{"12345", 12345, nil}, + atoui64Test{"012345", 012345, nil}, + atoui64Test{"0x12345", 0x12345, nil}, + atoui64Test{"0X12345", 0x12345, nil}, + atoui64Test{"12345x", 0, os.EINVAL}, + atoui64Test{"98765432100", 98765432100, nil}, + atoui64Test{"18446744073709551615", 1<<64-1, nil}, + atoui64Test{"18446744073709551616", 1<<64-1, os.ERANGE}, + atoui64Test{"18446744073709551620", 1<<64-1, os.ERANGE}, + atoui64Test{"0xFFFFFFFFFFFFFFFF", 1<<64-1, nil}, + atoui64Test{"0x10000000000000000", 1<<64-1, os.ERANGE}, + atoui64Test{"01777777777777777777777", 1<<64-1, nil}, + atoui64Test{"01777777777777777777778", 0, os.EINVAL}, + atoui64Test{"02000000000000000000000", 1<<64-1, os.ERANGE}, + atoui64Test{"0200000000000000000000", 1<<61, nil}, +} + +type atoi64Test struct { + in string; + out int64; + err os.Error; +} + +var atoi64test = []atoi64Test { + atoi64Test{"", 0, os.EINVAL}, + atoi64Test{"0", 0, nil}, + atoi64Test{"-0", 0, nil}, + atoi64Test{"1", 1, nil}, + atoi64Test{"-1", -1, nil}, + atoi64Test{"12345", 12345, nil}, + atoi64Test{"-12345", -12345, nil}, + atoi64Test{"012345", 012345, nil}, + atoi64Test{"-012345", -012345, nil}, + atoi64Test{"0x12345", 0x12345, nil}, + atoi64Test{"-0X12345", -0x12345, nil}, + atoi64Test{"12345x", 0, os.EINVAL}, + atoi64Test{"-12345x", 0, os.EINVAL}, + atoi64Test{"98765432100", 98765432100, nil}, + atoi64Test{"-98765432100", -98765432100, nil}, + atoi64Test{"9223372036854775807", 1<<63-1, nil}, + atoi64Test{"-9223372036854775807", -(1<<63-1), nil}, + atoi64Test{"9223372036854775808", 1<<63-1, os.ERANGE}, + atoi64Test{"-9223372036854775808", -1<<63, nil}, + atoi64Test{"9223372036854775809", 1<<63-1, os.ERANGE}, + atoi64Test{"-9223372036854775809", -1<<63, os.ERANGE}, +} + +type atoui32Test struct { + in string; + out uint32; + err os.Error; +} + +var atoui32tests = []atoui32Test { + atoui32Test{"", 0, os.EINVAL}, + atoui32Test{"0", 0, nil}, + atoui32Test{"1", 1, nil}, + atoui32Test{"12345", 12345, nil}, + atoui32Test{"012345", 012345, nil}, + atoui32Test{"0x12345", 0x12345, nil}, + atoui32Test{"0X12345", 0x12345, nil}, + atoui32Test{"12345x", 0, os.EINVAL}, + atoui32Test{"987654321", 987654321, nil}, + atoui32Test{"4294967295", 1<<32-1, nil}, + atoui32Test{"4294967296", 1<<32-1, os.ERANGE}, +} + +type atoi32Test struct { + in string; + out int32; + err os.Error; +} + +var atoi32tests = []atoi32Test { + atoi32Test{"", 0, os.EINVAL}, + atoi32Test{"0", 0, nil}, + atoi32Test{"-0", 0, nil}, + atoi32Test{"1", 1, nil}, + atoi32Test{"-1", -1, nil}, + atoi32Test{"12345", 12345, nil}, + atoi32Test{"-12345", -12345, nil}, + atoi32Test{"012345", 012345, nil}, + atoi32Test{"-012345", -012345, nil}, + atoi32Test{"0x12345", 0x12345, nil}, + atoi32Test{"-0X12345", -0x12345, nil}, + atoi32Test{"12345x", 0, os.EINVAL}, + atoi32Test{"-12345x", 0, os.EINVAL}, + atoi32Test{"987654321", 987654321, nil}, + atoi32Test{"-987654321", -987654321, nil}, + atoi32Test{"2147483647", 1<<31-1, nil}, + atoi32Test{"-2147483647", -(1<<31-1), nil}, + atoi32Test{"2147483648", 1<<31-1, os.ERANGE}, + atoi32Test{"-2147483648", -1<<31, nil}, + atoi32Test{"2147483649", 1<<31-1, os.ERANGE}, + atoi32Test{"-2147483649", -1<<31, os.ERANGE}, +} + +func TestAtoui64(t *testing.T) { + for i := 0; i < len(atoui64tests); i++ { + test := &atoui64tests[i]; + out, err := strconv.Atoui64(test.in); + if test.out != out || test.err != err { + t.Errorf("strconv.Atoui64(%v) = %v, %v want %v, %v\n", + test.in, out, err, test.out, test.err); + } + } +} + +func TestAtoi64(t *testing.T) { + for i := 0; i < len(atoi64test); i++ { + test := &atoi64test[i]; + out, err := strconv.Atoi64(test.in); + if test.out != out || test.err != err { + t.Errorf("strconv.Atoi64(%v) = %v, %v want %v, %v\n", + test.in, out, err, test.out, test.err); + } + } +} + +func TestAtoui(t *testing.T) { + switch intsize { + case 32: + for i := 0; i < len(atoui32tests); i++ { + test := &atoui32tests[i]; + out, err := strconv.Atoui(test.in); + if test.out != uint32(out) || test.err != err { + t.Errorf("strconv.Atoui(%v) = %v, %v want %v, %v\n", + test.in, out, err, test.out, test.err); + } + } + case 64: + for i := 0; i < len(atoui64tests); i++ { + test := &atoui64tests[i]; + out, err := strconv.Atoui(test.in); + if test.out != uint64(out) || test.err != err { + t.Errorf("strconv.Atoui(%v) = %v, %v want %v, %v\n", + test.in, out, err, test.out, test.err); + } + } + } +} + +func TestAtoi(t *testing.T) { + switch intsize { + case 32: + for i := 0; i < len(atoi32tests); i++ { + test := &atoi32tests[i]; + out, err := strconv.Atoi(test.in); + if test.out != int32(out) || test.err != err { + t.Errorf("strconv.Atoi(%v) = %v, %v want %v, %v\n", + test.in, out, err, test.out, test.err); + } + } + case 64: + for i := 0; i < len(atoi64test); i++ { + test := &atoi64test[i]; + out, err := strconv.Atoi(test.in); + if test.out != int64(out) || test.err != err { + t.Errorf("strconv.Atoi(%v) = %v, %v want %v, %v\n", + test.in, out, err, test.out, test.err); + } + } + } +} + diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go new file mode 100644 index 000000000..38d9c47fb --- /dev/null +++ b/src/pkg/strconv/decimal.go @@ -0,0 +1,392 @@ +// Copyright 2009 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. + +// Multiprecision decimal numbers. +// For floating-point formatting only; not general purpose. +// Only operations are assign and (binary) left/right shift. +// Can do binary floating point in multiprecision decimal precisely +// because 2 divides 10; cannot do decimal floating point +// in multiprecision binary precisely. + +package strconv + +import "bytes" + +type decimal struct { + // TODO(rsc): Can make d[] a bit smaller and add + // truncated bool; + d [2000] byte; // digits + nd int; // number of digits used + dp int; // decimal point +}; +func (a *decimal) String() string; +func (a *decimal) Assign(v uint64); +func (a *decimal) Shift(k int) *decimal; +func (a *decimal) Round(nd int) *decimal; +func (a *decimal) RoundUp(nd int) *decimal; +func (a *decimal) RoundDown(nd int) *decimal; +func (a *decimal) RoundedInteger() uint64; + + +func digitZero(dst []byte) int; + +func (a *decimal) String() string { + n := 10 + a.nd; + if a.dp > 0 { + n += a.dp; + } + if a.dp < 0 { + n += -a.dp; + } + + buf := make([]byte, n); + w := 0; + switch { + case a.nd == 0: + return "0"; + + case a.dp <= 0: + // zeros fill space between decimal point and digits + buf[w] = '0'; + w++; + buf[w] = '.'; + w++; + w += digitZero(buf[w:w+-a.dp]); + w += bytes.Copy(buf[w:w+a.nd], a.d[0:a.nd]); + + case a.dp < a.nd: + // decimal point in middle of digits + w += bytes.Copy(buf[w:w+a.dp], a.d[0:a.dp]); + buf[w] = '.'; + w++; + w += bytes.Copy(buf[w:w+a.nd-a.dp], a.d[a.dp:a.nd]); + + default: + // zeros fill space between digits and decimal point + w += bytes.Copy(buf[w:w+a.nd], a.d[0:a.nd]); + w += digitZero(buf[w:w+a.dp-a.nd]); + } + return string(buf[0:w]); +} + +func copy(dst []byte, src []byte) int { + for i := 0; i < len(dst); i++ { + dst[i] = src[i]; + } + return len(dst); +} + +func digitZero(dst []byte) int { + for i := 0; i < len(dst); i++ { + dst[i] = '0'; + } + return len(dst); +} + +// trim trailing zeros from number. +// (They are meaningless; the decimal point is tracked +// independent of the number of digits.) +func trim(a *decimal) { + for a.nd > 0 && a.d[a.nd-1] == '0' { + a.nd--; + } + if a.nd == 0 { + a.dp = 0; + } +} + +// Assign v to a. +func (a *decimal) Assign(v uint64) { + var buf [50]byte; + + // Write reversed decimal in buf. + n := 0; + for v > 0 { + v1 := v/10; + v -= 10*v1; + buf[n] = byte(v + '0'); + n++; + v = v1; + } + + // Reverse again to produce forward decimal in a.d. + a.nd = 0; + for n--; n>=0; n-- { + a.d[a.nd] = buf[n]; + a.nd++; + } + a.dp = a.nd; + trim(a); +} + +func newDecimal(i uint64) *decimal { + a := new(decimal); + a.Assign(i); + return a; +} + +// Maximum shift that we can do in one pass without overflow. +// Signed int has 31 bits, and we have to be able to accomodate 9<<k. +const maxShift = 27 + +// Binary shift right (* 2) by k bits. k <= maxShift to avoid overflow. +func rightShift(a *decimal, k uint) { + r := 0; // read pointer + w := 0; // write pointer + + // Pick up enough leading digits to cover first shift. + n := 0; + for ; n>>k == 0; r++ { + if r >= a.nd { + if n == 0 { + // a == 0; shouldn't get here, but handle anyway. + a.nd = 0; + return; + } + for n>>k == 0 { + n = n*10; + r++; + } + break; + } + c := int(a.d[r]); + n = n*10 + c-'0'; + } + a.dp -= r-1; + + // Pick up a digit, put down a digit. + for ; r < a.nd; r++ { + c := int(a.d[r]); + dig := n>>k; + n -= dig<<k; + a.d[w] = byte(dig+'0'); + w++; + n = n*10 + c-'0'; + } + + // Put down extra digits. + for n > 0 { + dig := n>>k; + n -= dig<<k; + a.d[w] = byte(dig+'0'); + w++; + n = n*10; + } + + a.nd = w; + trim(a); +} + +// Cheat sheet for left shift: table indexed by shift count giving +// number of new digits that will be introduced by that shift. +// +// For example, leftcheats[4] = {2, "625"}. That means that +// if we are shifting by 4 (multiplying by 16), it will add 2 digits +// when the string prefix is "625" through "999", and one fewer digit +// if the string prefix is "000" through "624". +// +// Credit for this trick goes to Ken. + +type leftCheat struct { + delta int; // number of new digits + cutoff string; // minus one digit if original < a. +} + +var leftcheats = []leftCheat { + // Leading digits of 1/2^i = 5^i. + // 5^23 is not an exact 64-bit floating point number, + // so have to use bc for the math. + /* + seq 27 | sed 's/^/5^/' | bc | + awk 'BEGIN{ print "\tleftCheat{ 0, \"\" }," } + { + log2 = log(2)/log(10) + printf("\tleftCheat{ %d, \"%s\" },\t// * %d\n", + int(log2*NR+1), $0, 2**NR) + }' + */ + leftCheat{ 0, "" }, + leftCheat{ 1, "5" }, // * 2 + leftCheat{ 1, "25" }, // * 4 + leftCheat{ 1, "125" }, // * 8 + leftCheat{ 2, "625" }, // * 16 + leftCheat{ 2, "3125" }, // * 32 + leftCheat{ 2, "15625" }, // * 64 + leftCheat{ 3, "78125" }, // * 128 + leftCheat{ 3, "390625" }, // * 256 + leftCheat{ 3, "1953125" }, // * 512 + leftCheat{ 4, "9765625" }, // * 1024 + leftCheat{ 4, "48828125" }, // * 2048 + leftCheat{ 4, "244140625" }, // * 4096 + leftCheat{ 4, "1220703125" }, // * 8192 + leftCheat{ 5, "6103515625" }, // * 16384 + leftCheat{ 5, "30517578125" }, // * 32768 + leftCheat{ 5, "152587890625" }, // * 65536 + leftCheat{ 6, "762939453125" }, // * 131072 + leftCheat{ 6, "3814697265625" }, // * 262144 + leftCheat{ 6, "19073486328125" }, // * 524288 + leftCheat{ 7, "95367431640625" }, // * 1048576 + leftCheat{ 7, "476837158203125" }, // * 2097152 + leftCheat{ 7, "2384185791015625" }, // * 4194304 + leftCheat{ 7, "11920928955078125" }, // * 8388608 + leftCheat{ 8, "59604644775390625" }, // * 16777216 + leftCheat{ 8, "298023223876953125" }, // * 33554432 + leftCheat{ 8, "1490116119384765625" }, // * 67108864 + leftCheat{ 9, "7450580596923828125" }, // * 134217728 +} + +// Is the leading prefix of b lexicographically less than s? +func prefixIsLessThan(b []byte, s string) bool { + for i := 0; i < len(s); i++ { + if i >= len(b) { + return true; + } + if b[i] != s[i] { + return b[i] < s[i]; + } + } + return false; +} + +// Binary shift left (/ 2) by k bits. k <= maxShift to avoid overflow. +func leftShift(a *decimal, k uint) { + delta := leftcheats[k].delta; + if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) { + delta--; + } + + r := a.nd; // read index + w := a.nd + delta; // write index + n := 0; + + // Pick up a digit, put down a digit. + for r--; r >= 0; r-- { + n += (int(a.d[r])-'0') << k; + quo := n/10; + rem := n - 10*quo; + w--; + a.d[w] = byte(rem+'0'); + n = quo; + } + + // Put down extra digits. + for n > 0 { + quo := n/10; + rem := n - 10*quo; + w--; + a.d[w] = byte(rem+'0'); + n = quo; + } + + if w != 0 { + // TODO: Remove - has no business panicking. + panicln("strconv: bad leftShift", w); + } + a.nd += delta; + a.dp += delta; + trim(a); +} + +// Binary shift left (k > 0) or right (k < 0). +// Returns receiver for convenience. +func (a *decimal) Shift(k int) *decimal { + switch { + case a.nd == 0: + // nothing to do: a == 0 + case k > 0: + for k > maxShift { + leftShift(a, maxShift); + k -= maxShift; + } + leftShift(a, uint(k)); + case k < 0: + for k < -maxShift { + rightShift(a, maxShift); + k += maxShift; + } + rightShift(a, uint(-k)); + } + return a; +} + +// If we chop a at nd digits, should we round up? +func shouldRoundUp(a *decimal, nd int) bool { + if nd <= 0 || nd >= a.nd { + return false; + } + if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even + return (a.d[nd-1] - '0') % 2 != 0; + } + // not halfway - digit tells all + return a.d[nd] >= '5'; +} + +// Round a to nd digits (or fewer). +// Returns receiver for convenience. +func (a *decimal) Round(nd int) *decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + if(shouldRoundUp(a, nd)) { + return a.RoundUp(nd); + } + return a.RoundDown(nd); +} + +// Round a down to nd digits (or fewer). +// Returns receiver for convenience. +func (a *decimal) RoundDown(nd int) *decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + a.nd = nd; + trim(a); + return a; +} + +// Round a up to nd digits (or fewer). +// Returns receiver for convenience. +func (a *decimal) RoundUp(nd int) *decimal { + if nd <= 0 || nd >= a.nd { + return a; + } + + // round up + for i := nd-1; i >= 0; i-- { + c := a.d[i]; + if c < '9' { // can stop after this digit + a.d[i]++; + a.nd = i+1; + return a; + } + } + + // Number is all 9s. + // Change to single 1 with adjusted decimal point. + a.d[0] = '1'; + a.nd = 1; + a.dp++; + return a; +} + +// Extract integer part, rounded appropriately. +// No guarantees about overflow. +func (a *decimal) RoundedInteger() uint64 { + if a.dp > 20 { + return 0xFFFFFFFFFFFFFFFF; + } + var i int; + n := uint64(0); + for i = 0; i < a.dp && i < a.nd; i++ { + n = n*10 + uint64(a.d[i] - '0'); + } + for ; i < a.dp; i++ { + n *= 10; + } + if shouldRoundUp(a, a.dp) { + n++; + } + return n; +} + diff --git a/src/pkg/strconv/decimal_test.go b/src/pkg/strconv/decimal_test.go new file mode 100644 index 000000000..bc82861bd --- /dev/null +++ b/src/pkg/strconv/decimal_test.go @@ -0,0 +1,119 @@ +// Copyright 2009 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 strconv + +import ( + "fmt"; + "strconv"; + "testing"; +) + +type shiftTest struct { + i uint64; + shift int; + out string; +} + +var shifttests = []shiftTest { + shiftTest{ 0, -100, "0" }, + shiftTest{ 0, 100, "0" }, + shiftTest{ 1, 100, "1267650600228229401496703205376" }, + shiftTest{ 1, -100, + "0.00000000000000000000000000000078886090522101180541" + "17285652827862296732064351090230047702789306640625" }, + shiftTest{ 12345678, 8, "3160493568" }, + shiftTest{ 12345678, -8, "48225.3046875" }, + shiftTest{ 195312, 9, "99999744" }, + shiftTest{ 1953125, 9, "1000000000" }, +} + +func TestDecimalShift(t *testing.T) { + ok := true; + for i := 0; i < len(shifttests); i++ { + test := &shifttests[i]; + s := strconv.newDecimal(test.i).Shift(test.shift).String(); + if s != test.out { + t.Errorf("Decimal %v << %v = %v, want %v\n", + test.i, test.shift, s, test.out); + } + } +} + +type roundTest struct { + i uint64; + nd int; + down, round, up string; + int uint64; +} + +var roundtests = []roundTest { + roundTest{ 0, 4, "0", "0", "0", 0 }, + roundTest{ 12344999, 4, "12340000", "12340000", "12350000", 12340000 }, + roundTest{ 12345000, 4, "12340000", "12340000", "12350000", 12340000 }, + roundTest{ 12345001, 4, "12340000", "12350000", "12350000", 12350000 }, + roundTest{ 23454999, 4, "23450000", "23450000", "23460000", 23450000 }, + roundTest{ 23455000, 4, "23450000", "23460000", "23460000", 23460000 }, + roundTest{ 23455001, 4, "23450000", "23460000", "23460000", 23460000 }, + + roundTest{ 99994999, 4, "99990000", "99990000", "100000000", 99990000 }, + roundTest{ 99995000, 4, "99990000", "100000000", "100000000", 100000000 }, + roundTest{ 99999999, 4, "99990000", "100000000", "100000000", 100000000 }, + + roundTest{ 12994999, 4, "12990000", "12990000", "13000000", 12990000 }, + roundTest{ 12995000, 4, "12990000", "13000000", "13000000", 13000000 }, + roundTest{ 12999999, 4, "12990000", "13000000", "13000000", 13000000 }, +} + +func TestDecimalRound(t *testing.T) { + for i := 0; i < len(roundtests); i++ { + test := &roundtests[i]; + s := strconv.newDecimal(test.i).RoundDown(test.nd).String(); + if s != test.down { + t.Errorf("Decimal %v RoundDown %d = %v, want %v\n", + test.i, test.nd, s, test.down); + } + s = strconv.newDecimal(test.i).Round(test.nd).String(); + if s != test.round { + t.Errorf("Decimal %v Round %d = %v, want %v\n", + test.i, test.nd, s, test.down); + } + s = strconv.newDecimal(test.i).RoundUp(test.nd).String(); + if s != test.up { + t.Errorf("Decimal %v RoundUp %d = %v, want %v\n", + test.i, test.nd, s, test.up); + } + } +} + +type roundIntTest struct { + i uint64; + shift int; + int uint64; +} + +var roundinttests = []roundIntTest { + roundIntTest{ 0, 100, 0 }, + roundIntTest{ 512, -8, 2 }, + roundIntTest{ 513, -8, 2 }, + roundIntTest{ 640, -8, 2 }, + roundIntTest{ 641, -8, 3 }, + roundIntTest{ 384, -8, 2 }, + roundIntTest{ 385, -8, 2 }, + roundIntTest{ 383, -8, 1 }, + roundIntTest{ 1, 100, 1<<64-1 }, + roundIntTest{ 1000, 0, 1000 }, +} + +func TestDecimalRoundedInteger(t *testing.T) { + for i := 0; i < len(roundinttests); i++ { + test := roundinttests[i]; + // TODO: should be able to use int := here. + int1 := strconv.newDecimal(test.i).Shift(test.shift).RoundedInteger(); + if int1 != test.int { + t.Errorf("Decimal %v >> %v RoundedInteger = %v, want %v\n", + test.i, test.shift, int1, test.int); + } + } +} diff --git a/src/pkg/strconv/fp_test.go b/src/pkg/strconv/fp_test.go new file mode 100644 index 000000000..60d7ce6cf --- /dev/null +++ b/src/pkg/strconv/fp_test.go @@ -0,0 +1,149 @@ +// Copyright 2009 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 strconv +import ( + "bufio"; + "fmt"; + "io"; + "os"; + "strconv"; + "strings"; + "testing"; +) + +func pow2(i int) float64 { + switch { + case i < 0: + return 1 / pow2(-i); + case i == 0: + return 1; + case i == 1: + return 2; + } + return pow2(i/2) * pow2(i-i/2); +} + +// Wrapper around strconv.Atof64. Handles dddddp+ddd (binary exponent) +// itself, passes the rest on to strconv.Atof64. +func myatof64(s string) (f float64, ok bool) { + a := strings.Split(s, "p"); + if len(a) == 2 { + n, err := strconv.Atoi64(a[0]); + if err != nil { + return 0, false; + } + e, err1 := strconv.Atoi(a[1]); + if err1 != nil { + println("bad e", a[1]); + return 0, false; + } + v := float64(n); + // We expect that v*pow2(e) fits in a float64, + // but pow2(e) by itself may not. Be careful. + if e <= -1000 { + v *= pow2(-1000); + e += 1000; + for e < 0 { + v /= 2; + e++; + } + return v, true; + } + if e >= 1000 { + v *= pow2(1000); + e -= 1000; + for e > 0 { + v *= 2; + e--; + } + return v, true; + } + return v*pow2(e), true; + } + f1, err := strconv.Atof64(s); + if err != nil { + return 0, false; + } + return f1, true; +} + +// Wrapper around strconv.Atof32. Handles dddddp+ddd (binary exponent) +// itself, passes the rest on to strconv.Atof32. +func myatof32(s string) (f float32, ok bool) { + a := strings.Split(s, "p"); + if len(a) == 2 { + n, err := strconv.Atoi(a[0]); + if err != nil { + println("bad n", a[0]); + return 0, false; + } + e, err1 := strconv.Atoi(a[1]); + if err1 != nil { + println("bad p", a[1]); + return 0, false; + } + return float32(float64(n)*pow2(e)), true; + } + f1, err1 := strconv.Atof32(s); + if err1 != nil { + return 0, false; + } + return f1, true; +} + +func TestFp(t *testing.T) { + f, err := os.Open("testfp.txt", os.O_RDONLY, 0); + if err != nil { + panicln("testfp: open testfp.txt:", err.String()); + } + defer f.Close(); + + b := bufio.NewReader(f); + + lineno := 0; + for { + line, err2 := b.ReadLineString('\n', false); + if err2 == io.ErrEOF { + break; + } + if err2 != nil { + panicln("testfp: read testfp.txt:", err2.String()); + } + lineno++; + if len(line) == 0 || line[0] == '#' { + continue + } + a := strings.Split(line, " "); + if len(a) != 4 { + t.Error("testfp.txt:", lineno, ": wrong field count\n"); + continue; + } + var s string; + var v float64; + switch a[0] { + case "float64": + var ok bool; + v, ok = myatof64(a[2]); + if !ok { + t.Error("testfp.txt:", lineno, ": cannot atof64 ", a[2]); + continue; + } + s = fmt.Sprintf(a[1], v); + case "float32": + v1, ok := myatof32(a[2]); + if !ok { + t.Error("testfp.txt:", lineno, ": cannot atof32 ", a[2]); + continue; + } + s = fmt.Sprintf(a[1], v1); + v = float64(v1); + } + if s != a[3] { + t.Error("testfp.txt:", lineno, ": ", a[0], " ", a[1], " ", a[2], " (", v, ") ", + "want ", a[3], " got ", s); + } +//else print("testfp.txt:", lineno, ": worked! ", s, "\n"); + } +} diff --git a/src/pkg/strconv/ftoa.go b/src/pkg/strconv/ftoa.go new file mode 100644 index 000000000..b17115175 --- /dev/null +++ b/src/pkg/strconv/ftoa.go @@ -0,0 +1,418 @@ +// Copyright 2009 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. + +// Binary to decimal floating point conversion. +// Algorithm: +// 1) store mantissa in multiprecision decimal +// 2) shift decimal by exponent +// 3) read digits out & format + +package strconv + +import ( + "math"; + "strconv"; +) + +// TODO: move elsewhere? +type floatInfo struct { + mantbits uint; + expbits uint; + bias int; +} +var float32info = floatInfo{ 23, 8, -127 } +var float64info = floatInfo{ 52, 11, -1023 } + +func fmtB(neg bool, mant uint64, exp int, flt *floatInfo) string +func fmtE(neg bool, d *decimal, prec int) string +func fmtF(neg bool, d *decimal, prec int) string +func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string +func max(a, b int) int +func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) + +func floatsize() int { + // Figure out whether float is float32 or float64. + // 1e-35 is representable in both, but 1e-70 + // is too small for a float32. + var f float = 1e-35; + if f*f == 0 { + return 32; + } + return 64; +} + +// Floatsize gives the size of the float type, either 32 or 64. +var FloatSize = floatsize() + +// Ftoa32 converts the 32-bit floating-point number f to a string, +// according to the format fmt and precision prec. +// +// The format fmt is one of +// 'b' (-ddddp±ddd, a binary exponent), +// 'e' (-d.dddde±dd, a decimal exponent), +// 'f' (-ddd.dddd, no exponent), or +// 'g' ('e' for large exponents, 'f' otherwise). +// +// The precision prec controls the number of digits +// (excluding the exponent) printed by the 'e', 'f', and 'g' formats. +// For 'e' and 'f' it is the number of digits after the decimal point. +// For 'g' it is the total number of digits. +// The special precision -1 uses the smallest number of digits +// necessary such that Atof32 will return f exactly. +// +// Ftoa32(f) is not the same as Ftoa64(float32(f)), +// because correct rounding and the number of digits +// needed to identify f depend on the precision of the representation. +func Ftoa32(f float32, fmt byte, prec int) string { + return genericFtoa(uint64(math.Float32bits(f)), fmt, prec, &float32info); +} + +// Ftoa64 is like Ftoa32 but converts a 64-bit floating-point number. +func Ftoa64(f float64, fmt byte, prec int) string { + return genericFtoa(math.Float64bits(f), fmt, prec, &float64info); +} + +// Ftoa behaves as Ftoa32 or Ftoa64, depending on the size of the float type. +func Ftoa(f float, fmt byte, prec int) string { + if FloatSize == 32 { + return Ftoa32(float32(f), fmt, prec); + } + return Ftoa64(float64(f), fmt, prec); +} + +func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string { + neg := bits>>flt.expbits>>flt.mantbits != 0; + exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1); + mant := bits & (uint64(1)<<flt.mantbits - 1); + + switch exp { + case 1<<flt.expbits - 1: + // Inf, NaN + if mant != 0 { + return "NaN"; + } + if neg { + return "-Inf"; + } + return "+Inf"; + + case 0: + // denormalized + exp++; + + default: + // add implicit top bit + mant |= uint64(1)<<flt.mantbits; + } + exp += flt.bias; + + // Pick off easy binary format. + if fmt == 'b' { + return fmtB(neg, mant, exp, flt); + } + + // Create exact decimal representation. + // The shift is exp - flt.mantbits because mant is a 1-bit integer + // followed by a flt.mantbits fraction, and we are treating it as + // a 1+flt.mantbits-bit integer. + d := newDecimal(mant).Shift(exp - int(flt.mantbits)); + + // Round appropriately. + // Negative precision means "only as much as needed to be exact." + shortest := false; + if prec < 0 { + shortest = true; + roundShortest(d, mant, exp, flt); + switch fmt { + case 'e': + prec = d.nd - 1; + case 'f': + prec = max(d.nd - d.dp, 0); + case 'g': + prec = d.nd; + } + } else { + switch fmt { + case 'e': + d.Round(prec+1); + case 'f': + d.Round(d.dp+prec); + case 'g': + if prec == 0 { + prec = 1; + } + d.Round(prec); + } + } + + switch fmt { + case 'e': + return fmtE(neg, d, prec); + case 'f': + return fmtF(neg, d, prec); + case 'g': + // trailing zeros are removed. + if prec > d.nd { + prec = d.nd; + } + // %e is used if the exponent from the conversion + // is less than -4 or greater than or equal to the precision. + // if precision was the shortest possible, use precision 6 for this decision. + eprec := prec; + if shortest { + eprec = 6 + } + exp := d.dp - 1; + if exp < -4 || exp >= eprec { + return fmtE(neg, d, prec - 1); + } + return fmtF(neg, d, max(prec - d.dp, 0)); + } + + return "%" + string(fmt); +} + +// Round d (= mant * 2^exp) to the shortest number of digits +// that will let the original floating point value be precisely +// reconstructed. Size is original floating point size (64 or 32). +func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { + // If mantissa is zero, the number is zero; stop now. + if mant == 0 { + d.nd = 0; + return; + } + + // TODO: Unless exp == minexp, if the number of digits in d + // is less than 17, it seems unlikely that it could not be + // the shortest possible number already. So maybe we can + // bail out without doing the extra multiprecision math here. + + // Compute upper and lower such that any decimal number + // between upper and lower (possibly inclusive) + // will round to the original floating point number. + + // d = mant << (exp - mantbits) + // Next highest floating point number is mant+1 << exp-mantbits. + // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1. + upper := newDecimal(mant*2+1).Shift(exp-int(flt.mantbits)-1); + + // d = mant << (exp - mantbits) + // Next lowest floating point number is mant-1 << exp-mantbits, + // unless mant-1 drops the significant bit and exp is not the minimum exp, + // in which case the next lowest is mant*2-1 << exp-mantbits-1. + // Either way, call it mantlo << explo-mantbits. + // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1. + minexp := flt.bias + 1; // minimum possible exponent + var mantlo uint64; + var explo int; + if mant > 1<<flt.mantbits || exp == minexp { + mantlo = mant - 1; + explo = exp; + } else { + mantlo = mant*2-1; + explo = exp-1; + } + lower := newDecimal(mantlo*2+1).Shift(explo-int(flt.mantbits)-1); + + // The upper and lower bounds are possible outputs only if + // the original mantissa is even, so that IEEE round-to-even + // would round to the original mantissa and not the neighbors. + inclusive := mant%2 == 0; + + // Now we can figure out the minimum number of digits required. + // Walk along until d has distinguished itself from upper and lower. + for i := 0; i < d.nd; i++ { + var l, m, u byte; // lower, middle, upper digits + if i < lower.nd { + l = lower.d[i]; + } else { + l = '0'; + } + m = d.d[i]; + if i < upper.nd { + u = upper.d[i]; + } else { + u = '0'; + } + + // Okay to round down (truncate) if lower has a different digit + // or if lower is inclusive and is exactly the result of rounding down. + okdown := l != m || (inclusive && l == m && i+1 == lower.nd); + + // Okay to round up if upper has a different digit and + // either upper is inclusive or upper is bigger than the result of rounding up. + okup := m != u && (inclusive || i+1 < upper.nd); + + // If it's okay to do either, then round to the nearest one. + // If it's okay to do only one, do it. + switch { + case okdown && okup: + d.Round(i+1); + return; + case okdown: + d.RoundDown(i+1); + return; + case okup: + d.RoundUp(i+1); + return; + } + } +} + +// %e: -d.ddddde±dd +func fmtE(neg bool, d *decimal, prec int) string { + buf := make([]byte, 3+max(prec, 0)+30); // "-0." + prec digits + exp + w := 0; // write index + + // sign + if neg { + buf[w] = '-'; + w++; + } + + // first digit + if d.nd == 0 { + buf[w] = '0'; + } else { + buf[w] = d.d[0]; + } + w++; + + // .moredigits + if prec > 0 { + buf[w] = '.'; + w++; + for i := 0; i < prec; i++ { + if 1+i < d.nd { + buf[w] = d.d[1+i]; + } else { + buf[w] = '0'; + } + w++; + } + } + + // e± + buf[w] = 'e'; + w++; + exp := d.dp - 1; + if d.nd == 0 { // special case: 0 has exponent 0 + exp = 0; + } + if exp < 0 { + buf[w] = '-'; + exp = -exp; + } else { + buf[w] = '+'; + } + w++; + + // dddd + // count digits + n := 0; + for e := exp; e > 0; e /= 10 { + n++; + } + // leading zeros + for i := n; i < 2; i++ { + buf[w] = '0'; + w++; + } + // digits + w += n; + n = 0; + for e := exp; e > 0; e /= 10 { + n++; + buf[w-n] = byte(e%10 + '0'); + } + + return string(buf[0:w]); +} + +// %f: -ddddddd.ddddd +func fmtF(neg bool, d *decimal, prec int) string { + buf := make([]byte, 1+max(d.dp, 1)+1+max(prec, 0)); + w := 0; + + // sign + if neg { + buf[w] = '-'; + w++; + } + + // integer, padded with zeros as needed. + if d.dp > 0 { + var i int; + for i = 0; i < d.dp && i < d.nd; i++ { + buf[w] = d.d[i]; + w++; + } + for ; i < d.dp; i++ { + buf[w] = '0'; + w++; + } + } else { + buf[w] = '0'; + w++; + } + + // fraction + if prec > 0 { + buf[w] = '.'; + w++; + for i := 0; i < prec; i++ { + if d.dp+i < 0 || d.dp+i >= d.nd { + buf[w] = '0'; + } else { + buf[w] = d.d[d.dp+i]; + } + w++; + } + } + + return string(buf[0:w]); +} + +// %b: -ddddddddp+ddd +func fmtB(neg bool, mant uint64, exp int, flt *floatInfo) string { + var buf [50]byte; + w := len(buf); + exp -= int(flt.mantbits); + esign := byte('+'); + if exp < 0 { + esign = '-'; + exp = -exp; + } + n := 0; + for exp > 0 || n < 1 { + n++; + w--; + buf[w] = byte(exp%10 + '0'); + exp /= 10 + } + w--; + buf[w] = esign; + w--; + buf[w] = 'p'; + n = 0; + for mant > 0 || n < 1 { + n++; + w--; + buf[w] = byte(mant%10 + '0'); + mant /= 10; + } + if neg { + w--; + buf[w] = '-'; + } + return string(buf[w:len(buf)]); +} + +func max(a, b int) int { + if a > b { + return a; + } + return b; +} + diff --git a/src/pkg/strconv/ftoa_test.go b/src/pkg/strconv/ftoa_test.go new file mode 100644 index 000000000..0f0baa514 --- /dev/null +++ b/src/pkg/strconv/ftoa_test.go @@ -0,0 +1,119 @@ +// Copyright 2009 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 strconv + +import ( + "math"; + "strconv"; + "testing" +) + +type ftoaTest struct { + f float64; + fmt byte; + prec int; + s string; +} + +func fdiv(a, b float64) float64 { return a / b } // keep compiler in the dark + +const ( + below1e23 = 99999999999999974834176; + above1e23 = 100000000000000008388608; +) + +var ftoatests = []ftoaTest { + ftoaTest{ 1, 'e', 5, "1.00000e+00" }, + ftoaTest{ 1, 'f', 5, "1.00000" }, + ftoaTest{ 1, 'g', 5, "1" }, + ftoaTest{ 1, 'g', -1, "1" }, + ftoaTest{ 20, 'g', -1, "20" }, + ftoaTest{ 1234567.8, 'g', -1, "1.2345678e+06" }, + ftoaTest{ 200000, 'g', -1, "200000" }, + ftoaTest{ 2000000, 'g', -1, "2e+06" }, + + ftoaTest{ 0, 'e', 5, "0.00000e+00" }, + ftoaTest{ 0, 'f', 5, "0.00000" }, + ftoaTest{ 0, 'g', 5, "0" }, + ftoaTest{ 0, 'g', -1, "0" }, + + ftoaTest{ -1, 'e', 5, "-1.00000e+00" }, + ftoaTest{ -1, 'f', 5, "-1.00000" }, + ftoaTest{ -1, 'g', 5, "-1" }, + ftoaTest{ -1, 'g', -1, "-1" }, + + ftoaTest{ 12, 'e', 5, "1.20000e+01" }, + ftoaTest{ 12, 'f', 5, "12.00000" }, + ftoaTest{ 12, 'g', 5, "12" }, + ftoaTest{ 12, 'g', -1, "12" }, + + ftoaTest{ 123456700, 'e', 5, "1.23457e+08" }, + ftoaTest{ 123456700, 'f', 5, "123456700.00000" }, + ftoaTest{ 123456700, 'g', 5, "1.2346e+08" }, + ftoaTest{ 123456700, 'g', -1, "1.234567e+08" }, + + ftoaTest{ 1.2345e6, 'e', 5, "1.23450e+06" }, + ftoaTest{ 1.2345e6, 'f', 5, "1234500.00000" }, + ftoaTest{ 1.2345e6, 'g', 5, "1.2345e+06" }, + + ftoaTest{ 1e23, 'e', 17, "9.99999999999999916e+22" }, + ftoaTest{ 1e23, 'f', 17, "99999999999999991611392.00000000000000000" }, + ftoaTest{ 1e23, 'g', 17, "9.9999999999999992e+22" }, + + ftoaTest{ 1e23, 'e', -1, "1e+23" }, + ftoaTest{ 1e23, 'f', -1, "100000000000000000000000" }, + ftoaTest{ 1e23, 'g', -1, "1e+23" }, + + ftoaTest{ below1e23, 'e', 17, "9.99999999999999748e+22" }, + ftoaTest{ below1e23, 'f', 17, "99999999999999974834176.00000000000000000" }, + ftoaTest{ below1e23, 'g', 17, "9.9999999999999975e+22" }, + + ftoaTest{ below1e23, 'e', -1, "9.999999999999997e+22" }, + ftoaTest{ below1e23, 'f', -1, "99999999999999970000000" }, + ftoaTest{ below1e23, 'g', -1, "9.999999999999997e+22" }, + + ftoaTest{ above1e23, 'e', 17, "1.00000000000000008e+23" }, + ftoaTest{ above1e23, 'f', 17, "100000000000000008388608.00000000000000000" }, + ftoaTest{ above1e23, 'g', 17, "1.0000000000000001e+23" }, + + ftoaTest{ above1e23, 'e', -1, "1.0000000000000001e+23" }, + ftoaTest{ above1e23, 'f', -1, "100000000000000010000000" }, + ftoaTest{ above1e23, 'g', -1, "1.0000000000000001e+23" }, + + ftoaTest{ fdiv(5e-304, 1e20), 'g', -1, "5e-324" }, + ftoaTest{ fdiv(-5e-304, 1e20), 'g', -1, "-5e-324" }, + + ftoaTest{ 32, 'g', -1, "32" }, + ftoaTest{ 32, 'g', 0, "3e+01" }, + + ftoaTest{ 100, 'x', -1, "%x" }, + + ftoaTest{ math.NaN(), 'g', -1, "NaN" }, + ftoaTest{ -math.NaN(), 'g', -1, "NaN" }, + ftoaTest{ math.Inf(0), 'g', -1, "+Inf" }, + ftoaTest{ math.Inf(-1), 'g', -1, "-Inf" }, + ftoaTest{ -math.Inf(0), 'g', -1, "-Inf" }, + + ftoaTest{ -1, 'b', -1, "-4503599627370496p-52" }, +} + +func TestFtoa(t *testing.T) { + if strconv.FloatSize != 32 { + panic("floatsize: ", strconv.FloatSize); + } + for i := 0; i < len(ftoatests); i++ { + test := &ftoatests[i]; + s := strconv.Ftoa64(test.f, test.fmt, test.prec); + if s != test.s { + t.Error("test", test.f, string(test.fmt), test.prec, "want", test.s, "got", s); + } + if float64(float32(test.f)) == test.f && test.fmt != 'b' { + s := strconv.Ftoa32(float32(test.f), test.fmt, test.prec); + if s != test.s { + t.Error("test32", test.f, string(test.fmt), test.prec, "want", test.s, "got", s); + } + } + } +} diff --git a/src/pkg/strconv/itoa.go b/src/pkg/strconv/itoa.go new file mode 100644 index 000000000..7f693ea8c --- /dev/null +++ b/src/pkg/strconv/itoa.go @@ -0,0 +1,49 @@ +// Copyright 2009 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 strconv + +// Itob64 returns the string representation of i in the given base. +func Itob64(i int64, base uint) string { + if i == 0 { + return "0" + } + + u := uint64(i); + if i < 0 { + u = -u; + } + + // Assemble decimal in reverse order. + var buf [32]byte; + j := len(buf); + b := uint64(base); + for u > 0 { + j--; + buf[j] = "0123456789abcdefghijklmnopqrstuvwxyz"[u%b]; + u /= b; + } + + if i < 0 { // add sign + j--; + buf[j] = '-' + } + + return string(buf[j:len(buf)]) +} + +// Itoa64 returns the decimal string representation of i. +func Itoa64(i int64) string { + return Itob64(i, 10); +} + +// Itob returns the string representation of i in the given base. +func Itob(i int, base uint) string { + return Itob64(int64(i), base); +} + +// Itoa returns the decimal string representation of i. +func Itoa(i int) string { + return Itob64(int64(i), 10); +} diff --git a/src/pkg/strconv/itoa_test.go b/src/pkg/strconv/itoa_test.go new file mode 100644 index 000000000..34caf9a32 --- /dev/null +++ b/src/pkg/strconv/itoa_test.go @@ -0,0 +1,111 @@ +// Copyright 2009 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 strconv + +import ( + "fmt"; + "os"; + "strconv"; + "testing"; +) + +type itob64Test struct { + in int64; + base uint; + out string; +} + +var itob64tests = []itob64Test { + itob64Test{ 0, 10, "0" }, + itob64Test{ 1, 10, "1" }, + itob64Test{ -1, 10, "-1" }, + itob64Test{ 12345678, 10, "12345678" }, + itob64Test{ -987654321, 10, "-987654321" }, + itob64Test{ 1<<31-1, 10, "2147483647" }, + itob64Test{ -1<<31+1, 10, "-2147483647" }, + itob64Test{ 1<<31, 10, "2147483648" }, + itob64Test{ -1<<31, 10, "-2147483648" }, + itob64Test{ 1<<31+1, 10, "2147483649" }, + itob64Test{ -1<<31-1, 10, "-2147483649" }, + itob64Test{ 1<<32-1, 10, "4294967295" }, + itob64Test{ -1<<32+1, 10, "-4294967295" }, + itob64Test{ 1<<32, 10, "4294967296" }, + itob64Test{ -1<<32, 10, "-4294967296" }, + itob64Test{ 1<<32+1, 10, "4294967297" }, + itob64Test{ -1<<32-1, 10, "-4294967297" }, + itob64Test{ 1<<50, 10, "1125899906842624" }, + itob64Test{ 1<<63-1, 10, "9223372036854775807" }, + itob64Test{ -1<<63+1, 10, "-9223372036854775807" }, + itob64Test{ -1<<63, 10, "-9223372036854775808" }, + + itob64Test{ 0, 2, "0" }, + itob64Test{ 10, 2, "1010" }, + itob64Test{ -1, 2, "-1" }, + itob64Test{ 1<<15, 2, "1000000000000000" }, + + itob64Test{ -8, 8, "-10" }, + itob64Test{ 057635436545, 8, "57635436545" }, + itob64Test{ 1<<24, 8, "100000000" }, + + itob64Test{ 16, 16, "10" }, + itob64Test{ -0x123456789abcdef, 16, "-123456789abcdef" }, + itob64Test{ 1<<63-1, 16, "7fffffffffffffff" }, + + itob64Test{ 16, 17, "g" }, + itob64Test{ 25, 25, "10" }, + itob64Test{ (((((17*35+24)*35+21)*35+34)*35+12)*35+24)*35+32, 35, "holycow" }, + itob64Test{ (((((17*36+24)*36+21)*36+34)*36+12)*36+24)*36+32, 36, "holycow" }, +} + +func TestItoa(t *testing.T) { + for i := 0; i < len(itob64tests); i++ { + test := itob64tests[i]; + + s := strconv.Itob64(test.in, test.base); + if s != test.out { + t.Errorf("strconv.Itob64(%v, %v) = %v want %v\n", + test.in, test.base, s, test.out); + } + + if int64(int(test.in)) == test.in { + s := strconv.Itob(int(test.in), test.base); + if s != test.out { + t.Errorf("strconv.Itob(%v, %v) = %v want %v\n", + test.in, test.base, s, test.out); + } + } + + if test.base == 10 { + s := strconv.Itoa64(test.in); + if s != test.out { + t.Errorf("strconv.Itoa64(%v) = %v want %v\n", + test.in, s, test.out); + } + + if int64(int(test.in)) == test.in { + s := strconv.Itoa(int(test.in)); + if s != test.out { + t.Errorf("strconv.Itoa(%v) = %v want %v\n", + test.in, s, test.out); + } + } + } + } +} + +// TODO: Use once there is a strconv.uitoa +type uitoa64Test struct { + in uint64; + out string; +} + +// TODO: should be able to call this atoui64tests. +var uitoa64tests = []uitoa64Test { + uitoa64Test{ 1<<63-1, "9223372036854775807" }, + uitoa64Test{ 1<<63, "9223372036854775808" }, + uitoa64Test{ 1<<63+1, "9223372036854775809" }, + uitoa64Test{ 1<<64-2, "18446744073709551614" }, + uitoa64Test{ 1<<64-1, "18446744073709551615" }, +} diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go new file mode 100644 index 000000000..8d7900d1d --- /dev/null +++ b/src/pkg/strconv/quote.go @@ -0,0 +1,229 @@ +// Copyright 2009 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 strconv + +import ( + "os"; + "utf8"; +) + +const lowerhex = "0123456789abcdef" + +// Quote returns a double-quoted Go string literal +// representing s. The returned string s uses Go escape +// sequences (\t, \n, \xFF, \u0100) for control characters +// and non-ASCII characters. +func Quote(s string) string { + // TODO(rsc): String accumulation could be more efficient. + t := `"`; + for ; len(s) > 0; s = s[1:len(s)] { + switch c := s[0]; { + case c == '"': + t += `\"`; + case c == '\\': + t += `\\`; + case ' ' <= c && c <= '~': + t += string(c); + case c == '\a': + t += `\a`; + case c == '\b': + t += `\b`; + case c == '\f': + t += `\f`; + case c == '\n': + t += `\n`; + case c == '\r': + t += `\r`; + case c == '\t': + t += `\t`; + case c == '\v': + t += `\v`; + + case c < utf8.RuneSelf: + t += `\x` + string(lowerhex[c>>4]) + string(lowerhex[c&0xF]); + + case utf8.FullRuneInString(s): + r, size := utf8.DecodeRuneInString(s); + if r == utf8.RuneError && size == 1 { + goto EscX; + } + s = s[size-1:len(s)]; // next iteration will slice off 1 more + if r < 0x10000 { + t += `\u`; + for j:=uint(0); j<4; j++ { + t += string(lowerhex[(r>>(12-4*j))&0xF]); + } + } else { + t += `\U`; + for j:=uint(0); j<8; j++ { + t += string(lowerhex[(r>>(28-4*j))&0xF]); + } + } + + default: + EscX: + t += `\x`; + t += string(lowerhex[c>>4]); + t += string(lowerhex[c&0xF]); + } + } + t += `"`; + return t; +} + +// CanBackquote returns whether the string s would be +// a valid Go string literal if enclosed in backquotes. +func CanBackquote(s string) bool { + for i := 0; i < len(s); i++ { + if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' { + return false; + } + } + return true; +} + +func unhex(b byte) (v int, ok bool) { + c := int(b); + switch { + case '0' <= c && c <= '9': + return c - '0', true; + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true; + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true; + } + return; +} + +func unquoteChar(s string, q byte) (t, ns string, err os.Error) { + err = os.EINVAL; // assume error for easy return + + // easy cases + switch c := s[0]; { + case c >= utf8.RuneSelf: + r, size := utf8.DecodeRuneInString(s); + return s[0:size], s[size:len(s)], nil; + case c == q: + return; + case c != '\\': + return s[0:1], s[1:len(s)], nil; + } + + // hard case: c is backslash + if len(s) <= 1 { + return; + } + c := s[1]; + s = s[2:len(s)]; + + switch c { + case 'a': + return "\a", s, nil; + case 'b': + return "\b", s, nil; + case 'f': + return "\f", s, nil; + case 'n': + return "\n", s, nil; + case 'r': + return "\r", s, nil; + case 't': + return "\t", s, nil; + case 'v': + return "\v", s, nil; + case 'x', 'u', 'U': + n := 0; + switch c { + case 'x': + n = 2; + case 'u': + n = 4; + case 'U': + n = 8; + } + v := 0; + if len(s) < n { + return; + } + for j := 0; j < n; j++ { + x, ok := unhex(s[j]); + if !ok { + return; + } + v = v<<4 | x; + } + s = s[n:len(s)]; + if c == 'x' { + // single-byte string, possibly not UTF-8 + return string([]byte{byte(v)}), s, nil; + } + if v > utf8.RuneMax { + return; + } + return string(v), s, nil; + case '0', '1', '2', '3', '4', '5', '6', '7': + v := int(c) - '0'; + if len(s) < 2 { + return; + } + for j := 0; j < 2; j++ { // one digit already; two more + x := int(s[j]) - '0'; + if x < 0 || x > 7 { + return; + } + v = (v<<3) | x; + } + s = s[2:len(s)]; + if v > 255 { + return; + } + return string(v), s, nil; + + case '\\', q: + return string(c), s, nil; + } + return; +} + +// Unquote interprets s as a single-quoted, double-quoted, +// or backquoted Go string literal, returning the string value +// that s quotes. (If s is single-quoted, it would be a Go +// character literal; Unquote returns the corresponding +// one-character string.) +func Unquote(s string) (t string, err os.Error) { + err = os.EINVAL; // assume error for easy return + n := len(s); + if n < 2 { + return; + } + quote := s[0]; + if quote != s[n-1] { + return; + } + s = s[1:n-1]; + + if quote == '`' { + return s, nil; + } + if quote != '"' && quote != '\'' { + return; + } + + // TODO(rsc): String accumulation could be more efficient. + var c, tt string; + var err1 os.Error; + for len(s) > 0 { + if c, s, err1 = unquoteChar(s, quote); err1 != nil { + err = err1; + return; + } + tt += c; + if quote == '\'' && len(s) != 0 { + // single-quoted must be single character + return; + } + } + return tt, nil +} diff --git a/src/pkg/strconv/quote_test.go b/src/pkg/strconv/quote_test.go new file mode 100644 index 000000000..0fc01ebae --- /dev/null +++ b/src/pkg/strconv/quote_test.go @@ -0,0 +1,170 @@ +// Copyright 2009 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 strconv + +import ( + "os"; + "strconv"; + "testing"; +) + +type quoteTest struct { + in string; + out string; +} + +var quotetests = []quoteTest { + quoteTest{ "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"` }, + quoteTest{ "\\", `"\\"` }, + quoteTest{ "abc\xffdef", `"abc\xffdef"` }, + quoteTest{ "\u263a", `"\u263a"` }, + quoteTest{ "\U0010ffff", `"\U0010ffff"` }, + quoteTest{ "\x04", `"\x04"` }, +} + +func TestQuote(t *testing.T) { + for i := 0; i < len(quotetests); i++ { + tt := quotetests[i]; + if out := Quote(tt.in); out != tt.out { + t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out); + } + } +} + +type canBackquoteTest struct { + in string; + out bool; +} + +var canbackquotetests = []canBackquoteTest { + canBackquoteTest{ "`", false }, + canBackquoteTest{ string(0), false }, + canBackquoteTest{ string(1), false }, + canBackquoteTest{ string(2), false }, + canBackquoteTest{ string(3), false }, + canBackquoteTest{ string(4), false }, + canBackquoteTest{ string(5), false }, + canBackquoteTest{ string(6), false }, + canBackquoteTest{ string(7), false }, + canBackquoteTest{ string(8), false }, + canBackquoteTest{ string(9), true }, // \t + canBackquoteTest{ string(10), false }, + canBackquoteTest{ string(11), false }, + canBackquoteTest{ string(12), false }, + canBackquoteTest{ string(13), false }, + canBackquoteTest{ string(14), false }, + canBackquoteTest{ string(15), false }, + canBackquoteTest{ string(16), false }, + canBackquoteTest{ string(17), false }, + canBackquoteTest{ string(18), false }, + canBackquoteTest{ string(19), false }, + canBackquoteTest{ string(20), false }, + canBackquoteTest{ string(21), false }, + canBackquoteTest{ string(22), false }, + canBackquoteTest{ string(23), false }, + canBackquoteTest{ string(24), false }, + canBackquoteTest{ string(25), false }, + canBackquoteTest{ string(26), false }, + canBackquoteTest{ string(27), false }, + canBackquoteTest{ string(28), false }, + canBackquoteTest{ string(29), false }, + canBackquoteTest{ string(30), false }, + canBackquoteTest{ string(31), false }, + canBackquoteTest{ `' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true }, + canBackquoteTest{ `0123456789`, true }, + canBackquoteTest{ `ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true }, + canBackquoteTest{ `abcdefghijklmnopqrstuvwxyz`, true }, + canBackquoteTest{ `☺`, true }, +} + +func TestCanBackquote(t *testing.T) { + for i := 0; i < len(canbackquotetests); i++ { + tt := canbackquotetests[i]; + if out := CanBackquote(tt.in); out != tt.out { + t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out); + } + } +} + +var unquotetests = []quoteTest { + quoteTest{ `""`, "" }, + quoteTest{ `"a"`, "a" }, + quoteTest{ `"abc"`, "abc" }, + quoteTest{ `"☺"`, "☺" }, + quoteTest{ `"hello world"`, "hello world" }, + quoteTest{ `"\xFF"`, "\xFF" }, + quoteTest{ `"\377"`, "\377" }, + quoteTest{ `"\u1234"`, "\u1234" }, + quoteTest{ `"\U00010111"`, "\U00010111" }, + quoteTest{ `"\U0001011111"`, "\U0001011111" }, + quoteTest{ `"\a\b\f\n\r\t\v\\\""`, "\a\b\f\n\r\t\v\\\"" }, + quoteTest{ `"'"`, "'" }, + + quoteTest{ `'a'`, "a" }, + quoteTest{ `'☹'`, "☹" }, + quoteTest{ `'\a'`, "\a" }, + quoteTest{ `'\x10'`, "\x10" }, + quoteTest{ `'\377'`, "\377" }, + quoteTest{ `'\u1234'`, "\u1234" }, + quoteTest{ `'\U00010111'`, "\U00010111" }, + quoteTest{ `'\t'`, "\t" }, + quoteTest{ `' '`, " " }, + quoteTest{ `'\''`, "'" }, + quoteTest{ `'"'`, "\"" }, + + quoteTest{ "``", `` }, + quoteTest{ "`a`", `a` }, + quoteTest{ "`abc`", `abc` }, + quoteTest{ "`☺`", `☺` }, + quoteTest{ "`hello world`", `hello world` }, + quoteTest{ "`\\xFF`", `\xFF` }, + quoteTest{ "`\\377`", `\377` }, + quoteTest{ "`\\`", `\` }, + quoteTest{ "` `", ` ` }, + quoteTest{ "` `", ` ` }, +} + +var misquoted = []string { + ``, + `"`, + `"a`, + `"'`, + `b"`, + `"\"`, + `'\'`, + `'ab'`, + `"\x1!"`, + `"\U12345678"`, + `"\z"`, + "`", + "`xxx", + "`\"", + `"\'"`, + `'\"'`, +} + +func TestUnquote(t *testing.T) { + for i := 0; i < len(unquotetests); i++ { + tt := unquotetests[i]; + if out, err := Unquote(tt.in); err != nil && out != tt.out { + t.Errorf("Unquote(%s) = %q, %s want %q, nil", tt.in, out, err, tt.out); + } + } + + // run the quote tests too, backward + for i := 0; i < len(quotetests); i++ { + tt := quotetests[i]; + if in, err := Unquote(tt.out); in != tt.in { + t.Errorf("Unquote(%s) = %q, %s, want %q, nil", tt.out, in, err, tt.in); + } + } + + for i := 0; i < len(misquoted); i++ { + s := misquoted[i]; + if out, err := Unquote(s); out != "" || err != os.EINVAL { + t.Errorf("Unquote(%q) = %q, %s want %q, %s", s, out, err, "", os.EINVAL); + } + } +} diff --git a/src/pkg/strconv/testfp.txt b/src/pkg/strconv/testfp.txt new file mode 100644 index 000000000..08d3c4ef0 --- /dev/null +++ b/src/pkg/strconv/testfp.txt @@ -0,0 +1,181 @@ +# Floating-point conversion test cases. +# Empty lines and lines beginning with # are ignored. +# The rest have four fields per line: type, format, input, and output. +# The input is given either in decimal or binary scientific notation. +# The output is the string that should be produced by formatting the +# input with the given format. +# +# The formats are as in C's printf, except that %b means print +# binary scientific notation: NpE = N x 2^E. + +# TODO: +# Powers of 10. +# Powers of 2. +# %.20g versions. +# random sources +# random targets +# random targets ± half a ULP + +# Difficult boundary cases, derived from tables given in +# Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion +# ftp://ftp.ee.lbl.gov/testbase-report.ps.Z + +# Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP +float64 %b 5e+125 6653062250012735p+365 +float64 %b 69e+267 4705683757438170p+841 +float64 %b 999e-026 6798841691080350p-129 +float64 %b 7861e-034 8975675289889240p-153 +float64 %b 75569e-254 6091718967192243p-880 +float64 %b 928609e-261 7849264900213743p-900 +float64 %b 9210917e+080 8341110837370930p+236 +float64 %b 84863171e+114 4625202867375927p+353 +float64 %b 653777767e+273 5068902999763073p+884 +float64 %b 5232604057e-298 5741343011915040p-1010 +float64 %b 27235667517e-109 6707124626673586p-380 +float64 %b 653532977297e-123 7078246407265384p-422 +float64 %b 3142213164987e-294 8219991337640559p-988 +float64 %b 46202199371337e-072 5224462102115359p-246 +float64 %b 231010996856685e-073 5224462102115359p-247 +float64 %b 9324754620109615e+212 5539753864394442p+705 +float64 %b 78459735791271921e+049 8388176519442766p+166 +float64 %b 272104041512242479e+200 5554409530847367p+670 +float64 %b 6802601037806061975e+198 5554409530847367p+668 +float64 %b 20505426358836677347e-221 4524032052079546p-722 +float64 %b 836168422905420598437e-234 5070963299887562p-760 +float64 %b 4891559871276714924261e+222 6452687840519111p+757 + +# Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP +float64 %b 9e-265 8168427841980010p-930 +float64 %b 85e-037 6360455125664090p-169 +float64 %b 623e+100 6263531988747231p+289 +float64 %b 3571e+263 6234526311072170p+833 +float64 %b 81661e+153 6696636728760206p+472 +float64 %b 920657e-023 5975405561110124p-109 +float64 %b 4603285e-024 5975405561110124p-110 +float64 %b 87575437e-309 8452160731874668p-1053 +float64 %b 245540327e+122 4985336549131723p+381 +float64 %b 6138508175e+120 4985336549131723p+379 +float64 %b 83356057653e+193 5986732817132056p+625 +float64 %b 619534293513e+124 4798406992060657p+399 +float64 %b 2335141086879e+218 5419088166961646p+713 +float64 %b 36167929443327e-159 8135819834632444p-536 +float64 %b 609610927149051e-255 4576664294594737p-850 +float64 %b 3743626360493413e-165 6898586531774201p-549 +float64 %b 94080055902682397e-242 6273271706052298p-800 +float64 %b 899810892172646163e+283 7563892574477827p+947 +float64 %b 7120190517612959703e+120 5385467232557565p+409 +float64 %b 25188282901709339043e-252 5635662608542340p-825 +float64 %b 308984926168550152811e-052 5644774693823803p-157 +float64 %b 6372891218502368041059e+064 4616868614322430p+233 + +# Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP +float64 %.0e 8511030020275656p-342 9e-88 +float64 %.1e 5201988407066741p-824 4.6e-233 +float64 %.2e 6406892948269899p+237 1.41e+87 +float64 %.3e 8431154198732492p+72 3.981e+37 +float64 %.4e 6475049196144587p+99 4.1040e+45 +float64 %.5e 8274307542972842p+726 2.92084e+234 +float64 %.6e 5381065484265332p-456 2.891946e-122 +float64 %.7e 6761728585499734p-1057 4.3787718e-303 +float64 %.8e 7976538478610756p+376 1.22770163e+129 +float64 %.9e 5982403858958067p+377 1.841552452e+129 +float64 %.10e 5536995190630837p+93 5.4835744350e+43 +float64 %.11e 7225450889282194p+710 3.89190181146e+229 +float64 %.12e 7225450889282194p+709 1.945950905732e+229 +float64 %.13e 8703372741147379p+117 1.4460958381605e+51 +float64 %.14e 8944262675275217p-1001 4.17367747458531e-286 +float64 %.15e 7459803696087692p-707 1.107950772878888e-197 +float64 %.16e 6080469016670379p-381 1.2345501366327440e-99 +float64 %.17e 8385515147034757p+721 9.25031711960365024e+232 +float64 %.18e 7514216811389786p-828 4.198047150284889840e-234 +float64 %.19e 8397297803260511p-345 1.1716315319786511046e-88 +float64 %.20e 6733459239310543p+202 4.32810072844612493629e+76 +float64 %.21e 8091450587292794p-473 3.317710118160031081518e-127 + +# Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP +float64 %.0e 6567258882077402p+952 3e+302 +float64 %.1e 6712731423444934p+535 7.6e+176 +float64 %.2e 6712731423444934p+534 3.78e+176 +float64 %.3e 5298405411573037p-957 4.350e-273 +float64 %.4e 5137311167659507p-144 2.3037e-28 +float64 %.5e 6722280709661868p+363 1.26301e+125 +float64 %.6e 5344436398034927p-169 7.142211e-36 +float64 %.7e 8369123604277281p-853 1.3934574e-241 +float64 %.8e 8995822108487663p-780 1.41463449e-219 +float64 %.9e 8942832835564782p-383 4.539277920e-100 +float64 %.10e 8942832835564782p-384 2.2696389598e-100 +float64 %.11e 8942832835564782p-385 1.13481947988e-100 +float64 %.12e 6965949469487146p-249 7.700366561890e-60 +float64 %.13e 6965949469487146p-250 3.8501832809448e-60 +float64 %.14e 6965949469487146p-251 1.92509164047238e-60 +float64 %.15e 7487252720986826p+548 6.898586531774201e+180 +float64 %.16e 5592117679628511p+164 1.3076622631878654e+65 +float64 %.17e 8887055249355788p+665 1.36052020756121240e+216 +float64 %.18e 6994187472632449p+690 3.592810217475959676e+223 +float64 %.19e 8797576579012143p+588 8.9125197712484551899e+192 +float64 %.20e 7363326733505337p+272 5.58769757362301140950e+97 +float64 %.21e 8549497411294502p-448 1.176257830728540379990e-119 + +# Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP +# NOTE: The lines with exponent p-149 have been changed from the +# paper. Those entries originally read p-150 and had a mantissa +# twice as large (and even), but IEEE single-precision has no p-150: +# that's the start of the denormals. +float32 %b 5e-20 15474250p-88 +float32 %b 67e+14 12479722p+29 +float32 %b 985e+15 14333636p+36 +# float32 %b 7693e-42 10979816p-150 +float32 %b 7693e-42 5489908p-149 +float32 %b 55895e-16 12888509p-61 +# float32 %b 996622e-44 14224264p-150 +float32 %b 996622e-44 7112132p-149 +float32 %b 7038531e-32 11420669p-107 +# float32 %b 60419369e-46 8623340p-150 +float32 %b 60419369e-46 4311670p-149 +float32 %b 702990899e-20 16209866p-61 +# float32 %b 6930161142e-48 9891056p-150 +float32 %b 6930161142e-48 4945528p-149 +float32 %b 25933168707e+13 14395800p+54 +float32 %b 596428896559e+20 12333860p+82 + +# Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP +float32 %b 3e-23 9507380p-98 +float32 %b 57e+18 12960300p+42 +float32 %b 789e-35 10739312p-130 +float32 %b 2539e-18 11990089p-72 +float32 %b 76173e+28 9845130p+86 +float32 %b 887745e-11 9760860p-40 +float32 %b 5382571e-37 11447463p-124 +float32 %b 82381273e-35 8554961p-113 +float32 %b 750486563e-38 9975678p-120 +float32 %b 3752432815e-39 9975678p-121 +float32 %b 75224575729e-45 13105970p-137 +float32 %b 459926601011e+15 12466336p+65 + +# Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP +float32 %.0e 12676506p-102 2e-24 +float32 %.1e 12676506p-103 1.2e-24 +float32 %.2e 15445013p+86 1.19e+33 +float32 %.3e 13734123p-138 3.941e-35 +float32 %.4e 12428269p-130 9.1308e-33 +float32 %.5e 15334037p-146 1.71900e-37 +float32 %.6e 11518287p-41 5.237910e-06 +float32 %.7e 12584953p-145 2.8216440e-37 +float32 %.8e 15961084p-125 3.75243281e-31 +float32 %.9e 14915817p-146 1.672120916e-37 +float32 %.10e 10845484p-102 2.1388945814e-24 +float32 %.11e 16431059p-61 7.12583594561e-12 + +# Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP +float32 %.0e 16093626p+69 1e+28 +float32 %.1e 9983778p+25 3.4e+14 +float32 %.2e 12745034p+104 2.59e+38 +float32 %.3e 12706553p+72 6.001e+28 +float32 %.4e 11005028p+45 3.8721e+20 +float32 %.5e 15059547p+71 3.55584e+28 +float32 %.6e 16015691p-99 2.526831e-23 +float32 %.7e 8667859p+56 6.2458507e+23 +float32 %.8e 14855922p-82 3.07213267e-18 +float32 %.9e 14855922p-83 1.536066333e-18 +float32 %.10e 10144164p-110 7.8147796834e-27 +float32 %.11e 13248074p+95 5.24810279937e+35 diff --git a/src/pkg/strings/Makefile b/src/pkg/strings/Makefile new file mode 100644 index 000000000..b6660cfc7 --- /dev/null +++ b/src/pkg/strings/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + strings.$O\ + + +phases: a1 +_obj$D/strings.a: phases + +a1: $(O1) + $(AR) grc _obj$D/strings.a strings.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/strings.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/strings.a + +packages: _obj$D/strings.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/strings.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/strings.a diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go new file mode 100644 index 000000000..2e3dc0215 --- /dev/null +++ b/src/pkg/strings/strings.go @@ -0,0 +1,178 @@ +// Copyright 2009 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. + +// A package of simple functions to manipulate strings. +package strings + +import "utf8" + +// Explode splits s into an array of UTF-8 sequences, one per Unicode character (still strings). +// Invalid UTF-8 sequences become correct encodings of U+FFF8. +func Explode(s string) []string { + a := make([]string, utf8.RuneCountInString(s)); + var size, rune int; + i := 0; + for len(s) > 0 { + rune, size = utf8.DecodeRuneInString(s); + s = s[size:len(s)]; + a[i] = string(rune); + i++; + } + return a +} + +// Count counts the number of non-overlapping instances of sep in s. +func Count(s, sep string) int { + if sep == "" { + return utf8.RuneCountInString(s)+1 + } + c := sep[0]; + n := 0; + for i := 0; i+len(sep) <= len(s); i++ { + if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) { + n++; + i += len(sep)-1 + } + } + return n +} + +// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s. +func Index(s, sep string) int { + n := len(sep); + if n == 0 { + return 0 + } + c := sep[0]; + for i := 0; i+n <= len(s); i++ { + if s[i] == c && (n == 1 || s[i:i+n] == sep) { + return i + } + } + return -1 +} + +// Split returns the array representing the substrings of s separated by string sep. Adjacent +// occurrences of sep produce empty substrings. If sep is empty, it is the same as Explode. +func Split(s, sep string) []string { + if sep == "" { + return Explode(s) + } + c := sep[0]; + start := 0; + n := Count(s, sep)+1; + a := make([]string, n); + na := 0; + for i := 0; i+len(sep) <= len(s); i++ { + if s[i] == c && (len(sep) == 1 || s[i:i+len(sep)] == sep) { + a[na] = s[start:i]; + na++; + start = i+len(sep); + i += len(sep)-1 + } + } + a[na] = s[start:len(s)]; + return a +} + +// Join concatenates the elements of a to create a single string. The separator string +// sep is placed between elements in the resulting string. +func Join(a []string, sep string) string { + if len(a) == 0 { + return "" + } + if len(a) == 1 { + return a[0] + } + n := len(sep) * (len(a)-1); + for i := 0; i < len(a); i++ { + n += len(a[i]) + } + + b := make([]byte, n); + bp := 0; + for i := 0; i < len(a); i++ { + s := a[i]; + for j := 0; j < len(s); j++ { + b[bp] = s[j]; + bp++ + } + if i + 1 < len(a) { + s = sep; + for j := 0; j < len(s); j++ { + b[bp] = s[j]; + bp++ + } + } + } + return string(b) +} + +// HasPrefix tests whether the string s begins with prefix. +func HasPrefix(s, prefix string) bool { + return len(s) >= len(prefix) && s[0:len(prefix)] == prefix +} + +// HasSuffix tests whether the string s ends with suffix. +func HasSuffix(s, suffix string) bool { + return len(s) >= len(suffix) && s[len(s)-len(suffix):len(s)] == suffix +} + +// Upper returns a copy of the string s, with all low ASCII lowercase letters +// converted to uppercase. +// TODO: full Unicode support +func UpperASCII(s string) string { + // Note, we can work byte-by-byte because UTF-8 multibyte characters + // don't use any low ASCII byte values. + b := make([]byte, len(s)); + for i := 0; i < len(s); i++ { + c := s[i]; + if 'a' <= c && c <= 'z' { + c -= 'a' - 'A'; + } + b[i] = c; + } + return string(b); +} + +// Upper returns a copy of the string s, with all low ASCII lowercase letters +// converted to lowercase. +// TODO: full Unicode support +func LowerASCII(s string) string { + // Note, we can work byte-by-byte because UTF-8 multibyte characters + // don't use any low ASCII byte values. + b := make([]byte, len(s)); + for i := 0; i < len(s); i++ { + c := s[i]; + if 'A' <= c && c <= 'Z' { + c += 'a' - 'A'; + } + b[i] = c; + } + return string(b); +} + +func isWhitespaceASCII(c byte) bool { + switch int(c) { + case ' ', '\t', '\r', '\n': + return true; + } + return false; +} + +// Trim returns a slice of the string s, with all leading and trailing whitespace +// removed. "Whitespace" for now defined as space, tab, CR, or LF. +// TODO: full Unicode whitespace support (need a unicode.IsWhitespace method) +func TrimSpaceASCII(s string) string { + // Note, we can work byte-by-byte because UTF-8 multibyte characters + // don't use any low ASCII byte values. + start, end := 0, len(s); + for start < end && isWhitespaceASCII(s[start]) { + start++; + } + for start < end && isWhitespaceASCII(s[end-1]) { + end--; + } + return s[start:end]; +} diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go new file mode 100644 index 000000000..05e662032 --- /dev/null +++ b/src/pkg/strings/strings_test.go @@ -0,0 +1,133 @@ +// Copyright 2009 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 strings + +import ( + "strings"; + "testing"; +) + +func eq(a, b []string) bool { + if len(a) != len(b) { + return false; + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false; + } + } + return true; +} + +var abcd = "abcd"; +var faces = "☺☻☹"; +var commas = "1,2,3,4"; +var dots = "1....2....3....4"; + +type ExplodeTest struct { + s string; + a []string; +} +var explodetests = []ExplodeTest { + ExplodeTest{ abcd, []string{"a", "b", "c", "d"} }, + ExplodeTest{ faces, []string{"☺", "☻", "☹" } }, +} +func TestExplode(t *testing.T) { + for i := 0; i < len(explodetests); i++ { + tt := explodetests[i]; + a := Explode(tt.s); + if !eq(a, tt.a) { + t.Errorf("Explode(%q) = %v; want %v", tt.s, a, tt.a); + continue; + } + s := Join(a, ""); + if s != tt.s { + t.Errorf(`Join(Explode(%q), "") = %q`, tt.s, s); + } + } +} + +type SplitTest struct { + s string; + sep string; + a []string; +} +var splittests = []SplitTest { + SplitTest{ abcd, "a", []string{"", "bcd"} }, + SplitTest{ abcd, "z", []string{"abcd"} }, + SplitTest{ abcd, "", []string{"a", "b", "c", "d"} }, + SplitTest{ commas, ",", []string{"1", "2", "3", "4"} }, + SplitTest{ dots, "...", []string{"1", ".2", ".3", ".4"} }, + SplitTest{ faces, "☹", []string{"☺☻", ""} }, + SplitTest{ faces, "~", []string{faces} }, + SplitTest{ faces, "", []string{"☺", "☻", "☹"} }, +} +func TestSplit(t *testing.T) { + for i := 0; i < len(splittests); i++ { + tt := splittests[i]; + a := Split(tt.s, tt.sep); + if !eq(a, tt.a) { + t.Errorf("Split(%q, %q) = %v; want %v", tt.s, tt.sep, a, tt.a); + continue; + } + s := Join(a, tt.sep); + if s != tt.s { + t.Errorf("Join(Split(%q, %q), %q) = %q", tt.s, tt.sep, tt.sep, s); + } + } +} + +// Test case for any function which accepts and returns a single string. +type StringTest struct { + in, out string; +} + +// Execute f on each test case. funcName should be the name of f; it's used +// in failure reports. +func runStringTests(t *testing.T, f func(string) string, funcName string, testCases []StringTest) { + for i, tc := range testCases { + actual := f(tc.in); + if (actual != tc.out) { + t.Errorf("%s(%q) = %q; want %q", funcName, tc.in, actual, tc.out); + } + } +} + +var upperASCIITests = []StringTest { + StringTest{"", ""}, + StringTest{"abc", "ABC"}, + StringTest{"AbC123", "ABC123"}, + StringTest{"azAZ09_", "AZAZ09_"} +} + +var lowerASCIITests = []StringTest { + StringTest{"", ""}, + StringTest{"abc", "abc"}, + StringTest{"AbC123", "abc123"}, + StringTest{"azAZ09_", "azaz09_"} +} + +var trimSpaceASCIITests = []StringTest { + StringTest{"", ""}, + StringTest{"abc", "abc"}, + StringTest{" ", ""}, + StringTest{" \t\r\n \t\t\r\r\n\n ", ""}, + StringTest{" \t\r\n x\t\t\r\r\n\n ", "x"}, + StringTest{" \t\r\n x\t\t\r\r\ny\n ", "x\t\t\r\r\ny"}, + StringTest{"1 \t\r\n2", "1 \t\r\n2"}, +} + +func TestUpperASCII(t *testing.T) { + runStringTests(t, UpperASCII, "UpperASCII", upperASCIITests); +} + +func TestLowerASCII(t *testing.T) { + runStringTests(t, LowerASCII, "LowerASCII", lowerASCIITests); +} + +func TestTrimSpaceASCII(t *testing.T) { + runStringTests(t, TrimSpaceASCII, "TrimSpaceASCII", trimSpaceASCIITests); +} + diff --git a/src/pkg/sync/Makefile b/src/pkg/sync/Makefile new file mode 100644 index 000000000..566853d57 --- /dev/null +++ b/src/pkg/sync/Makefile @@ -0,0 +1,61 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m asm_${GOARCH}.s mutex.go >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + asm_$(GOARCH).$O\ + mutex.$O\ + + +phases: a1 +_obj$D/sync.a: phases + +a1: $(O1) + $(AR) grc _obj$D/sync.a asm_$(GOARCH).$O mutex.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/sync.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/sync.a + +packages: _obj$D/sync.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/sync.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/sync.a diff --git a/src/pkg/sync/asm_386.s b/src/pkg/sync/asm_386.s new file mode 100644 index 000000000..f71182b75 --- /dev/null +++ b/src/pkg/sync/asm_386.s @@ -0,0 +1,23 @@ +// Copyright 2009 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. + +// func cas(val *int32, old, new int32) bool +// Atomically: +// if *val == old { +// *val = new; +// return true; +// }else +// return false; +TEXT sync·cas(SB), 7, $0 + MOVL 4(SP), BX + MOVL 8(SP), AX + MOVL 12(SP), CX + LOCK + CMPXCHGL CX, 0(BX) + JZ ok + MOVL $0, 16(SP) + RET +ok: + MOVL $1, 16(SP) + RET diff --git a/src/pkg/sync/asm_amd64.s b/src/pkg/sync/asm_amd64.s new file mode 100644 index 000000000..07389dd3b --- /dev/null +++ b/src/pkg/sync/asm_amd64.s @@ -0,0 +1,23 @@ +// Copyright 2009 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. + +// func cas(val *int32, old, new int32) bool +// Atomically: +// if *val == old { +// *val = new; +// return true; +// }else +// return false; +TEXT sync·cas(SB), 7, $0 + MOVQ 8(SP), BX + MOVL 16(SP), AX + MOVL 20(SP), CX + LOCK + CMPXCHGL CX, 0(BX) + JZ ok + MOVL $0, 24(SP) + RET +ok: + MOVL $1, 24(SP) + RET diff --git a/src/pkg/sync/mutex.go b/src/pkg/sync/mutex.go new file mode 100644 index 000000000..5a6311a83 --- /dev/null +++ b/src/pkg/sync/mutex.go @@ -0,0 +1,114 @@ +// Copyright 2009 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. + +// The sync package provides basic synchronization primitives +// such as mutual exclusion locks. These are intended for use +// by low-level library routines. Higher-level synchronization +// is better done via channels and communication. +package sync + +func cas(val *int32, old, new int32) bool +func semacquire(*int32) +func semrelease(*int32) + +// A Mutex is a mutual exclusion lock. +// Mutexes can be created as part of other structures; +// the zero value for a Mutex is an unlocked mutex. +type Mutex struct { + key int32; + sema int32; +} + +func xadd(val *int32, delta int32) (new int32) { + for { + v := *val; + if cas(val, v, v+delta) { + return v+delta; + } + } + panic("unreached") +} + +// Lock locks m. +// If the lock is already in use, the calling goroutine +// blocks until the mutex is available. +func (m *Mutex) Lock() { + if xadd(&m.key, 1) == 1 { + // changed from 0 to 1; we hold lock + return; + } + semacquire(&m.sema); +} + +// Unlock unlocks m. +// It is a run-time error if m is not locked on entry to Unlock. +// +// A locked Mutex is not associated with a particular goroutine. +// It is allowed for one goroutine to lock a Mutex and then +// arrange for another goroutine to unlock it. +func (m *Mutex) Unlock() { + if xadd(&m.key, -1) == 0 { + // changed from 1 to 0; no contention + return; + } + semrelease(&m.sema); +} + +// Stub implementation of r/w locks. +// This satisfies the semantics but +// is not terribly efficient. + +// The next comment goes in the BUGS section of the document, +// in its own paragraph, without the (rsc) tag. + +// BUG(rsc): RWMutex does not (yet) allow multiple readers; +// instead it behaves as if RLock and RUnlock were Lock and Unlock. + +// An RWMutex is a reader/writer mutual exclusion lock. +// The lock can be held by an arbitrary number of readers +// or a single writer. +// RWMutexes can be created as part of other +// structures; the zero value for a RWMutex is +// an unlocked mutex. +type RWMutex struct { + m Mutex; +} + +// RLock locks rw for reading. +// If the lock is already locked for writing or there is a writer already waiting +// to acquire the lock, RLock blocks until the writer has released the lock. +func (rw *RWMutex) RLock() { + rw.m.Lock(); +} + +// RUnlock undoes a single RLock call; +// it does not affect other simultaneous readers. +// It is a run-time error if rw is not locked for reading +// on entry to RUnlock. +func (rw *RWMutex) RUnlock() { + rw.m.Unlock(); +} + +// Lock locks rw for writing. +// If the lock is already locked for reading or writing, +// Lock blocks until the lock is available. +// To ensure that the lock eventually becomes available, +// a blocked Lock call excludes new readers from acquiring +// the lock. +func (rw *RWMutex) Lock() { + rw.m.Lock(); +} + +// Unlock unlocks rw for writing. +// It is a run-time error if rw is not locked for writing +// on entry to Unlock. +// +// Like for Mutexes, +// a locked RWMutex is not associated with a particular goroutine. +// It is allowed for one goroutine to RLock (Lock) an RWMutex and then +// arrange for another goroutine to RUnlock (Unlock) it. +func (rw *RWMutex) Unlock() { + rw.m.Unlock(); +} + diff --git a/src/pkg/sync/mutex_test.go b/src/pkg/sync/mutex_test.go new file mode 100644 index 000000000..819dbb9de --- /dev/null +++ b/src/pkg/sync/mutex_test.go @@ -0,0 +1,53 @@ +// Copyright 2009 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. + +// GOMAXPROCS=10 gotest + +package sync + +import ( + "sync"; + "testing" +) + +func HammerSemaphore(s *int32, cdone chan bool) { + for i := 0; i < 1000; i++ { + semacquire(s); + semrelease(s); + } + cdone <- true; +} + +func TestSemaphore(t *testing.T) { + s := new(int32); + *s = 1; + c := make(chan bool); + for i := 0; i < 10; i++ { + go HammerSemaphore(s, c); + } + for i := 0; i < 10; i++ { + <-c; + } +} + + +func HammerMutex(m *Mutex, cdone chan bool) { + for i := 0; i < 1000; i++ { + m.Lock(); + m.Unlock(); + } + cdone <- true; +} + +func TestMutex(t *testing.T) { + m := new(Mutex); + c := make(chan bool); + for i := 0; i < 10; i++ { + go HammerMutex(m, c); + } + for i := 0; i < 10; i++ { + <-c; + } +} + diff --git a/src/pkg/syscall/Makefile b/src/pkg/syscall/Makefile new file mode 100644 index 000000000..a5cc042d7 --- /dev/null +++ b/src/pkg/syscall/Makefile @@ -0,0 +1,97 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m asm_${GOOS}_${GOARCH}.s errstr.go exec.go syscall.go syscall_${GOOS}.go syscall_${GOOS}_${GOARCH}.go zerrors_${GOOS}_${GOARCH}.go zsyscall_${GOOS}_${GOARCH}.go zsysnum_${GOOS}_${GOARCH}.go ztypes_${GOOS}_${GOARCH}.go >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + asm_$(GOOS)_$(GOARCH).$O\ + syscall.$O\ + zerrors_$(GOOS)_$(GOARCH).$O\ + zsysnum_$(GOOS)_$(GOARCH).$O\ + ztypes_$(GOOS)_$(GOARCH).$O\ + +O2=\ + errstr.$O\ + zsyscall_$(GOOS)_$(GOARCH).$O\ + +O3=\ + syscall_$(GOOS)_$(GOARCH).$O\ + +O4=\ + syscall_$(GOOS).$O\ + +O5=\ + exec.$O\ + + +phases: a1 a2 a3 a4 a5 +_obj$D/syscall.a: phases + +a1: $(O1) + $(AR) grc _obj$D/syscall.a asm_$(GOOS)_$(GOARCH).$O syscall.$O zerrors_$(GOOS)_$(GOARCH).$O zsysnum_$(GOOS)_$(GOARCH).$O ztypes_$(GOOS)_$(GOARCH).$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/syscall.a errstr.$O zsyscall_$(GOOS)_$(GOARCH).$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/syscall.a syscall_$(GOOS)_$(GOARCH).$O + rm -f $(O3) + +a4: $(O4) + $(AR) grc _obj$D/syscall.a syscall_$(GOOS).$O + rm -f $(O4) + +a5: $(O5) + $(AR) grc _obj$D/syscall.a exec.$O + rm -f $(O5) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/syscall.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 +$(O5): a4 +$(O6): a5 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/syscall.a + +packages: _obj$D/syscall.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/syscall.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/syscall.a diff --git a/src/pkg/syscall/PORT b/src/pkg/syscall/PORT new file mode 100755 index 000000000..f3addcdb0 --- /dev/null +++ b/src/pkg/syscall/PORT @@ -0,0 +1,124 @@ +#!/bin/sh +# Copyright 2009 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. + +# The syscall package provides access to the raw system call +# interface of the underlying operating system. Porting Go to +# a new architecture/operating system combination requires +# some manual effort, though there are tools that automate +# much of the process. The auto-generated files have names +# beginning with z. +# +# This script prints suggested commands to generate z files +# for the current system. Running those commands is not automatic. +# This script is documentation more than anything else. +# +# * asm_${GOOS}_${GOARCH}.s +# +# This hand-written assembly file implements system call dispatch. +# There are three entry points: +# +# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr); +# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr); +# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr); +# +# The first and second are the standard ones; they differ only in +# how many arguments can be passed to the kernel. +# The third is for low-level use by the ForkExec wrapper; +# unlike the first two, it does not call into the scheduler to +# let it know that a system call is running. +# +# * syscall_${GOOS}.go +# +# This hand-written Go file implements system calls that need +# special handling and lists "//sys" comments giving prototypes +# for ones that can be auto-generated. Mksyscall reads those +# comments to generate the stubs. +# +# * syscall_${GOOS}_${GOARCH}.go +# +# Same as syscall_${GOOS}.go except that it contains code specific +# to ${GOOS} on one particular architecture. +# +# * types_${GOOS}.c +# +# This hand-written C file includes standard C headers and then +# creates typedef or enum names beginning with a dollar sign +# (use of $ in variable names is a gcc extension). The hardest +# part about preparing this file is figuring out which headers to +# include and which symbols need to be #defined to get the +# actual data structures that pass through to the kernel system calls. +# Some C libraries present alternate versions for binary compatibility +# and translate them on the way in and out of system calls, but +# there is almost always a #define that can get the real ones. +# See types_darwin.c and types_linux.c for examples. +# +# * types_${GOOS}_${GOARCH}.c +# +# Same as types_${GOOS}_${GOARCH}.go except that it contains +# definitions specific to ${GOOS} one one particular architecture. +# +# * zerror_${GOOS}_${GOARCH}.go +# +# This machine-generated file defines the system's error numbers, +# error strings, and signal numbers. The generator is "mkerrors". +# Usually no arguments are needed, but mkerrors will pass its +# arguments on to godefs. +# +# * zsyscall_${GOOS}_${GOARCH}.go +# +# Generated by mksyscall; see syscall_${GOOS}.go above. +# +# * zsysnum_${GOOS}_${GOARCH}.go +# +# Generated by mksysnum_${GOOS}. +# +# * ztypes_${GOOS}_${GOARCH}.go +# +# Generated by godefs; see types_${GOOS}.c above. + +GOOSARCH="${GOOS}_${GOARCH}" + +# defaults +mksyscall="mksyscall" +mkerrors="mkerrors" + +case "$GOOSARCH" in +_* | *_ | _) + echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2 + exit 1 + ;; +darwin_386) + mksyscall="mksyscall -l32" + mksysnum="mksysnum_darwin /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master" + mktypes="godefs -gsyscall -f-m32" + ;; +darwin_amd64) + mksysnum="mksysnum_darwin /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master" + mktypes="godefs -gsyscall -f-m64" + mkerrors="mkerrors" + ;; +linux_386) + mksysnum="mksysnum_linux /usr/include/asm/unistd_32.h" + mktypes="godefs -gsyscall -f-m32" + ;; +linux_amd64) + mksysnum="mksysnum_linux /usr/include/asm/unistd_64.h" + mktypes="godefs -gsyscall -f-m64" + ;; +*) + echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2 + exit 1 + ;; +esac + +echo "$mkerrors >zerrors_$GOOSARCH.go" +echo "$mksyscall syscall_$GOOS.go syscall_$GOOSARCH.go >zsyscall_$GOOSARCH.go" +echo "$mksysnum >zsysnum_$GOOSARCH.go" +echo "$mktypes types_$GOOS.c types_$GOOSARCH.c >ztypes_$GOOSARCH.go" + +port=$(ls *.go | grep -v _) +arch=$(ls *_$GOOSARCH.s *_$GOOSARCH.go *_$GOOS.go) +all=$(ls $port $arch) # sort them +echo gobuild $all diff --git a/src/pkg/syscall/asm_darwin_386.s b/src/pkg/syscall/asm_darwin_386.s new file mode 100644 index 000000000..a8ec5b00c --- /dev/null +++ b/src/pkg/syscall/asm_darwin_386.s @@ -0,0 +1,83 @@ +// Copyright 2009 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. + +// +// System call support for 386, Darwin +// + +// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32); +// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32); +// Trap # in AX, args on stack above caller pc. + +TEXT syscall·Syscall(SB),7,$0 + CALL sys·entersyscall(SB) + MOVL 4(SP), AX // syscall entry + // slide args down on top of system call number + LEAL 8(SP), SI + LEAL 4(SP), DI + CLD + MOVSL + MOVSL + MOVSL + INT $0x80 + JAE ok + MOVL $-1, 20(SP) // r1 + MOVL $-1, 24(SP) // r2 + MOVL AX, 28(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok: + MOVL AX, 20(SP) // r1 + MOVL DX, 24(SP) // r2 ??? + MOVL $0, 28(SP) // errno + CALL sys·exitsyscall(SB) + RET + +TEXT syscall·Syscall6(SB),7,$0 + CALL sys·entersyscall(SB) + MOVL 4(SP), AX // syscall entry + // slide args down on top of system call number + LEAL 8(SP), SI + LEAL 4(SP), DI + CLD + MOVSL + MOVSL + MOVSL + MOVSL + MOVSL + MOVSL + INT $0x80 + JAE ok6 + MOVL $-1, 32(SP) // r1 + MOVL $-1, 36(SP) // r2 + MOVL AX, 40(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok6: + MOVL AX, 32(SP) // r1 + MOVL DX, 36(SP) // r2 ??? + MOVL $0, 40(SP) // errno + CALL sys·exitsyscall(SB) + RET + +TEXT syscall·RawSyscall(SB),7,$0 + MOVL 4(SP), AX // syscall entry + // slide args down on top of system call number + LEAL 8(SP), SI + LEAL 4(SP), DI + CLD + MOVSL + MOVSL + MOVSL + INT $0x80 + JAE ok1 + MOVL $-1, 20(SP) // r1 + MOVL $-1, 24(SP) // r2 + MOVL AX, 28(SP) // errno + RET +ok1: + MOVL AX, 20(SP) // r1 + MOVL DX, 24(SP) // r2 ??? + MOVL $0, 28(SP) // errno + RET diff --git a/src/pkg/syscall/asm_darwin_amd64.s b/src/pkg/syscall/asm_darwin_amd64.s new file mode 100644 index 000000000..e1527977f --- /dev/null +++ b/src/pkg/syscall/asm_darwin_amd64.s @@ -0,0 +1,74 @@ +// Copyright 2009 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. + +// +// System call support for AMD64, Darwin +// + +// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); +// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64); +// Trap # in AX, args in DI SI DX, return in AX DX + +TEXT syscall·Syscall(SB),7,$0 + CALL sys·entersyscall(SB) + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 8(SP), AX // syscall entry + ADDQ $0x2000000, AX + SYSCALL + JCC ok + MOVQ $-1, 40(SP) // r1 + MOVQ $0, 48(SP) // r2 + MOVQ AX, 56(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok: + MOVQ AX, 40(SP) // r1 + MOVQ DX, 48(SP) // r2 + MOVQ $0, 56(SP) // errno + CALL sys·exitsyscall(SB) + RET + +TEXT syscall·Syscall6(SB),7,$0 + CALL sys·entersyscall(SB) + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 40(SP), R10 + MOVQ 48(SP), R8 + MOVQ 56(SP), R9 + MOVQ 8(SP), AX // syscall entry + ADDQ $0x2000000, AX + SYSCALL + JCC ok6 + MOVQ $-1, 64(SP) // r1 + MOVQ $0, 72(SP) // r2 + MOVQ AX, 80(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok6: + MOVQ AX, 64(SP) // r1 + MOVQ DX, 72(SP) // r2 + MOVQ $0, 80(SP) // errno + CALL sys·exitsyscall(SB) + RET + +TEXT syscall·RawSyscall(SB),7,$0 + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 8(SP), AX // syscall entry + ADDQ $0x2000000, AX + SYSCALL + JCC ok1 + MOVQ $-1, 40(SP) // r1 + MOVQ $0, 48(SP) // r2 + MOVQ AX, 56(SP) // errno + RET +ok1: + MOVQ AX, 40(SP) // r1 + MOVQ DX, 48(SP) // r2 + MOVQ $0, 56(SP) // errno + RET diff --git a/src/pkg/syscall/asm_linux_386.s b/src/pkg/syscall/asm_linux_386.s new file mode 100644 index 000000000..c6b01792d --- /dev/null +++ b/src/pkg/syscall/asm_linux_386.s @@ -0,0 +1,108 @@ +// Copyright 2009 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. + +// +// System calls for 386, Linux +// + +// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); +// Trap # in AX, args in BX CX DX SI DI, return in AX + +TEXT syscall·Syscall(SB),7,$0 + CALL sys·entersyscall(SB) + MOVL 4(SP), AX // syscall entry + MOVL 8(SP), BX + MOVL 12(SP), CX + MOVL 16(SP), DX + MOVL $0, SI + MOVL $0, DI + INT $0x80 + CMPL AX, $0xfffff001 + JLS ok + MOVL $-1, 20(SP) // r1 + MOVL $0, 24(SP) // r2 + NEGL AX + MOVL AX, 28(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok: + MOVL AX, 20(SP) // r1 + MOVL DX, 24(SP) // r2 + MOVL $0, 28(SP) // errno + CALL sys·exitsyscall(SB) + RET + +// Actually Syscall5 but the rest of the code expects it to be named Syscall6. +TEXT syscall·Syscall6(SB),7,$0 + CALL sys·entersyscall(SB) + MOVL 4(SP), AX // syscall entry + MOVL 8(SP), BX + MOVL 12(SP), CX + MOVL 16(SP), DX + MOVL 20(SP), SI + MOVL 24(SP), DI + // 28(SP) is ignored + INT $0x80 + CMPL AX, $0xfffff001 + JLS ok6 + MOVL $-1, 32(SP) // r1 + MOVL $0, 36(SP) // r2 + NEGL AX + MOVL AX, 40(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok6: + MOVL AX, 32(SP) // r1 + MOVL DX, 36(SP) // r2 + MOVL $0, 40(SP) // errno + CALL sys·exitsyscall(SB) + RET + +TEXT syscall·RawSyscall(SB),7,$0 + MOVL 4(SP), AX // syscall entry + MOVL 8(SP), BX + MOVL 12(SP), CX + MOVL 16(SP), DX + MOVL $0, SI + MOVL $0, DI + INT $0x80 + CMPL AX, $0xfffff001 + JLS ok1 + MOVL $-1, 20(SP) // r1 + MOVL $0, 24(SP) // r2 + NEGL AX + MOVL AX, 28(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok1: + MOVL AX, 20(SP) // r1 + MOVL DX, 24(SP) // r2 + MOVL $0, 28(SP) // errno + RET + +#define SYS_SOCKETCALL 102 /* from zsysnum_linux_386.go */ + +// func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int) +// Kernel interface gets call sub-number and pointer to a0. +TEXT syscall·socketcall(SB),7,$0 + CALL sys·entersyscall(SB) + MOVL $SYS_SOCKETCALL, AX // syscall entry + MOVL 4(SP), BX // socket call number + LEAL 8(SP), CX // pointer to call arguments + MOVL $0, DX + MOVL $0, SI + MOVL $0, DI + INT $0x80 + CMPL AX, $0xfffff001 + JLS oksock + MOVL $-1, 28(SP) // n + NEGL AX + MOVL AX, 32(SP) // errno + CALL sys·exitsyscall(SB) + RET +oksock: + MOVL AX, 28(SP) // n + MOVL $0, 32(SP) // errno + CALL sys·exitsyscall(SB) + RET diff --git a/src/pkg/syscall/asm_linux_amd64.s b/src/pkg/syscall/asm_linux_amd64.s new file mode 100644 index 000000000..cb93b481a --- /dev/null +++ b/src/pkg/syscall/asm_linux_amd64.s @@ -0,0 +1,78 @@ +// Copyright 2009 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. + +// +// System calls for AMD64, Linux +// + +// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64); +// Trap # in AX, args in DI SI DX R10 R8 R9, return in AX DX +// Note that this differs from "standard" ABI convention, which +// would pass 4th arg in CX, not R10. + +TEXT syscall·Syscall(SB),7,$0 + CALL sys·entersyscall(SB) + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 8(SP), AX // syscall entry + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS ok + MOVQ $-1, 40(SP) // r1 + MOVQ $0, 48(SP) // r2 + NEGQ AX + MOVQ AX, 56(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok: + MOVQ AX, 40(SP) // r1 + MOVQ DX, 48(SP) // r2 + MOVQ $0, 56(SP) // errno + CALL sys·exitsyscall(SB) + RET + +TEXT syscall·Syscall6(SB),7,$0 + CALL sys·entersyscall(SB) + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 40(SP), R10 + MOVQ 48(SP), R8 + MOVQ 56(SP), R9 + MOVQ 8(SP), AX // syscall entry + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS ok6 + MOVQ $-1, 64(SP) // r1 + MOVQ $0, 72(SP) // r2 + NEGQ AX + MOVQ AX, 80(SP) // errno + CALL sys·exitsyscall(SB) + RET +ok6: + MOVQ AX, 64(SP) // r1 + MOVQ DX, 72(SP) // r2 + MOVQ $0, 80(SP) // errno + CALL sys·exitsyscall(SB) + RET + +TEXT syscall·RawSyscall(SB),7,$0 + MOVQ 16(SP), DI + MOVQ 24(SP), SI + MOVQ 32(SP), DX + MOVQ 8(SP), AX // syscall entry + SYSCALL + CMPQ AX, $0xfffffffffffff001 + JLS ok1 + MOVQ $-1, 40(SP) // r1 + MOVQ $0, 48(SP) // r2 + NEGQ AX + MOVQ AX, 56(SP) // errno + RET +ok1: + MOVQ AX, 40(SP) // r1 + MOVQ DX, 48(SP) // r2 + MOVQ $0, 56(SP) // errno + RET diff --git a/src/pkg/syscall/errstr.go b/src/pkg/syscall/errstr.go new file mode 100644 index 000000000..67a529d34 --- /dev/null +++ b/src/pkg/syscall/errstr.go @@ -0,0 +1,30 @@ +// Copyright 2009 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 syscall + +import "syscall" + +func str(val int) string { // do it here rather than with fmt to avoid dependency + if val < 0 { + return "-" + str(-val); + } + var buf [32]byte; // big enough for int64 + i := len(buf)-1; + for val >= 10 { + buf[i] = byte(val%10 + '0'); + i--; + val /= 10; + } + buf[i] = byte(val + '0'); + return string(buf[i:len(buf)]); +} + +func Errstr(errno int) string { + if errno < 0 || errno >= int(len(errors)) { + return "error " + str(errno) + } + return errors[errno] +} + diff --git a/src/pkg/syscall/exec.go b/src/pkg/syscall/exec.go new file mode 100644 index 000000000..58fb05863 --- /dev/null +++ b/src/pkg/syscall/exec.go @@ -0,0 +1,305 @@ +// Copyright 2009 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. + +// Fork, exec, wait, etc. + +package syscall + +import ( + "sync"; + "syscall"; + "unsafe"; +) + +// Lock synchronizing creation of new file descriptors with fork. +// +// We want the child in a fork/exec sequence to inherit only the +// file descriptors we intend. To do that, we mark all file +// descriptors close-on-exec and then, in the child, explicitly +// unmark the ones we want the exec'ed program to keep. +// Unix doesn't make this easy: there is, in general, no way to +// allocate a new file descriptor close-on-exec. Instead you +// have to allocate the descriptor and then mark it close-on-exec. +// If a fork happens between those two events, the child's exec +// will inherit an unwanted file descriptor. +// +// This lock solves that race: the create new fd/mark close-on-exec +// operation is done holding ForkLock for reading, and the fork itself +// is done holding ForkLock for writing. At least, that's the idea. +// There are some complications. +// +// Some system calls that create new file descriptors can block +// for arbitrarily long times: open on a hung NFS server or named +// pipe, accept on a socket, and so on. We can't reasonably grab +// the lock across those operations. +// +// It is worse to inherit some file descriptors than others. +// If a non-malicious child accidentally inherits an open ordinary file, +// that's not a big deal. On the other hand, if a long-lived child +// accidentally inherits the write end of a pipe, then the reader +// of that pipe will not see EOF until that child exits, potentially +// causing the parent program to hang. This is a common problem +// in threaded C programs that use popen. +// +// Luckily, the file descriptors that are most important not to +// inherit are not the ones that can take an arbitrarily long time +// to create: pipe returns instantly, and the net package uses +// non-blocking I/O to accept on a listening socket. +// The rules for which file descriptor-creating operations use the +// ForkLock are as follows: +// +// 1) Pipe. Does not block. Use the ForkLock. +// 2) Socket. Does not block. Use the ForkLock. +// 3) Accept. If using non-blocking mode, use the ForkLock. +// Otherwise, live with the race. +// 4) Open. Can block. Use O_CLOEXEC if available (Linux). +// Otherwise, live with the race. +// 5) Dup. Does not block. Use the ForkLock. +// On Linux, could use fcntl F_DUPFD_CLOEXEC +// instead of the ForkLock, but only for dup(fd, -1). + +var ForkLock sync.RWMutex + +// Convert array of string to array +// of NUL-terminated byte pointer. +func StringArrayPtr(ss []string) []*byte { + bb := make([]*byte, len(ss)+1); + for i := 0; i < len(ss); i++ { + bb[i] = StringBytePtr(ss[i]); + } + bb[len(ss)] = nil; + return bb; +} + +func CloseOnExec(fd int) { + fcntl(fd, F_SETFD, FD_CLOEXEC); +} + +func SetNonblock(fd int, nonblocking bool) (errno int) { + flag, err := fcntl(fd, F_GETFL, 0); + if err != 0 { + return err; + } + if nonblocking { + flag |= O_NONBLOCK; + } else { + flag &= ^O_NONBLOCK; + } + flag, err = fcntl(fd, F_SETFL, flag); + return err; +} + + +// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. +// If a dup or exec fails, write the errno int to pipe. +// (Pipe is close-on-exec so if exec succeeds, it will be closed.) +// In the child, this function must not acquire any locks, because +// they might have been locked at the time of the fork. This means +// no rescheduling, no malloc calls, and no new stack segments. +// The calls to RawSyscall are okay because they are assembly +// functions that do not grow the stack. +func forkAndExecInChild(argv0 *byte, argv []*byte, envv []*byte, dir *byte, fd []int, pipe int) + (pid int, err int) +{ + // Declare all variables at top in case any + // declarations require heap allocation (e.g., err1). + var r1, r2, err1 uintptr; + var nextfd int; + var i int; + + darwin := OS == "darwin"; + + // About to call fork. + // No more allocation or calls of non-assembly functions. + r1, r2, err1 = RawSyscall(SYS_FORK, 0, 0, 0); + if err1 != 0 { + return 0, int(err1) + } + + // On Darwin: + // r1 = child pid in both parent and child. + // r2 = 0 in parent, 1 in child. + // Convert to normal Unix r1 = 0 in child. + if darwin && r2 == 1 { + r1 = 0; + } + + if r1 != 0 { + // parent; return PID + return int(r1), 0 + } + + // Fork succeeded, now in child. + + // Chdir + if dir != nil { + r1, r2, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0); + if err1 != 0 { + goto childerror; + } + } + + // Pass 1: look for fd[i] < i and move those up above len(fd) + // so that pass 2 won't stomp on an fd it needs later. + nextfd = int(len(fd)); + if pipe < nextfd { + r1, r2, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0); + if err1 != 0 { + goto childerror; + } + RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC); + pipe = nextfd; + nextfd++; + } + for i = 0; i < len(fd); i++ { + if fd[i] >= 0 && fd[i] < int(i) { + r1, r2, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0); + if err1 != 0 { + goto childerror; + } + RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC); + fd[i] = nextfd; + nextfd++; + if nextfd == pipe { // don't stomp on pipe + nextfd++; + } + } + } + + // Pass 2: dup fd[i] down onto i. + for i = 0; i < len(fd); i++ { + if fd[i] == -1 { + RawSyscall(SYS_CLOSE, uintptr(i), 0, 0); + continue; + } + if fd[i] == int(i) { + // dup2(i, i) won't clear close-on-exec flag on Linux, + // probably not elsewhere either. + r1, r2, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0); + if err1 != 0 { + goto childerror; + } + continue; + } + // The new fd is created NOT close-on-exec, + // which is exactly what we want. + r1, r2, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0); + if err1 != 0 { + goto childerror; + } + } + + // By convention, we don't close-on-exec the fds we are + // started with, so if len(fd) < 3, close 0, 1, 2 as needed. + // Programs that know they inherit fds >= 3 will need + // to set them close-on-exec. + for i = len(fd); i < 3; i++ { + RawSyscall(SYS_CLOSE, uintptr(i), 0, 0); + } + + // Time to exec. + r1, r2, err1 = RawSyscall(SYS_EXECVE, + uintptr(unsafe.Pointer(argv0)), + uintptr(unsafe.Pointer(&argv[0])), + uintptr(unsafe.Pointer(&envv[0]))); + +childerror: + // send error code on pipe + RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), uintptr(unsafe.Sizeof(err1))); + for { + RawSyscall(SYS_EXIT, 253, 0, 0); + } + + // Calling panic is not actually safe, + // but the for loop above won't break + // and this shuts up the compiler. + panic("unreached"); +} + +// Combination of fork and exec, careful to be thread safe. +func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) + (pid int, err int) +{ + var p [2]int; + var r1 int; + var n int; + var err1 uintptr; + var wstatus WaitStatus; + + p[0] = -1; + p[1] = -1; + + // Convert args to C form. + argv0p := StringBytePtr(argv0); + argvp := StringArrayPtr(argv); + envvp := StringArrayPtr(envv); + var dirp *byte; + if len(dir) > 0 { + dirp = StringBytePtr(dir); + } + + // Acquire the fork lock so that no other threads + // create new fds that are not yet close-on-exec + // before we fork. + ForkLock.Lock(); + + // Allocate child status pipe close on exec. + if err = Pipe(&p); err != 0 { + goto error; + } + var val int; + if val, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != 0 { + goto error; + } + if val, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != 0 { + goto error; + } + + // Kick off child. + pid, err = forkAndExecInChild(argv0p, argvp, envvp, dirp, fd, p[1]); + if err != 0 { + error: + if p[0] >= 0 { + Close(p[0]); + Close(p[1]); + } + ForkLock.Unlock(); + return 0, err + } + ForkLock.Unlock(); + + // Read child error status from pipe. + Close(p[1]); + n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)); + Close(p[0]); + if err != 0 || n != 0 { + if n == unsafe.Sizeof(err1) { + err = int(err1); + } + if err == 0 { + err = EPIPE; + } + + // Child failed; wait for it to exit, to make sure + // the zombies don't accumulate. + pid1, err1 := Wait4(pid, &wstatus, 0, nil); + for err1 == EINTR { + pid1, err1 = Wait4(pid, &wstatus, 0, nil); + } + return 0, err + } + + // Read got EOF, so pipe closed on exec, so exec succeeded. + return pid, 0 +} + +// Ordinary exec. +func Exec(argv0 string, argv []string, envv []string) (err int) { + r1, r2, err1 := RawSyscall(SYS_EXECVE, + uintptr(unsafe.Pointer(StringBytePtr(argv0))), + uintptr(unsafe.Pointer(&StringArrayPtr(argv)[0])), + uintptr(unsafe.Pointer(&StringArrayPtr(envv)[0]))); + return int(err1); +} + diff --git a/src/pkg/syscall/mkerrors b/src/pkg/syscall/mkerrors new file mode 100755 index 000000000..015f02145 --- /dev/null +++ b/src/pkg/syscall/mkerrors @@ -0,0 +1,94 @@ +#!/bin/sh +# Copyright 2009 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. + + +# Generate Go code listing error values (ENAMETOOLONG etc) +# and signal values (SIGALRM etc). They're unrelated except +# that we use the same method for finding them. + +errors=$( + echo '#include <errno.h>' | + # The gcc command line prints all the #defines + # it encounters while processing the input + gcc -x c - -E -dM | + egrep -h '#define E[A-Z0-9_]+ ' $files | + sed 's/#define //; s/ .*//' +) + +signals=$( + echo '#include <sys/signal.h>' | + gcc -x c - -E -dM | + egrep -h '#define SIG[^_]' | + egrep -v '#define (SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))' | + sed 's/#define //; s/ .*//' +) + +# Write godefs input. +( + echo '#include <errno.h>' + echo '#include <signal.h>' + echo 'enum {' + for i in $errors $signals + do + echo '$'"$i = $i," + done + echo '};' +) >_errors.c + +echo '// mkerrors' "$@" +echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT' +echo +godefs -gsyscall "$@" _errors.c + +# Run C program to print error strings. +( + echo " +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) + +enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below + +int errors[] = { +" + for i in $errors + do + echo ' '$i, + done + + echo ' +}; + +int +main(void) +{ + int i, j, e; + char buf[1024]; + + printf("\n\n// Error table\n"); + printf("var errors = [...]string {\n"); + for(i=0; i<nelem(errors); i++) { + e = errors[i]; + for(j=0; j<i; j++) + if(errors[j] == e) // duplicate value + goto next; + strcpy(buf, strerror(e)); + // lowercase first letter: Bad -> bad, but STREAM -> STREAM. + if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) + buf[0] += a - A; + printf("\t%d: \"%s\",\n", e, buf); + next:; + } + printf("}\n\n"); +} + +' +) >_errors.c + +gcc -o _errors _errors.c && ./_errors +rm -f _errors.c _errors diff --git a/src/pkg/syscall/mksyscall b/src/pkg/syscall/mksyscall new file mode 100755 index 000000000..850fc1e3f --- /dev/null +++ b/src/pkg/syscall/mksyscall @@ -0,0 +1,170 @@ +#!/usr/bin/perl +# Copyright 2009 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. + +# This program reads a file containing function prototypes +# (like syscall_darwin.go) and generates system call bodies. +# The prototypes are marked by lines beginning with "//sys" +# and read like func declarations if //sys is replaced by func, but: +# * The parameter lists must give a name for each argument. +# This includes return parameters. +# * The parameter lists must give a type for each argument: +# the (x, y, z int) shorthand is not allowed. +# * If the return parameter is an error number, it must be named errno. + +$cmdline = "mksyscall " . join(' ', @ARGV); +$errors = 0; +$_32bit = ""; + +if($ARGV[0] eq "-b32") { + $_32bit = "big-endian"; + shift; +} elsif($ARGV[0] eq "-l32") { + $_32bit = "little-endian"; + shift; +} + +if($ARGV[0] =~ /^-/) { + print STDERR "usage: mksyscall [-b32 | -l32] [file ...]\n"; + exit 1; +} + +sub parseparamlist($) { + my ($list) = @_; + $list =~ s/^\s*//; + $list =~ s/\s*$//; + if($list eq "") { + return (); + } + return split(/\s*,\s*/, $list); +} + +sub parseparam($) { + my ($p) = @_; + if($p !~ /^(\S*) (\S*)$/) { + print STDERR "$ARGV:$.: malformed parameter: $p\n"; + $errors = 1; + return ("xx", "int"); + } + return ($1, $2); +} + +$text = ""; +while(<>) { + chomp; + s/\s+/ /g; + s/^\s+//; + s/\s+$//; + next if !/^\/\/sys /; + + # Line must be of the form + # func Open(path string, mode int, perm int) (fd int, errno int) + # Split into name, in params, out params. + if(!/^\/\/sys (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(SYS_[A-Z0-9_]+))?$/) { + print STDERR "$ARGV:$.: malformed //sys declaration\n"; + $errors = 1; + next; + } + my ($func, $in, $out, $sysname) = ($1, $2, $3, $4); + + # Split argument lists on comma. + my @in = parseparamlist($in); + my @out = parseparamlist($out); + + # Go function header. + $text .= sprintf "func %s(%s) (%s) {\n", $func, join(', ', @in), join(', ', @out); + + # Prepare arguments to Syscall. + my @args = (); + my $n = 0; + foreach my $p (@in) { + my ($name, $type) = parseparam($p); + if($type =~ /^\*/) { + push @args, "uintptr(unsafe.Pointer($name))"; + } elsif($type eq "string") { + push @args, "uintptr(unsafe.Pointer(StringBytePtr($name)))"; + } elsif($type =~ /^\[\](.*)/) { + # Convert slice into pointer, length. + # Have to be careful not to take address of &a[0] if len == 0: + # pass nil in that case. + $text .= "\tvar _p$n *$1;\n"; + $text .= "\tif len($name) > 0 { _p$n = \&${name}[0]; }\n"; + push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))"; + $n++; + } elsif($type eq "int64" && $_32bit ne "") { + if($_32bit eq "big-endian") { + push @args, "uintptr($name >> 32)", "uintptr($name)"; + } else { + push @args, "uintptr($name)", "uintptr($name >> 32)"; + } + } else { + push @args, "uintptr($name)"; + } + } + + # Determine which form to use; pad args with zeros. + my $asm = "Syscall"; + if(@args <= 3) { + while(@args < 3) { + push @args, "0"; + } + } elsif(@args <= 6) { + $asm = "Syscall6"; + while(@args < 6) { + push @args, "0"; + } + } else { + print STDERR "$ARGV:$.: too many arguments to system call\n"; + } + + # System call number. + if($sysname eq "") { + $sysname = "SYS_$func"; + $sysname =~ s/([a-z])([A-Z])/${1}_$2/g; # turn FooBar into Foo_Bar + $sysname =~ y/a-z/A-Z/; + } + + # Actual call. + my $args = join(', ', @args); + $text .= "\tr0, r1, e1 := $asm($sysname, $args);\n"; + + # Assign return values. + for(my $i=0; $i<@out; $i++) { + my $p = $out[$i]; + my ($name, $type) = parseparam($p); + my $reg = ""; + if($name eq "errno") { + $reg = "e1"; + } else { + $reg = sprintf("r%d", $i); + } + if($type eq "bool") { + $reg = "$reg != 0"; + } + $text .= "\t$name = $type($reg);\n"; + } + + $text .= "\treturn;\n"; + $text .= "}\n\n"; +} + +if($errors) { + exit 1; +} + +print <<EOF; +// $cmdline +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import ( + "syscall"; + "unsafe"; +) + +$text + +EOF +exit 0; diff --git a/src/pkg/syscall/mksysnum_darwin b/src/pkg/syscall/mksysnum_darwin new file mode 100755 index 000000000..74e2dfde9 --- /dev/null +++ b/src/pkg/syscall/mksysnum_darwin @@ -0,0 +1,38 @@ +#!/usr/bin/perl +# Copyright 2009 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. +# +# Generate system call table for Darwin from master list +# (for example, xnu-1228/bsd/kern/syscalls.master). + +my $command = "mksysnum_darwin " . join(' ', @ARGV); + +print <<EOF; +// $command +// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT + +package syscall + +const ( +EOF + +while(<>){ + if(/^([0-9]+)\s+ALL\s+({ \S+\s+(\w+).*})/){ + my $num = $1; + my $proto = $2; + my $name = "SYS_$3"; + $name =~ y/a-z/A-Z/; + + # There are multiple entries for enosys and nosys, so comment them out. + if($name =~ /^SYS_E?NOSYS$/){ + $name = "// $name"; + } + + print " $name = $num; // $proto\n"; + } +} + +print <<EOF; +) +EOF diff --git a/src/pkg/syscall/mksysnum_linux b/src/pkg/syscall/mksysnum_linux new file mode 100755 index 000000000..2252bfd7c --- /dev/null +++ b/src/pkg/syscall/mksysnum_linux @@ -0,0 +1,31 @@ +#!/usr/bin/perl +# Copyright 2009 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. + +my $command = "mklinux ". join(' ', @ARGV); + +print <<EOF; +// Generated by mklinux; DO NOT EDIT. +// $command + +package syscall + +const( +EOF + +while(<>){ + if(/^#define __NR_(\w+)\s+([0-9]+)/){ + my $name = "SYS_$1"; + my $num = $2; + $name =~ y/a-z/A-Z/; + print " $name = $num;\n"; + } +} + +print <<EOF; +) + +func _darwin_system_call_conflict() { +} +EOF diff --git a/src/pkg/syscall/syscall.go b/src/pkg/syscall/syscall.go new file mode 100644 index 000000000..5ee44e3e8 --- /dev/null +++ b/src/pkg/syscall/syscall.go @@ -0,0 +1,37 @@ +// Copyright 2009 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. + +// This package contains an interface to the low-level operating system +// primitives. The details vary depending on the underlying system. +// Its primary use is inside other packages that provide a more portable +// interface to the system, such as "os", "time" and "net". Use those +// packages rather than this one if you can. +// For details of the functions and data types in this package consult +// the manuals for the appropriate operating system. +package syscall + +import ( + "syscall"; + "unsafe"; +) + +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) + +// StringByteSlice returns a NUL-terminated slice of bytes +// containing the text of s. +func StringByteSlice(s string) []byte { + a := make([]byte, len(s)+1); + for i := 0; i < len(s); i++ { + a[i] = s[i]; + } + return a; +} + +// StringBytePtr returns a pointer to a NUL-terminated array of bytes +// containing the text of s. +func StringBytePtr(s string) *byte { + return &StringByteSlice(s)[0]; +} diff --git a/src/pkg/syscall/syscall_darwin.go b/src/pkg/syscall/syscall_darwin.go new file mode 100644 index 000000000..011fc7a0f --- /dev/null +++ b/src/pkg/syscall/syscall_darwin.go @@ -0,0 +1,663 @@ +// Copyright 2009 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. + +// Darwin system calls. +// This file is compiled as ordinary Go code, +// but it is also input to mksyscall, +// which parses the //sys lines and generates system call stubs. +// Note that sometimes we use a lowercase //sys name and +// wrap it in our own nicer implementation. + +package syscall + +import ( + "syscall"; + "unsafe"; +) + +const OS = "darwin" + +/* + * Pseudo-system calls + */ +// The const provides a compile-time constant so clients +// can adjust to whether there is a working Getwd and avoid +// even linking this function into the binary. See ../os/getwd.go. +const ImplementsGetwd = false + +func Getwd() (string, int) { + return "", ENOTSUP; +} + + +/* + * Wrapped + */ + +//sys getgroups(ngid int, gid *_Gid_t) (n int, errno int) +//sys setgroups(ngid int, gid *_Gid_t) (errno int) + +func Getgroups() (gids []int, errno int) { + n, err := getgroups(0, nil); + if err != 0 { + return nil, errno; + } + if n == 0 { + return nil, 0; + } + + // Sanity check group count. Max is 16 on BSD. + if n < 0 || n > 1000 { + return nil, EINVAL; + } + + a := make([]_Gid_t, n); + n, err = getgroups(n, &a[0]); + if err != 0 { + return nil, errno; + } + gids = make([]int, n); + for i, v := range a[0:n] { + gids[i] = int(v); + } + return; +} + +func Setgroups(gids []int) (errno int) { + if len(gids) == 0 { + return setgroups(0, nil); + } + + a := make([]_Gid_t, len(gids)); + for i, v := range gids { + a[i] = _Gid_t(v); + } + return setgroups(len(a), &a[0]); +} + +// Wait status is 7 bits at bottom, either 0 (exited), +// 0x7F (stopped), or a signal number that caused an exit. +// The 0x80 bit is whether there was a core dump. +// An extra number (exit code, signal causing a stop) +// is in the high bits. + +type WaitStatus uint32 + +const ( + mask = 0x7F; + core = 0x80; + shift = 8; + + exited = 0; + stopped = 0x7F; +) + +func (w WaitStatus) Exited() bool { + return w&mask == exited; +} + +func (w WaitStatus) ExitStatus() int { + if w&mask != exited { + return -1; + } + return int(w >> shift); +} + +func (w WaitStatus) Signaled() bool { + return w&mask != stopped && w&mask != 0; +} + +func (w WaitStatus) Signal() int { + sig := int(w & mask); + if sig == stopped || sig == 0 { + return -1; + } + return sig; +} + +func (w WaitStatus) CoreDump() bool { + return w.Signaled() && w&core != 0; +} + +func (w WaitStatus) Stopped() bool { + return w&mask == stopped && w>>shift != SIGSTOP; +} + +func (w WaitStatus) Continued() bool { + return w&mask == stopped && w>>shift == SIGSTOP; +} + +func (w WaitStatus) StopSignal() int { + if !w.Stopped() { + return -1; + } + return int(w >> shift) & 0xFF; +} + +//sys wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { + var status _C_int; + wpid, errno = wait4(pid, &status, options, rusage); + if wstatus != nil { + *wstatus = WaitStatus(status); + } + return; +} + +//sys pipe() (r int, w int, errno int) +func Pipe(p []int) (errno int) { + if len(p) != 2 { + return EINVAL; + } + p[0], p[1], errno = pipe(); + return; +} + +// TODO(rsc): How does 386 return an int64 newoffset? +//sys lseek(fd int, offset int64, whence int) (newoffset uintptr, errno int) +func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) { + n, e := lseek(fd, offset, whence); + return int64(n), e; +} + +func Sleep(ns int64) (errno int) { + tv := NsecToTimeval(ns); + return Select(0, nil, nil, nil, &tv); +} + +//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) +//sys bind(s int, addr uintptr, addrlen _Socklen) (errno int) +//sys connect(s int, addr uintptr, addrlen _Socklen) (errno int) +//sys socket(domain int, typ int, proto int) (fd int, errno int) +//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) + +// For testing: clients can set this flag to force +// creation of IPv6 sockets to return EAFNOSUPPORT. +var SocketDisableIPv6 bool + +type Sockaddr interface { + sockaddr() (ptr uintptr, len _Socklen, errno int); // lowercase; only we can define Sockaddrs +} + +type SockaddrInet4 struct { + Port int; + Addr [4]byte; + raw RawSockaddrInet4; +} + +func (sa *SockaddrInet4) sockaddr() (uintptr, _Socklen, int) { + if sa.Port < 0 || sa.Port > 0xFFFF { + return 0, 0, EINVAL; + } + sa.raw.Len = SizeofSockaddrInet4; + sa.raw.Family = AF_INET; + p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)); + p[0] = byte(sa.Port>>8); + p[1] = byte(sa.Port); + for i := 0; i < len(sa.Addr); i++ { + sa.raw.Addr[i] = sa.Addr[i]; + } + return uintptr(unsafe.Pointer(&sa.raw)), _Socklen(sa.raw.Len), 0; +} + +type SockaddrInet6 struct { + Port int; + Addr [16]byte; + raw RawSockaddrInet6; +} + +func (sa *SockaddrInet6) sockaddr() (uintptr, _Socklen, int) { + if sa.Port < 0 || sa.Port > 0xFFFF { + return 0, 0, EINVAL; + } + sa.raw.Len = SizeofSockaddrInet6; + sa.raw.Family = AF_INET6; + p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)); + p[0] = byte(sa.Port>>8); + p[1] = byte(sa.Port); + for i := 0; i < len(sa.Addr); i++ { + sa.raw.Addr[i] = sa.Addr[i]; + } + return uintptr(unsafe.Pointer(&sa.raw)), _Socklen(sa.raw.Len), 0; +} + +type SockaddrUnix struct { + Name string; + raw RawSockaddrUnix; +} + +func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, int) { + name := sa.Name; + n := len(name); + if n >= len(sa.raw.Path) || n == 0 { + return 0, 0, EINVAL; + } + sa.raw.Len = byte(3 + n); // 2 for Family, Len; 1 for NUL + sa.raw.Family = AF_UNIX; + for i := 0; i < n; i++ { + sa.raw.Path[i] = int8(name[i]); + } + return uintptr(unsafe.Pointer(&sa.raw)), _Socklen(sa.raw.Len), 0; +} + +func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, int) { + switch rsa.Addr.Family { + case AF_UNIX: + pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa)); + if pp.Len < 3 || pp.Len > SizeofSockaddrUnix { + return nil, EINVAL + } + sa := new(SockaddrUnix); + n := int(pp.Len) - 3; // subtract leading Family, Len, terminating NUL + for i := 0; i < n; i++ { + if pp.Path[i] == 0 { + // found early NUL; assume Len is overestimating + n = i; + break; + } + } + bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0])); + sa.Name = string(bytes[0:n]); + return sa, 0; + + case AF_INET: + pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa)); + sa := new(SockaddrInet4); + p := (*[2]byte)(unsafe.Pointer(&pp.Port)); + sa.Port = int(p[0])<<8 + int(p[1]); + for i := 0; i < len(sa.Addr); i++ { + sa.Addr[i] = pp.Addr[i]; + } + return sa, 0; + + case AF_INET6: + pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)); + sa := new(SockaddrInet6); + p := (*[2]byte)(unsafe.Pointer(&pp.Port)); + sa.Port = int(p[0])<<8 + int(p[1]); + for i := 0; i < len(sa.Addr); i++ { + sa.Addr[i] = pp.Addr[i]; + } + return sa, 0; + } + return nil, EAFNOSUPPORT; +} + +func Accept(fd int) (nfd int, sa Sockaddr, errno int) { + var rsa RawSockaddrAny; + var len _Socklen = SizeofSockaddrAny; + nfd, errno = accept(fd, &rsa, &len); + if errno != 0 { + return; + } + sa, errno = anyToSockaddr(&rsa); + if errno != 0 { + Close(nfd); + nfd = 0; + } + return; +} + +func Bind(fd int, sa Sockaddr) (errno int) { + ptr, n, err := sa.sockaddr(); + if err != 0 { + return err; + } + return bind(fd, ptr, n); +} + +func Connect(fd int, sa Sockaddr) (errno int) { + ptr, n, err := sa.sockaddr(); + if err != 0 { + return err; + } + return connect(fd, ptr, n); +} + +func Socket(domain, typ, proto int) (fd, errno int) { + if domain == AF_INET6 && SocketDisableIPv6 { + return -1, EAFNOSUPPORT + } + fd, errno = socket(domain, typ, proto); + return; +} + +func SetsockoptInt(fd, level, opt int, value int) (errno int) { + var n = int32(value); + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4); +} + +func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) { + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(tv)), unsafe.Sizeof(*tv)); +} + +func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(l)), unsafe.Sizeof(*l)); +} + +//sys kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) +func Kevent(kq int, changes, events []Kevent_t, timeout *Timespec) (n int, errno int) { + var change, event uintptr; + if len(changes) > 0 { + change = uintptr(unsafe.Pointer(&changes[0])); + } + if len(events) > 0 { + event = uintptr(unsafe.Pointer(&events[0])); + } + return kevent(kq, change, len(changes), event, len(events), timeout); +} + +// TODO: wrap +// Acct(name nil-string) (errno int) +// Futimes(fd int, timeval *Timeval) (errno int) // Pointer to 2 timevals! +// Gethostuuid(uuid *byte, timeout *Timespec) (errno int) +// Getpeername(fd int, addr *Sockaddr, addrlen *int) (errno int) +// Getsockname(fd int, addr *Sockaddr, addrlen *int) (errno int) +// Getsockopt(s int, level int, name int, val *byte, vallen *int) (errno int) +// Madvise(addr *byte, len int, behav int) (errno int) +// Mprotect(addr *byte, len int, prot int) (errno int) +// Msync(addr *byte, len int, flags int) (errno int) +// Munmap(addr *byte, len int) (errno int) +// Ptrace(req int, pid int, addr uintptr, data int) (ret uintptr, errno int) +// Recvfrom(s int, buf *byte, nbuf int, flags int, from *Sockaddr, fromlen *int) (n int, errno int) +// Recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) +// Sendmsg(s int, msg *Msghdr, flags int) (n int, errno int) +// Sendto(s int, buf *byte, nbuf int, flags int, to *Sockaddr, addrlen int) (errno int) +// Utimes(path string, timeval *Timeval) (errno int) // Pointer to 2 timevals! +//sys fcntl(fd int, cmd int, arg int) (val int, errno int) + + +/* + * Exposed directly + */ +//sys Access(path string, flags int) (errno int) +//sys Adjtime(delta *Timeval, olddelta *Timeval) (errno int) +//sys Chdir(path string) (errno int) +//sys Chflags(path string, flags int) (errno int) +//sys Chmod(path string, mode int) (errno int) +//sys Chown(path string, uid int, gid int) (errno int) +//sys Chroot(path string) (errno int) +//sys Close(fd int) (errno int) +//sys Dup(fd int) (nfd int, errno int) +//sys Dup2(from int, to int) (errno int) +//sys Exchangedata(path1 string, path2 string, options int) (errno int) +//sys Exit(code int) +//sys Fchdir(fd int) (errno int) +//sys Fchflags(path string, flags int) (errno int) +//sys Fchmod(fd int, mode int) (errno int) +//sys Fchown(fd int, uid int, gid int) (errno int) +//sys Flock(fd int, how int) (errno int) +//sys Fpathconf(fd int, name int) (val int, errno int) +//sys Fstat(fd int, stat *Stat_t) (errno int) = SYS_FSTAT64 +//sys Fstatfs(fd int, stat *Statfs_t) (errno int) = SYS_FSTATFS64 +//sys Fsync(fd int) (errno int) +//sys Ftruncate(fd int, length int64) (errno int) +//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) = SYS_GETDIRENTRIES64 +//sys Getdtablesize() (size int) +//sys Getegid() (egid int) +//sys Geteuid() (uid int) +//sys Getfsstat(buf []Statfs_t, flags int) (n int, errno int) = SYS_GETFSSTAT64 +//sys Getgid() (gid int) +//sys Getpgid(pid int) (pgid int, errno int) +//sys Getpgrp() (pgrp int) +//sys Getpid() (pid int) +//sys Getppid() (ppid int) +//sys Getpriority(which int, who int) (prio int, errno int) +//sys Getrlimit(which int, lim *Rlimit) (errno int) +//sys Getrusage(who int, rusage *Rusage) (errno int) +//sys Getsid(pid int) (sid int, errno int) +//sys Getuid() (uid int) +//sys Issetugid() (tainted bool) +//sys Kill(pid int, signum int, posix int) (errno int) +//sys Kqueue() (fd int, errno int) +//sys Lchown(path string, uid int, gid int) (errno int) +//sys Link(path string, link string) (errno int) +//sys Listen(s int, backlog int) (errno int) +//sys Lstat(path string, stat *Stat_t) (errno int) = SYS_LSTAT64 +//sys Mkdir(path string, mode int) (errno int) +//sys Mkfifo(path string, mode int) (errno int) +//sys Mknod(path string, mode int, dev int) (errno int) +//sys Open(path string, mode int, perm int) (fd int, errno int) +//sys Pathconf(path string, name int) (val int, errno int) +//sys Pread(fd int, p []byte, offset int64) (n int, errno int) +//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int) +//sys Read(fd int, p []byte) (n int, errno int) +//sys Readlink(path string, buf []byte) (n int, errno int) +//sys Rename(from string, to string) (errno int) +//sys Revoke(path string) (errno int) +//sys Rmdir(path string) (errno int) +//sys Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int) +//sys Setegid(egid int) (errno int) +//sys Seteuid(euid int) (errno int) +//sys Setgid(gid int) (errno int) +//sys Setlogin(name string) (errno int) +//sys Setpgid(pid int, pgid int) (errno int) +//sys Setpriority(which int, who int, prio int) (errno int) +//sys Setprivexec(flag int) (errno int) +//sys Setregid(rgid int, egid int) (errno int) +//sys Setreuid(ruid int, euid int) (errno int) +//sys Setrlimit(which int, lim *Rlimit) (errno int) +//sys Setsid() (pid int, errno int) +//sys Settimeofday(tp *Timeval) (errno int) +//sys Setuid(uid int) (errno int) +//sys Stat(path string, stat *Stat_t) (errno int) = SYS_STAT64 +//sys Statfs(path string, stat *Statfs_t) (errno int) = SYS_STATFS64 +//sys Symlink(path string, link string) (errno int) +//sys Sync() (errno int) +//sys Truncate(path string, length int64) (errno int) +//sys Umask(newmask int) (errno int) +//sys Undelete(path string) (errno int) +//sys Unlink(path string) (errno int) +//sys Unmount(path string, flags int) (errno int) +//sys Write(fd int, p []byte) (n int, errno int) +//sys read(fd int, buf *byte, nbuf int) (n int, errno int) +//sys write(fd int, buf *byte, nbuf int) (n int, errno int) + + +/* + * Unimplemented + */ +// Profil +// Sigaction +// Sigprocmask +// Getlogin +// Sigpending +// Sigaltstack +// Ioctl +// Reboot +// Execve +// Vfork +// Sbrk +// Sstk +// Ovadvise +// Mincore +// Setitimer +// Swapon +// Select +// Sigsuspend +// Readv +// Writev +// Nfssvc +// Getfh +// Quotactl +// Mount +// Csops +// Waitid +// Add_profil +// Kdebug_trace +// Sigreturn +// Mmap +// __Sysctl +// Mlock +// Munlock +// Atsocket +// Kqueue_from_portset_np +// Kqueue_portset +// Getattrlist +// Setattrlist +// Getdirentriesattr +// Searchfs +// Delete +// Copyfile +// Poll +// Watchevent +// Waitevent +// Modwatch +// Getxattr +// Fgetxattr +// Setxattr +// Fsetxattr +// Removexattr +// Fremovexattr +// Listxattr +// Flistxattr +// Fsctl +// Initgroups +// Posix_spawn +// Nfsclnt +// Fhopen +// Minherit +// Semsys +// Msgsys +// Shmsys +// Semctl +// Semget +// Semop +// Msgctl +// Msgget +// Msgsnd +// Msgrcv +// Shmat +// Shmctl +// Shmdt +// Shmget +// Shm_open +// Shm_unlink +// Sem_open +// Sem_close +// Sem_unlink +// Sem_wait +// Sem_trywait +// Sem_post +// Sem_getvalue +// Sem_init +// Sem_destroy +// Open_extended +// Umask_extended +// Stat_extended +// Lstat_extended +// Fstat_extended +// Chmod_extended +// Fchmod_extended +// Access_extended +// Settid +// Gettid +// Setsgroups +// Getsgroups +// Setwgroups +// Getwgroups +// Mkfifo_extended +// Mkdir_extended +// Identitysvc +// Shared_region_check_np +// Shared_region_map_np +// __pthread_mutex_destroy +// __pthread_mutex_init +// __pthread_mutex_lock +// __pthread_mutex_trylock +// __pthread_mutex_unlock +// __pthread_cond_init +// __pthread_cond_destroy +// __pthread_cond_broadcast +// __pthread_cond_signal +// Setsid_with_pid +// __pthread_cond_timedwait +// Aio_fsync +// Aio_return +// Aio_suspend +// Aio_cancel +// Aio_error +// Aio_read +// Aio_write +// Lio_listio +// __pthread_cond_wait +// Iopolicysys +// Mlockall +// Munlockall +// __pthread_kill +// __pthread_sigmask +// __sigwait +// __disable_threadsignal +// __pthread_markcancel +// __pthread_canceled +// __semwait_signal +// Proc_info +// Sendfile +// Stat64_extended +// Lstat64_extended +// Fstat64_extended +// __pthread_chdir +// __pthread_fchdir +// Audit +// Auditon +// Getauid +// Setauid +// Getaudit +// Setaudit +// Getaudit_addr +// Setaudit_addr +// Auditctl +// Bsdthread_create +// Bsdthread_terminate +// Stack_snapshot +// Bsdthread_register +// Workq_open +// Workq_ops +// __mac_execve +// __mac_syscall +// __mac_get_file +// __mac_set_file +// __mac_get_link +// __mac_set_link +// __mac_get_proc +// __mac_set_proc +// __mac_get_fd +// __mac_set_fd +// __mac_get_pid +// __mac_get_lcid +// __mac_get_lctx +// __mac_set_lctx +// Setlcid +// Read_nocancel +// Write_nocancel +// Open_nocancel +// Close_nocancel +// Wait4_nocancel +// Recvmsg_nocancel +// Sendmsg_nocancel +// Recvfrom_nocancel +// Accept_nocancel +// Msync_nocancel +// Fcntl_nocancel +// Select_nocancel +// Fsync_nocancel +// Connect_nocancel +// Sigsuspend_nocancel +// Readv_nocancel +// Writev_nocancel +// Sendto_nocancel +// Pread_nocancel +// Pwrite_nocancel +// Waitid_nocancel +// Poll_nocancel +// Msgsnd_nocancel +// Msgrcv_nocancel +// Sem_wait_nocancel +// Aio_suspend_nocancel +// __sigwait_nocancel +// __semwait_signal_nocancel +// __mac_mount +// __mac_get_mount +// __mac_getfsstat + diff --git a/src/pkg/syscall/syscall_darwin_386.go b/src/pkg/syscall/syscall_darwin_386.go new file mode 100644 index 000000000..5633d7c03 --- /dev/null +++ b/src/pkg/syscall/syscall_darwin_386.go @@ -0,0 +1,49 @@ +// Copyright 2009 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 syscall + +import "syscall" + +func Getpagesize() int { + return 4096 +} + +func TimespecToNsec(ts Timespec) int64 { + return int64(ts.Sec)*1e9 + int64(ts.Nsec); +} + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = int32(nsec / 1e9); + ts.Nsec = int32(nsec % 1e9); + return; +} + +func TimevalToNsec(tv Timeval) int64 { + return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3; +} + +func NsecToTimeval(nsec int64) (tv Timeval) { + nsec += 999; // round up to microsecond + tv.Usec = int32(nsec%1e9 / 1e3); + tv.Sec = int32(nsec/1e9); + return; +} + +//sys gettimeofday(tp *Timeval) (sec int64, usec int32, errno int) +func Gettimeofday(tv *Timeval) (errno int) { + // The tv passed to gettimeofday must be non-nil + // but is otherwise unused. The answers come back + // in the two registers. + sec, usec, err := gettimeofday(tv); + tv.Sec = int32(sec); + tv.Usec = int32(usec); + return err; +} + +func SetKevent(k *Kevent_t, fd, mode, flags int) { + k.Ident = uint32(fd); + k.Filter = int16(mode); + k.Flags = uint16(flags); +} diff --git a/src/pkg/syscall/syscall_darwin_amd64.go b/src/pkg/syscall/syscall_darwin_amd64.go new file mode 100644 index 000000000..f7a93f121 --- /dev/null +++ b/src/pkg/syscall/syscall_darwin_amd64.go @@ -0,0 +1,49 @@ +// Copyright 2009 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 syscall + +import "syscall" + +func Getpagesize() int { + return 4096 +} + +func TimespecToNsec(ts Timespec) int64 { + return int64(ts.Sec)*1e9 + int64(ts.Nsec); +} + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = nsec / 1e9; + ts.Nsec = nsec % 1e9; + return; +} + +func TimevalToNsec(tv Timeval) int64 { + return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3; +} + +func NsecToTimeval(nsec int64) (tv Timeval) { + nsec += 999; // round up to microsecond + tv.Usec = int32(nsec%1e9 / 1e3); + tv.Sec = int64(nsec/1e9); + return; +} + +//sys gettimeofday(tp *Timeval) (sec int64, usec int32, errno int) +func Gettimeofday(tv *Timeval) (errno int) { + // The tv passed to gettimeofday must be non-nil + // but is otherwise unused. The answers come back + // in the two registers. + sec, usec, err := gettimeofday(tv); + tv.Sec = sec; + tv.Usec = usec; + return err; +} + +func SetKevent(k *Kevent_t, fd, mode, flags int) { + k.Ident = uint64(fd); + k.Filter = int16(mode); + k.Flags = uint16(flags); +} diff --git a/src/pkg/syscall/syscall_linux.go b/src/pkg/syscall/syscall_linux.go new file mode 100644 index 000000000..50f3938d7 --- /dev/null +++ b/src/pkg/syscall/syscall_linux.go @@ -0,0 +1,636 @@ +// Copyright 2009 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. + +// Linux system calls. +// This file is compiled as ordinary Go code, +// but it is also input to mksyscall, +// which parses the //sys lines and generates system call stubs. +// Note that sometimes we use a lowercase //sys name and +// wrap it in our own nicer implementation. + +package syscall + +import ( + "syscall"; + "unsafe"; +) + +const OS = "linux" + +/* + * Wrapped + */ + +//sys pipe(p *[2]_C_int) (errno int) +func Pipe(p []int) (errno int) { + if len(p) != 2 { + return EINVAL; + } + var pp [2]_C_int; + errno = pipe(&pp); + p[0] = int(pp[0]); + p[1] = int(pp[1]); + return; +} + +//sys utimes(path string, times *[2]Timeval) (errno int) +func Utimes(path string, tv []Timeval) (errno int) { + if len(tv) != 2 { + return EINVAL; + } + return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))); +} + +//sys futimesat(dirfd int, path string, times *[2]Timeval) (errno int) +func Futimesat(dirfd int, path string, tv []Timeval) (errno int) { + if len(tv) != 2 { + return EINVAL; + } + return futimesat(dirfd, path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))); +} + +const ImplementsGetwd = true; + +//sys Getcwd(buf []byte) (n int, errno int) +func Getwd() (wd string, errno int) { + var buf [PathMax]byte; + n, err := Getcwd(&buf); + if err != 0 { + return "", err; + } + // Getcwd returns the number of bytes written to buf, including the NUL. + if n < 1|| n > len(buf) || buf[n-1] != 0 { + return "", EINVAL; + } + return string(buf[0:n-1]), 0 +} + +//sys getgroups(n int, list *_Gid_t) (nn int, errno int) +//sys setgroups(n int, list *_Gid_t) (errno int) +func Getgroups() (gids []int, errno int) { + n, err := getgroups(0, nil); + if err != 0 { + return nil, errno; + } + if n == 0 { + return nil, 0; + } + + // Sanity check group count. Max is 1<<16 on Linux. + if n < 0 || n > 1<<20 { + return nil, EINVAL; + } + + a := make([]_Gid_t, n); + n, err = getgroups(n, &a[0]); + if err != 0 { + return nil, errno; + } + gids = make([]int, n); + for i, v := range a[0:n] { + gids[i] = int(v); + } + return; +} + +func Setgroups(gids []int) (errno int) { + if len(gids) == 0 { + return setgroups(0, nil); + } + + a := make([]_Gid_t, len(gids)); + for i, v := range gids { + a[i] = _Gid_t(v); + } + return setgroups(len(a), &a[0]); +} + +type WaitStatus uint32 + +// Wait status is 7 bits at bottom, either 0 (exited), +// 0x7F (stopped), or a signal number that caused an exit. +// The 0x80 bit is whether there was a core dump. +// An extra number (exit code, signal causing a stop) +// is in the high bits. At least that's the idea. +// There are various irregularities. For example, the +// "continued" status is 0xFFFF, distinguishing itself +// from stopped via the core dump bit. + +const ( + mask = 0x7F; + core = 0x80; + exited = 0x00; + stopped = 0x7F; + shift = 8; +) + +func (w WaitStatus) Exited() bool { + return w&mask == exited; +} + +func (w WaitStatus) Signaled() bool { + return w&mask != stopped && w&mask != exited; +} + +func (w WaitStatus) Stopped() bool { + return w&0xFF == stopped; +} + +func (w WaitStatus) Continued() bool { + return w == 0xFFFF; +} + +func (w WaitStatus) CoreDump() bool { + return w.Signaled() && w&core != 0; +} + +func (w WaitStatus) ExitStatus() int { + if !w.Exited() { + return -1; + } + return int(w >> shift) & 0xFF; +} + +func (w WaitStatus) Signal() int { + if !w.Signaled() { + return -1; + } + return int(w & mask); +} + +func (w WaitStatus) StopSignal() int { + if !w.Stopped() { + return -1; + } + return int(w >> shift) & 0xFF; +} + +//sys wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { + var status _C_int; + wpid, errno = wait4(pid, &status, options, rusage); + if wstatus != nil { + *wstatus = WaitStatus(status); + } + return; +} + +func Sleep(nsec int64) (errno int) { + tv := NsecToTimeval(nsec); + n, err := Select(0, nil, nil, nil, &tv); + return err; +} + +// Implemented in syscall_linux_*.go +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) +func bind(s int, addr uintptr, addrlen _Socklen) (errno int) +func connect(s int, addr uintptr, addrlen _Socklen) (errno int) +func socket(domain int, typ int, proto int) (fd int, errno int) +func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) +func Listen(s int, n int) (errno int) + +// For testing: clients can set this flag to force +// creation of IPv6 sockets to return EAFNOSUPPORT. +var SocketDisableIPv6 bool + +type Sockaddr interface { + sockaddr() (ptr uintptr, len _Socklen, errno int); // lowercase; only we can define Sockaddrs +} + +type SockaddrInet4 struct { + Port int; + Addr [4]byte; + raw RawSockaddrInet4; +} + +func (sa *SockaddrInet4) sockaddr() (uintptr, _Socklen, int) { + if sa.Port < 0 || sa.Port > 0xFFFF { + return 0, 0, EINVAL; + } + sa.raw.Family = AF_INET; + p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)); + p[0] = byte(sa.Port>>8); + p[1] = byte(sa.Port); + for i := 0; i < len(sa.Addr); i++ { + sa.raw.Addr[i] = sa.Addr[i]; + } + return uintptr(unsafe.Pointer(&sa.raw)), SizeofSockaddrInet4, 0; +} + +type SockaddrInet6 struct { + Port int; + Addr [16]byte; + raw RawSockaddrInet6; +} + +func (sa *SockaddrInet6) sockaddr() (uintptr, _Socklen, int) { + if sa.Port < 0 || sa.Port > 0xFFFF { + return 0, 0, EINVAL; + } + sa.raw.Family = AF_INET6; + p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port)); + p[0] = byte(sa.Port>>8); + p[1] = byte(sa.Port); + for i := 0; i < len(sa.Addr); i++ { + sa.raw.Addr[i] = sa.Addr[i]; + } + return uintptr(unsafe.Pointer(&sa.raw)), SizeofSockaddrInet6, 0; +} + +type SockaddrUnix struct { + Name string; + raw RawSockaddrUnix; +} + +func (sa *SockaddrUnix) sockaddr() (uintptr, _Socklen, int) { + name := sa.Name; + n := len(name); + if n >= len(sa.raw.Path) || n == 0 { + return 0, 0, EINVAL; + } + sa.raw.Family = AF_UNIX; + for i := 0; i < n; i++ { + sa.raw.Path[i] = int8(name[i]); + } + if sa.raw.Path[0] == '@' { + sa.raw.Path[0] = 0; + } + + // length is family, name, NUL. + return uintptr(unsafe.Pointer(&sa.raw)), 1 + _Socklen(n) + 1, 0; +} + +func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, int) { + switch rsa.Addr.Family { + case AF_UNIX: + pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa)); + sa := new(SockaddrUnix); + if pp.Path[0] == 0 { + // "Abstract" Unix domain socket. + // Rewrite leading NUL as @ for textual display. + // (This is the standard convention.) + // Not friendly to overwrite in place, + // but the callers below don't care. + pp.Path[0] = '@'; + } + + // Assume path ends at NUL. + // This is not technically the Linux semantics for + // abstract Unix domain sockets--they are supposed + // to be uninterpreted fixed-size binary blobs--but + // everyone uses this convention. + n := 0; + for n < len(pp.Path) && pp.Path[n] != 0 { + n++; + } + bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0])); + sa.Name = string(bytes[0:n]); + return sa, 0; + + case AF_INET: + pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa)); + sa := new(SockaddrInet4); + p := (*[2]byte)(unsafe.Pointer(&pp.Port)); + sa.Port = int(p[0])<<8 + int(p[1]); + for i := 0; i < len(sa.Addr); i++ { + sa.Addr[i] = pp.Addr[i]; + } + return sa, 0; + + case AF_INET6: + pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)); + sa := new(SockaddrInet6); + p := (*[2]byte)(unsafe.Pointer(&pp.Port)); + sa.Port = int(p[0])<<8 + int(p[1]); + for i := 0; i < len(sa.Addr); i++ { + sa.Addr[i] = pp.Addr[i]; + } + return sa, 0; + } + return nil, EAFNOSUPPORT; +} + +func Accept(fd int) (nfd int, sa Sockaddr, errno int) { + var rsa RawSockaddrAny; + var len _Socklen = SizeofSockaddrAny; + nfd, errno = accept(fd, &rsa, &len); + if errno != 0 { + return; + } + sa, errno = anyToSockaddr(&rsa); + if errno != 0 { + Close(nfd); + nfd = 0; + } + return; +} + +func Bind(fd int, sa Sockaddr) (errno int) { + ptr, n, err := sa.sockaddr(); + if err != 0 { + return err; + } + return bind(fd, ptr, n); +} + +func Connect(fd int, sa Sockaddr) (errno int) { + ptr, n, err := sa.sockaddr(); + if err != 0 { + return err; + } + return connect(fd, ptr, n); +} + +func Socket(domain, typ, proto int) (fd, errno int) { + if domain == AF_INET6 && SocketDisableIPv6 { + return -1, EAFNOSUPPORT + } + fd, errno = socket(domain, typ, proto); + return; +} + +func SetsockoptInt(fd, level, opt int, value int) (errno int) { + var n = int32(value); + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&n)), 4); +} + +func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) { + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(tv)), unsafe.Sizeof(*tv)); +} + +func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(l)), unsafe.Sizeof(*l)); +} + +// Sendto +// Recvfrom +// Sendmsg +// Recvmsg +// Getsockname +// Getpeername +// Socketpair +// Getsockopt + +/* + * Direct access + */ +//sys Access(path string, mode int) (errno int) +//sys Acct(path string) (errno int) +//sys Adjtimex(buf *Timex) (state int, errno int) +//sys Chdir(path string) (errno int) +//sys Chmod(path string, mode int) (errno int) +//sys Chown(path string, uid int, gid int) (errno int) +//sys Chroot(path string) (errno int) +//sys Close(fd int) (errno int) +//sys Creat(path string, mode int) (fd int, errno int) +//sys Dup(oldfd int) (fd int, errno int) +//sys Dup2(oldfd int, newfd int) (fd int, errno int) +//sys EpollCreate(size int) (fd int, errno int) +//sys EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) +//sys Exit(code int) = SYS_EXIT_GROUP +//sys Faccessat(dirfd int, path string, mode int, flags int) (errno int) +//sys Fallocate(fd int, mode int, off int64, len int64) (errno int) +//sys Fchdir(fd int) (errno int) +//sys Fchmod(fd int, mode int) (errno int) +//sys Fchmodat(dirfd int, path string, mode int, flags int) (errno int) +//sys Fchown(fd int, uid int, gid int) (errno int) +//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (errno int) +//sys fcntl(fd int, cmd int, arg int) (val int, errno int) +//sys Fdatasync(fd int) (errno int) +//sys Fstat(fd int, stat *Stat_t) (errno int) +//sys Fstatfs(fd int, buf *Statfs_t) (errno int) +//sys Fsync(fd int) (errno int) +//sys Ftruncate(fd int, length int64) (errno int) +//sys Getdents(fd int, buf []byte) (n int, errno int) = SYS_GETDENTS64 +//sys Getegid() (egid int) +//sys Geteuid() (euid int) +//sys Getgid() (gid int) +//sys Getpgid(pid int) (pgid int, errno int) +//sys Getpgrp() (pid int) +//sys Getpid() (pid int) +//sys Getppid() (ppid int) +//sys Getrlimit(resource int, rlim *Rlimit) (errno int) +//sys Getrusage(who int, rusage *Rusage) (errno int) +//sys Gettid() (tid int) +//sys Gettimeofday(tv *Timeval) (errno int) +//sys Getuid() (uid int) +//sys Ioperm(from int, num int, on int) (errno int) +//sys Iopl(level int) (errno int) +//sys Kill(pid int, sig int) (errno int) +//sys Klogctl(typ int, buf []byte) (n int, errno int) = SYS_SYSLOG +//sys Lchown(path string, uid int, gid int) (errno int) +//sys Link(oldpath string, newpath string) (errno int) +//sys Lstat(path string, stat *Stat_t) (errno int) +//sys Mkdir(path string, mode int) (errno int) +//sys Mkdirat(dirfd int, path string, mode int) (errno int) +//sys Mknod(path string, mode int, dev int) (errno int) +//sys Mknodat(dirfd int, path string, mode int, dev int) (errno int) +//sys Nanosleep(time *Timespec, leftover *Timespec) (errno int) +//sys Open(path string, mode int, perm int) (fd int, errno int) +//sys Openat(dirfd int, path string, flags int, mode int) (fd int, errno int) +//sys Pause() (errno int) +//sys PivotRoot(newroot string, putold string) (errno int) = SYS_PIVOT_ROOT +//sys Pread(fd int, p []byte, offset int64) (n int, errno int) = SYS_PREAD64 +//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int) = SYS_PWRITE64 +//sys Read(fd int, p []byte) (n int, errno int) +//sys Readlink(path string, buf []byte) (n int, errno int) +//sys Rename(oldpath string, newpath string) (errno int) +//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (errno int) +//sys Rmdir(path string) (errno int) +//sys Seek(fd int, offset int64, whence int) (off int64, errno int) = SYS_LSEEK +//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) +//sys Setdomainname(p []byte) (errno int) +//sys Setfsgid(gid int) (errno int) +//sys Setfsuid(uid int) (errno int) +//sys Setgid(gid int) (errno int) +//sys Sethostname(p []byte) (errno int) +//sys Setpgid(pid int, pgid int) (errno int) +//sys Setregid(rgid int, egid int) (errno int) +//sys Setresgid(rgid int, egid int, sgid int) (errno int) +//sys Setresuid(ruid int, euid int, suid int) (errno int) +//sys Setreuid(ruid int, euid int) (errno int) +//sys Setrlimit(resource int, rlim *Rlimit) (errno int) +//sys Setsid() (pid int) +//sys Settimeofday(tv *Timeval) (errno int) +//sys Setuid(uid int) (errno int) +//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, errno int) +//sys Stat(path string, stat *Stat_t) (errno int) +//sys Statfs(path string, buf *Statfs_t) (errno int) +//sys Symlink(oldpath string, newpath string) (errno int) +//sys Sync() +//sys SyncFileRange(fd int, off int64, n int64, flags int) (errno int) +//sys Sysinfo(info *Sysinfo_t) (errno int) +//sys Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) +//sys Tgkill(tgid int, tid int, sig int) (errno int) +//sys Time(t *Time_t) (tt Time_t, errno int) +//sys Times(tms *Tms) (ticks uintptr, errno int) +//sys Truncate(path string, length int64) (errno int) +//sys Umask(mask int) (oldmask int) +//sys Uname(buf *Utsname) (errno int) +//sys Unlink(path string) (errno int) +//sys Unlinkat(dirfd int, path string) (errno int) +//sys Unshare(flags int) (errno int) +//sys Ustat(dev int, ubuf *Ustat_t) (errno int) +//sys Utime(path string, buf *Utimbuf) (errno int) +//sys Write(fd int, p []byte) (n int, errno int) +//sys exitThread(code int) (errno int) = SYS_EXIT +//sys read(fd int, p *byte, np int) (n int, errno int) +//sys write(fd int, p *byte, np int) (n int, errno int) + +/* + * Unimplemented + */ +// AddKey +// AfsSyscall +// Alarm +// ArchPrctl +// Brk +// Capget +// Capset +// ClockGetres +// ClockGettime +// ClockNanosleep +// ClockSettime +// Clone +// CreateModule +// DeleteModule +// EpollCtlOld +// EpollPwait +// EpollWaitOld +// Eventfd +// Execve +// Fadvise64 +// Fgetxattr +// Flistxattr +// Flock +// Fork +// Fremovexattr +// Fsetxattr +// Futex +// GetKernelSyms +// GetMempolicy +// GetRobustList +// GetThreadArea +// Getitimer +// Getpmsg +// Getpriority +// Getxattr +// InotifyAddWatch +// InotifyInit +// InotifyRmWatch +// IoCancel +// IoDestroy +// IoGetevents +// IoSetup +// IoSubmit +// Ioctl +// IoprioGet +// IoprioSet +// KexecLoad +// Keyctl +// Lgetxattr +// Listxattr +// Llistxattr +// LookupDcookie +// Lremovexattr +// Lsetxattr +// Madvise +// Mbind +// MigratePages +// Mincore +// Mlock +// Mmap +// ModifyLdt +// Mount +// MovePages +// Mprotect +// MqGetsetattr +// MqNotify +// MqOpen +// MqTimedreceive +// MqTimedsend +// MqUnlink +// Mremap +// Msgctl +// Msgget +// Msgrcv +// Msgsnd +// Msync +// Munlock +// Munlockall +// Munmap +// Newfstatat +// Nfsservctl +// Personality +// Poll +// Ppoll +// Prctl +// Pselect6 +// Ptrace +// Putpmsg +// QueryModule +// Quotactl +// Readahead +// Readv +// Reboot +// RemapFilePages +// Removexattr +// RequestKey +// RestartSyscall +// RtSigaction +// RtSigpending +// RtSigprocmask +// RtSigqueueinfo +// RtSigreturn +// RtSigsuspend +// RtSigtimedwait +// SchedGetPriorityMax +// SchedGetPriorityMin +// SchedGetaffinity +// SchedGetparam +// SchedGetscheduler +// SchedRrGetInterval +// SchedSetaffinity +// SchedSetparam +// SchedYield +// Security +// Semctl +// Semget +// Semop +// Semtimedop +// Sendfile +// SetMempolicy +// SetRobustList +// SetThreadArea +// SetTidAddress +// Setpriority +// Setxattr +// Shmat +// Shmctl +// Shmdt +// Shmget +// Sigaltstack +// Signalfd +// Swapoff +// Swapon +// Sysfs +// TimerCreate +// TimerDelete +// TimerGetoverrun +// TimerGettime +// TimerSettime +// Timerfd +// Tkill (obsolete) +// Tuxcall +// Umount2 +// Uselib +// Utimensat +// Vfork +// Vhangup +// Vmsplice +// Vserver +// Waitid +// Writev +// _Sysctl diff --git a/src/pkg/syscall/syscall_linux_386.go b/src/pkg/syscall/syscall_linux_386.go new file mode 100644 index 000000000..9bf3f9cf0 --- /dev/null +++ b/src/pkg/syscall/syscall_linux_386.go @@ -0,0 +1,100 @@ +// Copyright 2009 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 syscall + +import ( + "syscall"; + "unsafe"; +) + +func Getpagesize() int { + return 4096 +} + +func TimespecToNsec(ts Timespec) int64 { + return int64(ts.Sec)*1e9 + int64(ts.Nsec); +} + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = int32(nsec / 1e9); + ts.Nsec = int32(nsec % 1e9); + return; +} + +func TimevalToNsec(tv Timeval) int64 { + return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3; +} + +func NsecToTimeval(nsec int64) (tv Timeval) { + nsec += 999; // round up to microsecond + tv.Sec = int32(nsec/1e9); + tv.Usec = int32(nsec%1e9 / 1e3); + return; +} + +// On x86 Linux, all the socket calls go through an extra indirection, +// I think because the 5-register system call interface can't handle +// the 6-argument calls like sendto and recvfrom. Instead the +// arguments to the underlying system call are the number below +// and a pointer to an array of uintptr. We hide the pointer in the +// socketcall assembly to avoid allocation on every system call. + +const ( + // see linux/net.h + _SOCKET = 1; + _BIND = 2; + _CONNECT = 3; + _LISTEN = 4; + _ACCEPT = 5; + _GETSOCKNAME = 6; + _GETPEERNAME = 7; + _SOCKETPAIR = 8; + _SEND = 9; + _RECV = 10; + _SENDTO = 11; + _RECVFROM = 12; + _SHUTDOWN = 13; + _SETSOCKOPT = 14; + _GETSOCKOPT = 15; + _SENDMSG = 16; + _RECVMSG = 17; +) + +func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int) + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) { + fd, errno = socketcall(_SOCKET, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0); + return; +} + +func bind(s int, addr uintptr, addrlen _Socklen) (errno int) { + var _ int; + _, errno = socketcall(_BIND, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0); + return; +} + +func connect(s int, addr uintptr, addrlen _Socklen) (errno int) { + var _ int; + _, errno = socketcall(_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0); + return; +} + +func socket(domain int, typ int, proto int) (fd int, errno int) { + fd, errno = socketcall(_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto), 0, 0, 0); + return; +} + +func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { + var _ int; + _, errno = socketcall(_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0); + return; +} + +func Listen(s int, n int) (errno int) { + var _ int; + _, errno = socketcall(_LISTEN, uintptr(s), uintptr(n), 0, 0, 0, 0); + return; +} + diff --git a/src/pkg/syscall/syscall_linux_amd64.go b/src/pkg/syscall/syscall_linux_amd64.go new file mode 100644 index 000000000..a2a58c35b --- /dev/null +++ b/src/pkg/syscall/syscall_linux_amd64.go @@ -0,0 +1,41 @@ +// Copyright 2009 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 syscall + +import "syscall" + +//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) +//sys bind(s int, addr uintptr, addrlen _Socklen) (errno int) +//sys connect(s int, addr uintptr, addrlen _Socklen) (errno int) +//sys socket(domain int, typ int, proto int) (fd int, errno int) +//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) +//sys Listen(s int, n int) (errno int) +//sys Shutdown(fd int, how int) (errno int) + +func Getpagesize() int { + return 4096 +} + +func TimespecToNsec(ts Timespec) int64 { + return int64(ts.Sec)*1e9 + int64(ts.Nsec); +} + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = nsec / 1e9; + ts.Nsec = nsec % 1e9; + return; +} + +func TimevalToNsec(tv Timeval) int64 { + return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3; +} + +func NsecToTimeval(nsec int64) (tv Timeval) { + nsec += 999; // round up to microsecond + tv.Sec = nsec/1e9; + tv.Usec = nsec%1e9 / 1e3; + return; +} + diff --git a/src/pkg/syscall/types_darwin.c b/src/pkg/syscall/types_darwin.c new file mode 100644 index 000000000..65afd6ca6 --- /dev/null +++ b/src/pkg/syscall/types_darwin.c @@ -0,0 +1,226 @@ +// Copyright 2009 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. + +/* +Input to godefs. See PORT. + */ + +#define __DARWIN_UNIX03 0 +#define KERNEL +#define _DARWIN_USE_64_BIT_INODE +#include <dirent.h> +#include <fcntl.h> +#include <mach/mach.h> +#include <mach/message.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <signal.h> +#include <stdio.h> +#include <sys/event.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/resource.h> +#include <sys/select.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/types.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <unistd.h> + +// Machine characteristics; for internal use. + +enum +{ + $sizeofPtr = sizeof(void*), + $sizeofShort = sizeof(short), + $sizeofInt = sizeof(int), + $sizeofLong = sizeof(long), + $sizeofLongLong = sizeof(long long), +}; + + +// Time + +typedef struct timespec $Timespec; +typedef struct timeval $Timeval; + +// Processes + +typedef struct rusage $Rusage; +typedef struct rlimit $Rlimit; + +typedef int $_C_int; +typedef gid_t $_Gid_t; + +// Files + +enum +{ + $O_RDONLY = O_RDONLY, + $O_WRONLY = O_WRONLY, + $O_RDWR = O_RDWR, + $O_APPEND = O_APPEND, + $O_ASYNC = O_ASYNC, + $O_CREAT = O_CREAT, + $O_NOCTTY = O_NOCTTY, + $O_NONBLOCK = O_NONBLOCK, + $O_SYNC = O_SYNC, + $O_TRUNC = O_TRUNC, + $O_CLOEXEC = 0, // not supported + + $F_GETFD = F_GETFD, + $F_SETFD = F_SETFD, + + $F_GETFL = F_GETFL, + $F_SETFL = F_SETFL, + + $FD_CLOEXEC = FD_CLOEXEC, + + $NAME_MAX = NAME_MAX +}; + +enum +{ // Directory mode bits + $S_IFMT = S_IFMT, + $S_IFIFO = S_IFIFO, + $S_IFCHR = S_IFCHR, + $S_IFDIR = S_IFDIR, + $S_IFBLK = S_IFBLK, + $S_IFREG = S_IFREG, + $S_IFLNK = S_IFLNK, + $S_IFSOCK = S_IFSOCK, + $S_IFWHT = S_IFWHT, + $S_ISUID = S_ISUID, + $S_ISGID = S_ISGID, + $S_ISVTX = S_ISVTX, + $S_IRUSR = S_IRUSR, + $S_IWUSR = S_IWUSR, + $S_IXUSR = S_IXUSR, +}; + +typedef struct stat64 $Stat_t; +typedef struct statfs64 $Statfs_t; + +typedef struct dirent $Dirent; + +// Wait status. + +enum +{ + $WNOHANG = WNOHANG, + $WUNTRACED = WUNTRACED, + $WEXITED = WEXITED, + $WSTOPPED = WSTOPPED, + $WCONTINUED = WCONTINUED, + $WNOWAIT = WNOWAIT, +}; + +// Sockets + +enum +{ + $AF_UNIX = AF_UNIX, + $AF_INET = AF_INET, + $AF_DATAKIT = AF_DATAKIT, + $AF_INET6 = AF_INET6, + + $SOCK_STREAM = SOCK_STREAM, + $SOCK_DGRAM = SOCK_DGRAM, + $SOCK_RAW = SOCK_RAW, + $SOCK_SEQPACKET = SOCK_SEQPACKET, + + $SOL_SOCKET = SOL_SOCKET, + + $SO_REUSEADDR = SO_REUSEADDR, + $SO_KEEPALIVE = SO_KEEPALIVE, + $SO_DONTROUTE = SO_DONTROUTE, + $SO_BROADCAST = SO_BROADCAST, + $SO_USELOOPBACK = SO_USELOOPBACK, + $SO_LINGER = SO_LINGER, + $SO_REUSEPORT = SO_REUSEPORT, + $SO_SNDBUF = SO_SNDBUF, + $SO_RCVBUF = SO_RCVBUF, + $SO_SNDTIMEO = SO_SNDTIMEO, + $SO_RCVTIMEO = SO_RCVTIMEO, + $SO_NOSIGPIPE = SO_NOSIGPIPE, + + $IPPROTO_TCP = IPPROTO_TCP, + $IPPROTO_UDP = IPPROTO_UDP, + + $TCP_NODELAY = TCP_NODELAY, + + $SOMAXCONN = SOMAXCONN +}; + +typedef struct sockaddr_in $RawSockaddrInet4; +typedef struct sockaddr_in6 $RawSockaddrInet6; +typedef struct sockaddr_un $RawSockaddrUnix; +typedef struct sockaddr $RawSockaddr; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +enum { + $SizeofSockaddrInet4 = sizeof(struct sockaddr_in), + $SizeofSockaddrInet6 = sizeof(struct sockaddr_in6), + $SizeofSockaddrAny = sizeof(struct sockaddr_any), + $SizeofSockaddrUnix = sizeof(struct sockaddr_un), +}; + +typedef struct sockaddr_any $RawSockaddrAny; +typedef socklen_t $_Socklen; +typedef struct linger $Linger; + +// Events (kqueue, kevent) + +enum { + // filters + $EVFILT_READ = EVFILT_READ, + $EVFILT_WRITE = EVFILT_WRITE, + $EVFILT_AIO = EVFILT_AIO, + $EVFILT_VNODE = EVFILT_VNODE, + $EVFILT_PROC = EVFILT_PROC, + $EVFILT_SIGNAL = EVFILT_SIGNAL, + $EVFILT_TIMER = EVFILT_TIMER, + $EVFILT_MACHPORT = EVFILT_MACHPORT, + $EVFILT_FS = EVFILT_FS, + + $EVFILT_SYSCOUNT = EVFILT_SYSCOUNT, + + // actions + $EV_ADD = EV_ADD, + $EV_DELETE = EV_DELETE, + $EV_DISABLE = EV_DISABLE, + $EV_RECEIPT = EV_RECEIPT, + + // flags + $EV_ONESHOT = EV_ONESHOT, + $EV_CLEAR = EV_CLEAR, + $EV_SYSFLAGS = EV_SYSFLAGS, + $EV_FLAG0 = EV_FLAG0, + $EV_FLAG1 = EV_FLAG1, + + // returned values + $EV_EOF = EV_EOF, + $EV_ERROR = EV_ERROR, +}; + +typedef struct kevent $Kevent_t; + +// Select + +typedef fd_set $FdSet; diff --git a/src/pkg/syscall/types_darwin_386.c b/src/pkg/syscall/types_darwin_386.c new file mode 100644 index 000000000..dd5356944 --- /dev/null +++ b/src/pkg/syscall/types_darwin_386.c @@ -0,0 +1 @@ +// Nothing to see here. diff --git a/src/pkg/syscall/types_darwin_amd64.c b/src/pkg/syscall/types_darwin_amd64.c new file mode 100644 index 000000000..dd5356944 --- /dev/null +++ b/src/pkg/syscall/types_darwin_amd64.c @@ -0,0 +1 @@ +// Nothing to see here. diff --git a/src/pkg/syscall/types_linux.c b/src/pkg/syscall/types_linux.c new file mode 100644 index 000000000..261772eac --- /dev/null +++ b/src/pkg/syscall/types_linux.c @@ -0,0 +1,217 @@ +// Copyright 2009 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. + +/* +Input to godefs. See PORT. + */ + +#define __DARWIN_UNIX03 0 +#define KERNEL + +#include <dirent.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <signal.h> +#include <stdio.h> +#include <sys/epoll.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/resource.h> +#include <sys/select.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <sys/sysinfo.h> +#include <sys/time.h> +#include <sys/times.h> +#include <sys/timex.h> +#include <sys/types.h> +#include <sys/un.h> +#include <sys/utsname.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> +#include <ustat.h> +#include <utime.h> + +// Machine characteristics; for internal use. + +enum +{ + $sizeofPtr = sizeof(void*), + $sizeofShort = sizeof(short), + $sizeofInt = sizeof(int), + $sizeofLong = sizeof(long), + $sizeofLongLong = sizeof(long long), + $PathMax = PATH_MAX, +}; + + +// Time + +typedef struct timespec $Timespec; +typedef struct timeval $Timeval; +typedef struct timex $Timex; +typedef time_t $Time_t; +typedef struct tms $Tms; +typedef struct utimbuf $Utimbuf; + +// Processes + +typedef struct rusage $Rusage; +typedef struct rlimit $Rlimit; + +typedef int $_C_int; +typedef gid_t $_Gid_t; + +// Files + +enum +{ + $O_RDONLY = O_RDONLY, + $O_WRONLY = O_WRONLY, + $O_RDWR = O_RDWR, + $O_APPEND = O_APPEND, + $O_ASYNC = O_ASYNC, + $O_CREAT = O_CREAT, + $O_NOCTTY = O_NOCTTY, + $O_NONBLOCK = O_NONBLOCK, + $O_SYNC = O_SYNC, + $O_TRUNC = O_TRUNC, + $O_CLOEXEC = 0, // not supported + + $F_GETFD = F_GETFD, + $F_SETFD = F_SETFD, + + $F_GETFL = F_GETFL, + $F_SETFL = F_SETFL, + + $FD_CLOEXEC = FD_CLOEXEC, + + $NAME_MAX = NAME_MAX +}; + +enum +{ // Directory mode bits + $S_IFMT = S_IFMT, + $S_IFIFO = S_IFIFO, + $S_IFCHR = S_IFCHR, + $S_IFDIR = S_IFDIR, + $S_IFBLK = S_IFBLK, + $S_IFREG = S_IFREG, + $S_IFLNK = S_IFLNK, + $S_IFSOCK = S_IFSOCK, + $S_ISUID = S_ISUID, + $S_ISGID = S_ISGID, + $S_ISVTX = S_ISVTX, + $S_IRUSR = S_IRUSR, + $S_IWUSR = S_IWUSR, + $S_IXUSR = S_IXUSR, +}; + +typedef struct stat $Stat_t; +typedef struct statfs $Statfs_t; + +typedef struct dirent $Dirent; + +// Wait status. + +enum +{ + $WNOHANG = WNOHANG, + $WUNTRACED = WUNTRACED, + $WEXITED = WEXITED, + $WSTOPPED = WSTOPPED, + $WCONTINUED = WCONTINUED, + $WNOWAIT = WNOWAIT, +}; + +// Sockets + +enum +{ + $AF_UNIX = AF_UNIX, + $AF_INET = AF_INET, + $AF_INET6 = AF_INET6, + + $SOCK_STREAM = SOCK_STREAM, + $SOCK_DGRAM = SOCK_DGRAM, + $SOCK_RAW = SOCK_RAW, + $SOCK_SEQPACKET = SOCK_SEQPACKET, + + $SOL_SOCKET = SOL_SOCKET, + + $SO_REUSEADDR = SO_REUSEADDR, + $SO_KEEPALIVE = SO_KEEPALIVE, + $SO_DONTROUTE = SO_DONTROUTE, + $SO_BROADCAST = SO_BROADCAST, + $SO_LINGER = SO_LINGER, + $SO_SNDBUF = SO_SNDBUF, + $SO_RCVBUF = SO_RCVBUF, + $SO_SNDTIMEO = SO_SNDTIMEO, + $SO_RCVTIMEO = SO_RCVTIMEO, + + $IPPROTO_TCP = IPPROTO_TCP, + $IPPROTO_UDP = IPPROTO_UDP, + + $TCP_NODELAY = TCP_NODELAY, + + $SOMAXCONN = SOMAXCONN +}; + +typedef struct sockaddr_in $RawSockaddrInet4; +typedef struct sockaddr_in6 $RawSockaddrInet6; +typedef struct sockaddr_un $RawSockaddrUnix; +typedef struct sockaddr $RawSockaddr; + +union sockaddr_all { + struct sockaddr s1; // this one gets used for fields + struct sockaddr_in s2; // these pad it out + struct sockaddr_in6 s3; +}; + +struct sockaddr_any { + struct sockaddr addr; + char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)]; +}; + +enum { + $SizeofSockaddrInet4 = sizeof(struct sockaddr_in), + $SizeofSockaddrInet6 = sizeof(struct sockaddr_in6), + $SizeofSockaddrAny = sizeof(struct sockaddr_any), + $SizeofSockaddrUnix = sizeof(struct sockaddr_un), +}; + +typedef struct sockaddr_any $RawSockaddrAny; +typedef socklen_t $_Socklen; +typedef struct linger $Linger; + +// Misc + +enum { + $EPOLLIN = EPOLLIN, + $EPOLLRDHUP = EPOLLRDHUP, + $EPOLLOUT = EPOLLOUT, + $EPOLLONESHOT = EPOLLONESHOT, + $EPOLL_CTL_MOD = EPOLL_CTL_MOD, + $EPOLL_CTL_ADD = EPOLL_CTL_ADD, + $EPOLL_CTL_DEL = EPOLL_CTL_DEL, +}; + +typedef fd_set $FdSet; +typedef struct sysinfo $Sysinfo_t; +typedef struct utsname $Utsname; +typedef struct ustat $Ustat_t; + +// The real epoll_event is a union, and godefs doesn't handle it well. +struct my_epoll_event { + uint32_t events; + int32_t fd; + int32_t pad; +}; + +typedef struct my_epoll_event $EpollEvent; diff --git a/src/pkg/syscall/types_linux_386.c b/src/pkg/syscall/types_linux_386.c new file mode 100644 index 000000000..3b5481af4 --- /dev/null +++ b/src/pkg/syscall/types_linux_386.c @@ -0,0 +1 @@ +// Nothing to see here diff --git a/src/pkg/syscall/types_linux_amd64.c b/src/pkg/syscall/types_linux_amd64.c new file mode 100644 index 000000000..3b5481af4 --- /dev/null +++ b/src/pkg/syscall/types_linux_amd64.c @@ -0,0 +1 @@ +// Nothing to see here diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go new file mode 100644 index 000000000..bc2d17656 --- /dev/null +++ b/src/pkg/syscall/zerrors_darwin_386.go @@ -0,0 +1,260 @@ +// mkerrors +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// godefs -gsyscall _errors.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + EMULTIHOP = 0x5f; + EAFNOSUPPORT = 0x2f; + EACCES = 0xd; + EDESTADDRREQ = 0x27; + EILSEQ = 0x5c; + ESPIPE = 0x1d; + EMLINK = 0x1f; + EPROGUNAVAIL = 0x4a; + ENOTTY = 0x19; + EBADF = 0x9; + ERANGE = 0x22; + ECANCELED = 0x59; + ETXTBSY = 0x1a; + ENOMEM = 0xc; + EINPROGRESS = 0x24; + ENOTEMPTY = 0x42; + ENOTBLK = 0xf; + EPROTOTYPE = 0x29; + ENOMSG = 0x5b; + ERPCMISMATCH = 0x49; + ENOTDIR = 0x14; + EALREADY = 0x25; + ETIMEDOUT = 0x3c; + ENEEDAUTH = 0x51; + ENODATA = 0x60; + EINTR = 0x4; + ENOLINK = 0x61; + EPERM = 0x1; + ENETDOWN = 0x32; + ESTALE = 0x46; + ENOTSOCK = 0x26; + ENOSR = 0x62; + EAUTH = 0x50; + ECHILD = 0xa; + EPIPE = 0x20; + ENOATTR = 0x5d; + EBADMSG = 0x5e; + EREMOTE = 0x47; + ETOOMANYREFS = 0x3b; + EPFNOSUPPORT = 0x2e; + EPROCUNAVAIL = 0x4c; + EADDRINUSE = 0x30; + ENETRESET = 0x34; + EISDIR = 0x15; + EIDRM = 0x5a; + EDEVERR = 0x53; + EINVAL = 0x16; + ESHUTDOWN = 0x3a; + EPWROFF = 0x52; + EOVERFLOW = 0x54; + EBUSY = 0x10; + EPROCLIM = 0x43; + EPROTO = 0x64; + ENODEV = 0x13; + EROFS = 0x1e; + E2BIG = 0x7; + EDEADLK = 0xb; + ECONNRESET = 0x36; + EBADMACHO = 0x58; + ENXIO = 0x6; + EBADRPC = 0x48; + ENAMETOOLONG = 0x3f; + ELAST = 0x67; + ESOCKTNOSUPPORT = 0x2c; + EADDRNOTAVAIL = 0x31; + ETIME = 0x65; + EPROTONOSUPPORT = 0x2b; + EIO = 0x5; + ENETUNREACH = 0x33; + EXDEV = 0x12; + EDQUOT = 0x45; + ENOSPC = 0x1c; + ENOEXEC = 0x8; + EMSGSIZE = 0x28; + EFTYPE = 0x4f; + EDOM = 0x21; + ENOSTR = 0x63; + EFBIG = 0x1b; + ESRCH = 0x3; + EHOSTDOWN = 0x40; + ENOLCK = 0x4d; + ENFILE = 0x17; + ENOSYS = 0x4e; + EBADARCH = 0x56; + ENOTCONN = 0x39; + ENOTSUP = 0x2d; + ECONNABORTED = 0x35; + EISCONN = 0x38; + ESHLIBVERS = 0x57; + EUSERS = 0x44; + ENOPROTOOPT = 0x2a; + EMFILE = 0x18; + ELOOP = 0x3e; + ENOBUFS = 0x37; + EFAULT = 0xe; + EWOULDBLOCK = 0x23; + EBADEXEC = 0x55; + ENOPOLICY = 0x67; + ECONNREFUSED = 0x3d; + EAGAIN = 0x23; + EEXIST = 0x11; + EPROGMISMATCH = 0x4b; + ENOENT = 0x2; + EHOSTUNREACH = 0x41; + EOPNOTSUPP = 0x66; + SIGBUS = 0xa; + SIGTTIN = 0x15; + SIGPROF = 0x1b; + SIGFPE = 0x8; + SIGHUP = 0x1; + SIGTTOU = 0x16; + SIGUSR1 = 0x1e; + SIGURG = 0x10; + SIGQUIT = 0x3; + SIGIO = 0x17; + SIGABRT = 0x6; + SIGINFO = 0x1d; + SIGUSR2 = 0x1f; + SIGTRAP = 0x5; + SIGVTALRM = 0x1a; + SIGSEGV = 0xb; + SIGCONT = 0x13; + SIGPIPE = 0xd; + SIGXFSZ = 0x19; + SIGCHLD = 0x14; + SIGSYS = 0xc; + SIGSTOP = 0x11; + SIGALRM = 0xe; + SIGTSTP = 0x12; + SIGEMT = 0x7; + SIGKILL = 0x9; + SIGXCPU = 0x18; + SIGILL = 0x4; + SIGINT = 0x2; + SIGIOT = 0x6; + SIGTERM = 0xf; + SIGWINCH = 0x1c; +) + +// Types + + +// Error table +var errors = [...]string { + 95: "EMULTIHOP (Reserved)", + 47: "address family not supported by protocol family", + 13: "permission denied", + 39: "destination address required", + 92: "illegal byte sequence", + 29: "illegal seek", + 31: "too many links", + 74: "RPC prog. not avail", + 25: "inappropriate ioctl for device", + 9: "bad file descriptor", + 34: "result too large", + 89: "operation canceled", + 26: "text file busy", + 12: "cannot allocate memory", + 36: "operation now in progress", + 66: "directory not empty", + 15: "block device required", + 41: "protocol wrong type for socket", + 91: "no message of desired type", + 73: "RPC version wrong", + 20: "not a directory", + 37: "operation already in progress", + 60: "operation timed out", + 81: "need authenticator", + 96: "no message available on STREAM", + 4: "interrupted system call", + 97: "ENOLINK (Reserved)", + 1: "operation not permitted", + 50: "network is down", + 70: "stale NFS file handle", + 38: "socket operation on non-socket", + 98: "no STREAM resources", + 80: "authentication error", + 10: "no child processes", + 32: "broken pipe", + 93: "attribute not found", + 94: "bad message", + 71: "too many levels of remote in path", + 59: "too many references: can't splice", + 46: "protocol family not supported", + 76: "bad procedure for program", + 48: "address already in use", + 52: "network dropped connection on reset", + 21: "is a directory", + 90: "identifier removed", + 83: "device error", + 22: "invalid argument", + 58: "can't send after socket shutdown", + 82: "device power is off", + 84: "value too large to be stored in data type", + 16: "resource busy", + 67: "too many processes", + 100: "protocol error", + 19: "operation not supported by device", + 30: "read-only file system", + 7: "argument list too long", + 11: "resource deadlock avoided", + 54: "connection reset by peer", + 88: "malformed Mach-o file", + 6: "device not configured", + 72: "RPC struct is bad", + 63: "file name too long", + 103: "policy not found", + 44: "socket type not supported", + 49: "can't assign requested address", + 101: "STREAM ioctl timeout", + 43: "protocol not supported", + 5: "input/output error", + 51: "network is unreachable", + 18: "cross-device link", + 69: "disc quota exceeded", + 28: "no space left on device", + 8: "exec format error", + 40: "message too long", + 79: "inappropriate file type or format", + 33: "numerical argument out of domain", + 99: "not a STREAM", + 27: "file too large", + 3: "no such process", + 64: "host is down", + 77: "no locks available", + 23: "too many open files in system", + 78: "function not implemented", + 86: "bad CPU type in executable", + 57: "socket is not connected", + 45: "operation not supported", + 53: "software caused connection abort", + 56: "socket is already connected", + 87: "shared library version mismatch", + 68: "too many users", + 42: "protocol not available", + 24: "too many open files", + 62: "too many levels of symbolic links", + 55: "no buffer space available", + 14: "bad address", + 35: "resource temporarily unavailable", + 85: "bad executable (or shared library)", + 61: "connection refused", + 17: "file exists", + 75: "program version wrong", + 2: "no such file or directory", + 65: "no route to host", + 102: "operation not supported on socket", +} + diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go new file mode 100644 index 000000000..bc2d17656 --- /dev/null +++ b/src/pkg/syscall/zerrors_darwin_amd64.go @@ -0,0 +1,260 @@ +// mkerrors +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// godefs -gsyscall _errors.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + EMULTIHOP = 0x5f; + EAFNOSUPPORT = 0x2f; + EACCES = 0xd; + EDESTADDRREQ = 0x27; + EILSEQ = 0x5c; + ESPIPE = 0x1d; + EMLINK = 0x1f; + EPROGUNAVAIL = 0x4a; + ENOTTY = 0x19; + EBADF = 0x9; + ERANGE = 0x22; + ECANCELED = 0x59; + ETXTBSY = 0x1a; + ENOMEM = 0xc; + EINPROGRESS = 0x24; + ENOTEMPTY = 0x42; + ENOTBLK = 0xf; + EPROTOTYPE = 0x29; + ENOMSG = 0x5b; + ERPCMISMATCH = 0x49; + ENOTDIR = 0x14; + EALREADY = 0x25; + ETIMEDOUT = 0x3c; + ENEEDAUTH = 0x51; + ENODATA = 0x60; + EINTR = 0x4; + ENOLINK = 0x61; + EPERM = 0x1; + ENETDOWN = 0x32; + ESTALE = 0x46; + ENOTSOCK = 0x26; + ENOSR = 0x62; + EAUTH = 0x50; + ECHILD = 0xa; + EPIPE = 0x20; + ENOATTR = 0x5d; + EBADMSG = 0x5e; + EREMOTE = 0x47; + ETOOMANYREFS = 0x3b; + EPFNOSUPPORT = 0x2e; + EPROCUNAVAIL = 0x4c; + EADDRINUSE = 0x30; + ENETRESET = 0x34; + EISDIR = 0x15; + EIDRM = 0x5a; + EDEVERR = 0x53; + EINVAL = 0x16; + ESHUTDOWN = 0x3a; + EPWROFF = 0x52; + EOVERFLOW = 0x54; + EBUSY = 0x10; + EPROCLIM = 0x43; + EPROTO = 0x64; + ENODEV = 0x13; + EROFS = 0x1e; + E2BIG = 0x7; + EDEADLK = 0xb; + ECONNRESET = 0x36; + EBADMACHO = 0x58; + ENXIO = 0x6; + EBADRPC = 0x48; + ENAMETOOLONG = 0x3f; + ELAST = 0x67; + ESOCKTNOSUPPORT = 0x2c; + EADDRNOTAVAIL = 0x31; + ETIME = 0x65; + EPROTONOSUPPORT = 0x2b; + EIO = 0x5; + ENETUNREACH = 0x33; + EXDEV = 0x12; + EDQUOT = 0x45; + ENOSPC = 0x1c; + ENOEXEC = 0x8; + EMSGSIZE = 0x28; + EFTYPE = 0x4f; + EDOM = 0x21; + ENOSTR = 0x63; + EFBIG = 0x1b; + ESRCH = 0x3; + EHOSTDOWN = 0x40; + ENOLCK = 0x4d; + ENFILE = 0x17; + ENOSYS = 0x4e; + EBADARCH = 0x56; + ENOTCONN = 0x39; + ENOTSUP = 0x2d; + ECONNABORTED = 0x35; + EISCONN = 0x38; + ESHLIBVERS = 0x57; + EUSERS = 0x44; + ENOPROTOOPT = 0x2a; + EMFILE = 0x18; + ELOOP = 0x3e; + ENOBUFS = 0x37; + EFAULT = 0xe; + EWOULDBLOCK = 0x23; + EBADEXEC = 0x55; + ENOPOLICY = 0x67; + ECONNREFUSED = 0x3d; + EAGAIN = 0x23; + EEXIST = 0x11; + EPROGMISMATCH = 0x4b; + ENOENT = 0x2; + EHOSTUNREACH = 0x41; + EOPNOTSUPP = 0x66; + SIGBUS = 0xa; + SIGTTIN = 0x15; + SIGPROF = 0x1b; + SIGFPE = 0x8; + SIGHUP = 0x1; + SIGTTOU = 0x16; + SIGUSR1 = 0x1e; + SIGURG = 0x10; + SIGQUIT = 0x3; + SIGIO = 0x17; + SIGABRT = 0x6; + SIGINFO = 0x1d; + SIGUSR2 = 0x1f; + SIGTRAP = 0x5; + SIGVTALRM = 0x1a; + SIGSEGV = 0xb; + SIGCONT = 0x13; + SIGPIPE = 0xd; + SIGXFSZ = 0x19; + SIGCHLD = 0x14; + SIGSYS = 0xc; + SIGSTOP = 0x11; + SIGALRM = 0xe; + SIGTSTP = 0x12; + SIGEMT = 0x7; + SIGKILL = 0x9; + SIGXCPU = 0x18; + SIGILL = 0x4; + SIGINT = 0x2; + SIGIOT = 0x6; + SIGTERM = 0xf; + SIGWINCH = 0x1c; +) + +// Types + + +// Error table +var errors = [...]string { + 95: "EMULTIHOP (Reserved)", + 47: "address family not supported by protocol family", + 13: "permission denied", + 39: "destination address required", + 92: "illegal byte sequence", + 29: "illegal seek", + 31: "too many links", + 74: "RPC prog. not avail", + 25: "inappropriate ioctl for device", + 9: "bad file descriptor", + 34: "result too large", + 89: "operation canceled", + 26: "text file busy", + 12: "cannot allocate memory", + 36: "operation now in progress", + 66: "directory not empty", + 15: "block device required", + 41: "protocol wrong type for socket", + 91: "no message of desired type", + 73: "RPC version wrong", + 20: "not a directory", + 37: "operation already in progress", + 60: "operation timed out", + 81: "need authenticator", + 96: "no message available on STREAM", + 4: "interrupted system call", + 97: "ENOLINK (Reserved)", + 1: "operation not permitted", + 50: "network is down", + 70: "stale NFS file handle", + 38: "socket operation on non-socket", + 98: "no STREAM resources", + 80: "authentication error", + 10: "no child processes", + 32: "broken pipe", + 93: "attribute not found", + 94: "bad message", + 71: "too many levels of remote in path", + 59: "too many references: can't splice", + 46: "protocol family not supported", + 76: "bad procedure for program", + 48: "address already in use", + 52: "network dropped connection on reset", + 21: "is a directory", + 90: "identifier removed", + 83: "device error", + 22: "invalid argument", + 58: "can't send after socket shutdown", + 82: "device power is off", + 84: "value too large to be stored in data type", + 16: "resource busy", + 67: "too many processes", + 100: "protocol error", + 19: "operation not supported by device", + 30: "read-only file system", + 7: "argument list too long", + 11: "resource deadlock avoided", + 54: "connection reset by peer", + 88: "malformed Mach-o file", + 6: "device not configured", + 72: "RPC struct is bad", + 63: "file name too long", + 103: "policy not found", + 44: "socket type not supported", + 49: "can't assign requested address", + 101: "STREAM ioctl timeout", + 43: "protocol not supported", + 5: "input/output error", + 51: "network is unreachable", + 18: "cross-device link", + 69: "disc quota exceeded", + 28: "no space left on device", + 8: "exec format error", + 40: "message too long", + 79: "inappropriate file type or format", + 33: "numerical argument out of domain", + 99: "not a STREAM", + 27: "file too large", + 3: "no such process", + 64: "host is down", + 77: "no locks available", + 23: "too many open files in system", + 78: "function not implemented", + 86: "bad CPU type in executable", + 57: "socket is not connected", + 45: "operation not supported", + 53: "software caused connection abort", + 56: "socket is already connected", + 87: "shared library version mismatch", + 68: "too many users", + 42: "protocol not available", + 24: "too many open files", + 62: "too many levels of symbolic links", + 55: "no buffer space available", + 14: "bad address", + 35: "resource temporarily unavailable", + 85: "bad executable (or shared library)", + 61: "connection refused", + 17: "file exists", + 75: "program version wrong", + 2: "no such file or directory", + 65: "no route to host", + 102: "operation not supported on socket", +} + diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go new file mode 100644 index 000000000..f1e7e011d --- /dev/null +++ b/src/pkg/syscall/zerrors_linux_386.go @@ -0,0 +1,316 @@ +// mkerrors +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// godefs -gsyscall _errors.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + EMULTIHOP = 0x48; + EUNATCH = 0x31; + EAFNOSUPPORT = 0x61; + EREMCHG = 0x4e; + EACCES = 0xd; + EL3RST = 0x2f; + EDESTADDRREQ = 0x59; + EILSEQ = 0x54; + ESPIPE = 0x1d; + EMLINK = 0x1f; + EOWNERDEAD = 0x82; + ENOTTY = 0x19; + EBADE = 0x34; + EBADF = 0x9; + EBADR = 0x35; + EADV = 0x44; + ERANGE = 0x22; + ECANCELED = 0x7d; + ETXTBSY = 0x1a; + ENOMEM = 0xc; + EINPROGRESS = 0x73; + ENOTBLK = 0xf; + EPROTOTYPE = 0x5b; + ERESTART = 0x55; + EISNAM = 0x78; + ENOMSG = 0x2a; + EALREADY = 0x72; + ETIMEDOUT = 0x6e; + ENODATA = 0x3d; + EINTR = 0x4; + ENOLINK = 0x43; + EPERM = 0x1; + ELOOP = 0x28; + ENETDOWN = 0x64; + ESTALE = 0x74; + ENOTSOCK = 0x58; + ENOSR = 0x3f; + ECHILD = 0xa; + ELNRNG = 0x30; + EPIPE = 0x20; + EBADMSG = 0x4a; + EBFONT = 0x3b; + EREMOTE = 0x42; + ETOOMANYREFS = 0x6d; + EPFNOSUPPORT = 0x60; + ENONET = 0x40; + EXFULL = 0x36; + EBADSLT = 0x39; + ENOTNAM = 0x76; + ELIBEXEC = 0x53; + ENOCSI = 0x32; + ENOTEMPTY = 0x27; + EADDRINUSE = 0x62; + ENETRESET = 0x66; + EISDIR = 0x15; + EIDRM = 0x2b; + ECOMM = 0x46; + EBADFD = 0x4d; + EL2HLT = 0x33; + ENOKEY = 0x7e; + EINVAL = 0x16; + ESHUTDOWN = 0x6c; + EKEYREJECTED = 0x81; + ELIBSCN = 0x51; + ENAVAIL = 0x77; + ENOSTR = 0x3c; + EOVERFLOW = 0x4b; + EUCLEAN = 0x75; + ENOMEDIUM = 0x7b; + EBUSY = 0x10; + EPROTO = 0x47; + ENODEV = 0x13; + EKEYEXPIRED = 0x7f; + EROFS = 0x1e; + ELIBACC = 0x4f; + E2BIG = 0x7; + EDEADLK = 0x23; + ECONNRESET = 0x68; + ENXIO = 0x6; + EBADRQC = 0x38; + ENAMETOOLONG = 0x24; + ESOCKTNOSUPPORT = 0x5e; + EDOTDOT = 0x49; + EADDRNOTAVAIL = 0x63; + ETIME = 0x3e; + EPROTONOSUPPORT = 0x5d; + ENOTRECOVERABLE = 0x83; + EIO = 0x5; + ENETUNREACH = 0x65; + EXDEV = 0x12; + EDQUOT = 0x7a; + EREMOTEIO = 0x79; + ENOSPC = 0x1c; + ENOEXEC = 0x8; + EMSGSIZE = 0x5a; + EDOM = 0x21; + EFBIG = 0x1b; + ESRCH = 0x3; + ECHRNG = 0x2c; + EHOSTDOWN = 0x70; + ENOLCK = 0x25; + ENFILE = 0x17; + ENOSYS = 0x26; + ENOTCONN = 0x6b; + ENOTSUP = 0x5f; + ESRMNT = 0x45; + EDEADLOCK = 0x23; + ECONNABORTED = 0x67; + ENOANO = 0x37; + EISCONN = 0x6a; + EUSERS = 0x57; + ENOPROTOOPT = 0x5c; + EMFILE = 0x18; + ENOBUFS = 0x69; + EL3HLT = 0x2e; + EFAULT = 0xe; + EWOULDBLOCK = 0xb; + ELIBBAD = 0x50; + ESTRPIPE = 0x56; + ECONNREFUSED = 0x6f; + EAGAIN = 0xb; + ELIBMAX = 0x52; + EEXIST = 0x11; + EL2NSYNC = 0x2d; + ENOENT = 0x2; + ENOPKG = 0x41; + EKEYREVOKED = 0x80; + EHOSTUNREACH = 0x71; + ENOTUNIQ = 0x4c; + EOPNOTSUPP = 0x5f; + ENOTDIR = 0x14; + EMEDIUMTYPE = 0x7c; + SIGBUS = 0x7; + SIGTTIN = 0x15; + SIGPROF = 0x1b; + SIGFPE = 0x8; + SIGHUP = 0x1; + SIGTTOU = 0x16; + SIGSTKFLT = 0x10; + SIGUSR1 = 0xa; + SIGURG = 0x17; + SIGQUIT = 0x3; + SIGCLD = 0x11; + SIGIO = 0x1d; + SIGABRT = 0x6; + SIGUSR2 = 0xc; + SIGTRAP = 0x5; + SIGVTALRM = 0x1a; + SIGPOLL = 0x1d; + SIGSEGV = 0xb; + SIGCONT = 0x12; + SIGPIPE = 0xd; + SIGWINCH = 0x1c; + SIGXFSZ = 0x19; + SIGCHLD = 0x11; + SIGSYS = 0x1f; + SIGSTOP = 0x13; + SIGALRM = 0xe; + SIGTSTP = 0x14; + SIGKILL = 0x9; + SIGXCPU = 0x18; + SIGUNUSED = 0x1f; + SIGPWR = 0x1e; + SIGILL = 0x4; + SIGINT = 0x2; + SIGIOT = 0x6; + SIGTERM = 0xf; +) + +// Types + + +// Error table +var errors = [...]string { + 72: "multihop attempted", + 49: "protocol driver not attached", + 97: "address family not supported by protocol", + 78: "remote address changed", + 13: "permission denied", + 47: "level 3 reset", + 89: "destination address required", + 84: "invalid or incomplete multibyte or wide character", + 29: "illegal seek", + 31: "too many links", + 130: "owner died", + 25: "inappropriate ioctl for device", + 52: "invalid exchange", + 9: "bad file descriptor", + 53: "invalid request descriptor", + 68: "advertise error", + 34: "numerical result out of range", + 125: "operation canceled", + 26: "text file busy", + 12: "cannot allocate memory", + 115: "operation now in progress", + 15: "block device required", + 91: "protocol wrong type for socket", + 85: "interrupted system call should be restarted", + 120: "is a named type file", + 42: "no message of desired type", + 114: "operation already in progress", + 110: "connection timed out", + 61: "no data available", + 4: "interrupted system call", + 67: "link has been severed", + 1: "operation not permitted", + 40: "too many levels of symbolic links", + 100: "network is down", + 116: "stale NFS file handle", + 88: "socket operation on non-socket", + 63: "out of streams resources", + 10: "no child processes", + 48: "link number out of range", + 32: "broken pipe", + 74: "bad message", + 59: "bad font file format", + 66: "object is remote", + 109: "too many references: cannot splice", + 96: "protocol family not supported", + 64: "machine is not on the network", + 54: "exchange full", + 57: "invalid slot", + 118: "not a XENIX named type file", + 83: "cannot exec a shared library directly", + 50: "no CSI structure available", + 39: "directory not empty", + 98: "address already in use", + 102: "network dropped connection on reset", + 21: "is a directory", + 43: "identifier removed", + 70: "communication error on send", + 77: "file descriptor in bad state", + 51: "level 2 halted", + 126: "required key not available", + 22: "invalid argument", + 108: "cannot send after transport endpoint shutdown", + 129: "key was rejected by service", + 81: ".lib section in a.out corrupted", + 119: "no XENIX semaphores available", + 60: "device not a stream", + 75: "value too large for defined data type", + 117: "structure needs cleaning", + 123: "no medium found", + 16: "device or resource busy", + 71: "protocol error", + 19: "no such device", + 127: "key has expired", + 30: "read-only file system", + 79: "can not access a needed shared library", + 7: "argument list too long", + 35: "resource deadlock avoided", + 104: "connection reset by peer", + 6: "no such device or address", + 56: "invalid request code", + 36: "file name too long", + 94: "socket type not supported", + 73: "RFS specific error", + 99: "cannot assign requested address", + 62: "timer expired", + 93: "protocol not supported", + 131: "state not recoverable", + 5: "input/output error", + 101: "network is unreachable", + 18: "invalid cross-device link", + 122: "disk quota exceeded", + 121: "remote I/O error", + 28: "no space left on device", + 8: "exec format error", + 90: "message too long", + 33: "numerical argument out of domain", + 27: "file too large", + 3: "no such process", + 44: "channel number out of range", + 112: "host is down", + 37: "no locks available", + 23: "too many open files in system", + 38: "function not implemented", + 107: "transport endpoint is not connected", + 95: "operation not supported", + 69: "srmount error", + 103: "software caused connection abort", + 55: "no anode", + 106: "transport endpoint is already connected", + 87: "too many users", + 92: "protocol not available", + 24: "too many open files", + 105: "no buffer space available", + 46: "level 3 halted", + 14: "bad address", + 11: "resource temporarily unavailable", + 80: "accessing a corrupted shared library", + 86: "streams pipe error", + 111: "connection refused", + 82: "attempting to link in too many shared libraries", + 17: "file exists", + 45: "level 2 not synchronized", + 2: "no such file or directory", + 65: "package not installed", + 128: "key has been revoked", + 113: "no route to host", + 76: "name not unique on network", + 20: "not a directory", + 124: "wrong medium type", +} + diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go new file mode 100644 index 000000000..f1e7e011d --- /dev/null +++ b/src/pkg/syscall/zerrors_linux_amd64.go @@ -0,0 +1,316 @@ +// mkerrors +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// godefs -gsyscall _errors.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + EMULTIHOP = 0x48; + EUNATCH = 0x31; + EAFNOSUPPORT = 0x61; + EREMCHG = 0x4e; + EACCES = 0xd; + EL3RST = 0x2f; + EDESTADDRREQ = 0x59; + EILSEQ = 0x54; + ESPIPE = 0x1d; + EMLINK = 0x1f; + EOWNERDEAD = 0x82; + ENOTTY = 0x19; + EBADE = 0x34; + EBADF = 0x9; + EBADR = 0x35; + EADV = 0x44; + ERANGE = 0x22; + ECANCELED = 0x7d; + ETXTBSY = 0x1a; + ENOMEM = 0xc; + EINPROGRESS = 0x73; + ENOTBLK = 0xf; + EPROTOTYPE = 0x5b; + ERESTART = 0x55; + EISNAM = 0x78; + ENOMSG = 0x2a; + EALREADY = 0x72; + ETIMEDOUT = 0x6e; + ENODATA = 0x3d; + EINTR = 0x4; + ENOLINK = 0x43; + EPERM = 0x1; + ELOOP = 0x28; + ENETDOWN = 0x64; + ESTALE = 0x74; + ENOTSOCK = 0x58; + ENOSR = 0x3f; + ECHILD = 0xa; + ELNRNG = 0x30; + EPIPE = 0x20; + EBADMSG = 0x4a; + EBFONT = 0x3b; + EREMOTE = 0x42; + ETOOMANYREFS = 0x6d; + EPFNOSUPPORT = 0x60; + ENONET = 0x40; + EXFULL = 0x36; + EBADSLT = 0x39; + ENOTNAM = 0x76; + ELIBEXEC = 0x53; + ENOCSI = 0x32; + ENOTEMPTY = 0x27; + EADDRINUSE = 0x62; + ENETRESET = 0x66; + EISDIR = 0x15; + EIDRM = 0x2b; + ECOMM = 0x46; + EBADFD = 0x4d; + EL2HLT = 0x33; + ENOKEY = 0x7e; + EINVAL = 0x16; + ESHUTDOWN = 0x6c; + EKEYREJECTED = 0x81; + ELIBSCN = 0x51; + ENAVAIL = 0x77; + ENOSTR = 0x3c; + EOVERFLOW = 0x4b; + EUCLEAN = 0x75; + ENOMEDIUM = 0x7b; + EBUSY = 0x10; + EPROTO = 0x47; + ENODEV = 0x13; + EKEYEXPIRED = 0x7f; + EROFS = 0x1e; + ELIBACC = 0x4f; + E2BIG = 0x7; + EDEADLK = 0x23; + ECONNRESET = 0x68; + ENXIO = 0x6; + EBADRQC = 0x38; + ENAMETOOLONG = 0x24; + ESOCKTNOSUPPORT = 0x5e; + EDOTDOT = 0x49; + EADDRNOTAVAIL = 0x63; + ETIME = 0x3e; + EPROTONOSUPPORT = 0x5d; + ENOTRECOVERABLE = 0x83; + EIO = 0x5; + ENETUNREACH = 0x65; + EXDEV = 0x12; + EDQUOT = 0x7a; + EREMOTEIO = 0x79; + ENOSPC = 0x1c; + ENOEXEC = 0x8; + EMSGSIZE = 0x5a; + EDOM = 0x21; + EFBIG = 0x1b; + ESRCH = 0x3; + ECHRNG = 0x2c; + EHOSTDOWN = 0x70; + ENOLCK = 0x25; + ENFILE = 0x17; + ENOSYS = 0x26; + ENOTCONN = 0x6b; + ENOTSUP = 0x5f; + ESRMNT = 0x45; + EDEADLOCK = 0x23; + ECONNABORTED = 0x67; + ENOANO = 0x37; + EISCONN = 0x6a; + EUSERS = 0x57; + ENOPROTOOPT = 0x5c; + EMFILE = 0x18; + ENOBUFS = 0x69; + EL3HLT = 0x2e; + EFAULT = 0xe; + EWOULDBLOCK = 0xb; + ELIBBAD = 0x50; + ESTRPIPE = 0x56; + ECONNREFUSED = 0x6f; + EAGAIN = 0xb; + ELIBMAX = 0x52; + EEXIST = 0x11; + EL2NSYNC = 0x2d; + ENOENT = 0x2; + ENOPKG = 0x41; + EKEYREVOKED = 0x80; + EHOSTUNREACH = 0x71; + ENOTUNIQ = 0x4c; + EOPNOTSUPP = 0x5f; + ENOTDIR = 0x14; + EMEDIUMTYPE = 0x7c; + SIGBUS = 0x7; + SIGTTIN = 0x15; + SIGPROF = 0x1b; + SIGFPE = 0x8; + SIGHUP = 0x1; + SIGTTOU = 0x16; + SIGSTKFLT = 0x10; + SIGUSR1 = 0xa; + SIGURG = 0x17; + SIGQUIT = 0x3; + SIGCLD = 0x11; + SIGIO = 0x1d; + SIGABRT = 0x6; + SIGUSR2 = 0xc; + SIGTRAP = 0x5; + SIGVTALRM = 0x1a; + SIGPOLL = 0x1d; + SIGSEGV = 0xb; + SIGCONT = 0x12; + SIGPIPE = 0xd; + SIGWINCH = 0x1c; + SIGXFSZ = 0x19; + SIGCHLD = 0x11; + SIGSYS = 0x1f; + SIGSTOP = 0x13; + SIGALRM = 0xe; + SIGTSTP = 0x14; + SIGKILL = 0x9; + SIGXCPU = 0x18; + SIGUNUSED = 0x1f; + SIGPWR = 0x1e; + SIGILL = 0x4; + SIGINT = 0x2; + SIGIOT = 0x6; + SIGTERM = 0xf; +) + +// Types + + +// Error table +var errors = [...]string { + 72: "multihop attempted", + 49: "protocol driver not attached", + 97: "address family not supported by protocol", + 78: "remote address changed", + 13: "permission denied", + 47: "level 3 reset", + 89: "destination address required", + 84: "invalid or incomplete multibyte or wide character", + 29: "illegal seek", + 31: "too many links", + 130: "owner died", + 25: "inappropriate ioctl for device", + 52: "invalid exchange", + 9: "bad file descriptor", + 53: "invalid request descriptor", + 68: "advertise error", + 34: "numerical result out of range", + 125: "operation canceled", + 26: "text file busy", + 12: "cannot allocate memory", + 115: "operation now in progress", + 15: "block device required", + 91: "protocol wrong type for socket", + 85: "interrupted system call should be restarted", + 120: "is a named type file", + 42: "no message of desired type", + 114: "operation already in progress", + 110: "connection timed out", + 61: "no data available", + 4: "interrupted system call", + 67: "link has been severed", + 1: "operation not permitted", + 40: "too many levels of symbolic links", + 100: "network is down", + 116: "stale NFS file handle", + 88: "socket operation on non-socket", + 63: "out of streams resources", + 10: "no child processes", + 48: "link number out of range", + 32: "broken pipe", + 74: "bad message", + 59: "bad font file format", + 66: "object is remote", + 109: "too many references: cannot splice", + 96: "protocol family not supported", + 64: "machine is not on the network", + 54: "exchange full", + 57: "invalid slot", + 118: "not a XENIX named type file", + 83: "cannot exec a shared library directly", + 50: "no CSI structure available", + 39: "directory not empty", + 98: "address already in use", + 102: "network dropped connection on reset", + 21: "is a directory", + 43: "identifier removed", + 70: "communication error on send", + 77: "file descriptor in bad state", + 51: "level 2 halted", + 126: "required key not available", + 22: "invalid argument", + 108: "cannot send after transport endpoint shutdown", + 129: "key was rejected by service", + 81: ".lib section in a.out corrupted", + 119: "no XENIX semaphores available", + 60: "device not a stream", + 75: "value too large for defined data type", + 117: "structure needs cleaning", + 123: "no medium found", + 16: "device or resource busy", + 71: "protocol error", + 19: "no such device", + 127: "key has expired", + 30: "read-only file system", + 79: "can not access a needed shared library", + 7: "argument list too long", + 35: "resource deadlock avoided", + 104: "connection reset by peer", + 6: "no such device or address", + 56: "invalid request code", + 36: "file name too long", + 94: "socket type not supported", + 73: "RFS specific error", + 99: "cannot assign requested address", + 62: "timer expired", + 93: "protocol not supported", + 131: "state not recoverable", + 5: "input/output error", + 101: "network is unreachable", + 18: "invalid cross-device link", + 122: "disk quota exceeded", + 121: "remote I/O error", + 28: "no space left on device", + 8: "exec format error", + 90: "message too long", + 33: "numerical argument out of domain", + 27: "file too large", + 3: "no such process", + 44: "channel number out of range", + 112: "host is down", + 37: "no locks available", + 23: "too many open files in system", + 38: "function not implemented", + 107: "transport endpoint is not connected", + 95: "operation not supported", + 69: "srmount error", + 103: "software caused connection abort", + 55: "no anode", + 106: "transport endpoint is already connected", + 87: "too many users", + 92: "protocol not available", + 24: "too many open files", + 105: "no buffer space available", + 46: "level 3 halted", + 14: "bad address", + 11: "resource temporarily unavailable", + 80: "accessing a corrupted shared library", + 86: "streams pipe error", + 111: "connection refused", + 82: "attempting to link in too many shared libraries", + 17: "file exists", + 45: "level 2 not synchronized", + 2: "no such file or directory", + 65: "package not installed", + 128: "key has been revoked", + 113: "no route to host", + 76: "name not unique on network", + 20: "not a directory", + 124: "wrong medium type", +} + diff --git a/src/pkg/syscall/zsyscall_darwin_386.go b/src/pkg/syscall/zsyscall_darwin_386.go new file mode 100644 index 000000000..61f7c01db --- /dev/null +++ b/src/pkg/syscall/zsyscall_darwin_386.go @@ -0,0 +1,624 @@ +// mksyscall -l32 syscall_darwin.go syscall_darwin_386.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import ( + "syscall"; + "unsafe"; +) + +func getgroups(ngid int, gid *_Gid_t) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0); + n = int(r0); + errno = int(e1); + return; +} + +func setgroups(ngid int, gid *_Gid_t) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0); + errno = int(e1); + return; +} + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) { + r0, r1, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0); + wpid = int(r0); + errno = int(e1); + return; +} + +func pipe() (r int, w int, errno int) { + r0, r1, e1 := Syscall(SYS_PIPE, 0, 0, 0); + r = int(r0); + w = int(r1); + errno = int(e1); + return; +} + +func lseek(fd int, offset int64, whence int) (newoffset uintptr, errno int) { + r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(offset >> 32), uintptr(whence), 0, 0); + newoffset = uintptr(r0); + errno = int(e1); + return; +} + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))); + fd = int(r0); + errno = int(e1); + return; +} + +func bind(s int, addr uintptr, addrlen _Socklen) (errno int) { + r0, r1, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)); + errno = int(e1); + return; +} + +func connect(s int, addr uintptr, addrlen _Socklen) (errno int) { + r0, r1, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)); + errno = int(e1); + return; +} + +func socket(domain int, typ int, proto int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)); + fd = int(r0); + errno = int(e1); + return; +} + +func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { + r0, r1, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0); + errno = int(e1); + return; +} + +func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) { + r0, r1, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))); + n = int(r0); + errno = int(e1); + return; +} + +func fcntl(fd int, cmd int, arg int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)); + val = int(r0); + errno = int(e1); + return; +} + +func Access(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Adjtime(delta *Timeval, olddelta *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0); + errno = int(e1); + return; +} + +func Chdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Chflags(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Chmod(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Chown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Chroot(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Close(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Dup(fd int) (nfd int, errno int) { + r0, r1, e1 := Syscall(SYS_DUP, uintptr(fd), 0, 0); + nfd = int(r0); + errno = int(e1); + return; +} + +func Dup2(from int, to int) (errno int) { + r0, r1, e1 := Syscall(SYS_DUP2, uintptr(from), uintptr(to), 0); + errno = int(e1); + return; +} + +func Exchangedata(path1 string, path2 string, options int) (errno int) { + r0, r1, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(StringBytePtr(path1))), uintptr(unsafe.Pointer(StringBytePtr(path2))), uintptr(options)); + errno = int(e1); + return; +} + +func Exit(code int) () { + r0, r1, e1 := Syscall(SYS_EXIT, uintptr(code), 0, 0); + return; +} + +func Fchdir(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Fchflags(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Fchmod(fd int, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Fchown(fd int, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Flock(fd int, how int) (errno int) { + r0, r1, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0); + errno = int(e1); + return; +} + +func Fpathconf(fd int, name int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0); + val = int(r0); + errno = int(e1); + return; +} + +func Fstat(fd int, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTAT64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Fstatfs(fd int, stat *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Fsync(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Ftruncate(fd int, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length >> 32)); + errno = int(e1); + return; +} + +func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall6(SYS_GETDIRENTRIES64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Getdtablesize() (size int) { + r0, r1, e1 := Syscall(SYS_GETDTABLESIZE, 0, 0, 0); + size = int(r0); + return; +} + +func Getegid() (egid int) { + r0, r1, e1 := Syscall(SYS_GETEGID, 0, 0, 0); + egid = int(r0); + return; +} + +func Geteuid() (uid int) { + r0, r1, e1 := Syscall(SYS_GETEUID, 0, 0, 0); + uid = int(r0); + return; +} + +func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) { + var _p0 *Statfs_t; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_GETFSSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags)); + n = int(r0); + errno = int(e1); + return; +} + +func Getgid() (gid int) { + r0, r1, e1 := Syscall(SYS_GETGID, 0, 0, 0); + gid = int(r0); + return; +} + +func Getpgid(pid int) (pgid int, errno int) { + r0, r1, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0); + pgid = int(r0); + errno = int(e1); + return; +} + +func Getpgrp() (pgrp int) { + r0, r1, e1 := Syscall(SYS_GETPGRP, 0, 0, 0); + pgrp = int(r0); + return; +} + +func Getpid() (pid int) { + r0, r1, e1 := Syscall(SYS_GETPID, 0, 0, 0); + pid = int(r0); + return; +} + +func Getppid() (ppid int) { + r0, r1, e1 := Syscall(SYS_GETPPID, 0, 0, 0); + ppid = int(r0); + return; +} + +func Getpriority(which int, who int) (prio int, errno int) { + r0, r1, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0); + prio = int(r0); + errno = int(e1); + return; +} + +func Getrlimit(which int, lim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0); + errno = int(e1); + return; +} + +func Getrusage(who int, rusage *Rusage) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0); + errno = int(e1); + return; +} + +func Getsid(pid int) (sid int, errno int) { + r0, r1, e1 := Syscall(SYS_GETSID, uintptr(pid), 0, 0); + sid = int(r0); + errno = int(e1); + return; +} + +func Getuid() (uid int) { + r0, r1, e1 := Syscall(SYS_GETUID, 0, 0, 0); + uid = int(r0); + return; +} + +func Issetugid() (tainted bool) { + r0, r1, e1 := Syscall(SYS_ISSETUGID, 0, 0, 0); + tainted = bool(r0 != 0); + return; +} + +func Kill(pid int, signum int, posix int) (errno int) { + r0, r1, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), uintptr(posix)); + errno = int(e1); + return; +} + +func Kqueue() (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_KQUEUE, 0, 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Lchown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Link(path string, link string) (errno int) { + r0, r1, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0); + errno = int(e1); + return; +} + +func Listen(s int, backlog int) (errno int) { + r0, r1, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0); + errno = int(e1); + return; +} + +func Lstat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Mkdir(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Mkfifo(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Mknod(path string, mode int, dev int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)); + errno = int(e1); + return; +} + +func Open(path string, mode int, perm int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)); + fd = int(r0); + errno = int(e1); + return; +} + +func Pathconf(path string, name int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0); + val = int(r0); + errno = int(e1); + return; +} + +func Pread(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), uintptr(offset >> 32), 0); + n = int(r0); + errno = int(e1); + return; +} + +func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), uintptr(offset >> 32), 0); + n = int(r0); + errno = int(e1); + return; +} + +func Read(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func Readlink(path string, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Rename(from string, to string) (errno int) { + r0, r1, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0); + errno = int(e1); + return; +} + +func Revoke(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Rmdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int) { + r0, r1, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0); + errno = int(e1); + return; +} + +func Setegid(egid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETEGID, uintptr(egid), 0, 0); + errno = int(e1); + return; +} + +func Seteuid(euid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETEUID, uintptr(euid), 0, 0); + errno = int(e1); + return; +} + +func Setgid(gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0); + errno = int(e1); + return; +} + +func Setlogin(name string) (errno int) { + r0, r1, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0); + errno = int(e1); + return; +} + +func Setpgid(pid int, pgid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0); + errno = int(e1); + return; +} + +func Setpriority(which int, who int, prio int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio)); + errno = int(e1); + return; +} + +func Setprivexec(flag int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPRIVEXEC, uintptr(flag), 0, 0); + errno = int(e1); + return; +} + +func Setregid(rgid int, egid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0); + errno = int(e1); + return; +} + +func Setreuid(ruid int, euid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0); + errno = int(e1); + return; +} + +func Setrlimit(which int, lim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0); + errno = int(e1); + return; +} + +func Setsid() (pid int, errno int) { + r0, r1, e1 := Syscall(SYS_SETSID, 0, 0, 0); + pid = int(r0); + errno = int(e1); + return; +} + +func Settimeofday(tp *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0); + errno = int(e1); + return; +} + +func Setuid(uid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0); + errno = int(e1); + return; +} + +func Stat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Statfs(path string, stat *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Symlink(path string, link string) (errno int) { + r0, r1, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0); + errno = int(e1); + return; +} + +func Sync() (errno int) { + r0, r1, e1 := Syscall(SYS_SYNC, 0, 0, 0); + errno = int(e1); + return; +} + +func Truncate(path string, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length >> 32)); + errno = int(e1); + return; +} + +func Umask(newmask int) (errno int) { + r0, r1, e1 := Syscall(SYS_UMASK, uintptr(newmask), 0, 0); + errno = int(e1); + return; +} + +func Undelete(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Unlink(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Unmount(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Write(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func read(fd int, buf *byte, nbuf int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)); + n = int(r0); + errno = int(e1); + return; +} + +func write(fd int, buf *byte, nbuf int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)); + n = int(r0); + errno = int(e1); + return; +} + +func gettimeofday(tp *Timeval) (sec int64, usec int32, errno int) { + r0, r1, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0); + sec = int64(r0); + usec = int32(r1); + errno = int(e1); + return; +} + + + diff --git a/src/pkg/syscall/zsyscall_darwin_amd64.go b/src/pkg/syscall/zsyscall_darwin_amd64.go new file mode 100644 index 000000000..c8a0b10a7 --- /dev/null +++ b/src/pkg/syscall/zsyscall_darwin_amd64.go @@ -0,0 +1,624 @@ +// mksyscall syscall_darwin.go syscall_darwin_amd64.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import ( + "syscall"; + "unsafe"; +) + +func getgroups(ngid int, gid *_Gid_t) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0); + n = int(r0); + errno = int(e1); + return; +} + +func setgroups(ngid int, gid *_Gid_t) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0); + errno = int(e1); + return; +} + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) { + r0, r1, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0); + wpid = int(r0); + errno = int(e1); + return; +} + +func pipe() (r int, w int, errno int) { + r0, r1, e1 := Syscall(SYS_PIPE, 0, 0, 0); + r = int(r0); + w = int(r1); + errno = int(e1); + return; +} + +func lseek(fd int, offset int64, whence int) (newoffset uintptr, errno int) { + r0, r1, e1 := Syscall(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(whence)); + newoffset = uintptr(r0); + errno = int(e1); + return; +} + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))); + fd = int(r0); + errno = int(e1); + return; +} + +func bind(s int, addr uintptr, addrlen _Socklen) (errno int) { + r0, r1, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)); + errno = int(e1); + return; +} + +func connect(s int, addr uintptr, addrlen _Socklen) (errno int) { + r0, r1, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)); + errno = int(e1); + return; +} + +func socket(domain int, typ int, proto int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)); + fd = int(r0); + errno = int(e1); + return; +} + +func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { + r0, r1, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0); + errno = int(e1); + return; +} + +func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) { + r0, r1, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))); + n = int(r0); + errno = int(e1); + return; +} + +func fcntl(fd int, cmd int, arg int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)); + val = int(r0); + errno = int(e1); + return; +} + +func Access(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Adjtime(delta *Timeval, olddelta *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0); + errno = int(e1); + return; +} + +func Chdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Chflags(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Chmod(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Chown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Chroot(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Close(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Dup(fd int) (nfd int, errno int) { + r0, r1, e1 := Syscall(SYS_DUP, uintptr(fd), 0, 0); + nfd = int(r0); + errno = int(e1); + return; +} + +func Dup2(from int, to int) (errno int) { + r0, r1, e1 := Syscall(SYS_DUP2, uintptr(from), uintptr(to), 0); + errno = int(e1); + return; +} + +func Exchangedata(path1 string, path2 string, options int) (errno int) { + r0, r1, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(StringBytePtr(path1))), uintptr(unsafe.Pointer(StringBytePtr(path2))), uintptr(options)); + errno = int(e1); + return; +} + +func Exit(code int) () { + r0, r1, e1 := Syscall(SYS_EXIT, uintptr(code), 0, 0); + return; +} + +func Fchdir(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Fchflags(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Fchmod(fd int, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Fchown(fd int, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Flock(fd int, how int) (errno int) { + r0, r1, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0); + errno = int(e1); + return; +} + +func Fpathconf(fd int, name int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0); + val = int(r0); + errno = int(e1); + return; +} + +func Fstat(fd int, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTAT64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Fstatfs(fd int, stat *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Fsync(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Ftruncate(fd int, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), 0); + errno = int(e1); + return; +} + +func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall6(SYS_GETDIRENTRIES64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Getdtablesize() (size int) { + r0, r1, e1 := Syscall(SYS_GETDTABLESIZE, 0, 0, 0); + size = int(r0); + return; +} + +func Getegid() (egid int) { + r0, r1, e1 := Syscall(SYS_GETEGID, 0, 0, 0); + egid = int(r0); + return; +} + +func Geteuid() (uid int) { + r0, r1, e1 := Syscall(SYS_GETEUID, 0, 0, 0); + uid = int(r0); + return; +} + +func Getfsstat(buf []Statfs_t, flags int) (n int, errno int) { + var _p0 *Statfs_t; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_GETFSSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags)); + n = int(r0); + errno = int(e1); + return; +} + +func Getgid() (gid int) { + r0, r1, e1 := Syscall(SYS_GETGID, 0, 0, 0); + gid = int(r0); + return; +} + +func Getpgid(pid int) (pgid int, errno int) { + r0, r1, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0); + pgid = int(r0); + errno = int(e1); + return; +} + +func Getpgrp() (pgrp int) { + r0, r1, e1 := Syscall(SYS_GETPGRP, 0, 0, 0); + pgrp = int(r0); + return; +} + +func Getpid() (pid int) { + r0, r1, e1 := Syscall(SYS_GETPID, 0, 0, 0); + pid = int(r0); + return; +} + +func Getppid() (ppid int) { + r0, r1, e1 := Syscall(SYS_GETPPID, 0, 0, 0); + ppid = int(r0); + return; +} + +func Getpriority(which int, who int) (prio int, errno int) { + r0, r1, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0); + prio = int(r0); + errno = int(e1); + return; +} + +func Getrlimit(which int, lim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0); + errno = int(e1); + return; +} + +func Getrusage(who int, rusage *Rusage) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0); + errno = int(e1); + return; +} + +func Getsid(pid int) (sid int, errno int) { + r0, r1, e1 := Syscall(SYS_GETSID, uintptr(pid), 0, 0); + sid = int(r0); + errno = int(e1); + return; +} + +func Getuid() (uid int) { + r0, r1, e1 := Syscall(SYS_GETUID, 0, 0, 0); + uid = int(r0); + return; +} + +func Issetugid() (tainted bool) { + r0, r1, e1 := Syscall(SYS_ISSETUGID, 0, 0, 0); + tainted = bool(r0 != 0); + return; +} + +func Kill(pid int, signum int, posix int) (errno int) { + r0, r1, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), uintptr(posix)); + errno = int(e1); + return; +} + +func Kqueue() (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_KQUEUE, 0, 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Lchown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Link(path string, link string) (errno int) { + r0, r1, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0); + errno = int(e1); + return; +} + +func Listen(s int, backlog int) (errno int) { + r0, r1, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0); + errno = int(e1); + return; +} + +func Lstat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Mkdir(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Mkfifo(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Mknod(path string, mode int, dev int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)); + errno = int(e1); + return; +} + +func Open(path string, mode int, perm int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)); + fd = int(r0); + errno = int(e1); + return; +} + +func Pathconf(path string, name int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(name), 0); + val = int(r0); + errno = int(e1); + return; +} + +func Pread(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Read(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func Readlink(path string, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Rename(from string, to string) (errno int) { + r0, r1, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(from))), uintptr(unsafe.Pointer(StringBytePtr(to))), 0); + errno = int(e1); + return; +} + +func Revoke(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Rmdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (errno int) { + r0, r1, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0); + errno = int(e1); + return; +} + +func Setegid(egid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETEGID, uintptr(egid), 0, 0); + errno = int(e1); + return; +} + +func Seteuid(euid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETEUID, uintptr(euid), 0, 0); + errno = int(e1); + return; +} + +func Setgid(gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0); + errno = int(e1); + return; +} + +func Setlogin(name string) (errno int) { + r0, r1, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0); + errno = int(e1); + return; +} + +func Setpgid(pid int, pgid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0); + errno = int(e1); + return; +} + +func Setpriority(which int, who int, prio int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio)); + errno = int(e1); + return; +} + +func Setprivexec(flag int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPRIVEXEC, uintptr(flag), 0, 0); + errno = int(e1); + return; +} + +func Setregid(rgid int, egid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0); + errno = int(e1); + return; +} + +func Setreuid(ruid int, euid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0); + errno = int(e1); + return; +} + +func Setrlimit(which int, lim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0); + errno = int(e1); + return; +} + +func Setsid() (pid int, errno int) { + r0, r1, e1 := Syscall(SYS_SETSID, 0, 0, 0); + pid = int(r0); + errno = int(e1); + return; +} + +func Settimeofday(tp *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0); + errno = int(e1); + return; +} + +func Setuid(uid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0); + errno = int(e1); + return; +} + +func Stat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Statfs(path string, stat *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Symlink(path string, link string) (errno int) { + r0, r1, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(StringBytePtr(link))), 0); + errno = int(e1); + return; +} + +func Sync() (errno int) { + r0, r1, e1 := Syscall(SYS_SYNC, 0, 0, 0); + errno = int(e1); + return; +} + +func Truncate(path string, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0); + errno = int(e1); + return; +} + +func Umask(newmask int) (errno int) { + r0, r1, e1 := Syscall(SYS_UMASK, uintptr(newmask), 0, 0); + errno = int(e1); + return; +} + +func Undelete(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Unlink(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Unmount(path string, flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), 0); + errno = int(e1); + return; +} + +func Write(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func read(fd int, buf *byte, nbuf int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)); + n = int(r0); + errno = int(e1); + return; +} + +func write(fd int, buf *byte, nbuf int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)); + n = int(r0); + errno = int(e1); + return; +} + +func gettimeofday(tp *Timeval) (sec int64, usec int32, errno int) { + r0, r1, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0); + sec = int64(r0); + usec = int32(r1); + errno = int(e1); + return; +} + + + diff --git a/src/pkg/syscall/zsyscall_linux_386.go b/src/pkg/syscall/zsyscall_linux_386.go new file mode 100644 index 000000000..ef323b088 --- /dev/null +++ b/src/pkg/syscall/zsyscall_linux_386.go @@ -0,0 +1,720 @@ +// mksyscall syscall_linux.go syscall_linux_386.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import ( + "syscall"; + "unsafe"; +) + +func pipe(p *[2]_C_int) (errno int) { + r0, r1, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0); + errno = int(e1); + return; +} + +func utimes(path string, times *[2]Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0); + errno = int(e1); + return; +} + +func futimesat(dirfd int, path string, times *[2]Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_FUTIMESAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times))); + errno = int(e1); + return; +} + +func Getcwd(buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_GETCWD, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0); + n = int(r0); + errno = int(e1); + return; +} + +func getgroups(n int, list *_Gid_t) (nn int, errno int) { + r0, r1, e1 := Syscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0); + nn = int(r0); + errno = int(e1); + return; +} + +func setgroups(n int, list *_Gid_t) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0); + errno = int(e1); + return; +} + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) { + r0, r1, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0); + wpid = int(r0); + errno = int(e1); + return; +} + +func Access(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Acct(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Adjtimex(buf *Timex) (state int, errno int) { + r0, r1, e1 := Syscall(SYS_ADJTIMEX, uintptr(unsafe.Pointer(buf)), 0, 0); + state = int(r0); + errno = int(e1); + return; +} + +func Chdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Chmod(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Chown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Chroot(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Close(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Creat(path string, mode int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Dup(oldfd int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Dup2(oldfd int, newfd int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0); + fd = int(r0); + errno = int(e1); + return; +} + +func EpollCreate(size int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) { + r0, r1, e1 := Syscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0); + errno = int(e1); + return; +} + +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) { + var _p0 *EpollEvent; + if len(events) > 0 { _p0 = &events[0]; } + r0, r1, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(unsafe.Pointer(_p0)), uintptr(len(events)), uintptr(msec), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Exit(code int) () { + r0, r1, e1 := Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0); + return; +} + +func Faccessat(dirfd int, path string, mode int, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0); + errno = int(e1); + return; +} + +func Fallocate(fd int, mode int, off int64, len int64) (errno int) { + r0, r1, e1 := Syscall6(SYS_FALLOCATE, uintptr(fd), uintptr(mode), uintptr(off), uintptr(off >> 32), uintptr(len), uintptr(len >> 32)); + errno = int(e1); + return; +} + +func Fchdir(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Fchmod(fd int, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Fchmodat(dirfd int, path string, mode int, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0); + errno = int(e1); + return; +} + +func Fchown(fd int, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Fchownat(dirfd int, path string, uid int, gid int, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0); + errno = int(e1); + return; +} + +func fcntl(fd int, cmd int, arg int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)); + val = int(r0); + errno = int(e1); + return; +} + +func Fdatasync(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FDATASYNC, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Fstat(fd int, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Fstatfs(fd int, buf *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(buf)), 0); + errno = int(e1); + return; +} + +func Fsync(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Ftruncate(fd int, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length >> 32)); + errno = int(e1); + return; +} + +func Getdents(fd int, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_GETDENTS64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Getegid() (egid int) { + r0, r1, e1 := Syscall(SYS_GETEGID, 0, 0, 0); + egid = int(r0); + return; +} + +func Geteuid() (euid int) { + r0, r1, e1 := Syscall(SYS_GETEUID, 0, 0, 0); + euid = int(r0); + return; +} + +func Getgid() (gid int) { + r0, r1, e1 := Syscall(SYS_GETGID, 0, 0, 0); + gid = int(r0); + return; +} + +func Getpgid(pid int) (pgid int, errno int) { + r0, r1, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0); + pgid = int(r0); + errno = int(e1); + return; +} + +func Getpgrp() (pid int) { + r0, r1, e1 := Syscall(SYS_GETPGRP, 0, 0, 0); + pid = int(r0); + return; +} + +func Getpid() (pid int) { + r0, r1, e1 := Syscall(SYS_GETPID, 0, 0, 0); + pid = int(r0); + return; +} + +func Getppid() (ppid int) { + r0, r1, e1 := Syscall(SYS_GETPPID, 0, 0, 0); + ppid = int(r0); + return; +} + +func Getrlimit(resource int, rlim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0); + errno = int(e1); + return; +} + +func Getrusage(who int, rusage *Rusage) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0); + errno = int(e1); + return; +} + +func Gettid() (tid int) { + r0, r1, e1 := Syscall(SYS_GETTID, 0, 0, 0); + tid = int(r0); + return; +} + +func Gettimeofday(tv *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0); + errno = int(e1); + return; +} + +func Getuid() (uid int) { + r0, r1, e1 := Syscall(SYS_GETUID, 0, 0, 0); + uid = int(r0); + return; +} + +func Ioperm(from int, num int, on int) (errno int) { + r0, r1, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on)); + errno = int(e1); + return; +} + +func Iopl(level int) (errno int) { + r0, r1, e1 := Syscall(SYS_IOPL, uintptr(level), 0, 0); + errno = int(e1); + return; +} + +func Kill(pid int, sig int) (errno int) { + r0, r1, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(sig), 0); + errno = int(e1); + return; +} + +func Klogctl(typ int, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Lchown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Link(oldpath string, newpath string) (errno int) { + r0, r1, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0); + errno = int(e1); + return; +} + +func Lstat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Mkdir(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Mkdirat(dirfd int, path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode)); + errno = int(e1); + return; +} + +func Mknod(path string, mode int, dev int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)); + errno = int(e1); + return; +} + +func Mknodat(dirfd int, path string, mode int, dev int) (errno int) { + r0, r1, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0); + errno = int(e1); + return; +} + +func Nanosleep(time *Timespec, leftover *Timespec) (errno int) { + r0, r1, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0); + errno = int(e1); + return; +} + +func Open(path string, mode int, perm int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)); + fd = int(r0); + errno = int(e1); + return; +} + +func Openat(dirfd int, path string, flags int, mode int) (fd int, errno int) { + r0, r1, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Pause() (errno int) { + r0, r1, e1 := Syscall(SYS_PAUSE, 0, 0, 0); + errno = int(e1); + return; +} + +func PivotRoot(newroot string, putold string) (errno int) { + r0, r1, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0); + errno = int(e1); + return; +} + +func Pread(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), uintptr(offset >> 32), 0); + n = int(r0); + errno = int(e1); + return; +} + +func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), uintptr(offset >> 32), 0); + n = int(r0); + errno = int(e1); + return; +} + +func Read(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func Readlink(path string, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Rename(oldpath string, newpath string) (errno int) { + r0, r1, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0); + errno = int(e1); + return; +} + +func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (errno int) { + r0, r1, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0); + errno = int(e1); + return; +} + +func Rmdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Seek(fd int, offset int64, whence int) (off int64, errno int) { + r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(offset >> 32), uintptr(whence), 0, 0); + off = int64(r0); + errno = int(e1); + return; +} + +func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) { + r0, r1, e1 := Syscall6(SYS_SELECT, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0); + n = int(r0); + errno = int(e1); + return; +} + +func Setdomainname(p []byte) (errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), 0); + errno = int(e1); + return; +} + +func Setfsgid(gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0); + errno = int(e1); + return; +} + +func Setfsuid(uid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0); + errno = int(e1); + return; +} + +func Setgid(gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0); + errno = int(e1); + return; +} + +func Sethostname(p []byte) (errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_SETHOSTNAME, uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), 0); + errno = int(e1); + return; +} + +func Setpgid(pid int, pgid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0); + errno = int(e1); + return; +} + +func Setregid(rgid int, egid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0); + errno = int(e1); + return; +} + +func Setresgid(rgid int, egid int, sgid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid)); + errno = int(e1); + return; +} + +func Setresuid(ruid int, euid int, suid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid)); + errno = int(e1); + return; +} + +func Setreuid(ruid int, euid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0); + errno = int(e1); + return; +} + +func Setrlimit(resource int, rlim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0); + errno = int(e1); + return; +} + +func Setsid() (pid int) { + r0, r1, e1 := Syscall(SYS_SETSID, 0, 0, 0); + pid = int(r0); + return; +} + +func Settimeofday(tv *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0); + errno = int(e1); + return; +} + +func Setuid(uid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0); + errno = int(e1); + return; +} + +func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, errno int) { + r0, r1, e1 := Syscall6(SYS_SPLICE, uintptr(rfd), uintptr(unsafe.Pointer(roff)), uintptr(wfd), uintptr(unsafe.Pointer(woff)), uintptr(len), uintptr(flags)); + n = int64(r0); + errno = int(e1); + return; +} + +func Stat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Statfs(path string, buf *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0); + errno = int(e1); + return; +} + +func Symlink(oldpath string, newpath string) (errno int) { + r0, r1, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0); + errno = int(e1); + return; +} + +func Sync() () { + r0, r1, e1 := Syscall(SYS_SYNC, 0, 0, 0); + return; +} + +func SyncFileRange(fd int, off int64, n int64, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_SYNC_FILE_RANGE, uintptr(fd), uintptr(off), uintptr(off >> 32), uintptr(n), uintptr(n >> 32), uintptr(flags)); + errno = int(e1); + return; +} + +func Sysinfo(info *Sysinfo_t) (errno int) { + r0, r1, e1 := Syscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0); + errno = int(e1); + return; +} + +func Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) { + r0, r1, e1 := Syscall6(SYS_TEE, uintptr(rfd), uintptr(wfd), uintptr(len), uintptr(flags), 0, 0); + n = int64(r0); + errno = int(e1); + return; +} + +func Tgkill(tgid int, tid int, sig int) (errno int) { + r0, r1, e1 := Syscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig)); + errno = int(e1); + return; +} + +func Time(t *Time_t) (tt Time_t, errno int) { + r0, r1, e1 := Syscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0); + tt = Time_t(r0); + errno = int(e1); + return; +} + +func Times(tms *Tms) (ticks uintptr, errno int) { + r0, r1, e1 := Syscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0); + ticks = uintptr(r0); + errno = int(e1); + return; +} + +func Truncate(path string, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), uintptr(length >> 32)); + errno = int(e1); + return; +} + +func Umask(mask int) (oldmask int) { + r0, r1, e1 := Syscall(SYS_UMASK, uintptr(mask), 0, 0); + oldmask = int(r0); + return; +} + +func Uname(buf *Utsname) (errno int) { + r0, r1, e1 := Syscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0); + errno = int(e1); + return; +} + +func Unlink(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Unlinkat(dirfd int, path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0); + errno = int(e1); + return; +} + +func Unshare(flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0); + errno = int(e1); + return; +} + +func Ustat(dev int, ubuf *Ustat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_USTAT, uintptr(dev), uintptr(unsafe.Pointer(ubuf)), 0); + errno = int(e1); + return; +} + +func Utime(path string, buf *Utimbuf) (errno int) { + r0, r1, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0); + errno = int(e1); + return; +} + +func Write(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func exitThread(code int) (errno int) { + r0, r1, e1 := Syscall(SYS_EXIT, uintptr(code), 0, 0); + errno = int(e1); + return; +} + +func read(fd int, p *byte, np int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)); + n = int(r0); + errno = int(e1); + return; +} + +func write(fd int, p *byte, np int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)); + n = int(r0); + errno = int(e1); + return; +} + + + diff --git a/src/pkg/syscall/zsyscall_linux_amd64.go b/src/pkg/syscall/zsyscall_linux_amd64.go new file mode 100644 index 000000000..490ffc392 --- /dev/null +++ b/src/pkg/syscall/zsyscall_linux_amd64.go @@ -0,0 +1,764 @@ +// mksyscall syscall_linux.go syscall_linux_amd64.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import ( + "syscall"; + "unsafe"; +) + +func pipe(p *[2]_C_int) (errno int) { + r0, r1, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0); + errno = int(e1); + return; +} + +func utimes(path string, times *[2]Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times)), 0); + errno = int(e1); + return; +} + +func futimesat(dirfd int, path string, times *[2]Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_FUTIMESAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(times))); + errno = int(e1); + return; +} + +func Getcwd(buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_GETCWD, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0); + n = int(r0); + errno = int(e1); + return; +} + +func getgroups(n int, list *_Gid_t) (nn int, errno int) { + r0, r1, e1 := Syscall(SYS_GETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0); + nn = int(r0); + errno = int(e1); + return; +} + +func setgroups(n int, list *_Gid_t) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGROUPS, uintptr(n), uintptr(unsafe.Pointer(list)), 0); + errno = int(e1); + return; +} + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, errno int) { + r0, r1, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0); + wpid = int(r0); + errno = int(e1); + return; +} + +func Access(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Acct(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_ACCT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Adjtimex(buf *Timex) (state int, errno int) { + r0, r1, e1 := Syscall(SYS_ADJTIMEX, uintptr(unsafe.Pointer(buf)), 0, 0); + state = int(r0); + errno = int(e1); + return; +} + +func Chdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Chmod(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Chown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Chroot(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Close(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Creat(path string, mode int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_CREAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Dup(oldfd int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_DUP, uintptr(oldfd), 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Dup2(oldfd int, newfd int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0); + fd = int(r0); + errno = int(e1); + return; +} + +func EpollCreate(size int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_EPOLL_CREATE, uintptr(size), 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) { + r0, r1, e1 := Syscall6(SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0); + errno = int(e1); + return; +} + +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) { + var _p0 *EpollEvent; + if len(events) > 0 { _p0 = &events[0]; } + r0, r1, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(unsafe.Pointer(_p0)), uintptr(len(events)), uintptr(msec), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Exit(code int) () { + r0, r1, e1 := Syscall(SYS_EXIT_GROUP, uintptr(code), 0, 0); + return; +} + +func Faccessat(dirfd int, path string, mode int, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_FACCESSAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0); + errno = int(e1); + return; +} + +func Fallocate(fd int, mode int, off int64, len int64) (errno int) { + r0, r1, e1 := Syscall6(SYS_FALLOCATE, uintptr(fd), uintptr(mode), uintptr(off), uintptr(len), 0, 0); + errno = int(e1); + return; +} + +func Fchdir(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Fchmod(fd int, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Fchmodat(dirfd int, path string, mode int, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(flags), 0, 0); + errno = int(e1); + return; +} + +func Fchown(fd int, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Fchownat(dirfd int, path string, uid int, gid int, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_FCHOWNAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid), uintptr(flags), 0); + errno = int(e1); + return; +} + +func fcntl(fd int, cmd int, arg int) (val int, errno int) { + r0, r1, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)); + val = int(r0); + errno = int(e1); + return; +} + +func Fdatasync(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FDATASYNC, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Fstat(fd int, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Fstatfs(fd int, buf *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_FSTATFS, uintptr(fd), uintptr(unsafe.Pointer(buf)), 0); + errno = int(e1); + return; +} + +func Fsync(fd int) (errno int) { + r0, r1, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0); + errno = int(e1); + return; +} + +func Ftruncate(fd int, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), 0); + errno = int(e1); + return; +} + +func Getdents(fd int, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_GETDENTS64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Getegid() (egid int) { + r0, r1, e1 := Syscall(SYS_GETEGID, 0, 0, 0); + egid = int(r0); + return; +} + +func Geteuid() (euid int) { + r0, r1, e1 := Syscall(SYS_GETEUID, 0, 0, 0); + euid = int(r0); + return; +} + +func Getgid() (gid int) { + r0, r1, e1 := Syscall(SYS_GETGID, 0, 0, 0); + gid = int(r0); + return; +} + +func Getpgid(pid int) (pgid int, errno int) { + r0, r1, e1 := Syscall(SYS_GETPGID, uintptr(pid), 0, 0); + pgid = int(r0); + errno = int(e1); + return; +} + +func Getpgrp() (pid int) { + r0, r1, e1 := Syscall(SYS_GETPGRP, 0, 0, 0); + pid = int(r0); + return; +} + +func Getpid() (pid int) { + r0, r1, e1 := Syscall(SYS_GETPID, 0, 0, 0); + pid = int(r0); + return; +} + +func Getppid() (ppid int) { + r0, r1, e1 := Syscall(SYS_GETPPID, 0, 0, 0); + ppid = int(r0); + return; +} + +func Getrlimit(resource int, rlim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0); + errno = int(e1); + return; +} + +func Getrusage(who int, rusage *Rusage) (errno int) { + r0, r1, e1 := Syscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0); + errno = int(e1); + return; +} + +func Gettid() (tid int) { + r0, r1, e1 := Syscall(SYS_GETTID, 0, 0, 0); + tid = int(r0); + return; +} + +func Gettimeofday(tv *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0); + errno = int(e1); + return; +} + +func Getuid() (uid int) { + r0, r1, e1 := Syscall(SYS_GETUID, 0, 0, 0); + uid = int(r0); + return; +} + +func Ioperm(from int, num int, on int) (errno int) { + r0, r1, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on)); + errno = int(e1); + return; +} + +func Iopl(level int) (errno int) { + r0, r1, e1 := Syscall(SYS_IOPL, uintptr(level), 0, 0); + errno = int(e1); + return; +} + +func Kill(pid int, sig int) (errno int) { + r0, r1, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(sig), 0); + errno = int(e1); + return; +} + +func Klogctl(typ int, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_SYSLOG, uintptr(typ), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Lchown(path string, uid int, gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(uid), uintptr(gid)); + errno = int(e1); + return; +} + +func Link(oldpath string, newpath string) (errno int) { + r0, r1, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0); + errno = int(e1); + return; +} + +func Lstat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_LSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Mkdir(path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0); + errno = int(e1); + return; +} + +func Mkdirat(dirfd int, path string, mode int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKDIRAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode)); + errno = int(e1); + return; +} + +func Mknod(path string, mode int, dev int) (errno int) { + r0, r1, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev)); + errno = int(e1); + return; +} + +func Mknodat(dirfd int, path string, mode int, dev int) (errno int) { + r0, r1, e1 := Syscall6(SYS_MKNODAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(dev), 0, 0); + errno = int(e1); + return; +} + +func Nanosleep(time *Timespec, leftover *Timespec) (errno int) { + r0, r1, e1 := Syscall(SYS_NANOSLEEP, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(leftover)), 0); + errno = int(e1); + return; +} + +func Open(path string, mode int, perm int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm)); + fd = int(r0); + errno = int(e1); + return; +} + +func Openat(dirfd int, path string, flags int, mode int) (fd int, errno int) { + r0, r1, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(flags), uintptr(mode), 0, 0); + fd = int(r0); + errno = int(e1); + return; +} + +func Pause() (errno int) { + r0, r1, e1 := Syscall(SYS_PAUSE, 0, 0, 0); + errno = int(e1); + return; +} + +func PivotRoot(newroot string, putold string) (errno int) { + r0, r1, e1 := Syscall(SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(StringBytePtr(newroot))), uintptr(unsafe.Pointer(StringBytePtr(putold))), 0); + errno = int(e1); + return; +} + +func Pread(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PREAD64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Pwrite(fd int, p []byte, offset int64) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall6(SYS_PWRITE64, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), uintptr(offset), 0, 0); + n = int(r0); + errno = int(e1); + return; +} + +func Read(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func Readlink(path string, buf []byte) (n int, errno int) { + var _p0 *byte; + if len(buf) > 0 { _p0 = &buf[0]; } + r0, r1, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf))); + n = int(r0); + errno = int(e1); + return; +} + +func Rename(oldpath string, newpath string) (errno int) { + r0, r1, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0); + errno = int(e1); + return; +} + +func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (errno int) { + r0, r1, e1 := Syscall6(SYS_RENAMEAT, uintptr(olddirfd), uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(newdirfd), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0, 0); + errno = int(e1); + return; +} + +func Rmdir(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Seek(fd int, offset int64, whence int) (off int64, errno int) { + r0, r1, e1 := Syscall(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(whence)); + off = int64(r0); + errno = int(e1); + return; +} + +func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) { + r0, r1, e1 := Syscall6(SYS_SELECT, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0); + n = int(r0); + errno = int(e1); + return; +} + +func Setdomainname(p []byte) (errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_SETDOMAINNAME, uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), 0); + errno = int(e1); + return; +} + +func Setfsgid(gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETFSGID, uintptr(gid), 0, 0); + errno = int(e1); + return; +} + +func Setfsuid(uid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETFSUID, uintptr(uid), 0, 0); + errno = int(e1); + return; +} + +func Setgid(gid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETGID, uintptr(gid), 0, 0); + errno = int(e1); + return; +} + +func Sethostname(p []byte) (errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_SETHOSTNAME, uintptr(unsafe.Pointer(_p0)), uintptr(len(p)), 0); + errno = int(e1); + return; +} + +func Setpgid(pid int, pgid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0); + errno = int(e1); + return; +} + +func Setregid(rgid int, egid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0); + errno = int(e1); + return; +} + +func Setresgid(rgid int, egid int, sgid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid)); + errno = int(e1); + return; +} + +func Setresuid(ruid int, euid int, suid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid)); + errno = int(e1); + return; +} + +func Setreuid(ruid int, euid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0); + errno = int(e1); + return; +} + +func Setrlimit(resource int, rlim *Rlimit) (errno int) { + r0, r1, e1 := Syscall(SYS_SETRLIMIT, uintptr(resource), uintptr(unsafe.Pointer(rlim)), 0); + errno = int(e1); + return; +} + +func Setsid() (pid int) { + r0, r1, e1 := Syscall(SYS_SETSID, 0, 0, 0); + pid = int(r0); + return; +} + +func Settimeofday(tv *Timeval) (errno int) { + r0, r1, e1 := Syscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tv)), 0, 0); + errno = int(e1); + return; +} + +func Setuid(uid int) (errno int) { + r0, r1, e1 := Syscall(SYS_SETUID, uintptr(uid), 0, 0); + errno = int(e1); + return; +} + +func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, errno int) { + r0, r1, e1 := Syscall6(SYS_SPLICE, uintptr(rfd), uintptr(unsafe.Pointer(roff)), uintptr(wfd), uintptr(unsafe.Pointer(woff)), uintptr(len), uintptr(flags)); + n = int64(r0); + errno = int(e1); + return; +} + +func Stat(path string, stat *Stat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(stat)), 0); + errno = int(e1); + return; +} + +func Statfs(path string, buf *Statfs_t) (errno int) { + r0, r1, e1 := Syscall(SYS_STATFS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0); + errno = int(e1); + return; +} + +func Symlink(oldpath string, newpath string) (errno int) { + r0, r1, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(StringBytePtr(oldpath))), uintptr(unsafe.Pointer(StringBytePtr(newpath))), 0); + errno = int(e1); + return; +} + +func Sync() () { + r0, r1, e1 := Syscall(SYS_SYNC, 0, 0, 0); + return; +} + +func SyncFileRange(fd int, off int64, n int64, flags int) (errno int) { + r0, r1, e1 := Syscall6(SYS_SYNC_FILE_RANGE, uintptr(fd), uintptr(off), uintptr(n), uintptr(flags), 0, 0); + errno = int(e1); + return; +} + +func Sysinfo(info *Sysinfo_t) (errno int) { + r0, r1, e1 := Syscall(SYS_SYSINFO, uintptr(unsafe.Pointer(info)), 0, 0); + errno = int(e1); + return; +} + +func Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) { + r0, r1, e1 := Syscall6(SYS_TEE, uintptr(rfd), uintptr(wfd), uintptr(len), uintptr(flags), 0, 0); + n = int64(r0); + errno = int(e1); + return; +} + +func Tgkill(tgid int, tid int, sig int) (errno int) { + r0, r1, e1 := Syscall(SYS_TGKILL, uintptr(tgid), uintptr(tid), uintptr(sig)); + errno = int(e1); + return; +} + +func Time(t *Time_t) (tt Time_t, errno int) { + r0, r1, e1 := Syscall(SYS_TIME, uintptr(unsafe.Pointer(t)), 0, 0); + tt = Time_t(r0); + errno = int(e1); + return; +} + +func Times(tms *Tms) (ticks uintptr, errno int) { + r0, r1, e1 := Syscall(SYS_TIMES, uintptr(unsafe.Pointer(tms)), 0, 0); + ticks = uintptr(r0); + errno = int(e1); + return; +} + +func Truncate(path string, length int64) (errno int) { + r0, r1, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(length), 0); + errno = int(e1); + return; +} + +func Umask(mask int) (oldmask int) { + r0, r1, e1 := Syscall(SYS_UMASK, uintptr(mask), 0, 0); + oldmask = int(r0); + return; +} + +func Uname(buf *Utsname) (errno int) { + r0, r1, e1 := Syscall(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0); + errno = int(e1); + return; +} + +func Unlink(path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0); + errno = int(e1); + return; +} + +func Unlinkat(dirfd int, path string) (errno int) { + r0, r1, e1 := Syscall(SYS_UNLINKAT, uintptr(dirfd), uintptr(unsafe.Pointer(StringBytePtr(path))), 0); + errno = int(e1); + return; +} + +func Unshare(flags int) (errno int) { + r0, r1, e1 := Syscall(SYS_UNSHARE, uintptr(flags), 0, 0); + errno = int(e1); + return; +} + +func Ustat(dev int, ubuf *Ustat_t) (errno int) { + r0, r1, e1 := Syscall(SYS_USTAT, uintptr(dev), uintptr(unsafe.Pointer(ubuf)), 0); + errno = int(e1); + return; +} + +func Utime(path string, buf *Utimbuf) (errno int) { + r0, r1, e1 := Syscall(SYS_UTIME, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Pointer(buf)), 0); + errno = int(e1); + return; +} + +func Write(fd int, p []byte) (n int, errno int) { + var _p0 *byte; + if len(p) > 0 { _p0 = &p[0]; } + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(_p0)), uintptr(len(p))); + n = int(r0); + errno = int(e1); + return; +} + +func exitThread(code int) (errno int) { + r0, r1, e1 := Syscall(SYS_EXIT, uintptr(code), 0, 0); + errno = int(e1); + return; +} + +func read(fd int, p *byte, np int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)); + n = int(r0); + errno = int(e1); + return; +} + +func write(fd int, p *byte, np int) (n int, errno int) { + r0, r1, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(np)); + n = int(r0); + errno = int(e1); + return; +} + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))); + fd = int(r0); + errno = int(e1); + return; +} + +func bind(s int, addr uintptr, addrlen _Socklen) (errno int) { + r0, r1, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)); + errno = int(e1); + return; +} + +func connect(s int, addr uintptr, addrlen _Socklen) (errno int) { + r0, r1, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)); + errno = int(e1); + return; +} + +func socket(domain int, typ int, proto int) (fd int, errno int) { + r0, r1, e1 := Syscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)); + fd = int(r0); + errno = int(e1); + return; +} + +func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { + r0, r1, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0); + errno = int(e1); + return; +} + +func Listen(s int, n int) (errno int) { + r0, r1, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(n), 0); + errno = int(e1); + return; +} + +func Shutdown(fd int, how int) (errno int) { + r0, r1, e1 := Syscall(SYS_SHUTDOWN, uintptr(fd), uintptr(how), 0); + errno = int(e1); + return; +} + + + diff --git a/src/pkg/syscall/zsysnum_darwin_386.go b/src/pkg/syscall/zsysnum_darwin_386.go new file mode 100644 index 000000000..c4c48c2a2 --- /dev/null +++ b/src/pkg/syscall/zsysnum_darwin_386.go @@ -0,0 +1,485 @@ +// mksysnum_darwin /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master +// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT + +package syscall + +const ( + // SYS_NOSYS = 0; // { int nosys(void); } { indirect syscall } + SYS_EXIT = 1; // { void exit(int rval); } + SYS_FORK = 2; // { int fork(void); } + SYS_READ = 3; // { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); } + SYS_WRITE = 4; // { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); } + SYS_OPEN = 5; // { int open(user_addr_t path, int flags, int mode); } + SYS_CLOSE = 6; // { int close(int fd); } + SYS_WAIT4 = 7; // { int wait4(int pid, user_addr_t status, int options, user_addr_t rusage); } + // SYS_NOSYS = 8; // { int nosys(void); } { old creat } + SYS_LINK = 9; // { int link(user_addr_t path, user_addr_t link); } + SYS_UNLINK = 10; // { int unlink(user_addr_t path); } + // SYS_NOSYS = 11; // { int nosys(void); } { old execv } + SYS_CHDIR = 12; // { int chdir(user_addr_t path); } + SYS_FCHDIR = 13; // { int fchdir(int fd); } + SYS_MKNOD = 14; // { int mknod(user_addr_t path, int mode, int dev); } + SYS_CHMOD = 15; // { int chmod(user_addr_t path, int mode); } + SYS_CHOWN = 16; // { int chown(user_addr_t path, int uid, int gid); } + SYS_OGETFSSTAT = 18; // { int ogetfsstat(user_addr_t buf, int bufsize, int flags); } + SYS_GETFSSTAT = 18; // { int getfsstat(user_addr_t buf, int bufsize, int flags); } + // SYS_NOSYS = 19; // { int nosys(void); } { old lseek } + SYS_GETPID = 20; // { int getpid(void); } + // SYS_NOSYS = 21; // { int nosys(void); } { old mount } + // SYS_NOSYS = 22; // { int nosys(void); } { old umount } + SYS_SETUID = 23; // { int setuid(uid_t uid); } + SYS_GETUID = 24; // { int getuid(void); } + SYS_GETEUID = 25; // { int geteuid(void); } + SYS_PTRACE = 26; // { int ptrace(int req, pid_t pid, caddr_t addr, int data); } + SYS_RECVMSG = 27; // { int recvmsg(int s, struct msghdr *msg, int flags); } + SYS_SENDMSG = 28; // { int sendmsg(int s, caddr_t msg, int flags); } + SYS_RECVFROM = 29; // { int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr); } + SYS_ACCEPT = 30; // { int accept(int s, caddr_t name, socklen_t *anamelen); } + SYS_GETPEERNAME = 31; // { int getpeername(int fdes, caddr_t asa, socklen_t *alen); } + SYS_GETSOCKNAME = 32; // { int getsockname(int fdes, caddr_t asa, socklen_t *alen); } + // SYS_NOSYS = 27; // { int nosys(void); } + // SYS_NOSYS = 28; // { int nosys(void); } + // SYS_NOSYS = 29; // { int nosys(void); } + // SYS_NOSYS = 30; // { int nosys(void); } + // SYS_NOSYS = 31; // { int nosys(void); } + // SYS_NOSYS = 32; // { int nosys(void); } + SYS_ACCESS = 33; // { int access(user_addr_t path, int flags); } + SYS_CHFLAGS = 34; // { int chflags(char *path, int flags); } + SYS_FCHFLAGS = 35; // { int fchflags(int fd, int flags); } + SYS_SYNC = 36; // { int sync(void); } + SYS_KILL = 37; // { int kill(int pid, int signum, int posix); } + // SYS_NOSYS = 38; // { int nosys(void); } { old stat } + SYS_GETPPID = 39; // { int getppid(void); } + // SYS_NOSYS = 40; // { int nosys(void); } { old lstat } + SYS_DUP = 41; // { int dup(u_int fd); } + SYS_PIPE = 42; // { int pipe(void); } + SYS_GETEGID = 43; // { int getegid(void); } + SYS_PROFIL = 44; // { int profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } + // SYS_NOSYS = 45; // { int nosys(void); } { old ktrace } + SYS_SIGACTION = 46; // { int sigaction(int signum, struct __sigaction *nsa, struct sigaction *osa); } + SYS_GETGID = 47; // { int getgid(void); } + SYS_SIGPROCMASK = 48; // { int sigprocmask(int how, user_addr_t mask, user_addr_t omask); } + SYS_GETLOGIN = 49; // { int getlogin(char *namebuf, u_int namelen); } + SYS_SETLOGIN = 50; // { int setlogin(char *namebuf); } + SYS_ACCT = 51; // { int acct(char *path); } + SYS_SIGPENDING = 52; // { int sigpending(struct sigvec *osv); } + SYS_SIGALTSTACK = 53; // { int sigaltstack(struct sigaltstack *nss, struct sigaltstack *oss); } + SYS_IOCTL = 54; // { int ioctl(int fd, u_long com, caddr_t data); } + SYS_REBOOT = 55; // { int reboot(int opt, char *command); } + SYS_REVOKE = 56; // { int revoke(char *path); } + SYS_SYMLINK = 57; // { int symlink(char *path, char *link); } + SYS_READLINK = 58; // { int readlink(char *path, char *buf, int count); } + SYS_EXECVE = 59; // { int execve(char *fname, char **argp, char **envp); } + SYS_UMASK = 60; // { int umask(int newmask); } + SYS_CHROOT = 61; // { int chroot(user_addr_t path); } + // SYS_NOSYS = 62; // { int nosys(void); } { old fstat } + // SYS_NOSYS = 63; // { int nosys(void); } { used internally, reserved } + // SYS_NOSYS = 64; // { int nosys(void); } { old getpagesize } + SYS_MSYNC = 65; // { int msync(caddr_t addr, size_t len, int flags); } + SYS_VFORK = 66; // { int vfork(void); } + // SYS_NOSYS = 67; // { int nosys(void); } { old vread } + // SYS_NOSYS = 68; // { int nosys(void); } { old vwrite } + SYS_SBRK = 69; // { int sbrk(int incr) NO_SYSCALL_STUB; } + SYS_SSTK = 70; // { int sstk(int incr) NO_SYSCALL_STUB; } + // SYS_NOSYS = 71; // { int nosys(void); } { old mmap } + SYS_OVADVISE = 72; // { int ovadvise(void) NO_SYSCALL_STUB; } { old vadvise } + SYS_MUNMAP = 73; // { int munmap(caddr_t addr, size_t len); } + SYS_MPROTECT = 74; // { int mprotect(caddr_t addr, size_t len, int prot); } + SYS_MADVISE = 75; // { int madvise(caddr_t addr, size_t len, int behav); } + // SYS_NOSYS = 76; // { int nosys(void); } { old vhangup } + // SYS_NOSYS = 77; // { int nosys(void); } { old vlimit } + SYS_MINCORE = 78; // { int mincore(user_addr_t addr, user_size_t len, user_addr_t vec); } + SYS_GETGROUPS = 79; // { int getgroups(u_int gidsetsize, gid_t *gidset); } + SYS_SETGROUPS = 80; // { int setgroups(u_int gidsetsize, gid_t *gidset); } + SYS_GETPGRP = 81; // { int getpgrp(void); } + SYS_SETPGID = 82; // { int setpgid(int pid, int pgid); } + SYS_SETITIMER = 83; // { int setitimer(u_int which, struct itimerval *itv, struct itimerval *oitv); } + // SYS_NOSYS = 84; // { int nosys(void); } { old wait } + SYS_SWAPON = 85; // { int swapon(void); } + SYS_GETITIMER = 86; // { int getitimer(u_int which, struct itimerval *itv); } + // SYS_NOSYS = 87; // { int nosys(void); } { old gethostname } + // SYS_NOSYS = 88; // { int nosys(void); } { old sethostname } + SYS_GETDTABLESIZE = 89; // { int getdtablesize(void); } + SYS_DUP2 = 90; // { int dup2(u_int from, u_int to); } + // SYS_NOSYS = 91; // { int nosys(void); } { old getdopt } + SYS_FCNTL = 92; // { int fcntl(int fd, int cmd, long arg); } + SYS_SELECT = 93; // { int select(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv); } + // SYS_NOSYS = 94; // { int nosys(void); } { old setdopt } + SYS_FSYNC = 95; // { int fsync(int fd); } + SYS_SETPRIORITY = 96; // { int setpriority(int which, id_t who, int prio); } + SYS_SOCKET = 97; // { int socket(int domain, int type, int protocol); } + SYS_CONNECT = 98; // { int connect(int s, caddr_t name, socklen_t namelen); } + // SYS_NOSYS = 97; // { int nosys(void); } + // SYS_NOSYS = 98; // { int nosys(void); } + // SYS_NOSYS = 99; // { int nosys(void); } { old accept } + SYS_GETPRIORITY = 100; // { int getpriority(int which, id_t who); } + // SYS_NOSYS = 101; // { int nosys(void); } { old send } + // SYS_NOSYS = 102; // { int nosys(void); } { old recv } + // SYS_NOSYS = 103; // { int nosys(void); } { old sigreturn } + SYS_BIND = 104; // { int bind(int s, caddr_t name, socklen_t namelen); } + SYS_SETSOCKOPT = 105; // { int setsockopt(int s, int level, int name, caddr_t val, socklen_t valsize); } + SYS_LISTEN = 106; // { int listen(int s, int backlog); } + // SYS_NOSYS = 104; // { int nosys(void); } + // SYS_NOSYS = 105; // { int nosys(void); } + // SYS_NOSYS = 106; // { int nosys(void); } + // SYS_NOSYS = 107; // { int nosys(void); } { old vtimes } + // SYS_NOSYS = 108; // { int nosys(void); } { old sigvec } + // SYS_NOSYS = 109; // { int nosys(void); } { old sigblock } + // SYS_NOSYS = 110; // { int nosys(void); } { old sigsetmask } + SYS_SIGSUSPEND = 111; // { int sigsuspend(sigset_t mask); } + // SYS_NOSYS = 112; // { int nosys(void); } { old sigstack } + // SYS_NOSYS = 113; // { int nosys(void); } { old recvmsg } + // SYS_NOSYS = 114; // { int nosys(void); } { old sendmsg } + // SYS_NOSYS = 113; // { int nosys(void); } + // SYS_NOSYS = 114; // { int nosys(void); } + // SYS_NOSYS = 115; // { int nosys(void); } { old vtrace } + SYS_GETTIMEOFDAY = 116; // { int gettimeofday(struct timeval *tp, struct timezone *tzp); } + SYS_GETRUSAGE = 117; // { int getrusage(int who, struct rusage *rusage); } + SYS_GETSOCKOPT = 118; // { int getsockopt(int s, int level, int name, caddr_t val, socklen_t *avalsize); } + // SYS_NOSYS = 118; // { int nosys(void); } + // SYS_NOSYS = 119; // { int nosys(void); } { old resuba } + SYS_READV = 120; // { user_ssize_t readv(int fd, struct iovec *iovp, u_int iovcnt); } + SYS_WRITEV = 121; // { user_ssize_t writev(int fd, struct iovec *iovp, u_int iovcnt); } + SYS_SETTIMEOFDAY = 122; // { int settimeofday(struct timeval *tv, struct timezone *tzp); } + SYS_FCHOWN = 123; // { int fchown(int fd, int uid, int gid); } + SYS_FCHMOD = 124; // { int fchmod(int fd, int mode); } + // SYS_NOSYS = 125; // { int nosys(void); } { old recvfrom } + SYS_SETREUID = 126; // { int setreuid(uid_t ruid, uid_t euid); } + SYS_SETREGID = 127; // { int setregid(gid_t rgid, gid_t egid); } + SYS_RENAME = 128; // { int rename(char *from, char *to); } + // SYS_NOSYS = 129; // { int nosys(void); } { old truncate } + // SYS_NOSYS = 130; // { int nosys(void); } { old ftruncate } + SYS_FLOCK = 131; // { int flock(int fd, int how); } + SYS_MKFIFO = 132; // { int mkfifo(user_addr_t path, int mode); } + SYS_SENDTO = 133; // { int sendto(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen); } + SYS_SHUTDOWN = 134; // { int shutdown(int s, int how); } + SYS_SOCKETPAIR = 135; // { int socketpair(int domain, int type, int protocol, int *rsv); } + // SYS_NOSYS = 133; // { int nosys(void); } + // SYS_NOSYS = 134; // { int nosys(void); } + // SYS_NOSYS = 135; // { int nosys(void); } + SYS_MKDIR = 136; // { int mkdir(user_addr_t path, int mode); } + SYS_RMDIR = 137; // { int rmdir(char *path); } + SYS_UTIMES = 138; // { int utimes(char *path, struct timeval *tptr); } + SYS_FUTIMES = 139; // { int futimes(int fd, struct timeval *tptr); } + SYS_ADJTIME = 140; // { int adjtime(struct timeval *delta, struct timeval *olddelta); } + // SYS_NOSYS = 141; // { int nosys(void); } { old getpeername } + SYS_GETHOSTUUID = 142; // { int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp); } + // SYS_NOSYS = 143; // { int nosys(void); } { old sethostid } + // SYS_NOSYS = 144; // { int nosys(void); } { old getrlimit } + // SYS_NOSYS = 145; // { int nosys(void); } { old setrlimit } + // SYS_NOSYS = 146; // { int nosys(void); } { old killpg } + SYS_SETSID = 147; // { int setsid(void); } + // SYS_NOSYS = 148; // { int nosys(void); } { old setquota } + // SYS_NOSYS = 149; // { int nosys(void); } { old qquota } + // SYS_NOSYS = 150; // { int nosys(void); } { old getsockname } + SYS_GETPGID = 151; // { int getpgid(pid_t pid); } + SYS_SETPRIVEXEC = 152; // { int setprivexec(int flag); } + SYS_PREAD = 153; // { user_ssize_t pread(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } + SYS_PWRITE = 154; // { user_ssize_t pwrite(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } + SYS_NFSSVC = 155; // { int nfssvc(int flag, caddr_t argp); } + // SYS_NOSYS = 155; // { int nosys(void); } + // SYS_NOSYS = 156; // { int nosys(void); } { old getdirentries } + SYS_STATFS = 157; // { int statfs(char *path, struct statfs *buf); } + SYS_FSTATFS = 158; // { int fstatfs(int fd, struct statfs *buf); } + SYS_UNMOUNT = 159; // { int unmount(user_addr_t path, int flags); } + // SYS_NOSYS = 160; // { int nosys(void); } { old async_daemon } + SYS_GETFH = 161; // { int getfh(char *fname, fhandle_t *fhp); } + // SYS_NOSYS = 161; // { int nosys(void); } + // SYS_NOSYS = 162; // { int nosys(void); } { old getdomainname } + // SYS_NOSYS = 163; // { int nosys(void); } { old setdomainname } + // SYS_NOSYS = 164; // { int nosys(void); } + SYS_QUOTACTL = 165; // { int quotactl(const char *path, int cmd, int uid, caddr_t arg); } + // SYS_NOSYS = 166; // { int nosys(void); } { old exportfs } + SYS_MOUNT = 167; // { int mount(char *type, char *path, int flags, caddr_t data); } + // SYS_NOSYS = 168; // { int nosys(void); } { old ustat } + SYS_CSOPS = 169; // { int csops(pid_t pid, uint32_t ops, user_addr_t useraddr, user_size_t usersize); } + // SYS_NOSYS = 171; // { int nosys(void); } { old wait3 } + // SYS_NOSYS = 172; // { int nosys(void); } { old rpause } + SYS_WAITID = 173; // { int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); } + // SYS_NOSYS = 174; // { int nosys(void); } { old getdents } + // SYS_NOSYS = 175; // { int nosys(void); } { old gc_control } + SYS_ADD_PROFIL = 176; // { int add_profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } + // SYS_NOSYS = 177; // { int nosys(void); } + // SYS_NOSYS = 178; // { int nosys(void); } + // SYS_NOSYS = 179; // { int nosys(void); } + SYS_KDEBUG_TRACE = 180; // { int kdebug_trace(int code, int arg1, int arg2, int arg3, int arg4, int arg5) NO_SYSCALL_STUB; } + SYS_SETGID = 181; // { int setgid(gid_t gid); } + SYS_SETEGID = 182; // { int setegid(gid_t egid); } + SYS_SETEUID = 183; // { int seteuid(uid_t euid); } + SYS_SIGRETURN = 184; // { int sigreturn(struct ucontext *uctx, int infostyle); } + // SYS_NOSYS = 186; // { int nosys(void); } + // SYS_NOSYS = 187; // { int nosys(void); } + SYS_STAT = 188; // { int stat(user_addr_t path, user_addr_t ub); } + SYS_FSTAT = 189; // { int fstat(int fd, user_addr_t ub); } + SYS_LSTAT = 190; // { int lstat(user_addr_t path, user_addr_t ub); } + SYS_PATHCONF = 191; // { int pathconf(char *path, int name); } + SYS_FPATHCONF = 192; // { int fpathconf(int fd, int name); } + // SYS_NOSYS = 193; // { int nosys(void); } + SYS_GETRLIMIT = 194; // { int getrlimit(u_int which, struct rlimit *rlp); } + SYS_SETRLIMIT = 195; // { int setrlimit(u_int which, struct rlimit *rlp); } + SYS_GETDIRENTRIES = 196; // { int getdirentries(int fd, char *buf, u_int count, long *basep); } + SYS_MMAP = 197; // { user_addr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } + // SYS_NOSYS = 198; // { int nosys(void); } { __syscall } + SYS_LSEEK = 199; // { off_t lseek(int fd, off_t offset, int whence); } + SYS_TRUNCATE = 200; // { int truncate(char *path, off_t length); } + SYS_FTRUNCATE = 201; // { int ftruncate(int fd, off_t length); } + SYS___SYSCTL = 202; // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } + SYS_MLOCK = 203; // { int mlock(caddr_t addr, size_t len); } + SYS_MUNLOCK = 204; // { int munlock(caddr_t addr, size_t len); } + SYS_UNDELETE = 205; // { int undelete(user_addr_t path); } + SYS_ATSOCKET = 206; // { int ATsocket(int proto); } + // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } + // SYS_NOSYS = 206; // { int nosys(void); } + // SYS_NOSYS = 207; // { int nosys(void); } + // SYS_NOSYS = 208; // { int nosys(void); } + // SYS_NOSYS = 209; // { int nosys(void); } + // SYS_NOSYS = 210; // { int nosys(void); } + // SYS_NOSYS = 211; // { int nosys(void); } + // SYS_NOSYS = 212; // { int nosys(void); } + // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } + SYS_KQUEUE_FROM_PORTSET_NP = 214; // { int kqueue_from_portset_np(int portset); } + SYS_KQUEUE_PORTSET_NP = 215; // { int kqueue_portset_np(int fd); } + SYS_GETATTRLIST = 220; // { int getattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } + SYS_SETATTRLIST = 221; // { int setattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } + SYS_GETDIRENTRIESATTR = 222; // { int getdirentriesattr(int fd, struct attrlist *alist, void *buffer, size_t buffersize, u_long *count, u_long *basep, u_long *newstate, u_long options); } + SYS_EXCHANGEDATA = 223; // { int exchangedata(const char *path1, const char *path2, u_long options); } + // SYS_NOSYS = 224; // { int nosys(void); } { was checkuseraccess } + SYS_SEARCHFS = 225; // { int searchfs(const char *path, struct fssearchblock *searchblock, u_long *nummatches, u_long scriptcode, u_long options, struct searchstate *state); } + SYS_DELETE = 226; // { int delete(user_addr_t path) NO_SYSCALL_STUB; } { private delete (Carbon semantics) } + SYS_COPYFILE = 227; // { int copyfile(char *from, char *to, int mode, int flags) NO_SYSCALL_STUB; } + // SYS_NOSYS = 228; // { int nosys(void); } + // SYS_NOSYS = 229; // { int nosys(void); } + SYS_POLL = 230; // { int poll(struct pollfd *fds, u_int nfds, int timeout); } + SYS_WATCHEVENT = 231; // { int watchevent(struct eventreq *u_req, int u_eventmask); } + SYS_WAITEVENT = 232; // { int waitevent(struct eventreq *u_req, struct timeval *tv); } + SYS_MODWATCH = 233; // { int modwatch(struct eventreq *u_req, int u_eventmask); } + SYS_GETXATTR = 234; // { user_ssize_t getxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_FGETXATTR = 235; // { user_ssize_t fgetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_SETXATTR = 236; // { int setxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_FSETXATTR = 237; // { int fsetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_REMOVEXATTR = 238; // { int removexattr(user_addr_t path, user_addr_t attrname, int options); } + SYS_FREMOVEXATTR = 239; // { int fremovexattr(int fd, user_addr_t attrname, int options); } + SYS_LISTXATTR = 240; // { user_ssize_t listxattr(user_addr_t path, user_addr_t namebuf, size_t bufsize, int options); } + SYS_FLISTXATTR = 241; // { user_ssize_t flistxattr(int fd, user_addr_t namebuf, size_t bufsize, int options); } + SYS_FSCTL = 242; // { int fsctl(const char *path, u_long cmd, caddr_t data, u_long options); } + SYS_INITGROUPS = 243; // { int initgroups(u_int gidsetsize, gid_t *gidset, int gmuid); } + SYS_POSIX_SPAWN = 244; // { int posix_spawn(pid_t *pid, const char *path, const struct _posix_spawn_args_desc *adesc, char **argv, char **envp); } + // SYS_NOSYS = 245; // { int nosys(void); } + // SYS_NOSYS = 246; // { int nosys(void); } + SYS_NFSCLNT = 247; // { int nfsclnt(int flag, caddr_t argp); } + // SYS_NOSYS = 247; // { int nosys(void); } + SYS_FHOPEN = 248; // { int fhopen(const struct fhandle *u_fhp, int flags); } + // SYS_NOSYS = 248; // { int nosys(void); } + // SYS_NOSYS = 249; // { int nosys(void); } + SYS_MINHERIT = 250; // { int minherit(void *addr, size_t len, int inherit); } + SYS_SEMSYS = 251; // { int semsys(u_int which, int a2, int a3, int a4, int a5); } + // SYS_NOSYS = 251; // { int nosys(void); } + SYS_MSGSYS = 252; // { int msgsys(u_int which, int a2, int a3, int a4, int a5); } + // SYS_NOSYS = 252; // { int nosys(void); } + SYS_SHMSYS = 253; // { int shmsys(u_int which, int a2, int a3, int a4); } + // SYS_NOSYS = 253; // { int nosys(void); } + SYS_SEMCTL = 254; // { int semctl(int semid, int semnum, int cmd, semun_t arg); } + SYS_SEMGET = 255; // { int semget(key_t key, int nsems, int semflg); } + SYS_SEMOP = 256; // { int semop(int semid, struct sembuf *sops, int nsops); } + // SYS_NOSYS = 257; // { int nosys(void); } + // SYS_NOSYS = 254; // { int nosys(void); } + // SYS_NOSYS = 255; // { int nosys(void); } + // SYS_NOSYS = 256; // { int nosys(void); } + // SYS_NOSYS = 257; // { int nosys(void); } + SYS_MSGCTL = 258; // { int msgctl(int msqid, int cmd, struct msqid_ds *buf); } + SYS_MSGGET = 259; // { int msgget(key_t key, int msgflg); } + SYS_MSGSND = 260; // { int msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg); } + SYS_MSGRCV = 261; // { user_ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + // SYS_NOSYS = 258; // { int nosys(void); } + // SYS_NOSYS = 259; // { int nosys(void); } + // SYS_NOSYS = 260; // { int nosys(void); } + // SYS_NOSYS = 261; // { int nosys(void); } + SYS_SHMAT = 262; // { user_addr_t shmat(int shmid, void *shmaddr, int shmflg); } + SYS_SHMCTL = 263; // { int shmctl(int shmid, int cmd, struct shmid_ds *buf); } + SYS_SHMDT = 264; // { int shmdt(void *shmaddr); } + SYS_SHMGET = 265; // { int shmget(key_t key, size_t size, int shmflg); } + // SYS_NOSYS = 262; // { int nosys(void); } + // SYS_NOSYS = 263; // { int nosys(void); } + // SYS_NOSYS = 264; // { int nosys(void); } + // SYS_NOSYS = 265; // { int nosys(void); } + SYS_SHM_OPEN = 266; // { int shm_open(const char *name, int oflag, int mode); } + SYS_SHM_UNLINK = 267; // { int shm_unlink(const char *name); } + SYS_SEM_OPEN = 268; // { user_addr_t sem_open(const char *name, int oflag, int mode, int value); } + SYS_SEM_CLOSE = 269; // { int sem_close(sem_t *sem); } + SYS_SEM_UNLINK = 270; // { int sem_unlink(const char *name); } + SYS_SEM_WAIT = 271; // { int sem_wait(sem_t *sem); } + SYS_SEM_TRYWAIT = 272; // { int sem_trywait(sem_t *sem); } + SYS_SEM_POST = 273; // { int sem_post(sem_t *sem); } + SYS_SEM_GETVALUE = 274; // { int sem_getvalue(sem_t *sem, int *sval); } + SYS_SEM_INIT = 275; // { int sem_init(sem_t *sem, int phsared, u_int value); } + SYS_SEM_DESTROY = 276; // { int sem_destroy(sem_t *sem); } + SYS_OPEN_EXTENDED = 277; // { int open_extended(user_addr_t path, int flags, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_UMASK_EXTENDED = 278; // { int umask_extended(int newmask, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_STAT_EXTENDED = 279; // { int stat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_LSTAT_EXTENDED = 280; // { int lstat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_FSTAT_EXTENDED = 281; // { int fstat_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_CHMOD_EXTENDED = 282; // { int chmod_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_FCHMOD_EXTENDED = 283; // { int fchmod_extended(int fd, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_ACCESS_EXTENDED = 284; // { int access_extended(user_addr_t entries, size_t size, user_addr_t results, uid_t uid) NO_SYSCALL_STUB; } + SYS_SETTID = 285; // { int settid(uid_t uid, gid_t gid) NO_SYSCALL_STUB; } + SYS_GETTID = 286; // { int gettid(uid_t *uidp, gid_t *gidp) NO_SYSCALL_STUB; } + SYS_SETSGROUPS = 287; // { int setsgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_GETSGROUPS = 288; // { int getsgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_SETWGROUPS = 289; // { int setwgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_GETWGROUPS = 290; // { int getwgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_MKFIFO_EXTENDED = 291; // { int mkfifo_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_MKDIR_EXTENDED = 292; // { int mkdir_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_IDENTITYSVC = 293; // { int identitysvc(int opcode, user_addr_t message) NO_SYSCALL_STUB; } + SYS_SHARED_REGION_CHECK_NP = 294; // { int shared_region_check_np(uint64_t *start_address) NO_SYSCALL_STUB; } + SYS_SHARED_REGION_MAP_NP = 295; // { int shared_region_map_np(int fd, uint32_t count, const struct shared_file_mapping_np *mappings) NO_SYSCALL_STUB; } + // SYS_NOSYS = 296; // { int nosys(void); } { old load_shared_file } + // SYS_NOSYS = 297; // { int nosys(void); } { old reset_shared_file } + // SYS_NOSYS = 298; // { int nosys(void); } { old new_system_shared_regions } + // SYS_ENOSYS = 299; // { int enosys(void); } { old shared_region_map_file_np } + // SYS_ENOSYS = 300; // { int enosys(void); } { old shared_region_make_private_np } + SYS___PTHREAD_MUTEX_DESTROY = 301; // { int __pthread_mutex_destroy(int mutexid); } + SYS___PTHREAD_MUTEX_INIT = 302; // { int __pthread_mutex_init(user_addr_t mutex, user_addr_t attr); } + SYS___PTHREAD_MUTEX_LOCK = 303; // { int __pthread_mutex_lock(int mutexid); } + SYS___PTHREAD_MUTEX_TRYLOCK = 304; // { int __pthread_mutex_trylock(int mutexid); } + SYS___PTHREAD_MUTEX_UNLOCK = 305; // { int __pthread_mutex_unlock(int mutexid); } + SYS___PTHREAD_COND_INIT = 306; // { int __pthread_cond_init(user_addr_t cond, user_addr_t attr); } + SYS___PTHREAD_COND_DESTROY = 307; // { int __pthread_cond_destroy(int condid); } + SYS___PTHREAD_COND_BROADCAST = 308; // { int __pthread_cond_broadcast(int condid); } + SYS___PTHREAD_COND_SIGNAL = 309; // { int __pthread_cond_signal(int condid); } + SYS_GETSID = 310; // { int getsid(pid_t pid); } + SYS_SETTID_WITH_PID = 311; // { int settid_with_pid(pid_t pid, int assume) NO_SYSCALL_STUB; } + SYS___PTHREAD_COND_TIMEDWAIT = 312; // { int __pthread_cond_timedwait(int condid, int mutexid, user_addr_t abstime); } + SYS_AIO_FSYNC = 313; // { int aio_fsync(int op, user_addr_t aiocbp); } + SYS_AIO_RETURN = 314; // { user_ssize_t aio_return(user_addr_t aiocbp); } + SYS_AIO_SUSPEND = 315; // { int aio_suspend(user_addr_t aiocblist, int nent, user_addr_t timeoutp); } + SYS_AIO_CANCEL = 316; // { int aio_cancel(int fd, user_addr_t aiocbp); } + SYS_AIO_ERROR = 317; // { int aio_error(user_addr_t aiocbp); } + SYS_AIO_READ = 318; // { int aio_read(user_addr_t aiocbp); } + SYS_AIO_WRITE = 319; // { int aio_write(user_addr_t aiocbp); } + SYS_LIO_LISTIO = 320; // { int lio_listio(int mode, user_addr_t aiocblist, int nent, user_addr_t sigp); } + SYS___PTHREAD_COND_WAIT = 321; // { int __pthread_cond_wait(int condid, int mutexid); } + SYS_IOPOLICYSYS = 322; // { int iopolicysys(int cmd, void *arg) NO_SYSCALL_STUB; } + // SYS_NOSYS = 323; // { int nosys(void); } + SYS_MLOCKALL = 324; // { int mlockall(int how); } + SYS_MUNLOCKALL = 325; // { int munlockall(int how); } + // SYS_NOSYS = 326; // { int nosys(void); } + SYS_ISSETUGID = 327; // { int issetugid(void); } + SYS___PTHREAD_KILL = 328; // { int __pthread_kill(int thread_port, int sig); } + SYS___PTHREAD_SIGMASK = 329; // { int __pthread_sigmask(int how, user_addr_t set, user_addr_t oset); } + SYS___SIGWAIT = 330; // { int __sigwait(user_addr_t set, user_addr_t sig); } + SYS___DISABLE_THREADSIGNAL = 331; // { int __disable_threadsignal(int value); } + SYS___PTHREAD_MARKCANCEL = 332; // { int __pthread_markcancel(int thread_port); } + SYS___PTHREAD_CANCELED = 333; // { int __pthread_canceled(int action); } + SYS___SEMWAIT_SIGNAL = 334; // { int __semwait_signal(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec); } + // SYS_NOSYS = 335; // { int nosys(void); } { old utrace } + SYS_PROC_INFO = 336; // { int proc_info(int32_t callnum,int32_t pid,uint32_t flavor, uint64_t arg,user_addr_t buffer,int32_t buffersize) NO_SYSCALL_STUB; } + SYS_SENDFILE = 337; // { int sendfile(int fd, int s, off_t offset, off_t *nbytes, struct sf_hdtr *hdtr, int flags); } + // SYS_NOSYS = 337; // { int nosys(void); } + SYS_STAT64 = 338; // { int stat64(user_addr_t path, user_addr_t ub); } + SYS_FSTAT64 = 339; // { int fstat64(int fd, user_addr_t ub); } + SYS_LSTAT64 = 340; // { int lstat64(user_addr_t path, user_addr_t ub); } + SYS_STAT64_EXTENDED = 341; // { int stat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_LSTAT64_EXTENDED = 342; // { int lstat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_FSTAT64_EXTENDED = 343; // { int fstat64_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_GETDIRENTRIES64 = 344; // { user_ssize_t getdirentries64(int fd, void *buf, user_size_t bufsize, off_t *position) NO_SYSCALL_STUB; } + SYS_STATFS64 = 345; // { int statfs64(char *path, struct statfs64 *buf); } + SYS_FSTATFS64 = 346; // { int fstatfs64(int fd, struct statfs64 *buf); } + SYS_GETFSSTAT64 = 347; // { int getfsstat64(user_addr_t buf, int bufsize, int flags); } + SYS___PTHREAD_CHDIR = 348; // { int __pthread_chdir(user_addr_t path); } + SYS___PTHREAD_FCHDIR = 349; // { int __pthread_fchdir(int fd); } + SYS_AUDIT = 350; // { int audit(void *record, int length); } + SYS_AUDITON = 351; // { int auditon(int cmd, void *data, int length); } + // SYS_NOSYS = 352; // { int nosys(void); } + SYS_GETAUID = 353; // { int getauid(au_id_t *auid); } + SYS_SETAUID = 354; // { int setauid(au_id_t *auid); } + SYS_GETAUDIT = 355; // { int getaudit(struct auditinfo *auditinfo); } + SYS_SETAUDIT = 356; // { int setaudit(struct auditinfo *auditinfo); } + SYS_GETAUDIT_ADDR = 357; // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } + SYS_SETAUDIT_ADDR = 358; // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } + SYS_AUDITCTL = 359; // { int auditctl(char *path); } + // SYS_NOSYS = 350; // { int nosys(void); } + // SYS_NOSYS = 351; // { int nosys(void); } + // SYS_NOSYS = 352; // { int nosys(void); } + // SYS_NOSYS = 353; // { int nosys(void); } + // SYS_NOSYS = 354; // { int nosys(void); } + // SYS_NOSYS = 355; // { int nosys(void); } + // SYS_NOSYS = 356; // { int nosys(void); } + // SYS_NOSYS = 357; // { int nosys(void); } + // SYS_NOSYS = 358; // { int nosys(void); } + // SYS_NOSYS = 359; // { int nosys(void); } + SYS_BSDTHREAD_CREATE = 360; // { user_addr_t bsdthread_create(user_addr_t func, user_addr_t func_arg, user_addr_t stack, user_addr_t pthread, uint32_t flags) NO_SYSCALL_STUB; } + SYS_BSDTHREAD_TERMINATE = 361; // { int bsdthread_terminate(user_addr_t stackaddr, size_t freesize, uint32_t port, uint32_t sem) NO_SYSCALL_STUB; } + SYS_KQUEUE = 362; // { int kqueue(void); } + SYS_KEVENT = 363; // { int kevent(int fd, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } + SYS_LCHOWN = 364; // { int lchown(user_addr_t path, uid_t owner, gid_t group); } + SYS_STACK_SNAPSHOT = 365; // { int stack_snapshot(pid_t pid, user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t options) NO_SYSCALL_STUB; } + SYS_BSDTHREAD_REGISTER = 366; // { int bsdthread_register(user_addr_t threadstart, user_addr_t wqthread, int pthsize) NO_SYSCALL_STUB; } + SYS_WORKQ_OPEN = 367; // { int workq_open(void) NO_SYSCALL_STUB; } + SYS_WORKQ_OPS = 368; // { int workq_ops(int options, user_addr_t item, int prio) NO_SYSCALL_STUB; } + // SYS_NOSYS = 369; // { int nosys(void); } + // SYS_NOSYS = 370; // { int nosys(void); } + // SYS_NOSYS = 371; // { int nosys(void); } + // SYS_NOSYS = 372; // { int nosys(void); } + // SYS_NOSYS = 373; // { int nosys(void); } + // SYS_NOSYS = 374; // { int nosys(void); } + // SYS_NOSYS = 375; // { int nosys(void); } + // SYS_NOSYS = 376; // { int nosys(void); } + // SYS_NOSYS = 377; // { int nosys(void); } + // SYS_NOSYS = 378; // { int nosys(void); } + // SYS_NOSYS = 379; // { int nosys(void); } + SYS___MAC_EXECVE = 380; // { int __mac_execve(char *fname, char **argp, char **envp, struct mac *mac_p); } + SYS___MAC_SYSCALL = 381; // { int __mac_syscall(char *policy, int call, user_addr_t arg); } + SYS___MAC_GET_FILE = 382; // { int __mac_get_file(char *path_p, struct mac *mac_p); } + SYS___MAC_SET_FILE = 383; // { int __mac_set_file(char *path_p, struct mac *mac_p); } + SYS___MAC_GET_LINK = 384; // { int __mac_get_link(char *path_p, struct mac *mac_p); } + SYS___MAC_SET_LINK = 385; // { int __mac_set_link(char *path_p, struct mac *mac_p); } + SYS___MAC_GET_PROC = 386; // { int __mac_get_proc(struct mac *mac_p); } + SYS___MAC_SET_PROC = 387; // { int __mac_set_proc(struct mac *mac_p); } + SYS___MAC_GET_FD = 388; // { int __mac_get_fd(int fd, struct mac *mac_p); } + SYS___MAC_SET_FD = 389; // { int __mac_set_fd(int fd, struct mac *mac_p); } + SYS___MAC_GET_PID = 390; // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } + SYS___MAC_GET_LCID = 391; // { int __mac_get_lcid(pid_t lcid, struct mac *mac_p); } + SYS___MAC_GET_LCTX = 392; // { int __mac_get_lctx(struct mac *mac_p); } + SYS___MAC_SET_LCTX = 393; // { int __mac_set_lctx(struct mac *mac_p); } + SYS_SETLCID = 394; // { int setlcid(pid_t pid, pid_t lcid) NO_SYSCALL_STUB; } + SYS_GETLCID = 395; // { int getlcid(pid_t pid) NO_SYSCALL_STUB; } + SYS_READ_NOCANCEL = 396; // { user_ssize_t read_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } + SYS_WRITE_NOCANCEL = 397; // { user_ssize_t write_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } + SYS_OPEN_NOCANCEL = 398; // { int open_nocancel(user_addr_t path, int flags, int mode) NO_SYSCALL_STUB; } + SYS_CLOSE_NOCANCEL = 399; // { int close_nocancel(int fd) NO_SYSCALL_STUB; } + SYS_WAIT4_NOCANCEL = 400; // { int wait4_nocancel(int pid, user_addr_t status, int options, user_addr_t rusage) NO_SYSCALL_STUB; } + SYS_RECVMSG_NOCANCEL = 401; // { int recvmsg_nocancel(int s, struct msghdr *msg, int flags) NO_SYSCALL_STUB; } + SYS_SENDMSG_NOCANCEL = 402; // { int sendmsg_nocancel(int s, caddr_t msg, int flags) NO_SYSCALL_STUB; } + SYS_RECVFROM_NOCANCEL = 403; // { int recvfrom_nocancel(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr) NO_SYSCALL_STUB; } + SYS_ACCEPT_NOCANCEL = 404; // { int accept_nocancel(int s, caddr_t name, socklen_t *anamelen) NO_SYSCALL_STUB; } + // SYS_NOSYS = 401; // { int nosys(void); } + // SYS_NOSYS = 402; // { int nosys(void); } + // SYS_NOSYS = 403; // { int nosys(void); } + // SYS_NOSYS = 404; // { int nosys(void); } + SYS_MSYNC_NOCANCEL = 405; // { int msync_nocancel(caddr_t addr, size_t len, int flags) NO_SYSCALL_STUB; } + SYS_FCNTL_NOCANCEL = 406; // { int fcntl_nocancel(int fd, int cmd, long arg) NO_SYSCALL_STUB; } + SYS_SELECT_NOCANCEL = 407; // { int select_nocancel(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv) NO_SYSCALL_STUB; } + SYS_FSYNC_NOCANCEL = 408; // { int fsync_nocancel(int fd) NO_SYSCALL_STUB; } + SYS_CONNECT_NOCANCEL = 409; // { int connect_nocancel(int s, caddr_t name, socklen_t namelen) NO_SYSCALL_STUB; } + // SYS_NOSYS = 409; // { int nosys(void); } + SYS_SIGSUSPEND_NOCANCEL = 410; // { int sigsuspend_nocancel(sigset_t mask) NO_SYSCALL_STUB; } + SYS_READV_NOCANCEL = 411; // { user_ssize_t readv_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } + SYS_WRITEV_NOCANCEL = 412; // { user_ssize_t writev_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } + SYS_SENDTO_NOCANCEL = 413; // { int sendto_nocancel(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen) NO_SYSCALL_STUB; } + // SYS_NOSYS = 413; // { int nosys(void); } + SYS_PREAD_NOCANCEL = 414; // { user_ssize_t pread_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } + SYS_PWRITE_NOCANCEL = 415; // { user_ssize_t pwrite_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } + SYS_WAITID_NOCANCEL = 416; // { int waitid_nocancel(idtype_t idtype, id_t id, siginfo_t *infop, int options) NO_SYSCALL_STUB; } + SYS_POLL_NOCANCEL = 417; // { int poll_nocancel(struct pollfd *fds, u_int nfds, int timeout) NO_SYSCALL_STUB; } + SYS_MSGSND_NOCANCEL = 418; // { int msgsnd_nocancel(int msqid, void *msgp, size_t msgsz, int msgflg) NO_SYSCALL_STUB; } + SYS_MSGRCV_NOCANCEL = 419; // { user_ssize_t msgrcv_nocancel(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) NO_SYSCALL_STUB; } + // SYS_NOSYS = 418; // { int nosys(void); } + // SYS_NOSYS = 419; // { int nosys(void); } + SYS_SEM_WAIT_NOCANCEL = 420; // { int sem_wait_nocancel(sem_t *sem) NO_SYSCALL_STUB; } + SYS_AIO_SUSPEND_NOCANCEL = 421; // { int aio_suspend_nocancel(user_addr_t aiocblist, int nent, user_addr_t timeoutp) NO_SYSCALL_STUB; } + SYS___SIGWAIT_NOCANCEL = 422; // { int __sigwait_nocancel(user_addr_t set, user_addr_t sig) NO_SYSCALL_STUB; } + SYS___SEMWAIT_SIGNAL_NOCANCEL = 423; // { int __semwait_signal_nocancel(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec) NO_SYSCALL_STUB; } + SYS___MAC_MOUNT = 424; // { int __mac_mount(char *type, char *path, int flags, caddr_t data, struct mac *mac_p); } + SYS___MAC_GET_MOUNT = 425; // { int __mac_get_mount(char *path, struct mac *mac_p); } + SYS___MAC_GETFSSTAT = 426; // { int __mac_getfsstat(user_addr_t buf, int bufsize, user_addr_t mac, int macsize, int flags); } +) diff --git a/src/pkg/syscall/zsysnum_darwin_amd64.go b/src/pkg/syscall/zsysnum_darwin_amd64.go new file mode 100644 index 000000000..c4c48c2a2 --- /dev/null +++ b/src/pkg/syscall/zsysnum_darwin_amd64.go @@ -0,0 +1,485 @@ +// mksysnum_darwin /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master +// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT + +package syscall + +const ( + // SYS_NOSYS = 0; // { int nosys(void); } { indirect syscall } + SYS_EXIT = 1; // { void exit(int rval); } + SYS_FORK = 2; // { int fork(void); } + SYS_READ = 3; // { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); } + SYS_WRITE = 4; // { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); } + SYS_OPEN = 5; // { int open(user_addr_t path, int flags, int mode); } + SYS_CLOSE = 6; // { int close(int fd); } + SYS_WAIT4 = 7; // { int wait4(int pid, user_addr_t status, int options, user_addr_t rusage); } + // SYS_NOSYS = 8; // { int nosys(void); } { old creat } + SYS_LINK = 9; // { int link(user_addr_t path, user_addr_t link); } + SYS_UNLINK = 10; // { int unlink(user_addr_t path); } + // SYS_NOSYS = 11; // { int nosys(void); } { old execv } + SYS_CHDIR = 12; // { int chdir(user_addr_t path); } + SYS_FCHDIR = 13; // { int fchdir(int fd); } + SYS_MKNOD = 14; // { int mknod(user_addr_t path, int mode, int dev); } + SYS_CHMOD = 15; // { int chmod(user_addr_t path, int mode); } + SYS_CHOWN = 16; // { int chown(user_addr_t path, int uid, int gid); } + SYS_OGETFSSTAT = 18; // { int ogetfsstat(user_addr_t buf, int bufsize, int flags); } + SYS_GETFSSTAT = 18; // { int getfsstat(user_addr_t buf, int bufsize, int flags); } + // SYS_NOSYS = 19; // { int nosys(void); } { old lseek } + SYS_GETPID = 20; // { int getpid(void); } + // SYS_NOSYS = 21; // { int nosys(void); } { old mount } + // SYS_NOSYS = 22; // { int nosys(void); } { old umount } + SYS_SETUID = 23; // { int setuid(uid_t uid); } + SYS_GETUID = 24; // { int getuid(void); } + SYS_GETEUID = 25; // { int geteuid(void); } + SYS_PTRACE = 26; // { int ptrace(int req, pid_t pid, caddr_t addr, int data); } + SYS_RECVMSG = 27; // { int recvmsg(int s, struct msghdr *msg, int flags); } + SYS_SENDMSG = 28; // { int sendmsg(int s, caddr_t msg, int flags); } + SYS_RECVFROM = 29; // { int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr); } + SYS_ACCEPT = 30; // { int accept(int s, caddr_t name, socklen_t *anamelen); } + SYS_GETPEERNAME = 31; // { int getpeername(int fdes, caddr_t asa, socklen_t *alen); } + SYS_GETSOCKNAME = 32; // { int getsockname(int fdes, caddr_t asa, socklen_t *alen); } + // SYS_NOSYS = 27; // { int nosys(void); } + // SYS_NOSYS = 28; // { int nosys(void); } + // SYS_NOSYS = 29; // { int nosys(void); } + // SYS_NOSYS = 30; // { int nosys(void); } + // SYS_NOSYS = 31; // { int nosys(void); } + // SYS_NOSYS = 32; // { int nosys(void); } + SYS_ACCESS = 33; // { int access(user_addr_t path, int flags); } + SYS_CHFLAGS = 34; // { int chflags(char *path, int flags); } + SYS_FCHFLAGS = 35; // { int fchflags(int fd, int flags); } + SYS_SYNC = 36; // { int sync(void); } + SYS_KILL = 37; // { int kill(int pid, int signum, int posix); } + // SYS_NOSYS = 38; // { int nosys(void); } { old stat } + SYS_GETPPID = 39; // { int getppid(void); } + // SYS_NOSYS = 40; // { int nosys(void); } { old lstat } + SYS_DUP = 41; // { int dup(u_int fd); } + SYS_PIPE = 42; // { int pipe(void); } + SYS_GETEGID = 43; // { int getegid(void); } + SYS_PROFIL = 44; // { int profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } + // SYS_NOSYS = 45; // { int nosys(void); } { old ktrace } + SYS_SIGACTION = 46; // { int sigaction(int signum, struct __sigaction *nsa, struct sigaction *osa); } + SYS_GETGID = 47; // { int getgid(void); } + SYS_SIGPROCMASK = 48; // { int sigprocmask(int how, user_addr_t mask, user_addr_t omask); } + SYS_GETLOGIN = 49; // { int getlogin(char *namebuf, u_int namelen); } + SYS_SETLOGIN = 50; // { int setlogin(char *namebuf); } + SYS_ACCT = 51; // { int acct(char *path); } + SYS_SIGPENDING = 52; // { int sigpending(struct sigvec *osv); } + SYS_SIGALTSTACK = 53; // { int sigaltstack(struct sigaltstack *nss, struct sigaltstack *oss); } + SYS_IOCTL = 54; // { int ioctl(int fd, u_long com, caddr_t data); } + SYS_REBOOT = 55; // { int reboot(int opt, char *command); } + SYS_REVOKE = 56; // { int revoke(char *path); } + SYS_SYMLINK = 57; // { int symlink(char *path, char *link); } + SYS_READLINK = 58; // { int readlink(char *path, char *buf, int count); } + SYS_EXECVE = 59; // { int execve(char *fname, char **argp, char **envp); } + SYS_UMASK = 60; // { int umask(int newmask); } + SYS_CHROOT = 61; // { int chroot(user_addr_t path); } + // SYS_NOSYS = 62; // { int nosys(void); } { old fstat } + // SYS_NOSYS = 63; // { int nosys(void); } { used internally, reserved } + // SYS_NOSYS = 64; // { int nosys(void); } { old getpagesize } + SYS_MSYNC = 65; // { int msync(caddr_t addr, size_t len, int flags); } + SYS_VFORK = 66; // { int vfork(void); } + // SYS_NOSYS = 67; // { int nosys(void); } { old vread } + // SYS_NOSYS = 68; // { int nosys(void); } { old vwrite } + SYS_SBRK = 69; // { int sbrk(int incr) NO_SYSCALL_STUB; } + SYS_SSTK = 70; // { int sstk(int incr) NO_SYSCALL_STUB; } + // SYS_NOSYS = 71; // { int nosys(void); } { old mmap } + SYS_OVADVISE = 72; // { int ovadvise(void) NO_SYSCALL_STUB; } { old vadvise } + SYS_MUNMAP = 73; // { int munmap(caddr_t addr, size_t len); } + SYS_MPROTECT = 74; // { int mprotect(caddr_t addr, size_t len, int prot); } + SYS_MADVISE = 75; // { int madvise(caddr_t addr, size_t len, int behav); } + // SYS_NOSYS = 76; // { int nosys(void); } { old vhangup } + // SYS_NOSYS = 77; // { int nosys(void); } { old vlimit } + SYS_MINCORE = 78; // { int mincore(user_addr_t addr, user_size_t len, user_addr_t vec); } + SYS_GETGROUPS = 79; // { int getgroups(u_int gidsetsize, gid_t *gidset); } + SYS_SETGROUPS = 80; // { int setgroups(u_int gidsetsize, gid_t *gidset); } + SYS_GETPGRP = 81; // { int getpgrp(void); } + SYS_SETPGID = 82; // { int setpgid(int pid, int pgid); } + SYS_SETITIMER = 83; // { int setitimer(u_int which, struct itimerval *itv, struct itimerval *oitv); } + // SYS_NOSYS = 84; // { int nosys(void); } { old wait } + SYS_SWAPON = 85; // { int swapon(void); } + SYS_GETITIMER = 86; // { int getitimer(u_int which, struct itimerval *itv); } + // SYS_NOSYS = 87; // { int nosys(void); } { old gethostname } + // SYS_NOSYS = 88; // { int nosys(void); } { old sethostname } + SYS_GETDTABLESIZE = 89; // { int getdtablesize(void); } + SYS_DUP2 = 90; // { int dup2(u_int from, u_int to); } + // SYS_NOSYS = 91; // { int nosys(void); } { old getdopt } + SYS_FCNTL = 92; // { int fcntl(int fd, int cmd, long arg); } + SYS_SELECT = 93; // { int select(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv); } + // SYS_NOSYS = 94; // { int nosys(void); } { old setdopt } + SYS_FSYNC = 95; // { int fsync(int fd); } + SYS_SETPRIORITY = 96; // { int setpriority(int which, id_t who, int prio); } + SYS_SOCKET = 97; // { int socket(int domain, int type, int protocol); } + SYS_CONNECT = 98; // { int connect(int s, caddr_t name, socklen_t namelen); } + // SYS_NOSYS = 97; // { int nosys(void); } + // SYS_NOSYS = 98; // { int nosys(void); } + // SYS_NOSYS = 99; // { int nosys(void); } { old accept } + SYS_GETPRIORITY = 100; // { int getpriority(int which, id_t who); } + // SYS_NOSYS = 101; // { int nosys(void); } { old send } + // SYS_NOSYS = 102; // { int nosys(void); } { old recv } + // SYS_NOSYS = 103; // { int nosys(void); } { old sigreturn } + SYS_BIND = 104; // { int bind(int s, caddr_t name, socklen_t namelen); } + SYS_SETSOCKOPT = 105; // { int setsockopt(int s, int level, int name, caddr_t val, socklen_t valsize); } + SYS_LISTEN = 106; // { int listen(int s, int backlog); } + // SYS_NOSYS = 104; // { int nosys(void); } + // SYS_NOSYS = 105; // { int nosys(void); } + // SYS_NOSYS = 106; // { int nosys(void); } + // SYS_NOSYS = 107; // { int nosys(void); } { old vtimes } + // SYS_NOSYS = 108; // { int nosys(void); } { old sigvec } + // SYS_NOSYS = 109; // { int nosys(void); } { old sigblock } + // SYS_NOSYS = 110; // { int nosys(void); } { old sigsetmask } + SYS_SIGSUSPEND = 111; // { int sigsuspend(sigset_t mask); } + // SYS_NOSYS = 112; // { int nosys(void); } { old sigstack } + // SYS_NOSYS = 113; // { int nosys(void); } { old recvmsg } + // SYS_NOSYS = 114; // { int nosys(void); } { old sendmsg } + // SYS_NOSYS = 113; // { int nosys(void); } + // SYS_NOSYS = 114; // { int nosys(void); } + // SYS_NOSYS = 115; // { int nosys(void); } { old vtrace } + SYS_GETTIMEOFDAY = 116; // { int gettimeofday(struct timeval *tp, struct timezone *tzp); } + SYS_GETRUSAGE = 117; // { int getrusage(int who, struct rusage *rusage); } + SYS_GETSOCKOPT = 118; // { int getsockopt(int s, int level, int name, caddr_t val, socklen_t *avalsize); } + // SYS_NOSYS = 118; // { int nosys(void); } + // SYS_NOSYS = 119; // { int nosys(void); } { old resuba } + SYS_READV = 120; // { user_ssize_t readv(int fd, struct iovec *iovp, u_int iovcnt); } + SYS_WRITEV = 121; // { user_ssize_t writev(int fd, struct iovec *iovp, u_int iovcnt); } + SYS_SETTIMEOFDAY = 122; // { int settimeofday(struct timeval *tv, struct timezone *tzp); } + SYS_FCHOWN = 123; // { int fchown(int fd, int uid, int gid); } + SYS_FCHMOD = 124; // { int fchmod(int fd, int mode); } + // SYS_NOSYS = 125; // { int nosys(void); } { old recvfrom } + SYS_SETREUID = 126; // { int setreuid(uid_t ruid, uid_t euid); } + SYS_SETREGID = 127; // { int setregid(gid_t rgid, gid_t egid); } + SYS_RENAME = 128; // { int rename(char *from, char *to); } + // SYS_NOSYS = 129; // { int nosys(void); } { old truncate } + // SYS_NOSYS = 130; // { int nosys(void); } { old ftruncate } + SYS_FLOCK = 131; // { int flock(int fd, int how); } + SYS_MKFIFO = 132; // { int mkfifo(user_addr_t path, int mode); } + SYS_SENDTO = 133; // { int sendto(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen); } + SYS_SHUTDOWN = 134; // { int shutdown(int s, int how); } + SYS_SOCKETPAIR = 135; // { int socketpair(int domain, int type, int protocol, int *rsv); } + // SYS_NOSYS = 133; // { int nosys(void); } + // SYS_NOSYS = 134; // { int nosys(void); } + // SYS_NOSYS = 135; // { int nosys(void); } + SYS_MKDIR = 136; // { int mkdir(user_addr_t path, int mode); } + SYS_RMDIR = 137; // { int rmdir(char *path); } + SYS_UTIMES = 138; // { int utimes(char *path, struct timeval *tptr); } + SYS_FUTIMES = 139; // { int futimes(int fd, struct timeval *tptr); } + SYS_ADJTIME = 140; // { int adjtime(struct timeval *delta, struct timeval *olddelta); } + // SYS_NOSYS = 141; // { int nosys(void); } { old getpeername } + SYS_GETHOSTUUID = 142; // { int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp); } + // SYS_NOSYS = 143; // { int nosys(void); } { old sethostid } + // SYS_NOSYS = 144; // { int nosys(void); } { old getrlimit } + // SYS_NOSYS = 145; // { int nosys(void); } { old setrlimit } + // SYS_NOSYS = 146; // { int nosys(void); } { old killpg } + SYS_SETSID = 147; // { int setsid(void); } + // SYS_NOSYS = 148; // { int nosys(void); } { old setquota } + // SYS_NOSYS = 149; // { int nosys(void); } { old qquota } + // SYS_NOSYS = 150; // { int nosys(void); } { old getsockname } + SYS_GETPGID = 151; // { int getpgid(pid_t pid); } + SYS_SETPRIVEXEC = 152; // { int setprivexec(int flag); } + SYS_PREAD = 153; // { user_ssize_t pread(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } + SYS_PWRITE = 154; // { user_ssize_t pwrite(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } + SYS_NFSSVC = 155; // { int nfssvc(int flag, caddr_t argp); } + // SYS_NOSYS = 155; // { int nosys(void); } + // SYS_NOSYS = 156; // { int nosys(void); } { old getdirentries } + SYS_STATFS = 157; // { int statfs(char *path, struct statfs *buf); } + SYS_FSTATFS = 158; // { int fstatfs(int fd, struct statfs *buf); } + SYS_UNMOUNT = 159; // { int unmount(user_addr_t path, int flags); } + // SYS_NOSYS = 160; // { int nosys(void); } { old async_daemon } + SYS_GETFH = 161; // { int getfh(char *fname, fhandle_t *fhp); } + // SYS_NOSYS = 161; // { int nosys(void); } + // SYS_NOSYS = 162; // { int nosys(void); } { old getdomainname } + // SYS_NOSYS = 163; // { int nosys(void); } { old setdomainname } + // SYS_NOSYS = 164; // { int nosys(void); } + SYS_QUOTACTL = 165; // { int quotactl(const char *path, int cmd, int uid, caddr_t arg); } + // SYS_NOSYS = 166; // { int nosys(void); } { old exportfs } + SYS_MOUNT = 167; // { int mount(char *type, char *path, int flags, caddr_t data); } + // SYS_NOSYS = 168; // { int nosys(void); } { old ustat } + SYS_CSOPS = 169; // { int csops(pid_t pid, uint32_t ops, user_addr_t useraddr, user_size_t usersize); } + // SYS_NOSYS = 171; // { int nosys(void); } { old wait3 } + // SYS_NOSYS = 172; // { int nosys(void); } { old rpause } + SYS_WAITID = 173; // { int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); } + // SYS_NOSYS = 174; // { int nosys(void); } { old getdents } + // SYS_NOSYS = 175; // { int nosys(void); } { old gc_control } + SYS_ADD_PROFIL = 176; // { int add_profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } + // SYS_NOSYS = 177; // { int nosys(void); } + // SYS_NOSYS = 178; // { int nosys(void); } + // SYS_NOSYS = 179; // { int nosys(void); } + SYS_KDEBUG_TRACE = 180; // { int kdebug_trace(int code, int arg1, int arg2, int arg3, int arg4, int arg5) NO_SYSCALL_STUB; } + SYS_SETGID = 181; // { int setgid(gid_t gid); } + SYS_SETEGID = 182; // { int setegid(gid_t egid); } + SYS_SETEUID = 183; // { int seteuid(uid_t euid); } + SYS_SIGRETURN = 184; // { int sigreturn(struct ucontext *uctx, int infostyle); } + // SYS_NOSYS = 186; // { int nosys(void); } + // SYS_NOSYS = 187; // { int nosys(void); } + SYS_STAT = 188; // { int stat(user_addr_t path, user_addr_t ub); } + SYS_FSTAT = 189; // { int fstat(int fd, user_addr_t ub); } + SYS_LSTAT = 190; // { int lstat(user_addr_t path, user_addr_t ub); } + SYS_PATHCONF = 191; // { int pathconf(char *path, int name); } + SYS_FPATHCONF = 192; // { int fpathconf(int fd, int name); } + // SYS_NOSYS = 193; // { int nosys(void); } + SYS_GETRLIMIT = 194; // { int getrlimit(u_int which, struct rlimit *rlp); } + SYS_SETRLIMIT = 195; // { int setrlimit(u_int which, struct rlimit *rlp); } + SYS_GETDIRENTRIES = 196; // { int getdirentries(int fd, char *buf, u_int count, long *basep); } + SYS_MMAP = 197; // { user_addr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } + // SYS_NOSYS = 198; // { int nosys(void); } { __syscall } + SYS_LSEEK = 199; // { off_t lseek(int fd, off_t offset, int whence); } + SYS_TRUNCATE = 200; // { int truncate(char *path, off_t length); } + SYS_FTRUNCATE = 201; // { int ftruncate(int fd, off_t length); } + SYS___SYSCTL = 202; // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } + SYS_MLOCK = 203; // { int mlock(caddr_t addr, size_t len); } + SYS_MUNLOCK = 204; // { int munlock(caddr_t addr, size_t len); } + SYS_UNDELETE = 205; // { int undelete(user_addr_t path); } + SYS_ATSOCKET = 206; // { int ATsocket(int proto); } + // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } + // SYS_NOSYS = 206; // { int nosys(void); } + // SYS_NOSYS = 207; // { int nosys(void); } + // SYS_NOSYS = 208; // { int nosys(void); } + // SYS_NOSYS = 209; // { int nosys(void); } + // SYS_NOSYS = 210; // { int nosys(void); } + // SYS_NOSYS = 211; // { int nosys(void); } + // SYS_NOSYS = 212; // { int nosys(void); } + // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } + SYS_KQUEUE_FROM_PORTSET_NP = 214; // { int kqueue_from_portset_np(int portset); } + SYS_KQUEUE_PORTSET_NP = 215; // { int kqueue_portset_np(int fd); } + SYS_GETATTRLIST = 220; // { int getattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } + SYS_SETATTRLIST = 221; // { int setattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } + SYS_GETDIRENTRIESATTR = 222; // { int getdirentriesattr(int fd, struct attrlist *alist, void *buffer, size_t buffersize, u_long *count, u_long *basep, u_long *newstate, u_long options); } + SYS_EXCHANGEDATA = 223; // { int exchangedata(const char *path1, const char *path2, u_long options); } + // SYS_NOSYS = 224; // { int nosys(void); } { was checkuseraccess } + SYS_SEARCHFS = 225; // { int searchfs(const char *path, struct fssearchblock *searchblock, u_long *nummatches, u_long scriptcode, u_long options, struct searchstate *state); } + SYS_DELETE = 226; // { int delete(user_addr_t path) NO_SYSCALL_STUB; } { private delete (Carbon semantics) } + SYS_COPYFILE = 227; // { int copyfile(char *from, char *to, int mode, int flags) NO_SYSCALL_STUB; } + // SYS_NOSYS = 228; // { int nosys(void); } + // SYS_NOSYS = 229; // { int nosys(void); } + SYS_POLL = 230; // { int poll(struct pollfd *fds, u_int nfds, int timeout); } + SYS_WATCHEVENT = 231; // { int watchevent(struct eventreq *u_req, int u_eventmask); } + SYS_WAITEVENT = 232; // { int waitevent(struct eventreq *u_req, struct timeval *tv); } + SYS_MODWATCH = 233; // { int modwatch(struct eventreq *u_req, int u_eventmask); } + SYS_GETXATTR = 234; // { user_ssize_t getxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_FGETXATTR = 235; // { user_ssize_t fgetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_SETXATTR = 236; // { int setxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_FSETXATTR = 237; // { int fsetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } + SYS_REMOVEXATTR = 238; // { int removexattr(user_addr_t path, user_addr_t attrname, int options); } + SYS_FREMOVEXATTR = 239; // { int fremovexattr(int fd, user_addr_t attrname, int options); } + SYS_LISTXATTR = 240; // { user_ssize_t listxattr(user_addr_t path, user_addr_t namebuf, size_t bufsize, int options); } + SYS_FLISTXATTR = 241; // { user_ssize_t flistxattr(int fd, user_addr_t namebuf, size_t bufsize, int options); } + SYS_FSCTL = 242; // { int fsctl(const char *path, u_long cmd, caddr_t data, u_long options); } + SYS_INITGROUPS = 243; // { int initgroups(u_int gidsetsize, gid_t *gidset, int gmuid); } + SYS_POSIX_SPAWN = 244; // { int posix_spawn(pid_t *pid, const char *path, const struct _posix_spawn_args_desc *adesc, char **argv, char **envp); } + // SYS_NOSYS = 245; // { int nosys(void); } + // SYS_NOSYS = 246; // { int nosys(void); } + SYS_NFSCLNT = 247; // { int nfsclnt(int flag, caddr_t argp); } + // SYS_NOSYS = 247; // { int nosys(void); } + SYS_FHOPEN = 248; // { int fhopen(const struct fhandle *u_fhp, int flags); } + // SYS_NOSYS = 248; // { int nosys(void); } + // SYS_NOSYS = 249; // { int nosys(void); } + SYS_MINHERIT = 250; // { int minherit(void *addr, size_t len, int inherit); } + SYS_SEMSYS = 251; // { int semsys(u_int which, int a2, int a3, int a4, int a5); } + // SYS_NOSYS = 251; // { int nosys(void); } + SYS_MSGSYS = 252; // { int msgsys(u_int which, int a2, int a3, int a4, int a5); } + // SYS_NOSYS = 252; // { int nosys(void); } + SYS_SHMSYS = 253; // { int shmsys(u_int which, int a2, int a3, int a4); } + // SYS_NOSYS = 253; // { int nosys(void); } + SYS_SEMCTL = 254; // { int semctl(int semid, int semnum, int cmd, semun_t arg); } + SYS_SEMGET = 255; // { int semget(key_t key, int nsems, int semflg); } + SYS_SEMOP = 256; // { int semop(int semid, struct sembuf *sops, int nsops); } + // SYS_NOSYS = 257; // { int nosys(void); } + // SYS_NOSYS = 254; // { int nosys(void); } + // SYS_NOSYS = 255; // { int nosys(void); } + // SYS_NOSYS = 256; // { int nosys(void); } + // SYS_NOSYS = 257; // { int nosys(void); } + SYS_MSGCTL = 258; // { int msgctl(int msqid, int cmd, struct msqid_ds *buf); } + SYS_MSGGET = 259; // { int msgget(key_t key, int msgflg); } + SYS_MSGSND = 260; // { int msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg); } + SYS_MSGRCV = 261; // { user_ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } + // SYS_NOSYS = 258; // { int nosys(void); } + // SYS_NOSYS = 259; // { int nosys(void); } + // SYS_NOSYS = 260; // { int nosys(void); } + // SYS_NOSYS = 261; // { int nosys(void); } + SYS_SHMAT = 262; // { user_addr_t shmat(int shmid, void *shmaddr, int shmflg); } + SYS_SHMCTL = 263; // { int shmctl(int shmid, int cmd, struct shmid_ds *buf); } + SYS_SHMDT = 264; // { int shmdt(void *shmaddr); } + SYS_SHMGET = 265; // { int shmget(key_t key, size_t size, int shmflg); } + // SYS_NOSYS = 262; // { int nosys(void); } + // SYS_NOSYS = 263; // { int nosys(void); } + // SYS_NOSYS = 264; // { int nosys(void); } + // SYS_NOSYS = 265; // { int nosys(void); } + SYS_SHM_OPEN = 266; // { int shm_open(const char *name, int oflag, int mode); } + SYS_SHM_UNLINK = 267; // { int shm_unlink(const char *name); } + SYS_SEM_OPEN = 268; // { user_addr_t sem_open(const char *name, int oflag, int mode, int value); } + SYS_SEM_CLOSE = 269; // { int sem_close(sem_t *sem); } + SYS_SEM_UNLINK = 270; // { int sem_unlink(const char *name); } + SYS_SEM_WAIT = 271; // { int sem_wait(sem_t *sem); } + SYS_SEM_TRYWAIT = 272; // { int sem_trywait(sem_t *sem); } + SYS_SEM_POST = 273; // { int sem_post(sem_t *sem); } + SYS_SEM_GETVALUE = 274; // { int sem_getvalue(sem_t *sem, int *sval); } + SYS_SEM_INIT = 275; // { int sem_init(sem_t *sem, int phsared, u_int value); } + SYS_SEM_DESTROY = 276; // { int sem_destroy(sem_t *sem); } + SYS_OPEN_EXTENDED = 277; // { int open_extended(user_addr_t path, int flags, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_UMASK_EXTENDED = 278; // { int umask_extended(int newmask, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_STAT_EXTENDED = 279; // { int stat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_LSTAT_EXTENDED = 280; // { int lstat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_FSTAT_EXTENDED = 281; // { int fstat_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_CHMOD_EXTENDED = 282; // { int chmod_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_FCHMOD_EXTENDED = 283; // { int fchmod_extended(int fd, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_ACCESS_EXTENDED = 284; // { int access_extended(user_addr_t entries, size_t size, user_addr_t results, uid_t uid) NO_SYSCALL_STUB; } + SYS_SETTID = 285; // { int settid(uid_t uid, gid_t gid) NO_SYSCALL_STUB; } + SYS_GETTID = 286; // { int gettid(uid_t *uidp, gid_t *gidp) NO_SYSCALL_STUB; } + SYS_SETSGROUPS = 287; // { int setsgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_GETSGROUPS = 288; // { int getsgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_SETWGROUPS = 289; // { int setwgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_GETWGROUPS = 290; // { int getwgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } + SYS_MKFIFO_EXTENDED = 291; // { int mkfifo_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_MKDIR_EXTENDED = 292; // { int mkdir_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } + SYS_IDENTITYSVC = 293; // { int identitysvc(int opcode, user_addr_t message) NO_SYSCALL_STUB; } + SYS_SHARED_REGION_CHECK_NP = 294; // { int shared_region_check_np(uint64_t *start_address) NO_SYSCALL_STUB; } + SYS_SHARED_REGION_MAP_NP = 295; // { int shared_region_map_np(int fd, uint32_t count, const struct shared_file_mapping_np *mappings) NO_SYSCALL_STUB; } + // SYS_NOSYS = 296; // { int nosys(void); } { old load_shared_file } + // SYS_NOSYS = 297; // { int nosys(void); } { old reset_shared_file } + // SYS_NOSYS = 298; // { int nosys(void); } { old new_system_shared_regions } + // SYS_ENOSYS = 299; // { int enosys(void); } { old shared_region_map_file_np } + // SYS_ENOSYS = 300; // { int enosys(void); } { old shared_region_make_private_np } + SYS___PTHREAD_MUTEX_DESTROY = 301; // { int __pthread_mutex_destroy(int mutexid); } + SYS___PTHREAD_MUTEX_INIT = 302; // { int __pthread_mutex_init(user_addr_t mutex, user_addr_t attr); } + SYS___PTHREAD_MUTEX_LOCK = 303; // { int __pthread_mutex_lock(int mutexid); } + SYS___PTHREAD_MUTEX_TRYLOCK = 304; // { int __pthread_mutex_trylock(int mutexid); } + SYS___PTHREAD_MUTEX_UNLOCK = 305; // { int __pthread_mutex_unlock(int mutexid); } + SYS___PTHREAD_COND_INIT = 306; // { int __pthread_cond_init(user_addr_t cond, user_addr_t attr); } + SYS___PTHREAD_COND_DESTROY = 307; // { int __pthread_cond_destroy(int condid); } + SYS___PTHREAD_COND_BROADCAST = 308; // { int __pthread_cond_broadcast(int condid); } + SYS___PTHREAD_COND_SIGNAL = 309; // { int __pthread_cond_signal(int condid); } + SYS_GETSID = 310; // { int getsid(pid_t pid); } + SYS_SETTID_WITH_PID = 311; // { int settid_with_pid(pid_t pid, int assume) NO_SYSCALL_STUB; } + SYS___PTHREAD_COND_TIMEDWAIT = 312; // { int __pthread_cond_timedwait(int condid, int mutexid, user_addr_t abstime); } + SYS_AIO_FSYNC = 313; // { int aio_fsync(int op, user_addr_t aiocbp); } + SYS_AIO_RETURN = 314; // { user_ssize_t aio_return(user_addr_t aiocbp); } + SYS_AIO_SUSPEND = 315; // { int aio_suspend(user_addr_t aiocblist, int nent, user_addr_t timeoutp); } + SYS_AIO_CANCEL = 316; // { int aio_cancel(int fd, user_addr_t aiocbp); } + SYS_AIO_ERROR = 317; // { int aio_error(user_addr_t aiocbp); } + SYS_AIO_READ = 318; // { int aio_read(user_addr_t aiocbp); } + SYS_AIO_WRITE = 319; // { int aio_write(user_addr_t aiocbp); } + SYS_LIO_LISTIO = 320; // { int lio_listio(int mode, user_addr_t aiocblist, int nent, user_addr_t sigp); } + SYS___PTHREAD_COND_WAIT = 321; // { int __pthread_cond_wait(int condid, int mutexid); } + SYS_IOPOLICYSYS = 322; // { int iopolicysys(int cmd, void *arg) NO_SYSCALL_STUB; } + // SYS_NOSYS = 323; // { int nosys(void); } + SYS_MLOCKALL = 324; // { int mlockall(int how); } + SYS_MUNLOCKALL = 325; // { int munlockall(int how); } + // SYS_NOSYS = 326; // { int nosys(void); } + SYS_ISSETUGID = 327; // { int issetugid(void); } + SYS___PTHREAD_KILL = 328; // { int __pthread_kill(int thread_port, int sig); } + SYS___PTHREAD_SIGMASK = 329; // { int __pthread_sigmask(int how, user_addr_t set, user_addr_t oset); } + SYS___SIGWAIT = 330; // { int __sigwait(user_addr_t set, user_addr_t sig); } + SYS___DISABLE_THREADSIGNAL = 331; // { int __disable_threadsignal(int value); } + SYS___PTHREAD_MARKCANCEL = 332; // { int __pthread_markcancel(int thread_port); } + SYS___PTHREAD_CANCELED = 333; // { int __pthread_canceled(int action); } + SYS___SEMWAIT_SIGNAL = 334; // { int __semwait_signal(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec); } + // SYS_NOSYS = 335; // { int nosys(void); } { old utrace } + SYS_PROC_INFO = 336; // { int proc_info(int32_t callnum,int32_t pid,uint32_t flavor, uint64_t arg,user_addr_t buffer,int32_t buffersize) NO_SYSCALL_STUB; } + SYS_SENDFILE = 337; // { int sendfile(int fd, int s, off_t offset, off_t *nbytes, struct sf_hdtr *hdtr, int flags); } + // SYS_NOSYS = 337; // { int nosys(void); } + SYS_STAT64 = 338; // { int stat64(user_addr_t path, user_addr_t ub); } + SYS_FSTAT64 = 339; // { int fstat64(int fd, user_addr_t ub); } + SYS_LSTAT64 = 340; // { int lstat64(user_addr_t path, user_addr_t ub); } + SYS_STAT64_EXTENDED = 341; // { int stat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_LSTAT64_EXTENDED = 342; // { int lstat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_FSTAT64_EXTENDED = 343; // { int fstat64_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } + SYS_GETDIRENTRIES64 = 344; // { user_ssize_t getdirentries64(int fd, void *buf, user_size_t bufsize, off_t *position) NO_SYSCALL_STUB; } + SYS_STATFS64 = 345; // { int statfs64(char *path, struct statfs64 *buf); } + SYS_FSTATFS64 = 346; // { int fstatfs64(int fd, struct statfs64 *buf); } + SYS_GETFSSTAT64 = 347; // { int getfsstat64(user_addr_t buf, int bufsize, int flags); } + SYS___PTHREAD_CHDIR = 348; // { int __pthread_chdir(user_addr_t path); } + SYS___PTHREAD_FCHDIR = 349; // { int __pthread_fchdir(int fd); } + SYS_AUDIT = 350; // { int audit(void *record, int length); } + SYS_AUDITON = 351; // { int auditon(int cmd, void *data, int length); } + // SYS_NOSYS = 352; // { int nosys(void); } + SYS_GETAUID = 353; // { int getauid(au_id_t *auid); } + SYS_SETAUID = 354; // { int setauid(au_id_t *auid); } + SYS_GETAUDIT = 355; // { int getaudit(struct auditinfo *auditinfo); } + SYS_SETAUDIT = 356; // { int setaudit(struct auditinfo *auditinfo); } + SYS_GETAUDIT_ADDR = 357; // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } + SYS_SETAUDIT_ADDR = 358; // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } + SYS_AUDITCTL = 359; // { int auditctl(char *path); } + // SYS_NOSYS = 350; // { int nosys(void); } + // SYS_NOSYS = 351; // { int nosys(void); } + // SYS_NOSYS = 352; // { int nosys(void); } + // SYS_NOSYS = 353; // { int nosys(void); } + // SYS_NOSYS = 354; // { int nosys(void); } + // SYS_NOSYS = 355; // { int nosys(void); } + // SYS_NOSYS = 356; // { int nosys(void); } + // SYS_NOSYS = 357; // { int nosys(void); } + // SYS_NOSYS = 358; // { int nosys(void); } + // SYS_NOSYS = 359; // { int nosys(void); } + SYS_BSDTHREAD_CREATE = 360; // { user_addr_t bsdthread_create(user_addr_t func, user_addr_t func_arg, user_addr_t stack, user_addr_t pthread, uint32_t flags) NO_SYSCALL_STUB; } + SYS_BSDTHREAD_TERMINATE = 361; // { int bsdthread_terminate(user_addr_t stackaddr, size_t freesize, uint32_t port, uint32_t sem) NO_SYSCALL_STUB; } + SYS_KQUEUE = 362; // { int kqueue(void); } + SYS_KEVENT = 363; // { int kevent(int fd, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } + SYS_LCHOWN = 364; // { int lchown(user_addr_t path, uid_t owner, gid_t group); } + SYS_STACK_SNAPSHOT = 365; // { int stack_snapshot(pid_t pid, user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t options) NO_SYSCALL_STUB; } + SYS_BSDTHREAD_REGISTER = 366; // { int bsdthread_register(user_addr_t threadstart, user_addr_t wqthread, int pthsize) NO_SYSCALL_STUB; } + SYS_WORKQ_OPEN = 367; // { int workq_open(void) NO_SYSCALL_STUB; } + SYS_WORKQ_OPS = 368; // { int workq_ops(int options, user_addr_t item, int prio) NO_SYSCALL_STUB; } + // SYS_NOSYS = 369; // { int nosys(void); } + // SYS_NOSYS = 370; // { int nosys(void); } + // SYS_NOSYS = 371; // { int nosys(void); } + // SYS_NOSYS = 372; // { int nosys(void); } + // SYS_NOSYS = 373; // { int nosys(void); } + // SYS_NOSYS = 374; // { int nosys(void); } + // SYS_NOSYS = 375; // { int nosys(void); } + // SYS_NOSYS = 376; // { int nosys(void); } + // SYS_NOSYS = 377; // { int nosys(void); } + // SYS_NOSYS = 378; // { int nosys(void); } + // SYS_NOSYS = 379; // { int nosys(void); } + SYS___MAC_EXECVE = 380; // { int __mac_execve(char *fname, char **argp, char **envp, struct mac *mac_p); } + SYS___MAC_SYSCALL = 381; // { int __mac_syscall(char *policy, int call, user_addr_t arg); } + SYS___MAC_GET_FILE = 382; // { int __mac_get_file(char *path_p, struct mac *mac_p); } + SYS___MAC_SET_FILE = 383; // { int __mac_set_file(char *path_p, struct mac *mac_p); } + SYS___MAC_GET_LINK = 384; // { int __mac_get_link(char *path_p, struct mac *mac_p); } + SYS___MAC_SET_LINK = 385; // { int __mac_set_link(char *path_p, struct mac *mac_p); } + SYS___MAC_GET_PROC = 386; // { int __mac_get_proc(struct mac *mac_p); } + SYS___MAC_SET_PROC = 387; // { int __mac_set_proc(struct mac *mac_p); } + SYS___MAC_GET_FD = 388; // { int __mac_get_fd(int fd, struct mac *mac_p); } + SYS___MAC_SET_FD = 389; // { int __mac_set_fd(int fd, struct mac *mac_p); } + SYS___MAC_GET_PID = 390; // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } + SYS___MAC_GET_LCID = 391; // { int __mac_get_lcid(pid_t lcid, struct mac *mac_p); } + SYS___MAC_GET_LCTX = 392; // { int __mac_get_lctx(struct mac *mac_p); } + SYS___MAC_SET_LCTX = 393; // { int __mac_set_lctx(struct mac *mac_p); } + SYS_SETLCID = 394; // { int setlcid(pid_t pid, pid_t lcid) NO_SYSCALL_STUB; } + SYS_GETLCID = 395; // { int getlcid(pid_t pid) NO_SYSCALL_STUB; } + SYS_READ_NOCANCEL = 396; // { user_ssize_t read_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } + SYS_WRITE_NOCANCEL = 397; // { user_ssize_t write_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } + SYS_OPEN_NOCANCEL = 398; // { int open_nocancel(user_addr_t path, int flags, int mode) NO_SYSCALL_STUB; } + SYS_CLOSE_NOCANCEL = 399; // { int close_nocancel(int fd) NO_SYSCALL_STUB; } + SYS_WAIT4_NOCANCEL = 400; // { int wait4_nocancel(int pid, user_addr_t status, int options, user_addr_t rusage) NO_SYSCALL_STUB; } + SYS_RECVMSG_NOCANCEL = 401; // { int recvmsg_nocancel(int s, struct msghdr *msg, int flags) NO_SYSCALL_STUB; } + SYS_SENDMSG_NOCANCEL = 402; // { int sendmsg_nocancel(int s, caddr_t msg, int flags) NO_SYSCALL_STUB; } + SYS_RECVFROM_NOCANCEL = 403; // { int recvfrom_nocancel(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr) NO_SYSCALL_STUB; } + SYS_ACCEPT_NOCANCEL = 404; // { int accept_nocancel(int s, caddr_t name, socklen_t *anamelen) NO_SYSCALL_STUB; } + // SYS_NOSYS = 401; // { int nosys(void); } + // SYS_NOSYS = 402; // { int nosys(void); } + // SYS_NOSYS = 403; // { int nosys(void); } + // SYS_NOSYS = 404; // { int nosys(void); } + SYS_MSYNC_NOCANCEL = 405; // { int msync_nocancel(caddr_t addr, size_t len, int flags) NO_SYSCALL_STUB; } + SYS_FCNTL_NOCANCEL = 406; // { int fcntl_nocancel(int fd, int cmd, long arg) NO_SYSCALL_STUB; } + SYS_SELECT_NOCANCEL = 407; // { int select_nocancel(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv) NO_SYSCALL_STUB; } + SYS_FSYNC_NOCANCEL = 408; // { int fsync_nocancel(int fd) NO_SYSCALL_STUB; } + SYS_CONNECT_NOCANCEL = 409; // { int connect_nocancel(int s, caddr_t name, socklen_t namelen) NO_SYSCALL_STUB; } + // SYS_NOSYS = 409; // { int nosys(void); } + SYS_SIGSUSPEND_NOCANCEL = 410; // { int sigsuspend_nocancel(sigset_t mask) NO_SYSCALL_STUB; } + SYS_READV_NOCANCEL = 411; // { user_ssize_t readv_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } + SYS_WRITEV_NOCANCEL = 412; // { user_ssize_t writev_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } + SYS_SENDTO_NOCANCEL = 413; // { int sendto_nocancel(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen) NO_SYSCALL_STUB; } + // SYS_NOSYS = 413; // { int nosys(void); } + SYS_PREAD_NOCANCEL = 414; // { user_ssize_t pread_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } + SYS_PWRITE_NOCANCEL = 415; // { user_ssize_t pwrite_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } + SYS_WAITID_NOCANCEL = 416; // { int waitid_nocancel(idtype_t idtype, id_t id, siginfo_t *infop, int options) NO_SYSCALL_STUB; } + SYS_POLL_NOCANCEL = 417; // { int poll_nocancel(struct pollfd *fds, u_int nfds, int timeout) NO_SYSCALL_STUB; } + SYS_MSGSND_NOCANCEL = 418; // { int msgsnd_nocancel(int msqid, void *msgp, size_t msgsz, int msgflg) NO_SYSCALL_STUB; } + SYS_MSGRCV_NOCANCEL = 419; // { user_ssize_t msgrcv_nocancel(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) NO_SYSCALL_STUB; } + // SYS_NOSYS = 418; // { int nosys(void); } + // SYS_NOSYS = 419; // { int nosys(void); } + SYS_SEM_WAIT_NOCANCEL = 420; // { int sem_wait_nocancel(sem_t *sem) NO_SYSCALL_STUB; } + SYS_AIO_SUSPEND_NOCANCEL = 421; // { int aio_suspend_nocancel(user_addr_t aiocblist, int nent, user_addr_t timeoutp) NO_SYSCALL_STUB; } + SYS___SIGWAIT_NOCANCEL = 422; // { int __sigwait_nocancel(user_addr_t set, user_addr_t sig) NO_SYSCALL_STUB; } + SYS___SEMWAIT_SIGNAL_NOCANCEL = 423; // { int __semwait_signal_nocancel(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec) NO_SYSCALL_STUB; } + SYS___MAC_MOUNT = 424; // { int __mac_mount(char *type, char *path, int flags, caddr_t data, struct mac *mac_p); } + SYS___MAC_GET_MOUNT = 425; // { int __mac_get_mount(char *path, struct mac *mac_p); } + SYS___MAC_GETFSSTAT = 426; // { int __mac_getfsstat(user_addr_t buf, int bufsize, user_addr_t mac, int macsize, int flags); } +) diff --git a/src/pkg/syscall/zsysnum_linux_386.go b/src/pkg/syscall/zsysnum_linux_386.go new file mode 100644 index 000000000..46c1112f8 --- /dev/null +++ b/src/pkg/syscall/zsysnum_linux_386.go @@ -0,0 +1,319 @@ +// Generated by mklinux; DO NOT EDIT. +// mklinux /usr/include/asm/unistd_32.h + +package syscall + +const( + SYS_RESTART_SYSCALL = 0; + SYS_EXIT = 1; + SYS_FORK = 2; + SYS_READ = 3; + SYS_WRITE = 4; + SYS_OPEN = 5; + SYS_CLOSE = 6; + SYS_WAITPID = 7; + SYS_CREAT = 8; + SYS_LINK = 9; + SYS_UNLINK = 10; + SYS_EXECVE = 11; + SYS_CHDIR = 12; + SYS_TIME = 13; + SYS_MKNOD = 14; + SYS_CHMOD = 15; + SYS_LCHOWN = 16; + SYS_BREAK = 17; + SYS_OLDSTAT = 18; + SYS_LSEEK = 19; + SYS_GETPID = 20; + SYS_MOUNT = 21; + SYS_UMOUNT = 22; + SYS_SETUID = 23; + SYS_GETUID = 24; + SYS_STIME = 25; + SYS_PTRACE = 26; + SYS_ALARM = 27; + SYS_OLDFSTAT = 28; + SYS_PAUSE = 29; + SYS_UTIME = 30; + SYS_STTY = 31; + SYS_GTTY = 32; + SYS_ACCESS = 33; + SYS_NICE = 34; + SYS_FTIME = 35; + SYS_SYNC = 36; + SYS_KILL = 37; + SYS_RENAME = 38; + SYS_MKDIR = 39; + SYS_RMDIR = 40; + SYS_DUP = 41; + SYS_PIPE = 42; + SYS_TIMES = 43; + SYS_PROF = 44; + SYS_BRK = 45; + SYS_SETGID = 46; + SYS_GETGID = 47; + SYS_SIGNAL = 48; + SYS_GETEUID = 49; + SYS_GETEGID = 50; + SYS_ACCT = 51; + SYS_UMOUNT2 = 52; + SYS_LOCK = 53; + SYS_IOCTL = 54; + SYS_FCNTL = 55; + SYS_MPX = 56; + SYS_SETPGID = 57; + SYS_ULIMIT = 58; + SYS_OLDOLDUNAME = 59; + SYS_UMASK = 60; + SYS_CHROOT = 61; + SYS_USTAT = 62; + SYS_DUP2 = 63; + SYS_GETPPID = 64; + SYS_GETPGRP = 65; + SYS_SETSID = 66; + SYS_SIGACTION = 67; + SYS_SGETMASK = 68; + SYS_SSETMASK = 69; + SYS_SETREUID = 70; + SYS_SETREGID = 71; + SYS_SIGSUSPEND = 72; + SYS_SIGPENDING = 73; + SYS_SETHOSTNAME = 74; + SYS_SETRLIMIT = 75; + SYS_GETRLIMIT = 76; + SYS_GETRUSAGE = 77; + SYS_GETTIMEOFDAY = 78; + SYS_SETTIMEOFDAY = 79; + SYS_GETGROUPS = 80; + SYS_SETGROUPS = 81; + SYS_SELECT = 82; + SYS_SYMLINK = 83; + SYS_OLDLSTAT = 84; + SYS_READLINK = 85; + SYS_USELIB = 86; + SYS_SWAPON = 87; + SYS_REBOOT = 88; + SYS_READDIR = 89; + SYS_MMAP = 90; + SYS_MUNMAP = 91; + SYS_TRUNCATE = 92; + SYS_FTRUNCATE = 93; + SYS_FCHMOD = 94; + SYS_FCHOWN = 95; + SYS_GETPRIORITY = 96; + SYS_SETPRIORITY = 97; + SYS_PROFIL = 98; + SYS_STATFS = 99; + SYS_FSTATFS = 100; + SYS_IOPERM = 101; + SYS_SOCKETCALL = 102; + SYS_SYSLOG = 103; + SYS_SETITIMER = 104; + SYS_GETITIMER = 105; + SYS_STAT = 106; + SYS_LSTAT = 107; + SYS_FSTAT = 108; + SYS_OLDUNAME = 109; + SYS_IOPL = 110; + SYS_VHANGUP = 111; + SYS_IDLE = 112; + SYS_VM86OLD = 113; + SYS_WAIT4 = 114; + SYS_SWAPOFF = 115; + SYS_SYSINFO = 116; + SYS_IPC = 117; + SYS_FSYNC = 118; + SYS_SIGRETURN = 119; + SYS_CLONE = 120; + SYS_SETDOMAINNAME = 121; + SYS_UNAME = 122; + SYS_MODIFY_LDT = 123; + SYS_ADJTIMEX = 124; + SYS_MPROTECT = 125; + SYS_SIGPROCMASK = 126; + SYS_CREATE_MODULE = 127; + SYS_INIT_MODULE = 128; + SYS_DELETE_MODULE = 129; + SYS_GET_KERNEL_SYMS = 130; + SYS_QUOTACTL = 131; + SYS_GETPGID = 132; + SYS_FCHDIR = 133; + SYS_BDFLUSH = 134; + SYS_SYSFS = 135; + SYS_PERSONALITY = 136; + SYS_AFS_SYSCALL = 137; + SYS_SETFSUID = 138; + SYS_SETFSGID = 139; + SYS__LLSEEK = 140; + SYS_GETDENTS = 141; + SYS__NEWSELECT = 142; + SYS_FLOCK = 143; + SYS_MSYNC = 144; + SYS_READV = 145; + SYS_WRITEV = 146; + SYS_GETSID = 147; + SYS_FDATASYNC = 148; + SYS__SYSCTL = 149; + SYS_MLOCK = 150; + SYS_MUNLOCK = 151; + SYS_MLOCKALL = 152; + SYS_MUNLOCKALL = 153; + SYS_SCHED_SETPARAM = 154; + SYS_SCHED_GETPARAM = 155; + SYS_SCHED_SETSCHEDULER = 156; + SYS_SCHED_GETSCHEDULER = 157; + SYS_SCHED_YIELD = 158; + SYS_SCHED_GET_PRIORITY_MAX = 159; + SYS_SCHED_GET_PRIORITY_MIN = 160; + SYS_SCHED_RR_GET_INTERVAL = 161; + SYS_NANOSLEEP = 162; + SYS_MREMAP = 163; + SYS_SETRESUID = 164; + SYS_GETRESUID = 165; + SYS_VM86 = 166; + SYS_QUERY_MODULE = 167; + SYS_POLL = 168; + SYS_NFSSERVCTL = 169; + SYS_SETRESGID = 170; + SYS_GETRESGID = 171; + SYS_PRCTL = 172; + SYS_RT_SIGRETURN = 173; + SYS_RT_SIGACTION = 174; + SYS_RT_SIGPROCMASK = 175; + SYS_RT_SIGPENDING = 176; + SYS_RT_SIGTIMEDWAIT = 177; + SYS_RT_SIGQUEUEINFO = 178; + SYS_RT_SIGSUSPEND = 179; + SYS_PREAD64 = 180; + SYS_PWRITE64 = 181; + SYS_CHOWN = 182; + SYS_GETCWD = 183; + SYS_CAPGET = 184; + SYS_CAPSET = 185; + SYS_SIGALTSTACK = 186; + SYS_SENDFILE = 187; + SYS_GETPMSG = 188; + SYS_PUTPMSG = 189; + SYS_VFORK = 190; + SYS_UGETRLIMIT = 191; + SYS_MMAP2 = 192; + SYS_TRUNCATE64 = 193; + SYS_FTRUNCATE64 = 194; + SYS_STAT64 = 195; + SYS_LSTAT64 = 196; + SYS_FSTAT64 = 197; + SYS_LCHOWN32 = 198; + SYS_GETUID32 = 199; + SYS_GETGID32 = 200; + SYS_GETEUID32 = 201; + SYS_GETEGID32 = 202; + SYS_SETREUID32 = 203; + SYS_SETREGID32 = 204; + SYS_GETGROUPS32 = 205; + SYS_SETGROUPS32 = 206; + SYS_FCHOWN32 = 207; + SYS_SETRESUID32 = 208; + SYS_GETRESUID32 = 209; + SYS_SETRESGID32 = 210; + SYS_GETRESGID32 = 211; + SYS_CHOWN32 = 212; + SYS_SETUID32 = 213; + SYS_SETGID32 = 214; + SYS_SETFSUID32 = 215; + SYS_SETFSGID32 = 216; + SYS_PIVOT_ROOT = 217; + SYS_MINCORE = 218; + SYS_MADVISE = 219; + SYS_MADVISE1 = 219; + SYS_GETDENTS64 = 220; + SYS_FCNTL64 = 221; + SYS_GETTID = 224; + SYS_READAHEAD = 225; + SYS_SETXATTR = 226; + SYS_LSETXATTR = 227; + SYS_FSETXATTR = 228; + SYS_GETXATTR = 229; + SYS_LGETXATTR = 230; + SYS_FGETXATTR = 231; + SYS_LISTXATTR = 232; + SYS_LLISTXATTR = 233; + SYS_FLISTXATTR = 234; + SYS_REMOVEXATTR = 235; + SYS_LREMOVEXATTR = 236; + SYS_FREMOVEXATTR = 237; + SYS_TKILL = 238; + SYS_SENDFILE64 = 239; + SYS_FUTEX = 240; + SYS_SCHED_SETAFFINITY = 241; + SYS_SCHED_GETAFFINITY = 242; + SYS_SET_THREAD_AREA = 243; + SYS_GET_THREAD_AREA = 244; + SYS_IO_SETUP = 245; + SYS_IO_DESTROY = 246; + SYS_IO_GETEVENTS = 247; + SYS_IO_SUBMIT = 248; + SYS_IO_CANCEL = 249; + SYS_FADVISE64 = 250; + SYS_EXIT_GROUP = 252; + SYS_LOOKUP_DCOOKIE = 253; + SYS_EPOLL_CREATE = 254; + SYS_EPOLL_CTL = 255; + SYS_EPOLL_WAIT = 256; + SYS_REMAP_FILE_PAGES = 257; + SYS_SET_TID_ADDRESS = 258; + SYS_TIMER_CREATE = 259; + SYS_STATFS64 = 268; + SYS_FSTATFS64 = 269; + SYS_TGKILL = 270; + SYS_UTIMES = 271; + SYS_FADVISE64_64 = 272; + SYS_VSERVER = 273; + SYS_MBIND = 274; + SYS_GET_MEMPOLICY = 275; + SYS_SET_MEMPOLICY = 276; + SYS_MQ_OPEN = 277; + SYS_KEXEC_LOAD = 283; + SYS_WAITID = 284; + SYS_ADD_KEY = 286; + SYS_REQUEST_KEY = 287; + SYS_KEYCTL = 288; + SYS_IOPRIO_SET = 289; + SYS_IOPRIO_GET = 290; + SYS_INOTIFY_INIT = 291; + SYS_INOTIFY_ADD_WATCH = 292; + SYS_INOTIFY_RM_WATCH = 293; + SYS_MIGRATE_PAGES = 294; + SYS_OPENAT = 295; + SYS_MKDIRAT = 296; + SYS_MKNODAT = 297; + SYS_FCHOWNAT = 298; + SYS_FUTIMESAT = 299; + SYS_FSTATAT64 = 300; + SYS_UNLINKAT = 301; + SYS_RENAMEAT = 302; + SYS_LINKAT = 303; + SYS_SYMLINKAT = 304; + SYS_READLINKAT = 305; + SYS_FCHMODAT = 306; + SYS_FACCESSAT = 307; + SYS_PSELECT6 = 308; + SYS_PPOLL = 309; + SYS_UNSHARE = 310; + SYS_SET_ROBUST_LIST = 311; + SYS_GET_ROBUST_LIST = 312; + SYS_SPLICE = 313; + SYS_SYNC_FILE_RANGE = 314; + SYS_TEE = 315; + SYS_VMSPLICE = 316; + SYS_MOVE_PAGES = 317; + SYS_GETCPU = 318; + SYS_EPOLL_PWAIT = 319; + SYS_UTIMENSAT = 320; + SYS_SIGNALFD = 321; + SYS_TIMERFD = 322; + SYS_EVENTFD = 323; + SYS_FALLOCATE = 324; +) + +func _darwin_system_call_conflict() { +} diff --git a/src/pkg/syscall/zsysnum_linux_amd64.go b/src/pkg/syscall/zsysnum_linux_amd64.go new file mode 100644 index 000000000..94424f3f3 --- /dev/null +++ b/src/pkg/syscall/zsysnum_linux_amd64.go @@ -0,0 +1,296 @@ +// Generated by mklinux; DO NOT EDIT. +// mklinux /usr/include/asm/unistd_64.h + +package syscall + +const( + SYS_READ = 0; + SYS_WRITE = 1; + SYS_OPEN = 2; + SYS_CLOSE = 3; + SYS_STAT = 4; + SYS_FSTAT = 5; + SYS_LSTAT = 6; + SYS_POLL = 7; + SYS_LSEEK = 8; + SYS_MMAP = 9; + SYS_MPROTECT = 10; + SYS_MUNMAP = 11; + SYS_BRK = 12; + SYS_RT_SIGACTION = 13; + SYS_RT_SIGPROCMASK = 14; + SYS_RT_SIGRETURN = 15; + SYS_IOCTL = 16; + SYS_PREAD64 = 17; + SYS_PWRITE64 = 18; + SYS_READV = 19; + SYS_WRITEV = 20; + SYS_ACCESS = 21; + SYS_PIPE = 22; + SYS_SELECT = 23; + SYS_SCHED_YIELD = 24; + SYS_MREMAP = 25; + SYS_MSYNC = 26; + SYS_MINCORE = 27; + SYS_MADVISE = 28; + SYS_SHMGET = 29; + SYS_SHMAT = 30; + SYS_SHMCTL = 31; + SYS_DUP = 32; + SYS_DUP2 = 33; + SYS_PAUSE = 34; + SYS_NANOSLEEP = 35; + SYS_GETITIMER = 36; + SYS_ALARM = 37; + SYS_SETITIMER = 38; + SYS_GETPID = 39; + SYS_SENDFILE = 40; + SYS_SOCKET = 41; + SYS_CONNECT = 42; + SYS_ACCEPT = 43; + SYS_SENDTO = 44; + SYS_RECVFROM = 45; + SYS_SENDMSG = 46; + SYS_RECVMSG = 47; + SYS_SHUTDOWN = 48; + SYS_BIND = 49; + SYS_LISTEN = 50; + SYS_GETSOCKNAME = 51; + SYS_GETPEERNAME = 52; + SYS_SOCKETPAIR = 53; + SYS_SETSOCKOPT = 54; + SYS_GETSOCKOPT = 55; + SYS_CLONE = 56; + SYS_FORK = 57; + SYS_VFORK = 58; + SYS_EXECVE = 59; + SYS_EXIT = 60; + SYS_WAIT4 = 61; + SYS_KILL = 62; + SYS_UNAME = 63; + SYS_SEMGET = 64; + SYS_SEMOP = 65; + SYS_SEMCTL = 66; + SYS_SHMDT = 67; + SYS_MSGGET = 68; + SYS_MSGSND = 69; + SYS_MSGRCV = 70; + SYS_MSGCTL = 71; + SYS_FCNTL = 72; + SYS_FLOCK = 73; + SYS_FSYNC = 74; + SYS_FDATASYNC = 75; + SYS_TRUNCATE = 76; + SYS_FTRUNCATE = 77; + SYS_GETDENTS = 78; + SYS_GETCWD = 79; + SYS_CHDIR = 80; + SYS_FCHDIR = 81; + SYS_RENAME = 82; + SYS_MKDIR = 83; + SYS_RMDIR = 84; + SYS_CREAT = 85; + SYS_LINK = 86; + SYS_UNLINK = 87; + SYS_SYMLINK = 88; + SYS_READLINK = 89; + SYS_CHMOD = 90; + SYS_FCHMOD = 91; + SYS_CHOWN = 92; + SYS_FCHOWN = 93; + SYS_LCHOWN = 94; + SYS_UMASK = 95; + SYS_GETTIMEOFDAY = 96; + SYS_GETRLIMIT = 97; + SYS_GETRUSAGE = 98; + SYS_SYSINFO = 99; + SYS_TIMES = 100; + SYS_PTRACE = 101; + SYS_GETUID = 102; + SYS_SYSLOG = 103; + SYS_GETGID = 104; + SYS_SETUID = 105; + SYS_SETGID = 106; + SYS_GETEUID = 107; + SYS_GETEGID = 108; + SYS_SETPGID = 109; + SYS_GETPPID = 110; + SYS_GETPGRP = 111; + SYS_SETSID = 112; + SYS_SETREUID = 113; + SYS_SETREGID = 114; + SYS_GETGROUPS = 115; + SYS_SETGROUPS = 116; + SYS_SETRESUID = 117; + SYS_GETRESUID = 118; + SYS_SETRESGID = 119; + SYS_GETRESGID = 120; + SYS_GETPGID = 121; + SYS_SETFSUID = 122; + SYS_SETFSGID = 123; + SYS_GETSID = 124; + SYS_CAPGET = 125; + SYS_CAPSET = 126; + SYS_RT_SIGPENDING = 127; + SYS_RT_SIGTIMEDWAIT = 128; + SYS_RT_SIGQUEUEINFO = 129; + SYS_RT_SIGSUSPEND = 130; + SYS_SIGALTSTACK = 131; + SYS_UTIME = 132; + SYS_MKNOD = 133; + SYS_USELIB = 134; + SYS_PERSONALITY = 135; + SYS_USTAT = 136; + SYS_STATFS = 137; + SYS_FSTATFS = 138; + SYS_SYSFS = 139; + SYS_GETPRIORITY = 140; + SYS_SETPRIORITY = 141; + SYS_SCHED_SETPARAM = 142; + SYS_SCHED_GETPARAM = 143; + SYS_SCHED_SETSCHEDULER = 144; + SYS_SCHED_GETSCHEDULER = 145; + SYS_SCHED_GET_PRIORITY_MAX = 146; + SYS_SCHED_GET_PRIORITY_MIN = 147; + SYS_SCHED_RR_GET_INTERVAL = 148; + SYS_MLOCK = 149; + SYS_MUNLOCK = 150; + SYS_MLOCKALL = 151; + SYS_MUNLOCKALL = 152; + SYS_VHANGUP = 153; + SYS_MODIFY_LDT = 154; + SYS_PIVOT_ROOT = 155; + SYS__SYSCTL = 156; + SYS_PRCTL = 157; + SYS_ARCH_PRCTL = 158; + SYS_ADJTIMEX = 159; + SYS_SETRLIMIT = 160; + SYS_CHROOT = 161; + SYS_SYNC = 162; + SYS_ACCT = 163; + SYS_SETTIMEOFDAY = 164; + SYS_MOUNT = 165; + SYS_UMOUNT2 = 166; + SYS_SWAPON = 167; + SYS_SWAPOFF = 168; + SYS_REBOOT = 169; + SYS_SETHOSTNAME = 170; + SYS_SETDOMAINNAME = 171; + SYS_IOPL = 172; + SYS_IOPERM = 173; + SYS_CREATE_MODULE = 174; + SYS_INIT_MODULE = 175; + SYS_DELETE_MODULE = 176; + SYS_GET_KERNEL_SYMS = 177; + SYS_QUERY_MODULE = 178; + SYS_QUOTACTL = 179; + SYS_NFSSERVCTL = 180; + SYS_GETPMSG = 181; + SYS_PUTPMSG = 182; + SYS_AFS_SYSCALL = 183; + SYS_TUXCALL = 184; + SYS_SECURITY = 185; + SYS_GETTID = 186; + SYS_READAHEAD = 187; + SYS_SETXATTR = 188; + SYS_LSETXATTR = 189; + SYS_FSETXATTR = 190; + SYS_GETXATTR = 191; + SYS_LGETXATTR = 192; + SYS_FGETXATTR = 193; + SYS_LISTXATTR = 194; + SYS_LLISTXATTR = 195; + SYS_FLISTXATTR = 196; + SYS_REMOVEXATTR = 197; + SYS_LREMOVEXATTR = 198; + SYS_FREMOVEXATTR = 199; + SYS_TKILL = 200; + SYS_TIME = 201; + SYS_FUTEX = 202; + SYS_SCHED_SETAFFINITY = 203; + SYS_SCHED_GETAFFINITY = 204; + SYS_SET_THREAD_AREA = 205; + SYS_IO_SETUP = 206; + SYS_IO_DESTROY = 207; + SYS_IO_GETEVENTS = 208; + SYS_IO_SUBMIT = 209; + SYS_IO_CANCEL = 210; + SYS_GET_THREAD_AREA = 211; + SYS_LOOKUP_DCOOKIE = 212; + SYS_EPOLL_CREATE = 213; + SYS_EPOLL_CTL_OLD = 214; + SYS_EPOLL_WAIT_OLD = 215; + SYS_REMAP_FILE_PAGES = 216; + SYS_GETDENTS64 = 217; + SYS_SET_TID_ADDRESS = 218; + SYS_RESTART_SYSCALL = 219; + SYS_SEMTIMEDOP = 220; + SYS_FADVISE64 = 221; + SYS_TIMER_CREATE = 222; + SYS_TIMER_SETTIME = 223; + SYS_TIMER_GETTIME = 224; + SYS_TIMER_GETOVERRUN = 225; + SYS_TIMER_DELETE = 226; + SYS_CLOCK_SETTIME = 227; + SYS_CLOCK_GETTIME = 228; + SYS_CLOCK_GETRES = 229; + SYS_CLOCK_NANOSLEEP = 230; + SYS_EXIT_GROUP = 231; + SYS_EPOLL_WAIT = 232; + SYS_EPOLL_CTL = 233; + SYS_TGKILL = 234; + SYS_UTIMES = 235; + SYS_VSERVER = 236; + SYS_MBIND = 237; + SYS_SET_MEMPOLICY = 238; + SYS_GET_MEMPOLICY = 239; + SYS_MQ_OPEN = 240; + SYS_MQ_UNLINK = 241; + SYS_MQ_TIMEDSEND = 242; + SYS_MQ_TIMEDRECEIVE = 243; + SYS_MQ_NOTIFY = 244; + SYS_MQ_GETSETATTR = 245; + SYS_KEXEC_LOAD = 246; + SYS_WAITID = 247; + SYS_ADD_KEY = 248; + SYS_REQUEST_KEY = 249; + SYS_KEYCTL = 250; + SYS_IOPRIO_SET = 251; + SYS_IOPRIO_GET = 252; + SYS_INOTIFY_INIT = 253; + SYS_INOTIFY_ADD_WATCH = 254; + SYS_INOTIFY_RM_WATCH = 255; + SYS_MIGRATE_PAGES = 256; + SYS_OPENAT = 257; + SYS_MKDIRAT = 258; + SYS_MKNODAT = 259; + SYS_FCHOWNAT = 260; + SYS_FUTIMESAT = 261; + SYS_NEWFSTATAT = 262; + SYS_UNLINKAT = 263; + SYS_RENAMEAT = 264; + SYS_LINKAT = 265; + SYS_SYMLINKAT = 266; + SYS_READLINKAT = 267; + SYS_FCHMODAT = 268; + SYS_FACCESSAT = 269; + SYS_PSELECT6 = 270; + SYS_PPOLL = 271; + SYS_UNSHARE = 272; + SYS_SET_ROBUST_LIST = 273; + SYS_GET_ROBUST_LIST = 274; + SYS_SPLICE = 275; + SYS_TEE = 276; + SYS_SYNC_FILE_RANGE = 277; + SYS_VMSPLICE = 278; + SYS_MOVE_PAGES = 279; + SYS_UTIMENSAT = 280; + SYS_EPOLL_PWAIT = 281; + SYS_SIGNALFD = 282; + SYS_TIMERFD = 283; + SYS_EVENTFD = 284; + SYS_FALLOCATE = 285; +) + +func _darwin_system_call_conflict() { +} diff --git a/src/pkg/syscall/ztypes_darwin_386.go b/src/pkg/syscall/ztypes_darwin_386.go new file mode 100644 index 000000000..29d0d9676 --- /dev/null +++ b/src/pkg/syscall/ztypes_darwin_386.go @@ -0,0 +1,246 @@ +// godefs -gsyscall -f-m32 types_darwin.c types_darwin_386.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + sizeofPtr = 0x4; + sizeofShort = 0x2; + sizeofInt = 0x4; + sizeofLong = 0x4; + sizeofLongLong = 0x8; + O_RDONLY = 0; + O_WRONLY = 0x1; + O_RDWR = 0x2; + O_APPEND = 0x8; + O_ASYNC = 0x40; + O_CREAT = 0x200; + O_NOCTTY = 0x20000; + O_NONBLOCK = 0x4; + O_SYNC = 0x80; + O_TRUNC = 0x400; + O_CLOEXEC = 0; + F_GETFD = 0x1; + F_SETFD = 0x2; + F_GETFL = 0x3; + F_SETFL = 0x4; + FD_CLOEXEC = 0x1; + NAME_MAX = 0xff; + S_IFMT = 0xf000; + S_IFIFO = 0x1000; + S_IFCHR = 0x2000; + S_IFDIR = 0x4000; + S_IFBLK = 0x6000; + S_IFREG = 0x8000; + S_IFLNK = 0xa000; + S_IFSOCK = 0xc000; + S_IFWHT = 0xe000; + S_ISUID = 0x800; + S_ISGID = 0x400; + S_ISVTX = 0x200; + S_IRUSR = 0x100; + S_IWUSR = 0x80; + S_IXUSR = 0x40; + WNOHANG = 0x1; + WUNTRACED = 0x2; + WEXITED = 0x4; + WSTOPPED = 0x7f; + WCONTINUED = 0x10; + WNOWAIT = 0x20; + AF_UNIX = 0x1; + AF_INET = 0x2; + AF_DATAKIT = 0x9; + AF_INET6 = 0x1e; + SOCK_STREAM = 0x1; + SOCK_DGRAM = 0x2; + SOCK_RAW = 0x3; + SOCK_SEQPACKET = 0x5; + SOL_SOCKET = 0xffff; + SO_REUSEADDR = 0x4; + SO_KEEPALIVE = 0x8; + SO_DONTROUTE = 0x10; + SO_BROADCAST = 0x20; + SO_USELOOPBACK = 0x40; + SO_LINGER = 0x80; + SO_REUSEPORT = 0x200; + SO_SNDBUF = 0x1001; + SO_RCVBUF = 0x1002; + SO_SNDTIMEO = 0x1005; + SO_RCVTIMEO = 0x1006; + SO_NOSIGPIPE = 0x1022; + IPPROTO_TCP = 0x6; + IPPROTO_UDP = 0x11; + TCP_NODELAY = 0x1; + SOMAXCONN = 0x80; + SizeofSockaddrInet4 = 0x10; + SizeofSockaddrInet6 = 0x1c; + SizeofSockaddrAny = 0x1c; + SizeofSockaddrUnix = 0x6a; + EVFILT_READ = -0x1; + EVFILT_WRITE = -0x2; + EVFILT_AIO = -0x3; + EVFILT_VNODE = -0x4; + EVFILT_PROC = -0x5; + EVFILT_SIGNAL = -0x6; + EVFILT_TIMER = -0x7; + EVFILT_MACHPORT = -0x8; + EVFILT_FS = -0x9; + EVFILT_SYSCOUNT = 0x9; + EV_ADD = 0x1; + EV_DELETE = 0x2; + EV_DISABLE = 0x8; + EV_RECEIPT = 0x40; + EV_ONESHOT = 0x10; + EV_CLEAR = 0x20; + EV_SYSFLAGS = 0xf000; + EV_FLAG0 = 0x1000; + EV_FLAG1 = 0x2000; + EV_EOF = 0x8000; + EV_ERROR = 0x4000; +) + +// Types + +type Timespec struct { + Sec int32; + Nsec int32; +} + +type Timeval struct { + Sec int32; + Usec int32; +} + +type Rusage struct { + Utime Timeval; + Stime Timeval; + Maxrss int32; + Ixrss int32; + Idrss int32; + Isrss int32; + Minflt int32; + Majflt int32; + Nswap int32; + Inblock int32; + Oublock int32; + Msgsnd int32; + Msgrcv int32; + Nsignals int32; + Nvcsw int32; + Nivcsw int32; +} + +type Rlimit struct { + Cur uint64; + Max uint64; +} + +type _C_int int32 + +type _Gid_t uint32 + +type Stat_t struct { + Dev int32; + Mode uint16; + Nlink uint16; + Ino uint64; + Uid uint32; + Gid uint32; + Rdev int32; + Atimespec Timespec; + Mtimespec Timespec; + Ctimespec Timespec; + Birthtimespec Timespec; + Size int64; + Blocks int64; + Blksize int32; + Flags uint32; + Gen uint32; + Lspare int32; + Qspare [2]int64; +} + +type Statfs_t struct { + Bsize uint32; + Iosize int32; + Blocks uint64; + Bfree uint64; + Bavail uint64; + Files uint64; + Ffree uint64; + Fsid [8]byte /* fsid */; + Owner uint32; + Type uint32; + Flags uint32; + Fssubtype uint32; + Fstypename [16]int8; + Mntonname [1024]int8; + Mntfromname [1024]int8; + Reserved [8]uint32; +} + +type Dirent struct { + Ino uint64; + Seekoff uint64; + Reclen uint16; + Namlen uint16; + Type uint8; + Name [1024]int8; + Pad0 [3]byte; +} + +type RawSockaddrInet4 struct { + Len uint8; + Family uint8; + Port uint16; + Addr [4]byte /* in_addr */; + Zero [8]int8; +} + +type RawSockaddrInet6 struct { + Len uint8; + Family uint8; + Port uint16; + Flowinfo uint32; + Addr [16]byte /* in6_addr */; + Scope_id uint32; +} + +type RawSockaddrUnix struct { + Len uint8; + Family uint8; + Path [104]int8; +} + +type RawSockaddr struct { + Len uint8; + Family uint8; + Data [14]int8; +} + +type RawSockaddrAny struct { + Addr RawSockaddr; + Pad [12]int8; +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32; + Linger int32; +} + +type Kevent_t struct { + Ident uint32; + Filter int16; + Flags uint16; + Fflags uint32; + Data int32; + Udata *byte; +} + +type FdSet struct { + Bits [32]int32; +} diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go new file mode 100644 index 000000000..0523c50a4 --- /dev/null +++ b/src/pkg/syscall/ztypes_darwin_amd64.go @@ -0,0 +1,248 @@ +// godefs -gsyscall -f-m64 types_darwin.c types_darwin_amd64.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + sizeofPtr = 0x8; + sizeofShort = 0x2; + sizeofInt = 0x4; + sizeofLong = 0x8; + sizeofLongLong = 0x8; + O_RDONLY = 0; + O_WRONLY = 0x1; + O_RDWR = 0x2; + O_APPEND = 0x8; + O_ASYNC = 0x40; + O_CREAT = 0x200; + O_NOCTTY = 0x20000; + O_NONBLOCK = 0x4; + O_SYNC = 0x80; + O_TRUNC = 0x400; + O_CLOEXEC = 0; + F_GETFD = 0x1; + F_SETFD = 0x2; + F_GETFL = 0x3; + F_SETFL = 0x4; + FD_CLOEXEC = 0x1; + NAME_MAX = 0xff; + S_IFMT = 0xf000; + S_IFIFO = 0x1000; + S_IFCHR = 0x2000; + S_IFDIR = 0x4000; + S_IFBLK = 0x6000; + S_IFREG = 0x8000; + S_IFLNK = 0xa000; + S_IFSOCK = 0xc000; + S_IFWHT = 0xe000; + S_ISUID = 0x800; + S_ISGID = 0x400; + S_ISVTX = 0x200; + S_IRUSR = 0x100; + S_IWUSR = 0x80; + S_IXUSR = 0x40; + WNOHANG = 0x1; + WUNTRACED = 0x2; + WEXITED = 0x4; + WSTOPPED = 0x7f; + WCONTINUED = 0x10; + WNOWAIT = 0x20; + AF_UNIX = 0x1; + AF_INET = 0x2; + AF_DATAKIT = 0x9; + AF_INET6 = 0x1e; + SOCK_STREAM = 0x1; + SOCK_DGRAM = 0x2; + SOCK_RAW = 0x3; + SOCK_SEQPACKET = 0x5; + SOL_SOCKET = 0xffff; + SO_REUSEADDR = 0x4; + SO_KEEPALIVE = 0x8; + SO_DONTROUTE = 0x10; + SO_BROADCAST = 0x20; + SO_USELOOPBACK = 0x40; + SO_LINGER = 0x80; + SO_REUSEPORT = 0x200; + SO_SNDBUF = 0x1001; + SO_RCVBUF = 0x1002; + SO_SNDTIMEO = 0x1005; + SO_RCVTIMEO = 0x1006; + SO_NOSIGPIPE = 0x1022; + IPPROTO_TCP = 0x6; + IPPROTO_UDP = 0x11; + TCP_NODELAY = 0x1; + SOMAXCONN = 0x80; + SizeofSockaddrInet4 = 0x10; + SizeofSockaddrInet6 = 0x1c; + SizeofSockaddrAny = 0x1c; + SizeofSockaddrUnix = 0x6a; + EVFILT_READ = -0x1; + EVFILT_WRITE = -0x2; + EVFILT_AIO = -0x3; + EVFILT_VNODE = -0x4; + EVFILT_PROC = -0x5; + EVFILT_SIGNAL = -0x6; + EVFILT_TIMER = -0x7; + EVFILT_MACHPORT = -0x8; + EVFILT_FS = -0x9; + EVFILT_SYSCOUNT = 0x9; + EV_ADD = 0x1; + EV_DELETE = 0x2; + EV_DISABLE = 0x8; + EV_RECEIPT = 0x40; + EV_ONESHOT = 0x10; + EV_CLEAR = 0x20; + EV_SYSFLAGS = 0xf000; + EV_FLAG0 = 0x1000; + EV_FLAG1 = 0x2000; + EV_EOF = 0x8000; + EV_ERROR = 0x4000; +) + +// Types + +type Timespec struct { + Sec int64; + Nsec int64; +} + +type Timeval struct { + Sec int64; + Usec int32; + Pad0 [4]byte; +} + +type Rusage struct { + Utime Timeval; + Stime Timeval; + Maxrss int64; + Ixrss int64; + Idrss int64; + Isrss int64; + Minflt int64; + Majflt int64; + Nswap int64; + Inblock int64; + Oublock int64; + Msgsnd int64; + Msgrcv int64; + Nsignals int64; + Nvcsw int64; + Nivcsw int64; +} + +type Rlimit struct { + Cur uint64; + Max uint64; +} + +type _C_int int32 + +type _Gid_t uint32 + +type Stat_t struct { + Dev int32; + Mode uint16; + Nlink uint16; + Ino uint64; + Uid uint32; + Gid uint32; + Rdev int32; + Pad0 [4]byte; + Atimespec Timespec; + Mtimespec Timespec; + Ctimespec Timespec; + Birthtimespec Timespec; + Size int64; + Blocks int64; + Blksize int32; + Flags uint32; + Gen uint32; + Lspare int32; + Qspare [2]int64; +} + +type Statfs_t struct { + Bsize uint32; + Iosize int32; + Blocks uint64; + Bfree uint64; + Bavail uint64; + Files uint64; + Ffree uint64; + Fsid [8]byte /* fsid */; + Owner uint32; + Type uint32; + Flags uint32; + Fssubtype uint32; + Fstypename [16]int8; + Mntonname [1024]int8; + Mntfromname [1024]int8; + Reserved [8]uint32; +} + +type Dirent struct { + Ino uint64; + Seekoff uint64; + Reclen uint16; + Namlen uint16; + Type uint8; + Name [1024]int8; + Pad0 [3]byte; +} + +type RawSockaddrInet4 struct { + Len uint8; + Family uint8; + Port uint16; + Addr [4]byte /* in_addr */; + Zero [8]int8; +} + +type RawSockaddrInet6 struct { + Len uint8; + Family uint8; + Port uint16; + Flowinfo uint32; + Addr [16]byte /* in6_addr */; + Scope_id uint32; +} + +type RawSockaddrUnix struct { + Len uint8; + Family uint8; + Path [104]int8; +} + +type RawSockaddr struct { + Len uint8; + Family uint8; + Data [14]int8; +} + +type RawSockaddrAny struct { + Addr RawSockaddr; + Pad [12]int8; +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32; + Linger int32; +} + +type Kevent_t struct { + Ident uint64; + Filter int16; + Flags uint16; + Fflags uint32; + Data int64; + Udata *byte; +} + +type FdSet struct { + Bits [32]int32; +} diff --git a/src/pkg/syscall/ztypes_linux_386.go b/src/pkg/syscall/ztypes_linux_386.go new file mode 100644 index 000000000..c3a083762 --- /dev/null +++ b/src/pkg/syscall/ztypes_linux_386.go @@ -0,0 +1,297 @@ +// godefs -gsyscall -f-m32 types_linux.c types_linux_386.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + sizeofPtr = 0x4; + sizeofShort = 0x2; + sizeofInt = 0x4; + sizeofLong = 0x4; + sizeofLongLong = 0x8; + PathMax = 0x1000; + O_RDONLY = 0; + O_WRONLY = 0x1; + O_RDWR = 0x2; + O_APPEND = 0x400; + O_ASYNC = 0x2000; + O_CREAT = 0x40; + O_NOCTTY = 0x100; + O_NONBLOCK = 0x800; + O_SYNC = 0x1000; + O_TRUNC = 0x200; + O_CLOEXEC = 0; + F_GETFD = 0x1; + F_SETFD = 0x2; + F_GETFL = 0x3; + F_SETFL = 0x4; + FD_CLOEXEC = 0x1; + NAME_MAX = 0xff; + S_IFMT = 0xf000; + S_IFIFO = 0x1000; + S_IFCHR = 0x2000; + S_IFDIR = 0x4000; + S_IFBLK = 0x6000; + S_IFREG = 0x8000; + S_IFLNK = 0xa000; + S_IFSOCK = 0xc000; + S_ISUID = 0x800; + S_ISGID = 0x400; + S_ISVTX = 0x200; + S_IRUSR = 0x100; + S_IWUSR = 0x80; + S_IXUSR = 0x40; + WNOHANG = 0x1; + WUNTRACED = 0x2; + WEXITED = 0x4; + WSTOPPED = 0x2; + WCONTINUED = 0x8; + WNOWAIT = 0x1000000; + AF_UNIX = 0x1; + AF_INET = 0x2; + AF_INET6 = 0xa; + SOCK_STREAM = 0x1; + SOCK_DGRAM = 0x2; + SOCK_RAW = 0x3; + SOCK_SEQPACKET = 0x5; + SOL_SOCKET = 0x1; + SO_REUSEADDR = 0x2; + SO_KEEPALIVE = 0x9; + SO_DONTROUTE = 0x5; + SO_BROADCAST = 0x6; + SO_LINGER = 0xd; + SO_SNDBUF = 0x7; + SO_RCVBUF = 0x8; + SO_SNDTIMEO = 0x15; + SO_RCVTIMEO = 0x14; + IPPROTO_TCP = 0x6; + IPPROTO_UDP = 0x11; + TCP_NODELAY = 0x1; + SOMAXCONN = 0x80; + SizeofSockaddrInet4 = 0x10; + SizeofSockaddrInet6 = 0x1c; + SizeofSockaddrAny = 0x1c; + SizeofSockaddrUnix = 0x6e; + EPOLLIN = 0x1; + EPOLLRDHUP = 0x2000; + EPOLLOUT = 0x4; + EPOLLONESHOT = 0x40000000; + EPOLL_CTL_MOD = 0x3; + EPOLL_CTL_ADD = 0x1; + EPOLL_CTL_DEL = 0x2; +) + +// Types + +type Timespec struct { + Sec int32; + Nsec int32; +} + +type Timeval struct { + Sec int32; + Usec int32; +} + +type Timex struct { + Modes uint32; + Offset int32; + Freq int32; + Maxerror int32; + Esterror int32; + Status int32; + Constant int32; + Precision int32; + Tolerance int32; + Time Timeval; + Tick int32; + Ppsfreq int32; + Jitter int32; + Shift int32; + Stabil int32; + Jitcnt int32; + Calcnt int32; + Errcnt int32; + Stbcnt int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; +} + +type Time_t int32 + +type Tms struct { + Utime int32; + Stime int32; + Cutime int32; + Cstime int32; +} + +type Utimbuf struct { + Actime int32; + Modtime int32; +} + +type Rusage struct { + Utime Timeval; + Stime Timeval; + Maxrss int32; + Ixrss int32; + Idrss int32; + Isrss int32; + Minflt int32; + Majflt int32; + Nswap int32; + Inblock int32; + Oublock int32; + Msgsnd int32; + Msgrcv int32; + Nsignals int32; + Nvcsw int32; + Nivcsw int32; +} + +type Rlimit struct { + Cur uint32; + Max uint32; +} + +type _C_int int32 + +type _Gid_t uint32 + +type Stat_t struct { + Dev uint64; + __pad1 uint16; + Pad0 [2]byte; + Ino uint32; + Mode uint32; + Nlink uint32; + Uid uint32; + Gid uint32; + Rdev uint64; + __pad2 uint16; + Pad1 [2]byte; + Size int32; + Blksize int32; + Blocks int32; + Atim Timespec; + Mtim Timespec; + Ctim Timespec; + __unused4 uint32; + __unused5 uint32; +} + +type Statfs_t struct { + Type int32; + Bsize int32; + Blocks uint32; + Bfree uint32; + Bavail uint32; + Files uint32; + Ffree uint32; + Fsid [8]byte /* __fsid_t */; + Namelen int32; + Frsize int32; + Spare [5]int32; +} + +type Dirent struct { + Ino uint32; + Off int32; + Reclen uint16; + Type uint8; + Name [256]int8; + Pad0 [1]byte; +} + +type RawSockaddrInet4 struct { + Family uint16; + Port uint16; + Addr [4]byte /* in_addr */; + Zero [8]uint8; +} + +type RawSockaddrInet6 struct { + Family uint16; + Port uint16; + Flowinfo uint32; + Addr [16]byte /* in6_addr */; + Scope_id uint32; +} + +type RawSockaddrUnix struct { + Family uint16; + Path [108]int8; +} + +type RawSockaddr struct { + Family uint16; + Data [14]int8; +} + +type RawSockaddrAny struct { + Addr RawSockaddr; + Pad [12]int8; +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32; + Linger int32; +} + +type FdSet struct { + __fds_bits [32]int32; +} + +type Sysinfo_t struct { + Uptime int32; + Loads [3]uint32; + Totalram uint32; + Freeram uint32; + Sharedram uint32; + Bufferram uint32; + Totalswap uint32; + Freeswap uint32; + Procs uint16; + Pad uint16; + Totalhigh uint32; + Freehigh uint32; + Unit uint32; + _f [8]int8; +} + +type Utsname struct { + Sysname [65]int8; + Nodename [65]int8; + Release [65]int8; + Version [65]int8; + Machine [65]int8; + __domainname [65]int8; +} + +type Ustat_t struct { + Tfree int32; + Tinode uint32; + Fname [6]int8; + Fpack [6]int8; +} + +type EpollEvent struct { + Events uint32; + Fd int32; + Pad int32; +} diff --git a/src/pkg/syscall/ztypes_linux_amd64.go b/src/pkg/syscall/ztypes_linux_amd64.go new file mode 100644 index 000000000..f17ebe139 --- /dev/null +++ b/src/pkg/syscall/ztypes_linux_amd64.go @@ -0,0 +1,300 @@ +// godefs -gsyscall -f-m64 types_linux.c types_linux_amd64.c + +// MACHINE GENERATED - DO NOT EDIT. + +package syscall + +// Constants +const ( + sizeofPtr = 0x8; + sizeofShort = 0x2; + sizeofInt = 0x4; + sizeofLong = 0x8; + sizeofLongLong = 0x8; + PathMax = 0x1000; + O_RDONLY = 0; + O_WRONLY = 0x1; + O_RDWR = 0x2; + O_APPEND = 0x400; + O_ASYNC = 0x2000; + O_CREAT = 0x40; + O_NOCTTY = 0x100; + O_NONBLOCK = 0x800; + O_SYNC = 0x1000; + O_TRUNC = 0x200; + O_CLOEXEC = 0; + F_GETFD = 0x1; + F_SETFD = 0x2; + F_GETFL = 0x3; + F_SETFL = 0x4; + FD_CLOEXEC = 0x1; + NAME_MAX = 0xff; + S_IFMT = 0xf000; + S_IFIFO = 0x1000; + S_IFCHR = 0x2000; + S_IFDIR = 0x4000; + S_IFBLK = 0x6000; + S_IFREG = 0x8000; + S_IFLNK = 0xa000; + S_IFSOCK = 0xc000; + S_ISUID = 0x800; + S_ISGID = 0x400; + S_ISVTX = 0x200; + S_IRUSR = 0x100; + S_IWUSR = 0x80; + S_IXUSR = 0x40; + WNOHANG = 0x1; + WUNTRACED = 0x2; + WEXITED = 0x4; + WSTOPPED = 0x2; + WCONTINUED = 0x8; + WNOWAIT = 0x1000000; + AF_UNIX = 0x1; + AF_INET = 0x2; + AF_INET6 = 0xa; + SOCK_STREAM = 0x1; + SOCK_DGRAM = 0x2; + SOCK_RAW = 0x3; + SOCK_SEQPACKET = 0x5; + SOL_SOCKET = 0x1; + SO_REUSEADDR = 0x2; + SO_KEEPALIVE = 0x9; + SO_DONTROUTE = 0x5; + SO_BROADCAST = 0x6; + SO_LINGER = 0xd; + SO_SNDBUF = 0x7; + SO_RCVBUF = 0x8; + SO_SNDTIMEO = 0x15; + SO_RCVTIMEO = 0x14; + IPPROTO_TCP = 0x6; + IPPROTO_UDP = 0x11; + TCP_NODELAY = 0x1; + SOMAXCONN = 0x80; + SizeofSockaddrInet4 = 0x10; + SizeofSockaddrInet6 = 0x1c; + SizeofSockaddrAny = 0x1c; + SizeofSockaddrUnix = 0x6e; + EPOLLIN = 0x1; + EPOLLRDHUP = 0x2000; + EPOLLOUT = 0x4; + EPOLLONESHOT = 0x40000000; + EPOLL_CTL_MOD = 0x3; + EPOLL_CTL_ADD = 0x1; + EPOLL_CTL_DEL = 0x2; +) + +// Types + +type Timespec struct { + Sec int64; + Nsec int64; +} + +type Timeval struct { + Sec int64; + Usec int64; +} + +type Timex struct { + Modes uint32; + Pad0 [4]byte; + Offset int64; + Freq int64; + Maxerror int64; + Esterror int64; + Status int32; + Pad1 [4]byte; + Constant int64; + Precision int64; + Tolerance int64; + Time Timeval; + Tick int64; + Ppsfreq int64; + Jitter int64; + Shift int32; + Pad2 [4]byte; + Stabil int64; + Jitcnt int64; + Calcnt int64; + Errcnt int64; + Stbcnt int64; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; + int32; +} + +type Time_t int64 + +type Tms struct { + Utime int64; + Stime int64; + Cutime int64; + Cstime int64; +} + +type Utimbuf struct { + Actime int64; + Modtime int64; +} + +type Rusage struct { + Utime Timeval; + Stime Timeval; + Maxrss int64; + Ixrss int64; + Idrss int64; + Isrss int64; + Minflt int64; + Majflt int64; + Nswap int64; + Inblock int64; + Oublock int64; + Msgsnd int64; + Msgrcv int64; + Nsignals int64; + Nvcsw int64; + Nivcsw int64; +} + +type Rlimit struct { + Cur uint64; + Max uint64; +} + +type _C_int int32 + +type _Gid_t uint32 + +type Stat_t struct { + Dev uint64; + Ino uint64; + Nlink uint64; + Mode uint32; + Uid uint32; + Gid uint32; + Pad0 int32; + Rdev uint64; + Size int64; + Blksize int64; + Blocks int64; + Atim Timespec; + Mtim Timespec; + Ctim Timespec; + __unused [3]int64; +} + +type Statfs_t struct { + Type int64; + Bsize int64; + Blocks uint64; + Bfree uint64; + Bavail uint64; + Files uint64; + Ffree uint64; + Fsid [8]byte /* __fsid_t */; + Namelen int64; + Frsize int64; + Spare [5]int64; +} + +type Dirent struct { + Ino uint64; + Off int64; + Reclen uint16; + Type uint8; + Name [256]int8; + Pad0 [5]byte; +} + +type RawSockaddrInet4 struct { + Family uint16; + Port uint16; + Addr [4]byte /* in_addr */; + Zero [8]uint8; +} + +type RawSockaddrInet6 struct { + Family uint16; + Port uint16; + Flowinfo uint32; + Addr [16]byte /* in6_addr */; + Scope_id uint32; +} + +type RawSockaddrUnix struct { + Family uint16; + Path [108]int8; +} + +type RawSockaddr struct { + Family uint16; + Data [14]int8; +} + +type RawSockaddrAny struct { + Addr RawSockaddr; + Pad [12]int8; +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32; + Linger int32; +} + +type FdSet struct { + __fds_bits [16]int64; +} + +type Sysinfo_t struct { + Uptime int64; + Loads [3]uint64; + Totalram uint64; + Freeram uint64; + Sharedram uint64; + Bufferram uint64; + Totalswap uint64; + Freeswap uint64; + Procs uint16; + Pad uint16; + Pad0 [4]byte; + Totalhigh uint64; + Freehigh uint64; + Unit uint32; + _f [2]int8; + Pad1 [4]byte; +} + +type Utsname struct { + Sysname [65]int8; + Nodename [65]int8; + Release [65]int8; + Version [65]int8; + Machine [65]int8; + __domainname [65]int8; +} + +type Ustat_t struct { + Tfree int32; + Pad0 [4]byte; + Tinode uint64; + Fname [6]int8; + Fpack [6]int8; + Pad1 [4]byte; +} + +type EpollEvent struct { + Events uint32; + Fd int32; + Pad int32; +} diff --git a/src/pkg/tabwriter/Makefile b/src/pkg/tabwriter/Makefile new file mode 100644 index 000000000..1c4518066 --- /dev/null +++ b/src/pkg/tabwriter/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + tabwriter.$O\ + + +phases: a1 +_obj$D/tabwriter.a: phases + +a1: $(O1) + $(AR) grc _obj$D/tabwriter.a tabwriter.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/tabwriter.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/tabwriter.a + +packages: _obj$D/tabwriter.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/tabwriter.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/tabwriter.a diff --git a/src/pkg/tabwriter/tabwriter.go b/src/pkg/tabwriter/tabwriter.go new file mode 100644 index 000000000..6799f72d1 --- /dev/null +++ b/src/pkg/tabwriter/tabwriter.go @@ -0,0 +1,437 @@ +// Copyright 2009 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. + +// The tabwriter package implements a write filter (tabwriter.Writer) +// that translates tabbed columns in input into properly aligned text, +// using the Elastic Tabstops algorithm described at +// http://nickgravgaard.com/elastictabstops/index.html. +// +package tabwriter + +import ( + "container/vector"; + "io"; + "os"; + "utf8"; +) + + +// ---------------------------------------------------------------------------- +// Filter implementation + +// A Writer is a filter that inserts padding around +// tab-delimited columns in its input to align them +// in the output. +// +// The Writer treats incoming bytes as UTF-8 encoded text +// consisting of tab-terminated cells. Cells in adjacent lines +// constitute a column. The Writer inserts padding as needed +// to make all cells in a column have the same width, effectively +// aligning the columns. Note that cells are tab-terminated, +// not tab-separated: trailing non-tab text at the end of a line +// is not part of any cell. +// +// The Writer assumes that all characters have the same width; +// this may not be true in some fonts, especially with certain +// UTF-8 characters. +// +// If a Writer is configured to filter HTML, HTML tags and entities +// are simply passed through and their widths are assumed to be zero +// for formatting purposes. +// +// The form feed character ('\f') acts like a newline but it also +// terminates all columns in the current line (effectively calling +// Flush). Cells in the next line start new columns. Unless found +// inside an HTML tag, form feed characters appear as newlines in +// the output. +// +// The Writer must buffer input internally, because proper spacing +// of one line may depend on the cells in future lines. Clients must +// call Flush when done calling Write. +// +type Writer struct { + // configuration + output io.Writer; + cellwidth int; + padding int; + padbytes [8]byte; + flags uint; + + // current state + html_char byte; // terminating char of html tag/entity, or 0 ('>', ';', or 0) + buf io.ByteBuffer; // collected text w/o tabs, newlines, or form feed chars + size int; // size of incomplete cell in bytes + width int; // width of incomplete cell in runes up to buf[pos] w/o ignored sections + pos int; // buffer position up to which width of incomplete cell has been computed + lines_size vector.Vector; // list of lines; each line is a list of cell sizes in bytes + lines_width vector.Vector; // list of lines; each line is a list of cell widths in runes + widths vector.IntVector; // list of column widths in runes - re-used during formatting +} + + +// Internal representation (current state): +// +// - all text written is appended to buf; form feed chars, tabs and newlines are stripped away +// - at any given time there is a (possibly empty) incomplete cell at the end +// (the cell starts after a tab or newline) +// - size is the number of bytes belonging to the cell so far +// - width is text width in runes of that cell from the start of the cell to +// position pos; html tags and entities are excluded from this width if html +// filtering is enabled +// - the sizes and widths of processed text are kept in the lines_size and +// lines_width arrays, which contain an array of sizes or widths for each line +// - the widths array is a temporary array with current widths used during +// formatting; it is kept in Writer because it's re-used +// +// |<---------- size ---------->| +// | | +// |<- width ->|<- ignored ->| | +// | | | | +// [---processed---tab------------<tag>...</tag>...] +// ^ ^ ^ +// | | | +// buf start of incomplete cell pos + + +func (b *Writer) addLine() { + b.lines_size.Push(vector.NewIntVector(0)); + b.lines_width.Push(vector.NewIntVector(0)); +} + + +// Formatting can be controlled with these flags. +const ( + // Ignore html tags and treat entities (starting with '&' + // and ending in ';') as single characters (width = 1). + FilterHTML = 1 << iota; + + // Force right-alignment of cell content. + // Default is left-alignment. + AlignRight; +) + + +// A Writer must be initialized with a call to Init. The first parameter (output) +// specifies the filter output. The remaining parameters control the formatting: +// +// cellwidth minimal cell width +// padding additional cell padding +// padchar ASCII char used for padding +// if padchar == '\t', the Writer will assume that the +// width of a '\t' in the formatted output is cellwidth, +// and cells are left-aligned independent of align_left +// (for correct-looking results, cellwidth must correspond +// to the tab width in the viewer displaying the result) +// flags formatting control +// +func (b *Writer) Init(output io.Writer, cellwidth, padding int, padchar byte, flags uint) *Writer { + if cellwidth < 0 { + panic("negative cellwidth"); + } + if padding < 0 { + panic("negative padding"); + } + b.output = output; + b.cellwidth = cellwidth; + b.padding = padding; + for i := len(b.padbytes) - 1; i >= 0; i-- { + b.padbytes[i] = padchar; + } + if padchar == '\t' { + // tab enforces left-alignment + flags &^= AlignRight; + } + b.flags = flags; + + b.lines_size.Init(0); + b.lines_width.Init(0); + b.widths.Init(0); + b.addLine(); // the very first line + + return b; +} + + +func (b *Writer) line(i int) (*vector.IntVector, *vector.IntVector) { + return + b.lines_size.At(i).(*vector.IntVector), + b.lines_width.At(i).(*vector.IntVector); +} + + +// debugging support (keep code around) +/* +func (b *Writer) dump() { + pos := 0; + for i := 0; i < b.lines_size.Len(); i++ { + line_size, line_width := b.line(i); + print("(", i, ") "); + for j := 0; j < line_size.Len(); j++ { + s := line_size.At(j); + print("[", string(b.buf.slice(pos, pos + s)), "]"); + pos += s; + } + print("\n"); + } + print("\n"); +} +*/ + + +func (b *Writer) write0(buf []byte) os.Error { + n, err := b.output.Write(buf); + if n != len(buf) && err == nil { + err = os.EIO; + } + return err; +} + + +var newline = []byte{'\n'} + +func (b *Writer) writePadding(textw, cellw int) os.Error { + if b.padbytes[0] == '\t' { + // make cell width a multiple of cellwidth + cellw = ((cellw + b.cellwidth - 1) / b.cellwidth) * b.cellwidth; + } + + n := cellw - textw; + if n < 0 { + panic("internal error"); + } + + if b.padbytes[0] == '\t' { + n = (n + b.cellwidth - 1) / b.cellwidth; + } + + for n > len(b.padbytes) { + if err := b.write0(&b.padbytes); err != nil { + return err; + } + n -= len(b.padbytes); + } + + return b.write0(b.padbytes[0 : n]); +} + + +func (b *Writer) writeLines(pos0 int, line0, line1 int) (int, os.Error) { + pos := pos0; + for i := line0; i < line1; i++ { + line_size, line_width := b.line(i); + for j := 0; j < line_size.Len(); j++ { + s, w := line_size.At(j), line_width.At(j); + + switch { + default: // align left + + if err := b.write0(b.buf.Data()[pos : pos + s]); err != nil { + return pos, err; + } + pos += s; + if j < b.widths.Len() { + if err := b.writePadding(w, b.widths.At(j)); err != nil { + return pos, err; + } + } + + case b.flags & AlignRight != 0: // align right + + if j < b.widths.Len() { + if err := b.writePadding(w, b.widths.At(j)); err != nil { + return pos, err; + } + } + if err := b.write0(b.buf.Data()[pos : pos + s]); err != nil { + return pos, err; + } + pos += s; + } + } + + if i+1 == b.lines_size.Len() { + // last buffered line - we don't have a newline, so just write + // any outstanding buffered data + if err := b.write0(b.buf.Data()[pos : pos + b.size]); err != nil { + return pos, err; + } + pos += b.size; + } else { + // not the last line - write newline + if err := b.write0(newline); err != nil { + return pos, err; + } + } + } + return pos, nil; +} + + +func (b *Writer) format(pos0 int, line0, line1 int) (pos int, err os.Error) { + pos = pos0; + column := b.widths.Len(); + last := line0; + for this := line0; this < line1; this++ { + line_size, line_width := b.line(this); + + if column < line_size.Len() - 1 { + // cell exists in this column + // (note that the last cell per line is ignored) + + // print unprinted lines until beginning of block + pos, err = b.writeLines(pos, last, this); + if err != nil { + return pos, err; + } + last = this; + + // column block begin + width := b.cellwidth; // minimal width + for ; this < line1; this++ { + line_size, line_width = b.line(this); + if column < line_size.Len() - 1 { + // cell exists in this column => update width + w := line_width.At(column) + b.padding; + if w > width { + width = w; + } + } else { + break + } + } + // column block end + + // format and print all columns to the right of this column + // (we know the widths of this column and all columns to the left) + b.widths.Push(width); + pos, err = b.format(pos, last, this); + b.widths.Pop(); + last = this; + } + } + + // print unprinted lines until end + return b.writeLines(pos, last, line1); +} + + +// Flush should be called after the last call to Write to ensure +// that any data buffered in the Writer is written to output. +// +func (b *Writer) Flush() os.Error { + _, err := b.format(0, 0, b.lines_size.Len()); + // reset (even in the presence of errors) + b.buf.Reset(); + b.size, b.width = 0, 0; + b.pos = 0; + b.lines_size.Init(0); + b.lines_width.Init(0); + b.addLine(); + return err; +} + + +func unicodeLen(buf []byte) int { + l := 0; + for i := 0; i < len(buf); { + if buf[i] < utf8.RuneSelf { + i++; + } else { + rune, size := utf8.DecodeRune(buf[i : len(buf)]); + i += size; + } + l++; + } + return l; +} + + +func (b *Writer) append(buf []byte) { + b.buf.Write(buf); + b.size += len(buf); +} + + +// Write writes buf to the writer b. +// The only errors returned are ones encountered +// while writing to the underlying output stream. +// +func (b *Writer) Write(buf []byte) (written int, err os.Error) { + i0, n := 0, len(buf); + + // split text into cells + for i := 0; i < n; i++ { + ch := buf[i]; + + if b.html_char == 0 { + // outside html tag/entity + switch ch { + case '\t', '\n', '\f': + b.append(buf[i0 : i]); + i0 = i + 1; // exclude ch from (next) cell + b.width += unicodeLen(b.buf.Data()[b.pos : b.buf.Len()]); + b.pos = b.buf.Len(); + + // terminate cell + last_size, last_width := b.line(b.lines_size.Len() - 1); + last_size.Push(b.size); + last_width.Push(b.width); + b.size, b.width = 0, 0; + + if ch != '\t' { + // terminate line + b.addLine(); + if ch == '\f' || last_size.Len() == 1 { + // A '\f' always forces a flush. Otherwise, if the previous + // line has only one cell which does not have an impact on + // the formatting of the following lines (the last cell per + // line is ignored by format()), thus we can flush the + // Writer contents. + if err = b.Flush(); err != nil { + return i0, err; + } + } + } + + case '<', '&': + if b.flags & FilterHTML != 0 { + b.append(buf[i0 : i]); + i0 = i; + b.width += unicodeLen(b.buf.Data()[b.pos : b.buf.Len()]); + b.pos = -1; // preventative - should not be used (will cause index out of bounds) + if ch == '<' { + b.html_char = '>'; + } else { + b.html_char = ';'; + } + } + } + + } else { + // inside html tag/entity + if ch == b.html_char { + // reached the end of tag/entity + b.append(buf[i0 : i + 1]); + i0 = i + 1; + if b.html_char == ';' { + b.width++; // count as one char + } + b.pos = b.buf.Len(); + b.html_char = 0; + } + } + } + + // append leftover text + b.append(buf[i0 : n]); + return n, nil; +} + + +// NewWriter allocates and initializes a new tabwriter.Writer. +// The parameters are the same as for the the Init function. +// +func NewWriter(output io.Writer, cellwidth, padding int, padchar byte, flags uint) *Writer { + return new(Writer).Init(output, cellwidth, padding, padchar, flags) +} diff --git a/src/pkg/tabwriter/tabwriter_test.go b/src/pkg/tabwriter/tabwriter_test.go new file mode 100644 index 000000000..7026446e6 --- /dev/null +++ b/src/pkg/tabwriter/tabwriter_test.go @@ -0,0 +1,380 @@ +// Copyright 2009 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 tabwriter + +import ( + "io"; + "os"; + "tabwriter"; + "testing"; +) + + +type buffer struct { + a []byte; +} + + +func (b *buffer) init(n int) { + b.a = make([]byte, n)[0 : 0]; +} + + +func (b *buffer) clear() { + b.a = b.a[0 : 0]; +} + + +func (b *buffer) Write(buf []byte) (written int, err os.Error) { + n := len(b.a); + m := len(buf); + if n + m <= cap(b.a) { + b.a = b.a[0 : n + m]; + for i := 0; i < m; i++ { + b.a[n+i] = buf[i]; + } + } else { + panicln("buffer too small", n, m, cap(b.a)); + } + return len(buf), nil; +} + + +func (b *buffer) String() string { + return string(b.a); +} + + +func write(t *testing.T, w *tabwriter.Writer, src string) { + written, err := io.WriteString(w, src); + if err != nil { + t.Errorf("--- src:\n%s\n--- write error: %v\n", src, err); + } + if written != len(src) { + t.Errorf("--- src:\n%s\n--- written = %d, len(src) = %d\n", src, written, len(src)); + } +} + + +func verify(t *testing.T, w *tabwriter.Writer, b *buffer, src, expected string) { + err := w.Flush(); + if err != nil { + t.Errorf("--- src:\n%s\n--- flush error: %v\n", src, err); + } + + res := b.String(); + if res != expected { + t.Errorf("--- src:\n%s\n--- found:\n%s\n--- expected:\n%s\n", src, res, expected) + } +} + + +func check(t *testing.T, tabwidth, padding int, padchar byte, flags uint, src, expected string) { + var b buffer; + b.init(1000); + + var w tabwriter.Writer; + w.Init(&b, tabwidth, padding, padchar, flags); + + // write all at once + b.clear(); + write(t, &w, src); + verify(t, &w, &b, src, expected); + + // write byte-by-byte + b.clear(); + for i := 0; i < len(src); i++ { + write(t, &w, src[i : i+1]); + } + verify(t, &w, &b, src, expected); + + // write using Fibonacci slice sizes + b.clear(); + for i, d := 0, 0; i < len(src); { + write(t, &w, src[i : i+d]); + i, d = i+d, d+1; + if i+d > len(src) { + d = len(src) - i; + } + } + verify(t, &w, &b, src, expected); +} + + +type entry struct { + tabwidth, padding int; + padchar byte; + flags uint; + src, expected string; +} + + +var tests = []entry { + entry{ + 8, 1, '.', 0, + "", + "" + }, + + entry{ + 8, 1, '.', 0, + "\n\n\n", + "\n\n\n" + }, + + entry{ + 8, 1, '.', 0, + "a\nb\nc", + "a\nb\nc" + }, + + entry{ + 8, 1, '.', 0, + "\t", // '\t' terminates an empty cell on last line - nothing to print + "" + }, + + entry{ + 8, 1, '.', tabwriter.AlignRight, + "\t", // '\t' terminates an empty cell on last line - nothing to print + "" + }, + + entry{ + 8, 1, '.', 0, + "*\t*", + "**" + }, + + entry{ + 8, 1, '.', 0, + "*\t*\n", + "*.......*\n" + }, + + entry{ + 8, 1, '.', 0, + "*\t*\t", + "*.......*" + }, + + entry{ + 8, 1, '.', tabwriter.AlignRight, + "*\t*\t", + ".......**" + }, + + entry{ + 8, 1, '.', 0, + "\t\n", + "........\n" + }, + + entry{ + 8, 1, '.', 0, + "a) foo", + "a) foo" + }, + + entry{ + 8, 1, ' ', 0, + "b) foo\tbar", // "bar" is not in any cell - not formatted, just flushed + "b) foobar" + }, + + entry{ + 8, 1, '.', 0, + "c) foo\tbar\t", + "c) foo..bar" + }, + + entry{ + 8, 1, '.', 0, + "d) foo\tbar\n", + "d) foo..bar\n" + }, + + entry{ + 8, 1, '.', 0, + "e) foo\tbar\t\n", + "e) foo..bar.....\n" + }, + + entry{ + 8, 1, '.', tabwriter.FilterHTML, + "f) f<o\t<b>bar</b>\t\n", + "f) f<o..<b>bar</b>.....\n" + }, + + entry{ + 8, 1, '*', 0, + "Hello, world!\n", + "Hello, world!\n" + }, + + entry{ + 0, 0, '.', 0, + "1\t2\t3\t4\n" + "11\t222\t3333\t44444\n", + + "1.2..3...4\n" + "11222333344444\n" + }, + + entry{ + 0, 0, '.', tabwriter.FilterHTML, + "1\t2<!---\f--->\t3\t4\n" // \f inside HTML is ignored + "11\t222\t3333\t44444\n", + + "1.2<!---\f--->..3...4\n" + "11222333344444\n" + }, + + entry{ + 0, 0, '.', 0, + "1\t2\t3\t4\f" // \f causes a newline and flush + "11\t222\t3333\t44444\n", + + "1234\n" + "11222333344444\n" + }, + + entry{ + 5, 0, '.', 0, + "1\t2\t3\t4\n", + "1....2....3....4\n" + }, + + entry{ + 5, 0, '.', 0, + "1\t2\t3\t4\t\n", + "1....2....3....4....\n" + }, + + entry{ + 8, 1, '.', 0, + "本\tb\tc\n" + "aa\t\u672c\u672c\u672c\tcccc\tddddd\n" + "aaa\tbbbb\n", + + "本.......b.......c\n" + "aa......本本本.....cccc....ddddd\n" + "aaa.....bbbb\n" + }, + + entry{ + 8, 1, ' ', tabwriter.AlignRight, + "a\tè\tc\t\n" + "aa\tèèè\tcccc\tddddd\t\n" + "aaa\tèèèè\t\n", + + " a è c\n" + " aa èèè cccc ddddd\n" + " aaa èèèè\n" + }, + + entry{ + 2, 0, ' ', 0, + "a\tb\tc\n" + "aa\tbbb\tcccc\n" + "aaa\tbbbb\n", + + "a b c\n" + "aa bbbcccc\n" + "aaabbbb\n" + }, + + entry{ + 8, 1, '_', 0, + "a\tb\tc\n" + "aa\tbbb\tcccc\n" + "aaa\tbbbb\n", + + "a_______b_______c\n" + "aa______bbb_____cccc\n" + "aaa_____bbbb\n" + }, + + entry{ + 4, 1, '-', 0, + "4444\t日本語\t22\t1\t333\n" + "999999999\t22\n" + "7\t22\n" + "\t\t\t88888888\n" + "\n" + "666666\t666666\t666666\t4444\n" + "1\t1\t999999999\t0000000000\n", + + "4444------日本語-22--1---333\n" + "999999999-22\n" + "7---------22\n" + "------------------88888888\n" + "\n" + "666666-666666-666666----4444\n" + "1------1------999999999-0000000000\n" + }, + + entry{ + 4, 3, '.', 0, + "4444\t333\t22\t1\t333\n" + "999999999\t22\n" + "7\t22\n" + "\t\t\t88888888\n" + "\n" + "666666\t666666\t666666\t4444\n" + "1\t1\t999999999\t0000000000\n", + + "4444........333...22...1...333\n" + "999999999...22\n" + "7...........22\n" + "....................88888888\n" + "\n" + "666666...666666...666666......4444\n" + "1........1........999999999...0000000000\n" + }, + + entry{ + 8, 1, '\t', tabwriter.FilterHTML, + "4444\t333\t22\t1\t333\n" + "999999999\t22\n" + "7\t22\n" + "\t\t\t88888888\n" + "\n" + "666666\t666666\t666666\t4444\n" + "1\t1\t<font color=red attr=日本語>999999999</font>\t0000000000\n", + + "4444\t\t333\t22\t1\t333\n" + "999999999\t22\n" + "7\t\t22\n" + "\t\t\t\t88888888\n" + "\n" + "666666\t666666\t666666\t\t4444\n" + "1\t1\t<font color=red attr=日本語>999999999</font>\t0000000000\n" + }, + + entry{ + 0, 2, ' ', tabwriter.AlignRight, + ".0\t.3\t2.4\t-5.1\t\n" + "23.0\t12345678.9\t2.4\t-989.4\t\n" + "5.1\t12.0\t2.4\t-7.0\t\n" + ".0\t0.0\t332.0\t8908.0\t\n" + ".0\t-.3\t456.4\t22.1\t\n" + ".0\t1.2\t44.4\t-13.3\t\t", + + " .0 .3 2.4 -5.1\n" + " 23.0 12345678.9 2.4 -989.4\n" + " 5.1 12.0 2.4 -7.0\n" + " .0 0.0 332.0 8908.0\n" + " .0 -.3 456.4 22.1\n" + " .0 1.2 44.4 -13.3" + }, +} + + +func Test(t *testing.T) { + for _, e := range tests { + check(t, e.tabwidth, e.padding, e.padchar, e.flags, e.src, e.expected); + } +} diff --git a/src/pkg/template/Makefile b/src/pkg/template/Makefile new file mode 100644 index 000000000..e91c08818 --- /dev/null +++ b/src/pkg/template/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + format.$O\ + +O2=\ + template.$O\ + + +phases: a1 a2 +_obj$D/template.a: phases + +a1: $(O1) + $(AR) grc _obj$D/template.a format.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/template.a template.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/template.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/template.a + +packages: _obj$D/template.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/template.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/template.a diff --git a/src/pkg/template/format.go b/src/pkg/template/format.go new file mode 100644 index 000000000..4fb5393b9 --- /dev/null +++ b/src/pkg/template/format.go @@ -0,0 +1,54 @@ +// Copyright 2009 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. + +// Template library: default formatters + +package template + +import ( + "fmt"; + "io"; + "reflect"; +) + +// StringFormatter formats into the default string representation. +// It is stored under the name "str" and is the default formatter. +// You can override the default formatter by storing your default +// under the name "" in your custom formatter map. +func StringFormatter(w io.Writer, value interface{}, format string) { + fmt.Fprint(w, value); +} + + +var esc_amp = io.StringBytes("&") +var esc_lt = io.StringBytes("<") +var esc_gt = io.StringBytes(">") + +// HtmlEscape writes to w the properly escaped HTML equivalent +// of the plain text data s. +func HtmlEscape(w io.Writer, s []byte) { + last := 0; + for i, c := range s { + if c == '&' || c == '<' || c == '>' { + w.Write(s[last:i]); + switch c { + case '&': + w.Write(esc_amp); + case '<': + w.Write(esc_lt); + case '>': + w.Write(esc_gt); + } + last = i+1; + } + } + w.Write(s[last:len(s)]); +} + +// HtmlFormatter formats arbitrary values for HTML +func HtmlFormatter(w io.Writer, value interface{}, format string) { + var b io.ByteBuffer; + fmt.Fprint(&b, value); + HtmlEscape(w, b.Data()); +} diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go new file mode 100644 index 000000000..a5e9b0c7d --- /dev/null +++ b/src/pkg/template/template.go @@ -0,0 +1,808 @@ +// Copyright 2009 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. + +/* + Data-driven templates for generating textual output such as + HTML. See + http://code.google.com/p/json-template/wiki/Reference + for full documentation of the template language. A summary: + + Templates are executed by applying them to a data structure. + Annotations in the template refer to elements of the data + structure (typically a field of a struct) to control execution + and derive values to be displayed. The template walks the + structure as it executes and the "cursor" @ represents the + value at the current location in the structure. + + Data items may be values or pointers; the interface hides the + indirection. + + Major constructs ({} are metacharacters; [] marks optional elements): + + {# comment } + + A one-line comment. + + {.section field} XXX [ {.or} YYY ] {.end} + + Set @ to the value of the field. It may be an explicit @ + to stay at the same point in the data. If the field is nil + or empty, execute YYY; otherwise execute XXX. + + {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} + + Like .section, but field must be an array or slice. XXX + is executed for each element. If the array is nil or empty, + YYY is executed instead. If the {.alternates with} marker + is present, ZZZ is executed between iterations of XXX. + + {field} + {field|formatter} + + Insert the value of the field into the output. Field is + first looked for in the cursor, as in .section and .repeated. + If it is not found, the search continues in outer sections + until the top level is reached. + + If a formatter is specified, it must be named in the formatter + map passed to the template set up routines or in the default + set ("html","str","") and is used to process the data for + output. The formatter function has signature + func(wr io.Write, data interface{}, formatter string) + where wr is the destination for output, data is the field + value, and formatter is its name at the invocation site. +*/ +package template + +import ( + "container/vector"; + "fmt"; + "io"; + "os"; + "reflect"; + "runtime"; + "strings"; + "template"; +) + +// Errors returned during parsing and execution. Users may extract the information and reformat +// if they desire. +type Error struct { + Line int; + Msg string; +} + +func (e *Error) String() string { + return fmt.Sprintf("line %d: %s", e.Line, e.Msg) +} + +// Most of the literals are aces. +var lbrace = []byte{ '{' } +var rbrace = []byte{ '}' } +var space = []byte{ ' ' } +var tab = []byte{ '\t' } + +// The various types of "tokens", which are plain text or (usually) brace-delimited descriptors +const ( + tokAlternates = iota; + tokComment; + tokEnd; + tokLiteral; + tokOr; + tokRepeated; + tokSection; + tokText; + tokVariable; +) + +// FormatterMap is the type describing the mapping from formatter +// names to the functions that implement them. +type FormatterMap map[string] func(io.Writer, interface{}, string) + +// Built-in formatters. +var builtins = FormatterMap { + "html" : HtmlFormatter, + "str" : StringFormatter, + "" : StringFormatter, +} + +// The parsed state of a template is a vector of xxxElement structs. +// Sections have line numbers so errors can be reported better during execution. + +// Plain text. +type textElement struct { + text []byte; +} + +// A literal such as .meta-left or .meta-right +type literalElement struct { + text []byte; +} + +// A variable to be evaluated +type variableElement struct { + linenum int; + name string; + formatter string; // TODO(r): implement pipelines +} + +// A .section block, possibly with a .or +type sectionElement struct { + linenum int; // of .section itself + field string; // cursor field for this block + start int; // first element + or int; // first element of .or block + end int; // one beyond last element +} + +// A .repeated block, possibly with a .or and a .alternates +type repeatedElement struct { + sectionElement; // It has the same structure... + altstart int; // ... except for alternates + altend int; +} + +// Template is the type that represents a template definition. +// It is unchanged after parsing. +type Template struct { + fmap FormatterMap; // formatters for variables + // Used during parsing: + ldelim, rdelim []byte; // delimiters; default {} + buf []byte; // input text to process + p int; // position in buf + linenum int; // position in input + errors chan os.Error; // for error reporting during parsing (only) + // Parsed results: + elems *vector.Vector; +} + +// Internal state for executing a Template. As we evaluate the struct, +// the data item descends into the fields associated with sections, etc. +// Parent is used to walk upwards to find variables higher in the tree. +type state struct { + parent *state; // parent in hierarchy + data reflect.Value; // the driver data for this section etc. + wr io.Writer; // where to send output + errors chan os.Error; // for reporting errors during execute +} + +func (parent *state) clone(data reflect.Value) *state { + return &state{parent, data, parent.wr, parent.errors} +} + +// New creates a new template with the specified formatter map (which +// may be nil) to define auxiliary functions for formatting variables. +func New(fmap FormatterMap) *Template { + t := new(Template); + t.fmap = fmap; + t.ldelim = lbrace; + t.rdelim = rbrace; + t.errors = make(chan os.Error); + t.elems = vector.New(0); + return t; +} + +// Generic error handler, called only from execError or parseError. +func error(errors chan os.Error, line int, err string, args ...) { + errors <- &Error{line, fmt.Sprintf(err, args)}; + runtime.Goexit(); +} + +// Report error and stop executing. The line number must be provided explicitly. +func (t *Template) execError(st *state, line int, err string, args ...) { + error(st.errors, line, err, args); +} + +// Report error and stop parsing. The line number comes from the template state. +func (t *Template) parseError(err string, args ...) { + error(t.errors, t.linenum, err, args) +} + +// -- Lexical analysis + +// Is c a white space character? +func white(c uint8) bool { + return c == ' ' || c == '\t' || c == '\r' || c == '\n' +} + +// Safely, does s[n:n+len(t)] == t? +func equal(s []byte, n int, t []byte) bool { + b := s[n:len(s)]; + if len(t) > len(b) { // not enough space left for a match. + return false + } + for i , c := range t { + if c != b[i] { + return false + } + } + return true +} + +// nextItem returns the next item from the input buffer. If the returned +// item is empty, we are at EOF. The item will be either a +// delimited string or a non-empty string between delimited +// strings. Tokens stop at (but include, if plain text) a newline. +// Action tokens on a line by themselves drop the white space on +// either side, up to and including the newline. +func (t *Template) nextItem() []byte { + sawLeft := false; // are we waiting for an opening delimiter? + special := false; // is this a {.foo} directive, which means trim white space? + // Delete surrounding white space if this {.foo} is the only thing on the line. + trim_white := t.p == 0 || t.buf[t.p-1] == '\n'; + only_white := true; // we have seen only white space so far + var i int; + start := t.p; +Loop: + for i = t.p; i < len(t.buf); i++ { + switch { + case t.buf[i] == '\n': + t.linenum++; + i++; + break Loop; + case white(t.buf[i]): + // white space, do nothing + case !sawLeft && equal(t.buf, i, t.ldelim): // sawLeft checked because delims may be equal + // anything interesting already on the line? + if !only_white { + break Loop; + } + // is it a directive or comment? + j := i + len(t.ldelim); // position after delimiter + if j+1 < len(t.buf) && (t.buf[j] == '.' || t.buf[j] == '#') { + special = true; + if trim_white && only_white { + start = i; + } + } else if i > t.p { // have some text accumulated so stop before delimiter + break Loop; + } + sawLeft = true; + i = j - 1; + case equal(t.buf, i, t.rdelim): + if !sawLeft { + t.parseError("unmatched closing delimiter") + } + sawLeft = false; + i += len(t.rdelim); + break Loop; + default: + only_white = false; + } + } + if sawLeft { + t.parseError("unmatched opening delimiter") + } + item := t.buf[start:i]; + if special && trim_white { + // consume trailing white space + for ; i < len(t.buf) && white(t.buf[i]); i++ { + if t.buf[i] == '\n' { + i++; + break // stop after newline + } + } + } + t.p = i; + return item +} + +// Turn a byte array into a white-space-split array of strings. +func words(buf []byte) []string { + s := make([]string, 0, 5); + p := 0; // position in buf + // one word per loop + for i := 0; ; i++ { + // skip white space + for ; p < len(buf) && white(buf[p]); p++ { + } + // grab word + start := p; + for ; p < len(buf) && !white(buf[p]); p++ { + } + if start == p { // no text left + break + } + if i == cap(s) { + ns := make([]string, 2*cap(s)); + for j := range s { + ns[j] = s[j] + } + s = ns; + } + s = s[0:i+1]; + s[i] = string(buf[start:p]) + } + return s +} + +// Analyze an item and return its token type and, if it's an action item, an array of +// its constituent words. +func (t *Template) analyze(item []byte) (tok int, w []string) { + // item is known to be non-empty + if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter + tok = tokText; + return + } + if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter + t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this + } + if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents + t.parseError("empty directive") + } + // Comment + if item[len(t.ldelim)] == '#' { + tok = tokComment; + return + } + // Split into words + w = words(item[len(t.ldelim): len(item)-len(t.rdelim)]); // drop final delimiter + if len(w) == 0 { + t.parseError("empty directive") + } + if len(w) == 1 && w[0][0] != '.' { + tok = tokVariable; + return; + } + switch w[0] { + case ".meta-left", ".meta-right", ".space", ".tab": + tok = tokLiteral; + return; + case ".or": + tok = tokOr; + return; + case ".end": + tok = tokEnd; + return; + case ".section": + if len(w) != 2 { + t.parseError("incorrect fields for .section: %s", item) + } + tok = tokSection; + return; + case ".repeated": + if len(w) != 3 || w[1] != "section" { + t.parseError("incorrect fields for .repeated: %s", item) + } + tok = tokRepeated; + return; + case ".alternates": + if len(w) != 2 || w[1] != "with" { + t.parseError("incorrect fields for .alternates: %s", item) + } + tok = tokAlternates; + return; + } + t.parseError("bad directive: %s", item); + return +} + +// -- Parsing + +// Allocate a new variable-evaluation element. +func (t *Template) newVariable(name_formatter string) (v *variableElement) { + name := name_formatter; + formatter := ""; + bar := strings.Index(name_formatter, "|"); + if bar >= 0 { + name = name_formatter[0:bar]; + formatter = name_formatter[bar+1:len(name_formatter)]; + } + // Probably ok, so let's build it. + v = &variableElement{t.linenum, name, formatter}; + + // We could remember the function address here and avoid the lookup later, + // but it's more dynamic to let the user change the map contents underfoot. + // We do require the name to be present, though. + + // Is it in user-supplied map? + if t.fmap != nil { + if fn, ok := t.fmap[formatter]; ok { + return + } + } + // Is it in builtin map? + if fn, ok := builtins[formatter]; ok { + return + } + t.parseError("unknown formatter: %s", formatter); + return +} + +// Grab the next item. If it's simple, just append it to the template. +// Otherwise return its details. +func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { + tok, w = t.analyze(item); + done = true; // assume for simplicity + switch tok { + case tokComment: + return; + case tokText: + t.elems.Push(&textElement{item}); + return; + case tokLiteral: + switch w[0] { + case ".meta-left": + t.elems.Push(&literalElement{t.ldelim}); + case ".meta-right": + t.elems.Push(&literalElement{t.rdelim}); + case ".space": + t.elems.Push(&literalElement{space}); + case ".tab": + t.elems.Push(&literalElement{tab}); + default: + t.parseError("internal error: unknown literal: %s", w[0]); + } + return; + case tokVariable: + t.elems.Push(t.newVariable(w[0])); + return; + } + return false, tok, w +} + +// parseSection and parseRepeated are mutually recursive +func (t *Template) parseSection(words []string) *sectionElement + +func (t *Template) parseRepeated(words []string) *repeatedElement { + r := new(repeatedElement); + t.elems.Push(r); + r.linenum = t.linenum; + r.field = words[2]; + // Scan section, collecting true and false (.or) blocks. + r.start = t.elems.Len(); + r.or = -1; + r.altstart = -1; + r.altend = -1; +Loop: + for { + item := t.nextItem(); + if len(item) == 0 { + t.parseError("missing .end for .repeated section") + } + done, tok, w := t.parseSimple(item); + if done { + continue + } + switch tok { + case tokEnd: + break Loop; + case tokOr: + if r.or >= 0 { + t.parseError("extra .or in .repeated section"); + } + r.altend = t.elems.Len(); + r.or = t.elems.Len(); + case tokSection: + t.parseSection(w); + case tokRepeated: + t.parseRepeated(w); + case tokAlternates: + if r.altstart >= 0 { + t.parseError("extra .alternates in .repeated section"); + } + if r.or >= 0 { + t.parseError(".alternates inside .or block in .repeated section"); + } + r.altstart = t.elems.Len(); + default: + t.parseError("internal error: unknown repeated section item: %s", item); + } + } + if r.altend < 0 { + r.altend = t.elems.Len() + } + r.end = t.elems.Len(); + return r; +} + +func (t *Template) parseSection(words []string) *sectionElement { + s := new(sectionElement); + t.elems.Push(s); + s.linenum = t.linenum; + s.field = words[1]; + // Scan section, collecting true and false (.or) blocks. + s.start = t.elems.Len(); + s.or = -1; +Loop: + for { + item := t.nextItem(); + if len(item) == 0 { + t.parseError("missing .end for .section") + } + done, tok, w := t.parseSimple(item); + if done { + continue + } + switch tok { + case tokEnd: + break Loop; + case tokOr: + if s.or >= 0 { + t.parseError("extra .or in .section"); + } + s.or = t.elems.Len(); + case tokSection: + t.parseSection(w); + case tokRepeated: + t.parseRepeated(w); + case tokAlternates: + t.parseError(".alternates not in .repeated"); + default: + t.parseError("internal error: unknown section item: %s", item); + } + } + s.end = t.elems.Len(); + return s; +} + +func (t *Template) parse() { + for { + item := t.nextItem(); + if len(item) == 0 { + break + } + done, tok, w := t.parseSimple(item); + if done { + continue + } + switch tok { + case tokOr, tokEnd, tokAlternates: + t.parseError("unexpected %s", w[0]); + case tokSection: + t.parseSection(w); + case tokRepeated: + t.parseRepeated(w); + default: + t.parseError("internal error: bad directive in parse: %s", item); + } + } +} + +// -- Execution + +// If the data for this template is a struct, find the named variable. +// The special name "@" (the "cursor") denotes the current data. +func (st *state) findVar(s string) reflect.Value { + if s == "@" { + return st.data + } + data := reflect.Indirect(st.data); + typ, ok := data.Type().(reflect.StructType); + if ok { + for i := 0; i < typ.Len(); i++ { + name, ftyp, tag, offset := typ.Field(i); + if name == s { + return data.(reflect.StructValue).Field(i) + } + } + } + return nil +} + +// Is there no data to look at? +func empty(v reflect.Value, indirect_ok bool) bool { + v = reflect.Indirect(v); + if v == nil { + return true + } + switch v.Type().Kind() { + case reflect.StringKind: + return v.(reflect.StringValue).Get() == ""; + case reflect.StructKind: + return false; + case reflect.ArrayKind: + return v.(reflect.ArrayValue).Len() == 0; + } + return true; +} + +// Look up a variable, up through the parent if necessary. +func (t *Template) varValue(v *variableElement, st *state) reflect.Value { + field := st.findVar(v.name); + if field == nil { + if st.parent == nil { + t.execError(st, t.linenum, "name not found: %s", v.name) + } + return t.varValue(v, st.parent); + } + return field; +} + +// Evaluate a variable, looking up through the parent if necessary. +// If it has a formatter attached ({var|formatter}) run that too. +func (t *Template) writeVariable(v *variableElement, st *state) { + formatter := v.formatter; + val := t.varValue(v, st).Interface(); + // is it in user-supplied map? + if t.fmap != nil { + if fn, ok := t.fmap[v.formatter]; ok { + fn(st.wr, val, v.formatter); + return; + } + } + // is it in builtin map? + if fn, ok := builtins[v.formatter]; ok { + fn(st.wr, val, v.formatter); + return; + } + t.execError(st, v.linenum, "missing formatter %s for variable %s", v.formatter, v.name) +} + +// execute{|Element|Section|Repeated} are mutually recursive +func (t *Template) executeSection(s *sectionElement, st *state) +func (t *Template) executeRepeated(r *repeatedElement, st *state) + +// Execute element i. Return next index to execute. +func (t *Template) executeElement(i int, st *state) int { + switch elem := t.elems.At(i).(type) { + case *textElement: + st.wr.Write(elem.text); + return i+1; + case *literalElement: + st.wr.Write(elem.text); + return i+1; + case *variableElement: + t.writeVariable(elem, st); + return i+1; + case *sectionElement: + t.executeSection(elem, st); + return elem.end; + case *repeatedElement: + t.executeRepeated(elem, st); + return elem.end; + } + e := t.elems.At(i); + t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.NewValue(e).Interface(), e); + return 0 +} + +// Execute the template. +func (t *Template) execute(start, end int, st *state) { + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Execute a .section +func (t *Template) executeSection(s *sectionElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := st.findVar(s.field); + if field == nil { + t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, reflect.Indirect(st.data).Type()); + } + st = st.clone(field); + start, end := s.start, s.or; + if !empty(field, true) { + // Execute the normal block. + if end < 0 { + end = s.end + } + } else { + // Execute the .or block. If it's missing, do nothing. + start, end = s.or, s.end; + if start < 0 { + return + } + } + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Execute a .repeated section +func (t *Template) executeRepeated(r *repeatedElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := st.findVar(r.field); + if field == nil { + t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, reflect.Indirect(st.data).Type()); + } + field = reflect.Indirect(field); + + // Must be an array/slice + if field != nil && field.Kind() != reflect.ArrayKind { + t.execError(st, r.linenum, ".repeated: %s has bad type %s", r.field, field.Type()); + } + if empty(field, true) { + // Execute the .or block, once. If it's missing, do nothing. + start, end := r.or, r.end; + if start >= 0 { + newst := st.clone(field); + for i := start; i < end; { + i = t.executeElement(i, newst) + } + } + return + } + // Execute the normal block. + start, end := r.start, r.or; + if end < 0 { + end = r.end + } + if r.altstart >= 0 { + end = r.altstart + } + if field != nil { + array := field.(reflect.ArrayValue); + for j := 0; j < array.Len(); j++ { + newst := st.clone(array.Elem(j)); + for i := start; i < end; { + i = t.executeElement(i, newst) + } + // If appropriate, do .alternates between elements + if j < array.Len() - 1 && r.altstart >= 0 { + for i := r.altstart; i < r.altend; i++ { + i = t.executeElement(i, newst) + } + } + } + } +} + +// A valid delimiter must contain no white space and be non-empty. +func validDelim(d []byte) bool { + if len(d) == 0 { + return false + } + for i, c := range d { + if white(c) { + return false + } + } + return true; +} + +// -- Public interface + +// Parse initializes a Template by parsing its definition. The string +// s contains the template text. If any errors occur, Parse returns +// the error. +func (t *Template) Parse(s string) os.Error { + if !validDelim(t.ldelim) || !validDelim(t.rdelim) { + return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} + } + t.buf = io.StringBytes(s); + t.p = 0; + t.linenum = 0; + go func() { + t.parse(); + t.errors <- nil; // clean return; + }(); + return <-t.errors; +} + +// Execute applies a parsed template to the specified data object, +// generating output to wr. +func (t *Template) Execute(data interface{}, wr io.Writer) os.Error { + // Extract the driver data. + val := reflect.NewValue(data); + errors := make(chan os.Error); + go func() { + t.p = 0; + t.execute(0, t.elems.Len(), &state{nil, val, wr, errors}); + errors <- nil; // clean return; + }(); + return <-errors; +} + +// SetDelims sets the left and right delimiters for operations in the +// template. They are validated during parsing. They could be +// validated here but it's better to keep the routine simple. The +// delimiters are very rarely invalid and Parse has the necessary +// error-handling interface already. +func (t *Template) SetDelims(left, right string) { + t.ldelim = io.StringBytes(left); + t.rdelim = io.StringBytes(right); +} + +// Parse creates a Template with default parameters (such as {} for +// metacharacters). The string s contains the template text while +// the formatter map fmap, which may be nil, defines auxiliary functions +// for formatting variables. The template is returned. If any errors +// occur, err will be non-nil. +func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) { + t = New(fmap); + err = t.Parse(s); + return +} diff --git a/src/pkg/template/template_test.go b/src/pkg/template/template_test.go new file mode 100644 index 000000000..9a81d274c --- /dev/null +++ b/src/pkg/template/template_test.go @@ -0,0 +1,331 @@ +// Copyright 2009 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 template + +import ( + "fmt"; + "io"; + "os"; + "reflect"; + "template"; + "testing"; +) + +type Test struct { + in, out string +} + +type T struct { + item string; + value string; +} + +type S struct { + header string; + integer int; + raw string; + data []T; + pdata []*T; + empty []*T; + emptystring string; + null []*T; +} + +var t1 = T{ "ItemNumber1", "ValueNumber1" } +var t2 = T{ "ItemNumber2", "ValueNumber2" } + +func uppercase(v interface{}) string { + s := v.(string); + t := ""; + for i := 0; i < len(s); i++ { + c := s[i]; + if 'a' <= c && c <= 'z' { + c = c + 'A' - 'a' + } + t += string(c); + } + return t; +} + +func plus1(v interface{}) string { + i := v.(int); + return fmt.Sprint(i + 1); +} + +func writer(f func(interface{}) string) (func(io.Writer, interface{}, string)) { + return func(w io.Writer, v interface{}, format string) { + io.WriteString(w, f(v)); + } +} + + +var formatters = FormatterMap { + "uppercase" : writer(uppercase), + "+1" : writer(plus1), +} + +var tests = []*Test { + // Simple + &Test{ "", "" }, + &Test{ "abc\ndef\n", "abc\ndef\n" }, + &Test{ " {.meta-left} \n", "{" }, + &Test{ " {.meta-right} \n", "}" }, + &Test{ " {.space} \n", " " }, + &Test{ " {.tab} \n", "\t" }, + &Test{ " {#comment} \n", "" }, + + // Variables at top level + &Test{ + "{header}={integer}\n", + + "Header=77\n" + }, + + // Section + &Test{ + "{.section data }\n" + "some text for the section\n" + "{.end}\n", + + "some text for the section\n" + }, + &Test{ + "{.section data }\n" + "{header}={integer}\n" + "{.end}\n", + + "Header=77\n" + }, + &Test{ + "{.section pdata }\n" + "{header}={integer}\n" + "{.end}\n", + + "Header=77\n" + }, + &Test{ + "{.section pdata }\n" + "data present\n" + "{.or}\n" + "data not present\n" + "{.end}\n", + + "data present\n" + }, + &Test{ + "{.section empty }\n" + "data present\n" + "{.or}\n" + "data not present\n" + "{.end}\n", + + "data not present\n" + }, + &Test{ + "{.section null }\n" + "data present\n" + "{.or}\n" + "data not present\n" + "{.end}\n", + + "data not present\n" + }, + &Test{ + "{.section pdata }\n" + "{header}={integer}\n" + "{.section @ }\n" + "{header}={integer}\n" + "{.end}\n" + "{.end}\n", + + "Header=77\n" + "Header=77\n" + }, + &Test{ + "{.section data}{.end} {header}\n", + + " Header\n" + }, + + // Repeated + &Test{ + "{.section pdata }\n" + "{.repeated section @ }\n" + "{item}={value}\n" + "{.end}\n" + "{.end}\n", + + "ItemNumber1=ValueNumber1\n" + "ItemNumber2=ValueNumber2\n" + }, + &Test{ + "{.section pdata }\n" + "{.repeated section @ }\n" + "{item}={value}\n" + "{.or}\n" + "this should not appear\n" + "{.end}\n" + "{.end}\n", + + "ItemNumber1=ValueNumber1\n" + "ItemNumber2=ValueNumber2\n" + }, + &Test{ + "{.section @ }\n" + "{.repeated section empty }\n" + "{item}={value}\n" + "{.or}\n" + "this should appear: empty field\n" + "{.end}\n" + "{.end}\n", + + "this should appear: empty field\n" + }, + &Test{ + "{.section pdata }\n" + "{.repeated section @ }\n" + "{item}={value}\n" + "{.alternates with}DIVIDER\n" + "{.or}\n" + "this should not appear\n" + "{.end}\n" + "{.end}\n", + + "ItemNumber1=ValueNumber1\n" + "DIVIDER\n" + "ItemNumber2=ValueNumber2\n" + }, + + // Formatters + &Test{ + "{.section pdata }\n" + "{header|uppercase}={integer|+1}\n" + "{header|html}={integer|str}\n" + "{.end}\n", + + "HEADER=78\n" + "Header=77\n" + }, + + &Test{ + "{raw}\n" + "{raw|html}\n", + + "&<>!@ #$%^\n" + "&<>!@ #$%^\n" + }, + + &Test{ + "{.section emptystring}emptystring{.end}\n" + "{.section header}header{.end}\n", + + "\nheader\n" + }, +} + +func TestAll(t *testing.T) { + s := new(S); + // initialized by hand for clarity. + s.header = "Header"; + s.integer = 77; + s.raw = "&<>!@ #$%^"; + s.data = []T{ t1, t2 }; + s.pdata = []*T{ &t1, &t2 }; + s.empty = []*T{ }; + s.null = nil; + + var buf io.ByteBuffer; + for i, test := range tests { + buf.Reset(); + tmpl, err := Parse(test.in, formatters); + if err != nil { + t.Error("unexpected parse error:", err); + continue; + } + err = tmpl.Execute(s, &buf); + if err != nil { + t.Error("unexpected execute error:", err) + } + if string(buf.Data()) != test.out { + t.Errorf("for %q: expected %q got %q", test.in, test.out, string(buf.Data())); + } + } +} + +func TestStringDriverType(t *testing.T) { + tmpl, err := Parse("template: {@}", nil); + if err != nil { + t.Error("unexpected parse error:", err) + } + var b io.ByteBuffer; + err = tmpl.Execute("hello", &b); + if err != nil { + t.Error("unexpected execute error:", err) + } + s := string(b.Data()); + if s != "template: hello" { + t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s) + } +} + +func TestTwice(t *testing.T) { + tmpl, err := Parse("template: {@}", nil); + if err != nil { + t.Error("unexpected parse error:", err) + } + var b io.ByteBuffer; + err = tmpl.Execute("hello", &b); + if err != nil { + t.Error("unexpected parse error:", err) + } + s := string(b.Data()); + text := "template: hello"; + if s != text { + t.Errorf("failed passing string as data: expected %q got %q", text, s); + } + err = tmpl.Execute("hello", &b); + if err != nil { + t.Error("unexpected parse error:", err) + } + s = string(b.Data()); + text += text; + if s != text { + t.Errorf("failed passing string as data: expected %q got %q", text, s); + } +} + +func TestCustomDelims(t *testing.T) { + // try various lengths. zero should catch error. + for i := 0; i < 7; i++ { + for j := 0; j < 7; j++ { + tmpl := New(nil); + // first two chars deliberately the same to test equal left and right delims + ldelim := "$!#$%^&"[0:i]; + rdelim := "$*&^%$!"[0:j]; + tmpl.SetDelims(ldelim, rdelim); + // if braces, this would be template: {@}{.meta-left}{.meta-right} + text := "template: " + + ldelim + "@" + rdelim + + ldelim + ".meta-left" + rdelim + + ldelim + ".meta-right" + rdelim; + err := tmpl.Parse(text); + if err != nil { + if i == 0 || j == 0 { // expected + continue + } + t.Error("unexpected parse error:", err) + } else if i == 0 || j == 0 { + t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim); + continue; + } + var b io.ByteBuffer; + err = tmpl.Execute("hello", &b); + s := string(b.Data()); + if s != "template: hello" + ldelim + rdelim { + t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s) + } + } + } +} diff --git a/src/pkg/testing/Makefile b/src/pkg/testing/Makefile new file mode 100644 index 000000000..493eba6f2 --- /dev/null +++ b/src/pkg/testing/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + testing.$O\ + + +phases: a1 +_obj$D/testing.a: phases + +a1: $(O1) + $(AR) grc _obj$D/testing.a testing.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/testing.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/testing.a + +packages: _obj$D/testing.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/testing.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/testing.a diff --git a/src/pkg/testing/iotest/Makefile b/src/pkg/testing/iotest/Makefile new file mode 100644 index 000000000..1d01041f5 --- /dev/null +++ b/src/pkg/testing/iotest/Makefile @@ -0,0 +1,61 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D=/testing/ + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + logger.$O\ + reader.$O\ + + +phases: a1 +_obj$D/iotest.a: phases + +a1: $(O1) + $(AR) grc _obj$D/iotest.a logger.$O reader.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/iotest.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/iotest.a + +packages: _obj$D/iotest.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/iotest.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/iotest.a diff --git a/src/pkg/testing/iotest/logger.go b/src/pkg/testing/iotest/logger.go new file mode 100644 index 000000000..8ee574080 --- /dev/null +++ b/src/pkg/testing/iotest/logger.go @@ -0,0 +1,55 @@ +// Copyright 2009 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 iotest + +import ( + "io"; + "log"; + "os"; +) + +type writeLogger struct { + prefix string; + w io.Writer; +} + +func (l *writeLogger) Write(p []byte) (n int, err os.Error) { + n, err = l.w.Write(p); + if err != nil { + log.Stdoutf("%s %x: %v", l.prefix, p[0:n], err); + } else { + log.Stdoutf("%s %x", l.prefix, p[0:n]); + } + return; +} + +// NewWriteLogger returns a writer that behaves like w except +// that it logs (using log.Stdout) each write to standard output, +// printing the prefix and the hexadecimal data written. +func NewWriteLogger(prefix string, w io.Writer) io.Writer { + return &writeLogger{prefix, w} +} + +type readLogger struct { + prefix string; + r io.Reader; +} + +func (l *readLogger) Read(p []byte) (n int, err os.Error) { + n, err = l.r.Read(p); + if err != nil { + log.Stdoutf("%s %x: %v", l.prefix, p[0:n], err); + } else { + log.Stdoutf("%s %x", l.prefix, p[0:n]); + } + return; +} + +// NewReadLogger returns a writer that behaves like w except +// that it logs (using log.Stdout) each write to standard output, +// printing the prefix and the hexadecimal data written. +func NewReadLogger(prefix string, r io.Reader) io.Reader { + return &readLogger{prefix, r} +} diff --git a/src/pkg/testing/iotest/reader.go b/src/pkg/testing/iotest/reader.go new file mode 100644 index 000000000..0bb863338 --- /dev/null +++ b/src/pkg/testing/iotest/reader.go @@ -0,0 +1,44 @@ +// Copyright 2009 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. + +// The iotest package implements Readers and Writers +// useful only for testing. +package iotest + +import ( + "io"; + "os"; +) + +type oneByteReader struct { + r io.Reader; +} + +func (r *oneByteReader) Read(p []byte) (int, os.Error) { + if len(p) == 0 { + return 0, nil; + } + return r.r.Read(p[0:1]); +} + +// OneByteReader returns a Reader that implements +// each non-empty Read by reading one byte from r. +func OneByteReader(r io.Reader) io.Reader { + return &oneByteReader{r}; +} + +type halfReader struct { + r io.Reader; +} + +func (r *halfReader) Read(p []byte) (int, os.Error) { + return r.r.Read(p[0:(len(p)+1)/2]); +} + +// HalfReader returns a Reader that implements Read +// by reading half as many requested bytes from r. +func HalfReader(r io.Reader) io.Reader { + return &halfReader{r}; +} + diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go new file mode 100644 index 000000000..330fadd3a --- /dev/null +++ b/src/pkg/testing/testing.go @@ -0,0 +1,155 @@ +// Copyright 2009 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. + +// The testing package provides support for automated testing of Go packages. +// It is intended to be used in concert with the ``gotest'' utility, which automates +// execution of any function of the form +// func TestXxx(*testing.T) +// where Xxx can by any alphanumeric string (but the first letter must not be in +// [a-z]) and serves to identify the test routine. +// These TestXxx routines should be declared within the package they are testing. +package testing + +import ( + "flag"; + "fmt"; + "os"; + "regexp"; + "runtime"; +) + +// Report as tests are run; default is silent for success. +var chatty = flag.Bool("v", false, "verbose: print additional output") +var match = flag.String("match", "", "regular expression to select tests to run") + + +// Insert final newline if needed and tabs after internal newlines. +func tabify(s string) string { + n := len(s); + if n > 0 && s[n-1] != '\n' { + s += "\n"; + n++; + } + for i := 0; i < n - 1; i++ { // -1 to avoid final newline + if s[i] == '\n' { + return s[0:i+1] + "\t" + tabify(s[i+1:n]); + } + } + return s +} + +// T is a type passed to Test functions to manage test state and support formatted test logs. +// Logs are accumulated during execution and dumped to standard error when done. +type T struct { + errors string; + failed bool; + ch chan *T; +} + +// Fail marks the Test function as having failed but continues execution. +func (t *T) Fail() { + t.failed = true +} + +// Failed returns whether the Test function has failed. +func (t *T) Failed() bool { + return t.failed +} + +// FailNow marks the Test function as having failed and stops its execution. +// Execution will continue at the next Test. +func (t *T) FailNow() { + t.Fail(); + t.ch <- t; + runtime.Goexit(); +} + +// Log formats its arguments using default formatting, analogous to Print(), +// and records the text in the error log. +func (t *T) Log(args ...) { + t.errors += "\t" + tabify(fmt.Sprintln(args)); +} + +// Log formats its arguments according to the format, analogous to Printf(), +// and records the text in the error log. +func (t *T) Logf(format string, args ...) { + t.errors += "\t" + tabify(fmt.Sprintf(format, args)); +} + +// Error is equivalent to Log() followed by Fail(). +func (t *T) Error(args ...) { + t.Log(args); + t.Fail(); +} + +// Errorf is equivalent to Logf() followed by Fail(). +func (t *T) Errorf(format string, args ...) { + t.Logf(format, args); + t.Fail(); +} + +// Fatal is equivalent to Log() followed by FailNow(). +func (t *T) Fatal(args ...) { + t.Log(args); + t.FailNow(); +} + +// Fatalf is equivalent to Logf() followed by FailNow(). +func (t *T) Fatalf(format string, args ...) { + t.Logf(format, args); + t.FailNow(); +} + +// An internal type but exported because it is cross-package; part of the implementation +// of gotest. +type Test struct { + Name string; + F func(*T); +} + +func tRunner(t *T, test *Test) { + test.F(t); + t.ch <- t; +} + +// An internal function but exported because it is cross-package; part of the implementation +// of gotest. +func Main(tests []Test) { + flag.Parse(); + args := flag.Args(); + ok := true; + if len(tests) == 0 { + println("testing: warning: no tests to run"); + } + re, err := regexp.Compile(*match); + if err != nil { + println("invalid regexp for -match:", err.String()); + os.Exit(1); + } + for i := 0; i < len(tests); i++ { + if !re.Match(tests[i].Name) { + continue; + } + if *chatty { + println("=== RUN ", tests[i].Name); + } + t := new(T); + t.ch = make(chan *T); + go tRunner(t, &tests[i]); + <-t.ch; + if t.failed { + println("--- FAIL:", tests[i].Name); + print(t.errors); + ok = false; + } else if *chatty { + println("--- PASS:", tests[i].Name); + print(t.errors); + } + } + if !ok { + println("FAIL"); + os.Exit(1); + } + println("PASS"); +} diff --git a/src/pkg/time/Makefile b/src/pkg/time/Makefile new file mode 100644 index 000000000..8d3c3b65f --- /dev/null +++ b/src/pkg/time/Makefile @@ -0,0 +1,77 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + sleep.$O\ + zoneinfo.$O\ + +O2=\ + time.$O\ + +O3=\ + tick.$O\ + + +phases: a1 a2 a3 +_obj$D/time.a: phases + +a1: $(O1) + $(AR) grc _obj$D/time.a sleep.$O zoneinfo.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/time.a time.$O + rm -f $(O2) + +a3: $(O3) + $(AR) grc _obj$D/time.a tick.$O + rm -f $(O3) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/time.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 +$(O4): a3 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/time.a + +packages: _obj$D/time.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/time.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/time.a diff --git a/src/pkg/time/sleep.go b/src/pkg/time/sleep.go new file mode 100644 index 000000000..3bb76cf47 --- /dev/null +++ b/src/pkg/time/sleep.go @@ -0,0 +1,17 @@ +// Copyright 2009 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 time + +import ( + "os"; + "syscall"; + "unsafe"; +) + +// Sleep pauses the current goroutine for ns nanoseconds. +// It returns os.EINTR if interrupted. +func Sleep(ns int64) os.Error { + return os.ErrnoToError(syscall.Sleep(ns)); +} diff --git a/src/pkg/time/tick.go b/src/pkg/time/tick.go new file mode 100644 index 000000000..53e2234f8 --- /dev/null +++ b/src/pkg/time/tick.go @@ -0,0 +1,62 @@ +// Copyright 2009 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 time + +import ( + "syscall"; + "time"; + "unsafe"; +) + +// TODO(rsc): This implementation of time.Tick is a +// simple placeholder. Eventually, there will need to be +// a single central time server no matter how many tickers +// are active. There also needs to be a way to cancel a ticker. +// +// Also, if timeouts become part of the select statement, +// perhaps the Ticker is just: +// +// func Ticker(ns int64, c chan int64) { +// for { +// select { timeout ns: } +// nsec, err := time.Nanoseconds(); +// c <- nsec; +// } + +func ticker(ns int64, c chan int64) { + var tv syscall.Timeval; + now := time.Nanoseconds(); + when := now; + for { + when += ns; // next alarm + + // if c <- now took too long, skip ahead + if when < now { + // one big step + when += (now-when)/ns * ns; + } + for when <= now { + // little steps until when > now + when += ns + } + + time.Sleep(when - now); + now = time.Nanoseconds(); + c <- now; + } +} + +// Tick creates a synchronous channel that will send the time, in nanoseconds, +// every ns nanoseconds. It adjusts the intervals to make up for pauses in +// delivery of the ticks. +func Tick(ns int64) chan int64 { + if ns <= 0 { + return nil + } + c := make(chan int64); + go ticker(ns, c); + return c; +} + diff --git a/src/pkg/time/tick_test.go b/src/pkg/time/tick_test.go new file mode 100644 index 000000000..0667be679 --- /dev/null +++ b/src/pkg/time/tick_test.go @@ -0,0 +1,29 @@ +// Copyright 2009 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 time + +import ( + "testing"; + "time"; +) + +func TestTick(t *testing.T) { + const ( + Delta = 100*1e6; + Count = 10; + ); + c := Tick(Delta); + t0 := Nanoseconds(); + for i := 0; i < Count; i++ { + <-c; + } + t1 := Nanoseconds(); + ns := t1 - t0; + target := int64(Delta*Count); + slop := target*2/10; + if ns < target - slop || ns > target + slop { + t.Fatalf("%d ticks of %g ns took %g ns, expected %g", Count, float64(Delta), float64(ns), float64(target)); + } +} diff --git a/src/pkg/time/time.go b/src/pkg/time/time.go new file mode 100644 index 000000000..ea9b66cbc --- /dev/null +++ b/src/pkg/time/time.go @@ -0,0 +1,372 @@ +// Copyright 2009 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. + +// The time package provides functionality for measuring and +// displaying time. +package time + +import ( + "os"; + "time" +) + +// Seconds reports the number of seconds since the Unix epoch, +// January 1, 1970 00:00:00 UTC. +func Seconds() int64 { + sec, nsec, err := os.Time(); + if err != nil { + panic("time: os.Time: ", err.String()); + } + return sec +} + +// Nanoseconds reports the number of nanoseconds since the Unix epoch, +// January 1, 1970 00:00:00 UTC. +func Nanoseconds() int64 { + sec, nsec, err := os.Time(); + if err != nil { + panic("time: os.Time: ", err.String()); + } + return sec*1e9 + nsec +} + +// Days of the week. +const ( + Sunday = iota; + Monday; + Tuesday; + Wednesday; + Thursday; + Friday; + Saturday; +) + +// Time is the struct representing a parsed time value. +type Time struct { + Year int64; // 2008 is 2008 + Month, Day int; // Sep-17 is 9, 17 + Hour, Minute, Second int; // 10:43:12 is 10, 43, 12 + Weekday int; // Sunday, Monday, ... + ZoneOffset int; // seconds west of UTC + Zone string; +} + +var nonleapyear = []int{ + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +} +var leapyear = []int{ + 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +} + +func months(year int64) []int { + if year%4 == 0 && (year%100 != 0 || year%400 == 0) { + return leapyear + } + return nonleapyear +} + +const ( + secondsPerDay = 24*60*60; + + daysPer400Years = 365*400+97; + daysPer100Years = 365*100+24; + daysPer4Years = 365*4+1; + + days1970To2001 = 31*365+8; +) + +// SecondsToUTC converts sec, in number of seconds since the Unix epoch, +// into a parsed Time value in the UTC time zone. +func SecondsToUTC(sec int64) *Time { + t := new(Time); + + // Split into time and day. + day := sec/secondsPerDay; + sec -= day*secondsPerDay; + if sec < 0 { + day--; + sec += secondsPerDay + } + + // Time + t.Hour = int(sec/3600); + t.Minute = int((sec/60)%60); + t.Second = int(sec%60); + + // Day 0 = January 1, 1970 was a Thursday + t.Weekday = int((day + Thursday) % 7); + if t.Weekday < 0 { + t.Weekday += 7 + } + + // Change day from 0 = 1970 to 0 = 2001, + // to make leap year calculations easier + // (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.) + day -= days1970To2001; + + year := int64(2001); + if day < 0 { + // Go back enough 400 year cycles to make day positive. + n := -day/daysPer400Years + 1; + year -= 400*n; + day += daysPer400Years*n; + } else { + // Cut off 400 year cycles. + n := day/daysPer400Years; + year += 400*n; + day -= daysPer400Years*n; + } + + // Cut off 100-year cycles + n := day/daysPer100Years; + year += 100*n; + day -= daysPer100Years*n; + + // Cut off 4-year cycles + n = day/daysPer4Years; + year += 4*n; + day -= daysPer4Years*n; + + // Cut off non-leap years. + n = day/365; + year += n; + day -= 365*n; + + t.Year = year; + + // If someone ever needs yearday, + // tyearday = day (+1?) + + months := months(year); + var m int; + yday := int(day); + for m = 0; m < 12 && yday >= months[m]; m++ { + yday -= months[m] + } + t.Month = m+1; + t.Day = yday+1; + t.Zone = "UTC"; + + return t; +} + +// UTC returns the current time as a parsed Time value in the UTC time zone. +func UTC() *Time { + return SecondsToUTC(Seconds()) +} + +// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch, +// into a parsed Time value in the local time zone. +func SecondsToLocalTime(sec int64) *Time { + z, offset, err := time.lookupTimezone(sec); + if err != nil { + return SecondsToUTC(sec) + } + t := SecondsToUTC(sec+int64(offset)); + t.Zone = z; + t.ZoneOffset = offset; + return t +} + +// LocalTime returns the current time as a parsed Time value in the local time zone. +func LocalTime() *Time { + return SecondsToLocalTime(Seconds()) +} + +// Seconds returns the number of seconds since January 1, 1970 represented by the +// parsed Time value. +func (t *Time) Seconds() int64 { + // First, accumulate days since January 1, 2001. + // Using 2001 instead of 1970 makes the leap-year + // handling easier (see SecondsToUTC), because + // it is at the beginning of the 4-, 100-, and 400-year cycles. + day := int64(0); + + // Rewrite year to be >= 2001. + year := t.Year; + if year < 2001 { + n := (2001 - year)/400 + 1; + year += 400*n; + day -= daysPer400Years*n; + } + + // Add in days from 400-year cycles. + n := (year - 2001) / 400; + year -= 400*n; + day += daysPer400Years*n; + + // Add in 100-year cycles. + n = (year - 2001) / 100; + year -= 100*n; + day += daysPer100Years*n; + + // Add in 4-year cycles. + n = (year - 2001) / 4; + year -= 4*n; + day += daysPer4Years*n; + + // Add in non-leap years. + n = year - 2001; + day += 365*n; + + // Add in days this year. + months := months(t.Year); + for m := 0; m < t.Month-1; m++ { + day += int64(months[m]) + } + day += int64(t.Day - 1); + + // Convert days to seconds since January 1, 2001. + sec := day * secondsPerDay; + + // Add in time elapsed today. + sec += int64(t.Hour) * 3600; + sec += int64(t.Minute) * 60; + sec += int64(t.Second); + + // Convert from seconds since 2001 to seconds since 1970. + sec += days1970To2001 * secondsPerDay; + + // Account for local time zone. + sec -= int64(t.ZoneOffset); + return sec +} + +var longDayNames = []string{ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" +} + +var shortDayNames = []string{ + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" +} + +var shortMonthNames = []string{ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +} + +func copy(dst []byte, s string) { + for i := 0; i < len(s); i++ { + dst[i] = s[i] + } +} + +func decimal(dst []byte, n int) { + if n < 0 { + n = 0 + } + for i := len(dst)-1; i >= 0; i-- { + dst[i] = byte(n%10 + '0'); + n /= 10 + } +} + +func addString(buf []byte, bp int, s string) int { + n := len(s); + copy(buf[bp:bp+n], s); + return bp+n +} + +// Just enough of strftime to implement the date formats below. +// Not exported. +func format(t *Time, fmt string) string { + buf := make([]byte, 128); + bp := 0; + + for i := 0; i < len(fmt); i++ { + if fmt[i] == '%' { + i++; + switch fmt[i] { + case 'A': // %A full weekday name + bp = addString(buf, bp, longDayNames[t.Weekday]); + case 'a': // %a abbreviated weekday name + bp = addString(buf, bp, shortDayNames[t.Weekday]); + case 'b': // %b abbreviated month name + bp = addString(buf, bp, shortMonthNames[t.Month-1]); + case 'd': // %d day of month (01-31) + decimal(buf[bp:bp+2], t.Day); + bp += 2; + case 'e': // %e day of month ( 1-31) + if t.Day >= 10 { + decimal(buf[bp:bp+2], t.Day) + } else { + buf[bp] = ' '; + buf[bp+1] = byte(t.Day + '0') + } + bp += 2; + case 'H': // %H hour 00-23 + decimal(buf[bp:bp+2], t.Hour); + bp += 2; + case 'M': // %M minute 00-59 + decimal(buf[bp:bp+2], t.Minute); + bp += 2; + case 'S': // %S second 00-59 + decimal(buf[bp:bp+2], t.Second); + bp += 2; + case 'Y': // %Y year 2008 + decimal(buf[bp:bp+4], int(t.Year)); + bp += 4; + case 'y': // %y year 08 + decimal(buf[bp:bp+2], int(t.Year%100)); + bp += 2; + case 'Z': + bp = addString(buf, bp, t.Zone); + default: + buf[bp] = '%'; + buf[bp+1] = fmt[i]; + bp += 2 + } + } else { + buf[bp] = fmt[i]; + bp++; + } + } + return string(buf[0:bp]) +} + +// Asctime formats the parsed time value in the style of +// ANSI C asctime: Sun Nov 6 08:49:37 1994 +func (t *Time) Asctime() string { + return format(t, "%a %b %e %H:%M:%S %Y") +} + +// RFC850 formats the parsed time value in the style of +// RFC 850: Sunday, 06-Nov-94 08:49:37 UTC +func (t *Time) RFC850() string { + return format(t, "%A, %d-%b-%y %H:%M:%S %Z") +} + +// RFC1123 formats the parsed time value in the style of +// RFC 1123: Sun, 06 Nov 1994 08:49:37 UTC +func (t *Time) RFC1123() string { + return format(t, "%a, %d %b %Y %H:%M:%S %Z") +} + +// String formats the parsed time value in the style of +// date(1) - Sun Nov 6 08:49:37 UTC 1994 +func (t *Time) String() string { + return format(t, "%a %b %e %H:%M:%S %Z %Y") +} diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go new file mode 100644 index 000000000..41e6736e8 --- /dev/null +++ b/src/pkg/time/time_test.go @@ -0,0 +1,85 @@ +// Copyright 2009 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 time + +import ( + "os"; + "testing"; + "time"; +) + +func init() { + // Force US Pacific time for daylight-savings + // tests below (localtests). Needs to be set + // before the first call into the time library. + os.Setenv("TZ", "US/Pacific"); +} + +type TimeTest struct { + seconds int64; + golden Time; +} + +var utctests = []TimeTest { + TimeTest{0, Time{1970, 1, 1, 0, 0, 0, Thursday, 0, "UTC"}}, + TimeTest{1221681866, Time{2008, 9, 17, 20, 4, 26, Wednesday, 0, "UTC"}}, + TimeTest{-1221681866, Time{1931, 4, 16, 3, 55, 34, Thursday, 0, "UTC"}}, + TimeTest{1e18, Time{31688740476, 10, 23, 1, 46, 40, Friday, 0, "UTC"}}, + TimeTest{-1e18, Time{-31688736537, 3, 10, 22, 13, 20, Tuesday, 0, "UTC"}}, + TimeTest{0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, Sunday, 0, "UTC"}}, + TimeTest{-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, Sunday, 0, "UTC"}} +} + +var localtests = []TimeTest { + TimeTest{0, Time{1969, 12, 31, 16, 0, 0, Wednesday, -8*60*60, "PST"}}, + TimeTest{1221681866, Time{2008, 9, 17, 13, 4, 26, Wednesday, -7*60*60, "PDT"}} +} + +func same(t, u *Time) bool { + return t.Year == u.Year + && t.Month == u.Month + && t.Day == u.Day + && t.Hour == u.Hour + && t.Minute == u.Minute + && t.Second == u.Second + && t.Weekday == u.Weekday + && t.ZoneOffset == u.ZoneOffset + && t.Zone == u.Zone +} + +func TestSecondsToUTC(t *testing.T) { + for i := 0; i < len(utctests); i++ { + sec := utctests[i].seconds; + golden := &utctests[i].golden; + tm := SecondsToUTC(sec); + newsec := tm.Seconds(); + if newsec != sec { + t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec); + } + if !same(tm, golden) { + t.Errorf("SecondsToUTC(%d):", sec); + t.Errorf(" want=%v", *golden); + t.Errorf(" have=%v", *tm); + } + } +} + +func TestSecondsToLocalTime(t *testing.T) { + for i := 0; i < len(localtests); i++ { + sec := localtests[i].seconds; + golden := &localtests[i].golden; + tm := SecondsToLocalTime(sec); + newsec := tm.Seconds(); + if newsec != sec { + t.Errorf("SecondsToLocalTime(%d).Seconds() = %d", sec, newsec); + } + if !same(tm, golden) { + t.Errorf("SecondsToLocalTime(%d):", sec); + t.Errorf(" want=%v", *golden); + t.Errorf(" have=%v", *tm); + } + } +} + diff --git a/src/pkg/time/zoneinfo.go b/src/pkg/time/zoneinfo.go new file mode 100644 index 000000000..751afc931 --- /dev/null +++ b/src/pkg/time/zoneinfo.go @@ -0,0 +1,285 @@ +// Copyright 2009 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. + +// Parse "zoneinfo" time zone file. +// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. +// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo, +// and ftp://munnari.oz.au/pub/oldtz/ + +package time + +import ( + "io"; + "once"; + "os" +) + +const ( + maxFileSize = 8192; // actual files are closer to 1K + headerSize = 4+16+4*7; + + zoneDir = "/usr/share/zoneinfo/"; +) + +// Errors that can be generated recovering time zone information. +type TimeZoneError struct { + os.ErrorString +} + +var errShort = TimeZoneError{ "time: short zone file" } +var errInvalid = TimeZoneError{ "time: invalid zone file" } +var errLong = TimeZoneError{ "time: zone file too long" } + +// Simple I/O interface to binary blob of data. +type data struct { + p []byte; + error bool; +} + + +func (d *data) read(n int) []byte { + if len(d.p) < n { + d.p = nil; + d.error = true; + return nil; + } + p := d.p[0:n]; + d.p = d.p[n:len(d.p)]; + return p +} + +func (d *data) big4() (n uint32, ok bool) { + p := d.read(4); + if len(p) < 4 { + d.error = true; + return 0, false + } + return uint32(p[0]) << 24 | uint32(p[1]) << 16 | uint32(p[2]) << 8 | uint32(p[3]), true +} + +func (d *data) byte() (n byte, ok bool) { + p := d.read(1); + if len(p) < 1 { + d.error = true; + return 0, false + } + return p[0], true +} + + +// Make a string by stopping at the first NUL +func byteString(p []byte) string { + for i := 0; i < len(p); i++ { + if p[i] == 0 { + return string(p[0:i]) + } + } + return string(p) +} + +// Parsed representation +type zone struct { + utcoff int; + isdst bool; + name string; +} + +type zonetime struct { + time int32; // transition time, in seconds since 1970 GMT + zone *zone; // the zone that goes into effect at that time + isstd, isutc bool; // ignored - no idea what these mean +} + +func parseinfo(bytes []byte) (zt []zonetime, err os.Error) { + d := data{bytes, false}; + + // 4-byte magic "TZif" + if magic := d.read(4); string(magic) != "TZif" { + return nil, TimeZoneError{ "time: bad zone magic" } + } + + // 1-byte version, then 15 bytes of padding + var p []byte; + if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' { + return nil, TimeZoneError { "time: bad zone file version" } + } + vers := p[0]; + + // six big-endian 32-bit integers: + // number of UTC/local indicators + // number of standard/wall indicators + // number of leap seconds + // number of transition times + // number of local time zones + // number of characters of time zone abbrev strings + const ( + NUTCLocal = iota; + NStdWall; + NLeap; + NTime; + NZone; + NChar + ) + var n [6]int; + for i := 0; i < 6; i++ { + nn, ok := d.big4(); + if !ok { + return nil, errShort + } + n[i] = int(nn); + } + + // Transition times. + txtimes := data{d.read(n[NTime]*4), false}; + + // Time zone indices for transition times. + txzones := d.read(n[NTime]); + + // Zone info structures + zonedata := data{d.read(n[NZone]*6), false}; + + // Time zone abbreviations. + abbrev := d.read(n[NChar]); + + // Leap-second time pairs + leapdata := data{d.read(n[NLeap]*8), false}; + + // Whether tx times associated with local time types + // are specified as standard time or wall time. + isstd := d.read(n[NStdWall]); + + // Whether tx times associated with local time types + // are specified as UTC or local time. + isutc := d.read(n[NUTCLocal]); + + if d.error { // ran out of data + return nil, errShort + } + + // If version == 2, the entire file repeats, this time using + // 8-byte ints for txtimes and leap seconds. + // We won't need those until 2106. + + // Now we can build up a useful data structure. + // First the zone information. + // utcoff[4] isdst[1] nameindex[1] + z := make([]zone, n[NZone]); + for i := 0; i < len(z); i++ { + var ok bool; + var n uint32; + if n, ok = zonedata.big4(); !ok { + return nil, errShort + } + z[i].utcoff = int(n); + var b byte; + if b, ok = zonedata.byte(); !ok { + return nil, errShort + } + z[i].isdst = b != 0; + if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) { + return nil, errInvalid + } + z[i].name = byteString(abbrev[b:len(abbrev)]) + } + + // Now the transition time info. + zt = make([]zonetime, n[NTime]); + for i := 0; i < len(zt); i++ { + var ok bool; + var n uint32; + if n, ok = txtimes.big4(); !ok { + return nil, errShort + } + zt[i].time = int32(n); + if int(txzones[i]) >= len(z) { + return nil, errInvalid + } + zt[i].zone = &z[txzones[i]]; + if i < len(isstd) { + zt[i].isstd = isstd[i] != 0 + } + if i < len(isutc) { + zt[i].isutc = isutc[i] != 0 + } + } + return zt, nil +} + +func readfile(name string, max int) (p []byte, err os.Error) { + f, e := os.Open(name, os.O_RDONLY, 0); + if e != nil { + return nil, e; + } + p = make([]byte, max); + n, err1 := io.FullRead(f, p); + f.Close(); + if err1 == nil { // too long + return nil, errLong; + } + if err1 != io.ErrEOF { + return nil, err1; + } + return p[0:n], nil; +} + +func readinfofile(name string) ([]zonetime, os.Error) { + buf, err := readfile(name, maxFileSize); + if err != nil { + goto Error; + } + tx, err := parseinfo(buf); + if err != nil { + goto Error; + } + return tx, nil; + +Error: + if tzerr, ok := err.(TimeZoneError); ok { + tzerr.ErrorString = os.ErrorString(tzerr.String() + ": " + name) + } + return nil, err +} + +var zones []zonetime +var zoneerr os.Error + +func setupZone() { + // consult $TZ to find the time zone to use. + // no $TZ means use the system default /etc/localtime. + // $TZ="" means use UTC. + // $TZ="foo" means use /usr/share/zoneinfo/foo. + + tz, err := os.Getenv("TZ"); + var file string; + switch { + case err == os.ENOENV: + zones, zoneerr = readinfofile("/etc/localtime"); + case err != nil: + zoneerr = err; + case len(tz) > 0: + zones, zoneerr = readinfofile(zoneDir + tz); + case len(tz) == 0: + // do nothing: use UTC + } +} + +func lookupTimezone(sec int64) (zone string, offset int, err os.Error) { + once.Do(setupZone); + if zoneerr != nil || len(zones) == 0 { + return "UTC", 0, zoneerr + } + + // Binary search for entry with largest time <= sec + tz := zones; + for len(tz) > 1 { + m := len(tz)/2; + if sec < int64(tz[m].time) { + tz = tz[0:m] + } else { + tz = tz[m:len(tz)] + } + } + z := tz[0].zone; + return z.name, z.utcoff, nil +} diff --git a/src/pkg/unicode/Makefile b/src/pkg/unicode/Makefile new file mode 100644 index 000000000..de1677b8d --- /dev/null +++ b/src/pkg/unicode/Makefile @@ -0,0 +1,68 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + letter.$O\ + +O2=\ + decimaldigit.$O\ + + +phases: a1 a2 +_obj$D/unicode.a: phases + +a1: $(O1) + $(AR) grc _obj$D/unicode.a letter.$O + rm -f $(O1) + +a2: $(O2) + $(AR) grc _obj$D/unicode.a decimaldigit.$O + rm -f $(O2) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/unicode.a + +$(O1): newpkg +$(O2): a1 +$(O3): a2 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/unicode.a + +packages: _obj$D/unicode.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/unicode.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/unicode.a diff --git a/src/pkg/unicode/decimaldigit.go b/src/pkg/unicode/decimaldigit.go new file mode 100644 index 000000000..1165e3ae1 --- /dev/null +++ b/src/pkg/unicode/decimaldigit.go @@ -0,0 +1,52 @@ +// Copyright 2009 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 unicode + +// TODO: Generated by hand starting with +// http://www.unicode.org/Public/UNIDATA/UnicodeData.txt +// These ranges are the characters with the third field "Nd". +// Should generate automatically etc. + +import "unicode" + +// Decimal digit is the set of Unicode characters with the "decimal digit" property. +var DecimalDigit = []Range{ + Range{0x0030, 0x0039, 1}, + Range{0x0660, 0x0669, 1}, + Range{0x06F0, 0x06F9, 1}, + Range{0x07C0, 0x07C9, 1}, + Range{0x0966, 0x096F, 1}, + Range{0x09E6, 0x09EF, 1}, + Range{0x0A66, 0x0A6F, 1}, + Range{0x0AE6, 0x0AEF, 1}, + Range{0x0B66, 0x0B6F, 1}, + Range{0x0BE6, 0x0BEF, 1}, + Range{0x0C66, 0x0C6F, 1}, + Range{0x0CE6, 0x0CEF, 1}, + Range{0x0D66, 0x0D6F, 1}, + Range{0x0E50, 0x0E59, 1}, + Range{0x0ED0, 0x0ED9, 1}, + Range{0x0F20, 0x0F29, 1}, + Range{0x1040, 0x1049, 1}, + Range{0x1090, 0x1099, 1}, + Range{0x17E0, 0x17E9, 1}, + Range{0x1810, 0x1819, 1}, + Range{0x1946, 0x194F, 1}, + Range{0x19D0, 0x19D9, 1}, + Range{0x1B50, 0x1B59, 1}, + Range{0x1BB0, 0x1BB9, 1}, + Range{0x1C40, 0x1C49, 1}, + Range{0x1C50, 0x1C59, 1}, + Range{0xA620, 0xA629, 1}, + Range{0xA8D0, 0xA8D9, 1}, + Range{0xA900, 0xA909, 1}, + Range{0xAA50, 0xAA59, 1}, + Range{0xFF10, 0xFF19, 1}, +} + +// IsDecimalDigit reports whether the rune is a decimal digit. +func IsDecimalDigit(rune int) bool { + return Is(DecimalDigit, rune); +} diff --git a/src/pkg/unicode/decimaldigit_test.go b/src/pkg/unicode/decimaldigit_test.go new file mode 100644 index 000000000..f7b470c67 --- /dev/null +++ b/src/pkg/unicode/decimaldigit_test.go @@ -0,0 +1,375 @@ +// Copyright 2009 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 unicode + +import ( + "testing"; + "unicode"; +) + +// To get data: +// grep '^....;[^;]*;Nd;' UnicodeData.txt +// To generate this table: +// ,s/([^;]+).+/ 0x\1, \/\/ &/g +var testDecimal = []int{ + 0x0030, // 0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;; + 0x0031, // 0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;; + 0x0032, // 0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;; + 0x0033, // 0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;; + 0x0034, // 0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;; + 0x0035, // 0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;; + 0x0036, // 0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;; + 0x0037, // 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; + 0x0038, // 0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;; + 0x0039, // 0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;; + 0x0660, // 0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;; + 0x0661, // 0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;; + 0x0662, // 0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;; + 0x0663, // 0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;; + 0x0664, // 0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;; + 0x0665, // 0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;; + 0x0666, // 0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;; + 0x0667, // 0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;; + 0x0668, // 0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;; + 0x0669, // 0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;; + 0x06F0, // 06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;; + 0x06F1, // 06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;; + 0x06F2, // 06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;; + 0x06F3, // 06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;; + 0x06F4, // 06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;; + 0x06F5, // 06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;; + 0x06F6, // 06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;; + 0x06F7, // 06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;; + 0x06F8, // 06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;; + 0x06F9, // 06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;; + 0x07C0, // 07C0;NKO DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;; + 0x07C1, // 07C1;NKO DIGIT ONE;Nd;0;R;;1;1;1;N;;;;; + 0x07C2, // 07C2;NKO DIGIT TWO;Nd;0;R;;2;2;2;N;;;;; + 0x07C3, // 07C3;NKO DIGIT THREE;Nd;0;R;;3;3;3;N;;;;; + 0x07C4, // 07C4;NKO DIGIT FOUR;Nd;0;R;;4;4;4;N;;;;; + 0x07C5, // 07C5;NKO DIGIT FIVE;Nd;0;R;;5;5;5;N;;;;; + 0x07C6, // 07C6;NKO DIGIT SIX;Nd;0;R;;6;6;6;N;;;;; + 0x07C7, // 07C7;NKO DIGIT SEVEN;Nd;0;R;;7;7;7;N;;;;; + 0x07C8, // 07C8;NKO DIGIT EIGHT;Nd;0;R;;8;8;8;N;;;;; + 0x07C9, // 07C9;NKO DIGIT NINE;Nd;0;R;;9;9;9;N;;;;; + 0x0966, // 0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0967, // 0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0968, // 0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0969, // 0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x096A, // 096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x096B, // 096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x096C, // 096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x096D, // 096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x096E, // 096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x096F, // 096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x09E6, // 09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x09E7, // 09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x09E8, // 09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x09E9, // 09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x09EA, // 09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x09EB, // 09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x09EC, // 09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x09ED, // 09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x09EE, // 09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x09EF, // 09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0A66, // 0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0A67, // 0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0A68, // 0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0A69, // 0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0A6A, // 0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0A6B, // 0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0A6C, // 0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0A6D, // 0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0A6E, // 0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0A6F, // 0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0AE6, // 0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0AE7, // 0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0AE8, // 0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0AE9, // 0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0AEA, // 0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0AEB, // 0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0AEC, // 0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0AED, // 0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0AEE, // 0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0AEF, // 0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0B66, // 0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0B67, // 0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0B68, // 0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0B69, // 0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0B6A, // 0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0B6B, // 0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0B6C, // 0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0B6D, // 0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0B6E, // 0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0B6F, // 0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0BE6, // 0BE6;TAMIL DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0BE7, // 0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0BE8, // 0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0BE9, // 0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0BEA, // 0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0BEB, // 0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0BEC, // 0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0BED, // 0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0BEE, // 0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0BEF, // 0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0C66, // 0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0C67, // 0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0C68, // 0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0C69, // 0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0C6A, // 0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0C6B, // 0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0C6C, // 0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0C6D, // 0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0C6E, // 0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0C6F, // 0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0CE6, // 0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0CE7, // 0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0CE8, // 0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0CE9, // 0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0CEA, // 0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0CEB, // 0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0CEC, // 0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0CED, // 0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0CEE, // 0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0CEF, // 0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0D66, // 0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0D67, // 0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0D68, // 0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0D69, // 0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0D6A, // 0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0D6B, // 0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0D6C, // 0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0D6D, // 0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0D6E, // 0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0D6F, // 0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0E50, // 0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0E51, // 0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0E52, // 0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0E53, // 0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0E54, // 0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0E55, // 0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0E56, // 0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0E57, // 0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0E58, // 0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0E59, // 0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0ED0, // 0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0ED1, // 0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0ED2, // 0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0ED3, // 0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0ED4, // 0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0ED5, // 0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0ED6, // 0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0ED7, // 0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0ED8, // 0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0ED9, // 0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x0F20, // 0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x0F21, // 0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x0F22, // 0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x0F23, // 0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x0F24, // 0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x0F25, // 0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x0F26, // 0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x0F27, // 0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x0F28, // 0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x0F29, // 0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1040, // 1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1041, // 1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1042, // 1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1043, // 1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x1044, // 1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x1045, // 1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x1046, // 1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x1047, // 1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x1048, // 1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x1049, // 1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1090, // 1090;MYANMAR SHAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1091, // 1091;MYANMAR SHAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1092, // 1092;MYANMAR SHAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1093, // 1093;MYANMAR SHAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x1094, // 1094;MYANMAR SHAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x1095, // 1095;MYANMAR SHAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x1096, // 1096;MYANMAR SHAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x1097, // 1097;MYANMAR SHAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x1098, // 1098;MYANMAR SHAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x1099, // 1099;MYANMAR SHAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x17E0, // 17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x17E1, // 17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x17E2, // 17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x17E3, // 17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x17E4, // 17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x17E5, // 17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x17E6, // 17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x17E7, // 17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x17E8, // 17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x17E9, // 17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1810, // 1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1811, // 1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1812, // 1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1813, // 1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x1814, // 1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x1815, // 1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x1816, // 1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x1817, // 1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x1818, // 1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x1819, // 1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1946, // 1946;LIMBU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1947, // 1947;LIMBU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1948, // 1948;LIMBU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1949, // 1949;LIMBU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x194A, // 194A;LIMBU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x194B, // 194B;LIMBU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x194C, // 194C;LIMBU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x194D, // 194D;LIMBU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x194E, // 194E;LIMBU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x194F, // 194F;LIMBU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x19D0, // 19D0;NEW TAI LUE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x19D1, // 19D1;NEW TAI LUE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x19D2, // 19D2;NEW TAI LUE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x19D3, // 19D3;NEW TAI LUE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x19D4, // 19D4;NEW TAI LUE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x19D5, // 19D5;NEW TAI LUE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x19D6, // 19D6;NEW TAI LUE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x19D7, // 19D7;NEW TAI LUE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x19D8, // 19D8;NEW TAI LUE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x19D9, // 19D9;NEW TAI LUE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1B50, // 1B50;BALINESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1B51, // 1B51;BALINESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1B52, // 1B52;BALINESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1B53, // 1B53;BALINESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x1B54, // 1B54;BALINESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x1B55, // 1B55;BALINESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x1B56, // 1B56;BALINESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x1B57, // 1B57;BALINESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x1B58, // 1B58;BALINESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x1B59, // 1B59;BALINESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1BB0, // 1BB0;SUNDANESE DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1BB1, // 1BB1;SUNDANESE DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1BB2, // 1BB2;SUNDANESE DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1BB3, // 1BB3;SUNDANESE DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x1BB4, // 1BB4;SUNDANESE DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x1BB5, // 1BB5;SUNDANESE DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x1BB6, // 1BB6;SUNDANESE DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x1BB7, // 1BB7;SUNDANESE DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x1BB8, // 1BB8;SUNDANESE DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x1BB9, // 1BB9;SUNDANESE DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1C40, // 1C40;LEPCHA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1C41, // 1C41;LEPCHA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1C42, // 1C42;LEPCHA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1C43, // 1C43;LEPCHA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x1C44, // 1C44;LEPCHA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x1C45, // 1C45;LEPCHA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x1C46, // 1C46;LEPCHA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x1C47, // 1C47;LEPCHA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x1C48, // 1C48;LEPCHA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x1C49, // 1C49;LEPCHA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0x1C50, // 1C50;OL CHIKI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0x1C51, // 1C51;OL CHIKI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0x1C52, // 1C52;OL CHIKI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0x1C53, // 1C53;OL CHIKI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0x1C54, // 1C54;OL CHIKI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0x1C55, // 1C55;OL CHIKI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0x1C56, // 1C56;OL CHIKI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0x1C57, // 1C57;OL CHIKI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0x1C58, // 1C58;OL CHIKI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0x1C59, // 1C59;OL CHIKI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0xA620, // A620;VAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0xA621, // A621;VAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0xA622, // A622;VAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0xA623, // A623;VAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0xA624, // A624;VAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0xA625, // A625;VAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0xA626, // A626;VAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0xA627, // A627;VAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0xA628, // A628;VAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0xA629, // A629;VAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0xA8D0, // A8D0;SAURASHTRA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0xA8D1, // A8D1;SAURASHTRA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0xA8D2, // A8D2;SAURASHTRA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0xA8D3, // A8D3;SAURASHTRA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0xA8D4, // A8D4;SAURASHTRA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0xA8D5, // A8D5;SAURASHTRA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0xA8D6, // A8D6;SAURASHTRA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0xA8D7, // A8D7;SAURASHTRA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0xA8D8, // A8D8;SAURASHTRA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0xA8D9, // A8D9;SAURASHTRA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0xA900, // A900;KAYAH LI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0xA901, // A901;KAYAH LI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0xA902, // A902;KAYAH LI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0xA903, // A903;KAYAH LI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0xA904, // A904;KAYAH LI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0xA905, // A905;KAYAH LI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0xA906, // A906;KAYAH LI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0xA907, // A907;KAYAH LI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0xA908, // A908;KAYAH LI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0xA909, // A909;KAYAH LI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0xAA50, // AA50;CHAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; + 0xAA51, // AA51;CHAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; + 0xAA52, // AA52;CHAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; + 0xAA53, // AA53;CHAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; + 0xAA54, // AA54;CHAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; + 0xAA55, // AA55;CHAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; + 0xAA56, // AA56;CHAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; + 0xAA57, // AA57;CHAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; + 0xAA58, // AA58;CHAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; + 0xAA59, // AA59;CHAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; + 0xFF10, // FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN;<wide> 0030;0;0;0;N;;;;; + 0xFF11, // FF11;FULLWIDTH DIGIT ONE;Nd;0;EN;<wide> 0031;1;1;1;N;;;;; + 0xFF12, // FF12;FULLWIDTH DIGIT TWO;Nd;0;EN;<wide> 0032;2;2;2;N;;;;; + 0xFF13, // FF13;FULLWIDTH DIGIT THREE;Nd;0;EN;<wide> 0033;3;3;3;N;;;;; + 0xFF14, // FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN;<wide> 0034;4;4;4;N;;;;; + 0xFF15, // FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN;<wide> 0035;5;5;5;N;;;;; + 0xFF16, // FF16;FULLWIDTH DIGIT SIX;Nd;0;EN;<wide> 0036;6;6;6;N;;;;; + 0xFF17, // FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN;<wide> 0037;7;7;7;N;;;;; + 0xFF18, // FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN;<wide> 0038;8;8;8;N;;;;; + 0xFF19, // FF19;FULLWIDTH DIGIT NINE;Nd;0;EN;<wide> 0039;9;9;9;N;;;;; +} + +var testLetter = []int{ + 0x41, + 0x61, + 0xaa, + 0xba, + 0xc8, + 0xdb, + 0xf9, + 0x2ec, + 0x535, + 0x6e6, + 0x93d, + 0xa15, + 0xb99, + 0xdc0, + 0xedd, + 0x1000, + 0x1200, + 0x1312, + 0x1401, + 0x1885, + 0x2c00, + 0xa800, + 0xf900, + 0xfa30, + 0xffda, + 0xffdc, + 0x10000, + 0x10300, + 0x10400, + 0x20000, + 0x2f800, + 0x2fa1d, +} + +func TestIsDecimalDigit(t *testing.T) { + for i, r := range(testDecimal) { + if !IsDecimalDigit(r) { + t.Errorf("IsDecimalDigit(%#x) = false, want true\n", r); + } + } + for i, r := range(testLetter) { + if IsDecimalDigit(r) { + t.Errorf("IsDecimalDigit(%#x) = true, want false\n", r); + } + } +} diff --git a/src/pkg/unicode/letter.go b/src/pkg/unicode/letter.go new file mode 100644 index 000000000..d7aa678c9 --- /dev/null +++ b/src/pkg/unicode/letter.go @@ -0,0 +1,578 @@ +// Copyright 2009 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. + +// Unicode table access. A quick start for now. + +// TODO: Generated by hand. +// Should generate automatically from Unicode +// tables, expand to other properties, split into files, +// link in only the tables that are used by the program, +// etc. + +// This package provides data and functions to test some properties of Unicode code points. +// It is rudimentary but will improve. +package unicode + +// The representation of a range of Unicode code points. The range runs from Lo to Hi +// inclusive and has the specified stride. +type Range struct { + Lo int; + Hi int; + Stride int; +} + +// Upper is the set of Unicode upper case letters. +var Upper = []Range{ + Range{0x0041, 0x005a, 1}, + Range{0x00c0, 0x00d6, 1}, + Range{0x00d8, 0x00de, 1}, + Range{0x0100, 0x0136, 2}, + Range{0x0139, 0x0147, 2}, + Range{0x014a, 0x0176, 2}, + Range{0x0178, 0x0179, 1}, + Range{0x017b, 0x017d, 2}, + Range{0x0181, 0x0182, 1}, + Range{0x0184, 0x0184, 1}, + Range{0x0186, 0x0187, 1}, + Range{0x0189, 0x018b, 1}, + Range{0x018e, 0x0191, 1}, + Range{0x0193, 0x0194, 1}, + Range{0x0196, 0x0198, 1}, + Range{0x019c, 0x019d, 1}, + Range{0x019f, 0x01a0, 1}, + Range{0x01a2, 0x01a4, 2}, + Range{0x01a6, 0x01a7, 1}, + Range{0x01a9, 0x01ac, 3}, + Range{0x01ae, 0x01af, 1}, + Range{0x01b1, 0x01b3, 1}, + Range{0x01b5, 0x01b5, 1}, + Range{0x01b7, 0x01b8, 1}, + Range{0x01bc, 0x01c4, 8}, + Range{0x01c7, 0x01cd, 3}, + Range{0x01cf, 0x01db, 2}, + Range{0x01de, 0x01ee, 2}, + Range{0x01f1, 0x01f4, 3}, + Range{0x01f6, 0x01f8, 1}, + Range{0x01fa, 0x0232, 2}, + Range{0x023a, 0x023b, 1}, + Range{0x023d, 0x023e, 1}, + Range{0x0241, 0x0241, 1}, + Range{0x0243, 0x0246, 1}, + Range{0x0248, 0x024e, 2}, + Range{0x0370, 0x0372, 2}, + Range{0x0376, 0x0386, 16}, + Range{0x0388, 0x038a, 1}, + Range{0x038c, 0x038c, 1}, + Range{0x038e, 0x038f, 1}, + Range{0x0391, 0x03a1, 1}, + Range{0x03a3, 0x03ab, 1}, + Range{0x03cf, 0x03cf, 1}, + Range{0x03d2, 0x03d4, 1}, + Range{0x03d8, 0x03ee, 2}, + Range{0x03f4, 0x03f7, 3}, + Range{0x03f9, 0x03fa, 1}, + Range{0x03fd, 0x042f, 1}, + Range{0x0460, 0x0480, 2}, + Range{0x048a, 0x04be, 2}, + Range{0x04c0, 0x04c1, 1}, + Range{0x04c3, 0x04cd, 2}, + Range{0x04d0, 0x0522, 2}, + Range{0x0531, 0x0556, 1}, + Range{0x10a0, 0x10c5, 1}, + Range{0x1e00, 0x1e94, 2}, + Range{0x1e9e, 0x1efe, 2}, + Range{0x1f08, 0x1f0f, 1}, + Range{0x1f18, 0x1f1d, 1}, + Range{0x1f28, 0x1f2f, 1}, + Range{0x1f38, 0x1f3f, 1}, + Range{0x1f48, 0x1f4d, 1}, + Range{0x1f59, 0x1f5f, 2}, + Range{0x1f68, 0x1f6f, 1}, + Range{0x1fb8, 0x1fbb, 1}, + Range{0x1fc8, 0x1fcb, 1}, + Range{0x1fd8, 0x1fdb, 1}, + Range{0x1fe8, 0x1fec, 1}, + Range{0x1ff8, 0x1ffb, 1}, + Range{0x2102, 0x2107, 5}, + Range{0x210b, 0x210d, 1}, + Range{0x2110, 0x2112, 1}, + Range{0x2115, 0x2115, 1}, + Range{0x2119, 0x211d, 1}, + Range{0x2124, 0x2128, 2}, + Range{0x212a, 0x212d, 1}, + Range{0x2130, 0x2133, 1}, + Range{0x213e, 0x213f, 1}, + Range{0x2145, 0x2183, 62}, + Range{0x2c00, 0x2c2e, 1}, + Range{0x2c60, 0x2c60, 1}, + Range{0x2c62, 0x2c64, 1}, + Range{0x2c67, 0x2c6b, 2}, + Range{0x2c6d, 0x2c6f, 1}, + Range{0x2c72, 0x2c75, 3}, + Range{0x2c80, 0x2ce2, 2}, + Range{0xa640, 0xa65e, 2}, + Range{0xa662, 0xa66c, 2}, + Range{0xa680, 0xa696, 2}, + Range{0xa722, 0xa72e, 2}, + Range{0xa732, 0xa76e, 2}, + Range{0xa779, 0xa77b, 2}, + Range{0xa77d, 0xa77e, 1}, + Range{0xa780, 0xa786, 2}, + Range{0xa78b, 0xa78b, 1}, + Range{0xff21, 0xff3a, 1}, + Range{0x10400, 0x10427, 1}, + Range{0x1d400, 0x1d419, 1}, + Range{0x1d434, 0x1d44d, 1}, + Range{0x1d468, 0x1d481, 1}, + Range{0x1d49c, 0x1d49c, 1}, + Range{0x1d49e, 0x1d49f, 1}, + Range{0x1d4a2, 0x1d4a2, 1}, + Range{0x1d4a5, 0x1d4a6, 1}, + Range{0x1d4a9, 0x1d4ac, 1}, + Range{0x1d4ae, 0x1d4b5, 1}, + Range{0x1d4d0, 0x1d4e9, 1}, + Range{0x1d504, 0x1d505, 1}, + Range{0x1d507, 0x1d50a, 1}, + Range{0x1d50d, 0x1d514, 1}, + Range{0x1d516, 0x1d51c, 1}, + Range{0x1d538, 0x1d539, 1}, + Range{0x1d53b, 0x1d53e, 1}, + Range{0x1d540, 0x1d544, 1}, + Range{0x1d546, 0x1d546, 1}, + Range{0x1d54a, 0x1d550, 1}, + Range{0x1d56c, 0x1d585, 1}, + Range{0x1d5a0, 0x1d5b9, 1}, + Range{0x1d5d4, 0x1d5ed, 1}, + Range{0x1d608, 0x1d621, 1}, + Range{0x1d63c, 0x1d655, 1}, + Range{0x1d670, 0x1d689, 1}, + Range{0x1d6a8, 0x1d6c0, 1}, + Range{0x1d6e2, 0x1d6fa, 1}, + Range{0x1d71c, 0x1d734, 1}, + Range{0x1d756, 0x1d76e, 1}, + Range{0x1d790, 0x1d7a8, 1}, + Range{0x1d7ca, 0x1d7ca, 1}, +} + +// Letter is the set of Unicode letters. +var Letter = []Range { + Range{0x0041, 0x005a, 1}, + Range{0x0061, 0x007a, 1}, + Range{0x00aa, 0x00b5, 11}, + Range{0x00ba, 0x00ba, 1}, + Range{0x00c0, 0x00d6, 1}, + Range{0x00d8, 0x00f6, 1}, + Range{0x00f8, 0x02c1, 1}, + Range{0x02c6, 0x02d1, 1}, + Range{0x02e0, 0x02e4, 1}, + Range{0x02ec, 0x02ee, 2}, + Range{0x0370, 0x0374, 1}, + Range{0x0376, 0x0377, 1}, + Range{0x037a, 0x037d, 1}, + Range{0x0386, 0x0386, 1}, + Range{0x0388, 0x038a, 1}, + Range{0x038c, 0x038c, 1}, + Range{0x038e, 0x03a1, 1}, + Range{0x03a3, 0x03f5, 1}, + Range{0x03f7, 0x0481, 1}, + Range{0x048a, 0x0523, 1}, + Range{0x0531, 0x0556, 1}, + Range{0x0559, 0x0559, 1}, + Range{0x0561, 0x0587, 1}, + Range{0x05d0, 0x05ea, 1}, + Range{0x05f0, 0x05f2, 1}, + Range{0x0621, 0x064a, 1}, + Range{0x066e, 0x066f, 1}, + Range{0x0671, 0x06d3, 1}, + Range{0x06d5, 0x06d5, 1}, + Range{0x06e5, 0x06e6, 1}, + Range{0x06ee, 0x06ef, 1}, + Range{0x06fa, 0x06fc, 1}, + Range{0x06ff, 0x0710, 17}, + Range{0x0712, 0x072f, 1}, + Range{0x074d, 0x07a5, 1}, + Range{0x07b1, 0x07b1, 1}, + Range{0x07ca, 0x07ea, 1}, + Range{0x07f4, 0x07f5, 1}, + Range{0x07fa, 0x07fa, 1}, + Range{0x0904, 0x0939, 1}, + Range{0x093d, 0x0950, 19}, + Range{0x0958, 0x0961, 1}, + Range{0x0971, 0x0972, 1}, + Range{0x097b, 0x097f, 1}, + Range{0x0985, 0x098c, 1}, + Range{0x098f, 0x0990, 1}, + Range{0x0993, 0x09a8, 1}, + Range{0x09aa, 0x09b0, 1}, + Range{0x09b2, 0x09b2, 1}, + Range{0x09b6, 0x09b9, 1}, + Range{0x09bd, 0x09ce, 17}, + Range{0x09dc, 0x09dd, 1}, + Range{0x09df, 0x09e1, 1}, + Range{0x09f0, 0x09f1, 1}, + Range{0x0a05, 0x0a0a, 1}, + Range{0x0a0f, 0x0a10, 1}, + Range{0x0a13, 0x0a28, 1}, + Range{0x0a2a, 0x0a30, 1}, + Range{0x0a32, 0x0a33, 1}, + Range{0x0a35, 0x0a36, 1}, + Range{0x0a38, 0x0a39, 1}, + Range{0x0a59, 0x0a5c, 1}, + Range{0x0a5e, 0x0a5e, 1}, + Range{0x0a72, 0x0a74, 1}, + Range{0x0a85, 0x0a8d, 1}, + Range{0x0a8f, 0x0a91, 1}, + Range{0x0a93, 0x0aa8, 1}, + Range{0x0aaa, 0x0ab0, 1}, + Range{0x0ab2, 0x0ab3, 1}, + Range{0x0ab5, 0x0ab9, 1}, + Range{0x0abd, 0x0ad0, 19}, + Range{0x0ae0, 0x0ae1, 1}, + Range{0x0b05, 0x0b0c, 1}, + Range{0x0b0f, 0x0b10, 1}, + Range{0x0b13, 0x0b28, 1}, + Range{0x0b2a, 0x0b30, 1}, + Range{0x0b32, 0x0b33, 1}, + Range{0x0b35, 0x0b39, 1}, + Range{0x0b3d, 0x0b3d, 1}, + Range{0x0b5c, 0x0b5d, 1}, + Range{0x0b5f, 0x0b61, 1}, + Range{0x0b71, 0x0b83, 18}, + Range{0x0b85, 0x0b8a, 1}, + Range{0x0b8e, 0x0b90, 1}, + Range{0x0b92, 0x0b95, 1}, + Range{0x0b99, 0x0b9a, 1}, + Range{0x0b9c, 0x0b9c, 1}, + Range{0x0b9e, 0x0b9f, 1}, + Range{0x0ba3, 0x0ba4, 1}, + Range{0x0ba8, 0x0baa, 1}, + Range{0x0bae, 0x0bb9, 1}, + Range{0x0bd0, 0x0bd0, 1}, + Range{0x0c05, 0x0c0c, 1}, + Range{0x0c0e, 0x0c10, 1}, + Range{0x0c12, 0x0c28, 1}, + Range{0x0c2a, 0x0c33, 1}, + Range{0x0c35, 0x0c39, 1}, + Range{0x0c3d, 0x0c3d, 1}, + Range{0x0c58, 0x0c59, 1}, + Range{0x0c60, 0x0c61, 1}, + Range{0x0c85, 0x0c8c, 1}, + Range{0x0c8e, 0x0c90, 1}, + Range{0x0c92, 0x0ca8, 1}, + Range{0x0caa, 0x0cb3, 1}, + Range{0x0cb5, 0x0cb9, 1}, + Range{0x0cbd, 0x0cde, 33}, + Range{0x0ce0, 0x0ce1, 1}, + Range{0x0d05, 0x0d0c, 1}, + Range{0x0d0e, 0x0d10, 1}, + Range{0x0d12, 0x0d28, 1}, + Range{0x0d2a, 0x0d39, 1}, + Range{0x0d3d, 0x0d3d, 1}, + Range{0x0d60, 0x0d61, 1}, + Range{0x0d7a, 0x0d7f, 1}, + Range{0x0d85, 0x0d96, 1}, + Range{0x0d9a, 0x0db1, 1}, + Range{0x0db3, 0x0dbb, 1}, + Range{0x0dbd, 0x0dbd, 1}, + Range{0x0dc0, 0x0dc6, 1}, + Range{0x0e01, 0x0e30, 1}, + Range{0x0e32, 0x0e33, 1}, + Range{0x0e40, 0x0e46, 1}, + Range{0x0e81, 0x0e82, 1}, + Range{0x0e84, 0x0e84, 1}, + Range{0x0e87, 0x0e88, 1}, + Range{0x0e8a, 0x0e8d, 3}, + Range{0x0e94, 0x0e97, 1}, + Range{0x0e99, 0x0e9f, 1}, + Range{0x0ea1, 0x0ea3, 1}, + Range{0x0ea5, 0x0ea7, 2}, + Range{0x0eaa, 0x0eab, 1}, + Range{0x0ead, 0x0eb0, 1}, + Range{0x0eb2, 0x0eb3, 1}, + Range{0x0ebd, 0x0ebd, 1}, + Range{0x0ec0, 0x0ec4, 1}, + Range{0x0ec6, 0x0ec6, 1}, + Range{0x0edc, 0x0edd, 1}, + Range{0x0f00, 0x0f00, 1}, + Range{0x0f40, 0x0f47, 1}, + Range{0x0f49, 0x0f6c, 1}, + Range{0x0f88, 0x0f8b, 1}, + Range{0x1000, 0x102a, 1}, + Range{0x103f, 0x103f, 1}, + Range{0x1050, 0x1055, 1}, + Range{0x105a, 0x105d, 1}, + Range{0x1061, 0x1061, 1}, + Range{0x1065, 0x1066, 1}, + Range{0x106e, 0x1070, 1}, + Range{0x1075, 0x1081, 1}, + Range{0x108e, 0x108e, 1}, + Range{0x10a0, 0x10c5, 1}, + Range{0x10d0, 0x10fa, 1}, + Range{0x10fc, 0x10fc, 1}, + Range{0x1100, 0x1159, 1}, + Range{0x115f, 0x11a2, 1}, + Range{0x11a8, 0x11f9, 1}, + Range{0x1200, 0x1248, 1}, + Range{0x124a, 0x124d, 1}, + Range{0x1250, 0x1256, 1}, + Range{0x1258, 0x1258, 1}, + Range{0x125a, 0x125d, 1}, + Range{0x1260, 0x1288, 1}, + Range{0x128a, 0x128d, 1}, + Range{0x1290, 0x12b0, 1}, + Range{0x12b2, 0x12b5, 1}, + Range{0x12b8, 0x12be, 1}, + Range{0x12c0, 0x12c0, 1}, + Range{0x12c2, 0x12c5, 1}, + Range{0x12c8, 0x12d6, 1}, + Range{0x12d8, 0x1310, 1}, + Range{0x1312, 0x1315, 1}, + Range{0x1318, 0x135a, 1}, + Range{0x1380, 0x138f, 1}, + Range{0x13a0, 0x13f4, 1}, + Range{0x1401, 0x166c, 1}, + Range{0x166f, 0x1676, 1}, + Range{0x1681, 0x169a, 1}, + Range{0x16a0, 0x16ea, 1}, + Range{0x1700, 0x170c, 1}, + Range{0x170e, 0x1711, 1}, + Range{0x1720, 0x1731, 1}, + Range{0x1740, 0x1751, 1}, + Range{0x1760, 0x176c, 1}, + Range{0x176e, 0x1770, 1}, + Range{0x1780, 0x17b3, 1}, + Range{0x17d7, 0x17dc, 5}, + Range{0x1820, 0x1877, 1}, + Range{0x1880, 0x18a8, 1}, + Range{0x18aa, 0x18aa, 1}, + Range{0x1900, 0x191c, 1}, + Range{0x1950, 0x196d, 1}, + Range{0x1970, 0x1974, 1}, + Range{0x1980, 0x19a9, 1}, + Range{0x19c1, 0x19c7, 1}, + Range{0x1a00, 0x1a16, 1}, + Range{0x1b05, 0x1b33, 1}, + Range{0x1b45, 0x1b4b, 1}, + Range{0x1b83, 0x1ba0, 1}, + Range{0x1bae, 0x1baf, 1}, + Range{0x1c00, 0x1c23, 1}, + Range{0x1c4d, 0x1c4f, 1}, + Range{0x1c5a, 0x1c7d, 1}, + Range{0x1d00, 0x1dbf, 1}, + Range{0x1e00, 0x1f15, 1}, + Range{0x1f18, 0x1f1d, 1}, + Range{0x1f20, 0x1f45, 1}, + Range{0x1f48, 0x1f4d, 1}, + Range{0x1f50, 0x1f57, 1}, + Range{0x1f59, 0x1f5d, 2}, + Range{0x1f5f, 0x1f7d, 1}, + Range{0x1f80, 0x1fb4, 1}, + Range{0x1fb6, 0x1fbc, 1}, + Range{0x1fbe, 0x1fbe, 1}, + Range{0x1fc2, 0x1fc4, 1}, + Range{0x1fc6, 0x1fcc, 1}, + Range{0x1fd0, 0x1fd3, 1}, + Range{0x1fd6, 0x1fdb, 1}, + Range{0x1fe0, 0x1fec, 1}, + Range{0x1ff2, 0x1ff4, 1}, + Range{0x1ff6, 0x1ffc, 1}, + Range{0x2071, 0x207f, 14}, + Range{0x2090, 0x2094, 1}, + Range{0x2102, 0x2107, 5}, + Range{0x210a, 0x2113, 1}, + Range{0x2115, 0x2115, 1}, + Range{0x2119, 0x211d, 1}, + Range{0x2124, 0x2128, 2}, + Range{0x212a, 0x212d, 1}, + Range{0x212f, 0x2139, 1}, + Range{0x213c, 0x213f, 1}, + Range{0x2145, 0x2149, 1}, + Range{0x214e, 0x214e, 1}, + Range{0x2183, 0x2184, 1}, + Range{0x2c00, 0x2c2e, 1}, + Range{0x2c30, 0x2c5e, 1}, + Range{0x2c60, 0x2c6f, 1}, + Range{0x2c71, 0x2c7d, 1}, + Range{0x2c80, 0x2ce4, 1}, + Range{0x2d00, 0x2d25, 1}, + Range{0x2d30, 0x2d65, 1}, + Range{0x2d6f, 0x2d6f, 1}, + Range{0x2d80, 0x2d96, 1}, + Range{0x2da0, 0x2da6, 1}, + Range{0x2da8, 0x2dae, 1}, + Range{0x2db0, 0x2db6, 1}, + Range{0x2db8, 0x2dbe, 1}, + Range{0x2dc0, 0x2dc6, 1}, + Range{0x2dc8, 0x2dce, 1}, + Range{0x2dd0, 0x2dd6, 1}, + Range{0x2dd8, 0x2dde, 1}, + Range{0x2e2f, 0x2e2f, 1}, + Range{0x3005, 0x3006, 1}, + Range{0x3031, 0x3035, 1}, + Range{0x303b, 0x303c, 1}, + Range{0x3041, 0x3096, 1}, + Range{0x309d, 0x309f, 1}, + Range{0x30a1, 0x30fa, 1}, + Range{0x30fc, 0x30ff, 1}, + Range{0x3105, 0x312d, 1}, + Range{0x3131, 0x318e, 1}, + Range{0x31a0, 0x31b7, 1}, + Range{0x31f0, 0x31ff, 1}, + Range{0x3400, 0x4db5, 1}, + Range{0x4e00, 0x9fc3, 1}, + Range{0xa000, 0xa48c, 1}, + Range{0xa500, 0xa60c, 1}, + Range{0xa610, 0xa61f, 1}, + Range{0xa62a, 0xa62b, 1}, + Range{0xa640, 0xa65f, 1}, + Range{0xa662, 0xa66e, 1}, + Range{0xa67f, 0xa697, 1}, + Range{0xa717, 0xa71f, 1}, + Range{0xa722, 0xa788, 1}, + Range{0xa78b, 0xa78c, 1}, + Range{0xa7fb, 0xa801, 1}, + Range{0xa803, 0xa805, 1}, + Range{0xa807, 0xa80a, 1}, + Range{0xa80c, 0xa822, 1}, + Range{0xa840, 0xa873, 1}, + Range{0xa882, 0xa8b3, 1}, + Range{0xa90a, 0xa925, 1}, + Range{0xa930, 0xa946, 1}, + Range{0xaa00, 0xaa28, 1}, + Range{0xaa40, 0xaa42, 1}, + Range{0xaa44, 0xaa4b, 1}, + Range{0xac00, 0xd7a3, 1}, + Range{0xf900, 0xfa2d, 1}, + Range{0xfa30, 0xfa6a, 1}, + Range{0xfa70, 0xfad9, 1}, + Range{0xfb00, 0xfb06, 1}, + Range{0xfb13, 0xfb17, 1}, + Range{0xfb1d, 0xfb1d, 1}, + Range{0xfb1f, 0xfb28, 1}, + Range{0xfb2a, 0xfb36, 1}, + Range{0xfb38, 0xfb3c, 1}, + Range{0xfb3e, 0xfb3e, 1}, + Range{0xfb40, 0xfb41, 1}, + Range{0xfb43, 0xfb44, 1}, + Range{0xfb46, 0xfbb1, 1}, + Range{0xfbd3, 0xfd3d, 1}, + Range{0xfd50, 0xfd8f, 1}, + Range{0xfd92, 0xfdc7, 1}, + Range{0xfdf0, 0xfdfb, 1}, + Range{0xfe70, 0xfe74, 1}, + Range{0xfe76, 0xfefc, 1}, + Range{0xff21, 0xff3a, 1}, + Range{0xff41, 0xff5a, 1}, + Range{0xff66, 0xffbe, 1}, + Range{0xffc2, 0xffc7, 1}, + Range{0xffca, 0xffcf, 1}, + Range{0xffd2, 0xffd7, 1}, + Range{0xffda, 0xffdc, 1}, + Range{0x10000, 0x1000b, 1}, + Range{0x1000d, 0x10026, 1}, + Range{0x10028, 0x1003a, 1}, + Range{0x1003c, 0x1003d, 1}, + Range{0x1003f, 0x1004d, 1}, + Range{0x10050, 0x1005d, 1}, + Range{0x10080, 0x100fa, 1}, + Range{0x10280, 0x1029c, 1}, + Range{0x102a0, 0x102d0, 1}, + Range{0x10300, 0x1031e, 1}, + Range{0x10330, 0x10340, 1}, + Range{0x10342, 0x10349, 1}, + Range{0x10380, 0x1039d, 1}, + Range{0x103a0, 0x103c3, 1}, + Range{0x103c8, 0x103cf, 1}, + Range{0x10400, 0x1049d, 1}, + Range{0x10800, 0x10805, 1}, + Range{0x10808, 0x10808, 1}, + Range{0x1080a, 0x10835, 1}, + Range{0x10837, 0x10838, 1}, + Range{0x1083c, 0x1083f, 3}, + Range{0x10900, 0x10915, 1}, + Range{0x10920, 0x10939, 1}, + Range{0x10a00, 0x10a00, 1}, + Range{0x10a10, 0x10a13, 1}, + Range{0x10a15, 0x10a17, 1}, + Range{0x10a19, 0x10a33, 1}, + Range{0x12000, 0x1236e, 1}, + Range{0x1d400, 0x1d454, 1}, + Range{0x1d456, 0x1d49c, 1}, + Range{0x1d49e, 0x1d49f, 1}, + Range{0x1d4a2, 0x1d4a2, 1}, + Range{0x1d4a5, 0x1d4a6, 1}, + Range{0x1d4a9, 0x1d4ac, 1}, + Range{0x1d4ae, 0x1d4b9, 1}, + Range{0x1d4bb, 0x1d4bb, 1}, + Range{0x1d4bd, 0x1d4c3, 1}, + Range{0x1d4c5, 0x1d505, 1}, + Range{0x1d507, 0x1d50a, 1}, + Range{0x1d50d, 0x1d514, 1}, + Range{0x1d516, 0x1d51c, 1}, + Range{0x1d51e, 0x1d539, 1}, + Range{0x1d53b, 0x1d53e, 1}, + Range{0x1d540, 0x1d544, 1}, + Range{0x1d546, 0x1d546, 1}, + Range{0x1d54a, 0x1d550, 1}, + Range{0x1d552, 0x1d6a5, 1}, + Range{0x1d6a8, 0x1d6c0, 1}, + Range{0x1d6c2, 0x1d6da, 1}, + Range{0x1d6dc, 0x1d6fa, 1}, + Range{0x1d6fc, 0x1d714, 1}, + Range{0x1d716, 0x1d734, 1}, + Range{0x1d736, 0x1d74e, 1}, + Range{0x1d750, 0x1d76e, 1}, + Range{0x1d770, 0x1d788, 1}, + Range{0x1d78a, 0x1d7a8, 1}, + Range{0x1d7aa, 0x1d7c2, 1}, + Range{0x1d7c4, 0x1d7cb, 1}, + Range{0x20000, 0x2a6d6, 1}, + Range{0x2f800, 0x2fa1d, 1}, +} + +// Is tests whether rune is in the specified table of ranges. +func Is(ranges []Range, rune int) bool { + // common case: rune is ASCII or Latin-1 + if rune < 0x100 { + for i := 0; i < len(ranges); i++ { + r := ranges[i]; + if rune > r.Hi { + continue; + } + if rune < r.Lo { + return false; + } + return (rune - r.Lo) % r.Stride == 0; + } + return false; + } + + // binary search over ranges + lo := 0; + hi := len(ranges); + for lo < hi { + m := lo + (hi - lo)/2; + r := ranges[m]; + if r.Lo <= rune && rune <= r.Hi { + return (rune - r.Lo) % r.Stride == 0; + } + if rune < r.Lo { + hi = m; + } else { + lo = m+1; + } + } + return false; +} + +// IsLetter reports whether the rune is an upper case letter. +func IsUpper(rune int) bool { + return Is(Upper, rune); +} + +// IsLetter reports whether the rune is a letter. +func IsLetter(rune int) bool { + return Is(Letter, rune); +} + diff --git a/src/pkg/unicode/letter_test.go b/src/pkg/unicode/letter_test.go new file mode 100644 index 000000000..d39d74e6b --- /dev/null +++ b/src/pkg/unicode/letter_test.go @@ -0,0 +1,129 @@ +// Copyright 2009 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 unicode + +import ( + "testing"; + "unicode"; +) + +var upper = []int{ + 0x41, + 0xc0, + 0xd8, + 0x100, + 0x139, + 0x14a, + 0x178, + 0x181, + 0x376, + 0x3cf, + 0x1f2a, + 0x2102, + 0x2c00, + 0x2c10, + 0x2c20, + 0xa650, + 0xa722, + 0xff3a, + 0x10400, + 0x1d400, + 0x1d7ca, +} + +var notupper = []int{ + 0x40, + 0x5b, + 0x61, + 0x185, + 0x1b0, + 0x377, + 0x387, + 0x2150, + 0xffff, + 0x10000, +} + +var letter = []int{ + 0x41, + 0x61, + 0xaa, + 0xba, + 0xc8, + 0xdb, + 0xf9, + 0x2ec, + 0x535, + 0x6e6, + 0x93d, + 0xa15, + 0xb99, + 0xdc0, + 0xedd, + 0x1000, + 0x1200, + 0x1312, + 0x1401, + 0x1885, + 0x2c00, + 0xa800, + 0xf900, + 0xfa30, + 0xffda, + 0xffdc, + 0x10000, + 0x10300, + 0x10400, + 0x20000, + 0x2f800, + 0x2fa1d, +} + +var notletter = []int{ + 0x20, + 0x35, + 0x375, + 0x620, + 0x700, + 0xfffe, + 0x1ffff, + 0x10ffff, +} + +func TestIsLetter(t *testing.T) { + for i, r := range(upper) { + if !IsLetter(r) { + t.Errorf("IsLetter(%#x) = false, want true\n", r); + } + } + for i, r := range(letter) { + if !IsLetter(r) { + t.Errorf("IsLetter(%#x) = false, want true\n", r); + } + } + for i, r := range(notletter) { + if IsLetter(r) { + t.Errorf("IsLetter(%#x) = true, want false\n", r); + } + } +} + +func TestIsUpper(t *testing.T) { + for i, r := range(upper) { + if !IsUpper(r) { + t.Errorf("IsUpper(%#x) = false, want true\n", r); + } + } + for i, r := range(notupper) { + if IsUpper(r) { + t.Errorf("IsUpper(%#x) = true, want false\n", r); + } + } + for i, r := range(notletter) { + if IsUpper(r) { + t.Errorf("IsUpper(%#x) = true, want false\n", r); + } + } +} diff --git a/src/pkg/unsafe/unsafe.go b/src/pkg/unsafe/unsafe.go new file mode 100644 index 000000000..b19af405b --- /dev/null +++ b/src/pkg/unsafe/unsafe.go @@ -0,0 +1,44 @@ +// Copyright 2009 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. + +/* + The unsafe package contains operations that step around the type safety of Go programs. + */ +package unsafe + +// ArbitraryType is here for the purposes of documentation only and is not actually +// part of the unsafe package. It represents the type of an arbitrary Go expression. +type ArbitraryType int + +// Pointer represents a pointer to an arbitrary type. There are three special operations +// available for type Pointer that are not available for other types. +// 1) A pointer value of any type can be converted to a Pointer. +// 2) A uintptr can be converted to a Pointer. +// 3) A Pointer can be converted to a uintptr. +// Pointer therefore allows a program to defeat the type system and read and write +// arbitrary memory. It should be used with extreme care. +type Pointer *ArbitraryType + +// Sizeof returns the size in bytes occupied by the value v. The size is that of the +// "top level" of the value only. For instance, if v is a slice, it returns the size of +// the slice descriptor, not the size of the memory referenced by the slice. +func Sizeof(v ArbitraryType) int + +// Offsetof returns the offset within the struct of the field represented by v, +// which must be of the form struct_value.field. In other words, it returns the +// number of bytes between the start of the struct and the start of the field. +func Offsetof(v ArbitraryType) int + +// Alignof returns the alignment of the value v. It is the minimum value m such +// that the address of a variable with the type of v will always always be zero mod m. +// If v is of the form obj.f, it returns the alignment of field f within struct object obj. +func Alignof(v ArbitraryType) int + +// Reflect unpacks an interface value into its internal value word and its type string. +// The boolean indir is true if the value is a pointer to the real value. +func Reflect(i interface {}) (value uint64, typestring string, indir bool) + +// Unreflect inverts Reflect: Given a value word, a type string, and the indirect bit, +// it returns an empty interface value with those contents. +func Unreflect(value uint64, typestring string, indir bool) (ret interface {}) diff --git a/src/pkg/utf8/Makefile b/src/pkg/utf8/Makefile new file mode 100644 index 000000000..b5ad083b2 --- /dev/null +++ b/src/pkg/utf8/Makefile @@ -0,0 +1,60 @@ +# Copyright 2009 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. + +# DO NOT EDIT. Automatically generated by gobuild. +# gobuild -m >Makefile + +D= + +include $(GOROOT)/src/Make.$(GOARCH) +AR=gopack + +default: packages + +clean: + rm -rf *.[$(OS)] *.a [$(OS)].out _obj + +test: packages + gotest + +coverage: packages + gotest + 6cov -g `pwd` | grep -v '_test\.go:' + +%.$O: %.go + $(GC) -I_obj $*.go + +%.$O: %.c + $(CC) $*.c + +%.$O: %.s + $(AS) $*.s + +O1=\ + utf8.$O\ + + +phases: a1 +_obj$D/utf8.a: phases + +a1: $(O1) + $(AR) grc _obj$D/utf8.a utf8.$O + rm -f $(O1) + + +newpkg: clean + mkdir -p _obj$D + $(AR) grc _obj$D/utf8.a + +$(O1): newpkg +$(O2): a1 + +nuke: clean + rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/utf8.a + +packages: _obj$D/utf8.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D + cp _obj$D/utf8.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/utf8.a diff --git a/src/pkg/utf8/utf8.go b/src/pkg/utf8/utf8.go new file mode 100644 index 000000000..9c2ac790d --- /dev/null +++ b/src/pkg/utf8/utf8.go @@ -0,0 +1,291 @@ +// Copyright 2009 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. + +// Functions and constants to support text encoded in UTF-8. +// This package calls a Unicode character a rune for brevity. +package utf8 + +// Numbers fundamental to the encoding. +const ( + RuneError = 0xFFFD; // the "error" Rune or "replacement character". + RuneSelf = 0x80; // characters below Runeself are represented as themselves in a single byte. + RuneMax = 0x10FFFF; // maximum Unicode code point. + UTFMax = 4; // maximum number of bytes of a UTF-8 encoded Unicode character. +) + +const ( + _T1 = 0x00; // 0000 0000 + _Tx = 0x80; // 1000 0000 + _T2 = 0xC0; // 1100 0000 + _T3 = 0xE0; // 1110 0000 + _T4 = 0xF0; // 1111 0000 + _T5 = 0xF8; // 1111 1000 + + _Maskx = 0x3F; // 0011 1111 + _Mask2 = 0x1F; // 0001 1111 + _Mask3 = 0x0F; // 0000 1111 + _Mask4 = 0x07; // 0000 0111 + + _Rune1Max = 1<<7 - 1; + _Rune2Max = 1<<11 - 1; + _Rune3Max = 1<<16 - 1; + _Rune4Max = 1<<21 - 1; +) + +func decodeRuneInternal(p []byte) (rune, size int, short bool) { + n := len(p); + if n < 1 { + return RuneError, 0, true; + } + c0 := p[0]; + + // 1-byte, 7-bit sequence? + if c0 < _Tx { + return int(c0), 1, false + } + + // unexpected continuation byte? + if c0 < _T2 { + return RuneError, 1, false + } + + // need first continuation byte + if n < 2 { + return RuneError, 1, true + } + c1 := p[1]; + if c1 < _Tx || _T2 <= c1 { + return RuneError, 1, false + } + + // 2-byte, 11-bit sequence? + if c0 < _T3 { + rune = int(c0&_Mask2)<<6 | int(c1&_Maskx); + if rune <= _Rune1Max { + return RuneError, 1, false + } + return rune, 2, false + } + + // need second continuation byte + if n < 3 { + return RuneError, 1, true + } + c2 := p[2]; + if c2 < _Tx || _T2 <= c2 { + return RuneError, 1, false + } + + // 3-byte, 16-bit sequence? + if c0 < _T4 { + rune = int(c0&_Mask3)<<12 | int(c1&_Maskx)<<6 | int(c2&_Maskx); + if rune <= _Rune2Max { + return RuneError, 1, false + } + return rune, 3, false + } + + // need third continuation byte + if n < 4 { + return RuneError, 1, true + } + c3 := p[3]; + if c3 < _Tx || _T2 <= c3 { + return RuneError, 1, false + } + + // 4-byte, 21-bit sequence? + if c0 < _T5 { + rune = int(c0&_Mask4)<<18 | int(c1&_Maskx)<<12 | int(c2&_Maskx)<<6 | int(c3&_Maskx); + if rune <= _Rune3Max { + return RuneError, 1, false + } + return rune, 4, false + } + + // error + return RuneError, 1, false +} + +func decodeRuneInStringInternal(s string) (rune, size int, short bool) { + n := len(s); + if n < 1 { + return RuneError, 0, true; + } + c0 := s[0]; + + // 1-byte, 7-bit sequence? + if c0 < _Tx { + return int(c0), 1, false + } + + // unexpected continuation byte? + if c0 < _T2 { + return RuneError, 1, false + } + + // need first continuation byte + if n < 2 { + return RuneError, 1, true + } + c1 := s[1]; + if c1 < _Tx || _T2 <= c1 { + return RuneError, 1, false + } + + // 2-byte, 11-bit sequence? + if c0 < _T3 { + rune = int(c0&_Mask2)<<6 | int(c1&_Maskx); + if rune <= _Rune1Max { + return RuneError, 1, false + } + return rune, 2, false + } + + // need second continuation byte + if n < 3 { + return RuneError, 1, true + } + c2 := s[2]; + if c2 < _Tx || _T2 <= c2 { + return RuneError, 1, false + } + + // 3-byte, 16-bit sequence? + if c0 < _T4 { + rune = int(c0&_Mask3)<<12 | int(c1&_Maskx)<<6 | int(c2&_Maskx); + if rune <= _Rune2Max { + return RuneError, 1, false + } + return rune, 3, false + } + + // need third continuation byte + if n < 4 { + return RuneError, 1, true + } + c3 := s[3]; + if c3 < _Tx || _T2 <= c3 { + return RuneError, 1, false + } + + // 4-byte, 21-bit sequence? + if c0 < _T5 { + rune = int(c0&_Mask4)<<18 | int(c1&_Maskx)<<12 | int(c2&_Maskx)<<6 | int(c3&_Maskx); + if rune <= _Rune3Max { + return RuneError, 1, false + } + return rune, 4, false + } + + // error + return RuneError, 1, false +} + +// FullRune reports whether the bytes in p begin with a full UTF-8 encoding of a rune. +// An invalid encoding is considered a full Rune since it will convert as a width-1 error rune. +func FullRune(p []byte) bool { + rune, size, short := decodeRuneInternal(p); + return !short +} + +// FullRuneInString is like FullRune but its input is a string. +func FullRuneInString(s string) bool { + rune, size, short := decodeRuneInStringInternal(s); + return !short +} + +// DecodeRune unpacks the first UTF-8 encoding in p and returns the rune and its width in bytes. +func DecodeRune(p []byte) (rune, size int) { + var short bool; + rune, size, short = decodeRuneInternal(p); + return; +} + +// DecodeRuneInString is like DecodeRune but its input is a string. +func DecodeRuneInString(s string) (rune, size int) { + var short bool; + rune, size, short = decodeRuneInStringInternal(s); + return; +} + +// RuneLen returns the number of bytes required to encode the rune. +func RuneLen(rune int) int { + switch { + case rune <= _Rune1Max: + return 1; + case rune <= _Rune2Max: + return 2; + case rune <= _Rune3Max: + return 3; + case rune <= _Rune4Max: + return 4; + } + return -1; +} + +// EncodeRune writes into p (which must be large enough) the UTF-8 encoding of the rune. +// It returns the number of bytes written. +func EncodeRune(rune int, p []byte) int { + if rune <= _Rune1Max { + p[0] = byte(rune); + return 1; + } + + if rune <= _Rune2Max { + p[0] = _T2 | byte(rune>>6); + p[1] = _Tx | byte(rune)&_Maskx; + return 2; + } + + if rune > RuneMax { + rune = RuneError + } + + if rune <= _Rune3Max { + p[0] = _T3 | byte(rune>>12); + p[1] = _Tx | byte(rune>>6)&_Maskx; + p[2] = _Tx | byte(rune)&_Maskx; + return 3; + } + + p[0] = _T4 | byte(rune>>18); + p[1] = _Tx | byte(rune>>12)&_Maskx; + p[2] = _Tx | byte(rune>>6)&_Maskx; + p[3] = _Tx | byte(rune)&_Maskx; + return 4; +} + +// RuneCount returns the number of runes in p. Erroneous and short +// encodings are treated as single runes of width 1 byte. +func RuneCount(p []byte) int { + i := 0; + var n int; + for n = 0; i < len(p); n++ { + if p[i] < RuneSelf { + i++; + } else { + rune, size := DecodeRune(p[i:len(p)]); + i += size; + } + } + return n; +} + +// RuneCountInString is like RuneCount but its input is a string. +func RuneCountInString(s string) int { + ei := len(s); + i := 0; + n := 0; + for n = 0; i < ei; n++ { + if s[i] < RuneSelf { + i++; + } else { + rune, size, short := decodeRuneInStringInternal(s[i:ei]); + i += size; + } + } + return n; +} + diff --git a/src/pkg/utf8/utf8_test.go b/src/pkg/utf8/utf8_test.go new file mode 100644 index 000000000..f60b0b17e --- /dev/null +++ b/src/pkg/utf8/utf8_test.go @@ -0,0 +1,168 @@ +// Copyright 2009 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 utf8 + +import ( + "bytes"; + "fmt"; + "io"; + "testing"; + "utf8"; +) + +type Utf8Map struct { + rune int; + str string; +} + +var utf8map = []Utf8Map { + Utf8Map{ 0x0000, "\x00" }, + Utf8Map{ 0x0001, "\x01" }, + Utf8Map{ 0x007e, "\x7e" }, + Utf8Map{ 0x007f, "\x7f" }, + Utf8Map{ 0x0080, "\xc2\x80" }, + Utf8Map{ 0x0081, "\xc2\x81" }, + Utf8Map{ 0x00bf, "\xc2\xbf" }, + Utf8Map{ 0x00c0, "\xc3\x80" }, + Utf8Map{ 0x00c1, "\xc3\x81" }, + Utf8Map{ 0x00c8, "\xc3\x88" }, + Utf8Map{ 0x00d0, "\xc3\x90" }, + Utf8Map{ 0x00e0, "\xc3\xa0" }, + Utf8Map{ 0x00f0, "\xc3\xb0" }, + Utf8Map{ 0x00f8, "\xc3\xb8" }, + Utf8Map{ 0x00ff, "\xc3\xbf" }, + Utf8Map{ 0x0100, "\xc4\x80" }, + Utf8Map{ 0x07ff, "\xdf\xbf" }, + Utf8Map{ 0x0800, "\xe0\xa0\x80" }, + Utf8Map{ 0x0801, "\xe0\xa0\x81" }, + Utf8Map{ 0xfffe, "\xef\xbf\xbe" }, + Utf8Map{ 0xffff, "\xef\xbf\xbf" }, + Utf8Map{ 0x10000, "\xf0\x90\x80\x80" }, + Utf8Map{ 0x10001, "\xf0\x90\x80\x81" }, + Utf8Map{ 0x10fffe, "\xf4\x8f\xbf\xbe" }, + Utf8Map{ 0x10ffff, "\xf4\x8f\xbf\xbf" }, +} + +// io.StringBytes with one extra byte at end +func makeBytes(s string) []byte { + s += "\x00"; + b := io.StringBytes(s); + return b[0:len(s)-1]; +} + +func TestFullRune(t *testing.T) { + for i := 0; i < len(utf8map); i++ { + m := utf8map[i]; + b := makeBytes(m.str); + if !utf8.FullRune(b) { + t.Errorf("FullRune(%q) (rune %04x) = false, want true", b, m.rune); + } + s := m.str; + if !utf8.FullRuneInString(s) { + t.Errorf("FullRuneInString(%q) (rune %04x) = false, want true", s, m.rune); + } + b1 := b[0:len(b)-1]; + if utf8.FullRune(b1) { + t.Errorf("FullRune(%q) = true, want false", b1); + } + s1 := string(b1); + if utf8.FullRuneInString(s1) { + t.Errorf("FullRune(%q) = true, want false", s1); + } + } +} + +func TestEncodeRune(t *testing.T) { + for i := 0; i < len(utf8map); i++ { + m := utf8map[i]; + b := makeBytes(m.str); + var buf [10]byte; + n := utf8.EncodeRune(m.rune, &buf); + b1 := buf[0:n]; + if !bytes.Equal(b, b1) { + t.Errorf("EncodeRune(0x%04x) = %q want %q", m.rune, b1, b); + } + } +} + +func TestDecodeRune(t *testing.T) { + for i := 0; i < len(utf8map); i++ { + m := utf8map[i]; + b := makeBytes(m.str); + rune, size := utf8.DecodeRune(b); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b, rune, size, m.rune, len(b)); + } + s := m.str; + rune, size = utf8.DecodeRuneInString(s); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", s, rune, size, m.rune, len(b)); + } + + // there's an extra byte that bytes left behind - make sure trailing byte works + rune, size = utf8.DecodeRune(b[0:cap(b)]); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b, rune, size, m.rune, len(b)); + } + s = m.str+"\x00"; + rune, size = utf8.DecodeRuneInString(s); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRuneInString(%q) = 0x%04x, %d want 0x%04x, %d", s, rune, size, m.rune, len(b)); + } + + // make sure missing bytes fail + wantsize := 1; + if wantsize >= len(b) { + wantsize = 0; + } + rune, size = utf8.DecodeRune(b[0:len(b)-1]); + if rune != RuneError || size != wantsize { + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b[0:len(b)-1], rune, size, RuneError, wantsize); + } + s = m.str[0:len(m.str)-1]; + rune, size = utf8.DecodeRuneInString(s); + if rune != RuneError || size != wantsize { + t.Errorf("DecodeRuneInString(%q) = 0x%04x, %d want 0x%04x, %d", s, rune, size, RuneError, wantsize); + } + + // make sure bad sequences fail + if len(b) == 1 { + b[0] = 0x80; + } else { + b[len(b)-1] = 0x7F; + } + rune, size = utf8.DecodeRune(b); + if rune != RuneError || size != 1 { + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b, rune, size, RuneError, 1); + } + s = string(b); + rune, size = utf8.DecodeRune(b); + if rune != RuneError || size != 1 { + t.Errorf("DecodeRuneInString(%q) = 0x%04x, %d want 0x%04x, %d", s, rune, size, RuneError, 1); + } + } +} + +type RuneCountTest struct { + in string; + out int; +} +var runecounttests = []RuneCountTest { + RuneCountTest{ "abcd", 4 }, + RuneCountTest{ "☺☻☹", 3 }, + RuneCountTest{ "1,2,3,4", 7 }, + RuneCountTest{ "\xe2\x00", 2 }, +} +func TestRuneCount(t *testing.T) { + for i := 0; i < len(runecounttests); i++ { + tt := runecounttests[i]; + if out := utf8.RuneCountInString(tt.in); out != tt.out { + t.Errorf("RuneCountInString(%q) = %d, want %d", tt.in, out, tt.out); + } + if out := utf8.RuneCount(makeBytes(tt.in)); out != tt.out { + t.Errorf("RuneCount(%q) = %d, want %d", tt.in, out, tt.out); + } + } +} diff --git a/src/pkg/xml/xml.go b/src/pkg/xml/xml.go new file mode 100644 index 000000000..bd944337e --- /dev/null +++ b/src/pkg/xml/xml.go @@ -0,0 +1,426 @@ +// Copyright 2009 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. + +// NOTE(rsc): Actually, this package is just a description +// of an implementation that hasn't been written yet. + +// This package implements an XML parser but relies on +// clients to implement the parsing actions. + +// An XML document is a single XML element. +// +// An XML element is either a start tag and an end tag, +// like <tag>...</tag>, or a combined start/end tag <tag/>. +// The latter is identical in semantics to <tag></tag>, +// and this parser does not distinguish them. +// +// The start (or combined start/end) tag can have +// name="value" attributes inside the angle brackets after +// the tag name, as in <img src="http://google.com/icon.png" alt="Google">. +// Names are drawn from a fixed set of alphabetic letters; +// Values are strings quoted with single or double quotes. +// +// An element made up of distinct start and end tags can +// contain free-form text and other elements inside it, +// as in <a href="http://www.google.com">Google</a> +// or <b><a href="http://www.google.com">Google</a></b>. +// The former is an <a> element with the text "Google" inside it. +// The latter is a <b> element with that <a> element inside it. +// In general, an element can contain a sequence of elements +// and text inside it. In XML, white space inside an element is +// always counted as text--it is never discarded by the parser. +// XML parsers do translate \r and \r\n into \n in text. +// +// This parser reads an XML document and calls methods on a +// Builder interface object in response to the text. +// It calls the builder's StartElement, Text, and EndElement +// methods, mimicking the structure of the text. +// For example, the simple XML document: +// +// <a href="http://www.google.com"> +// <img src="http://www.google.com/icon.png" alt="Google" /> +// <br/></a> +// +// results in the following sequence of builder calls: +// +// StartElement("a", []Attr(Attr("href", "http://www.google.com"))); +// Text("\n\t"); +// StartElement("img", []Attr(Attr("src", "http://www.google.com/icon.png"), +// Attr("alt", "Google"))); +// EndElement("img"); +// Text("\n"); +// StartElement("br", []Attr()); +// EndElement("br"); +// EndElement("a"); +// +// There are, of course, a few more details, but the story so far +// should be enough for the majority of uses. The details are: +// +// * XML documents typically begin with an XML declaration line like +// <?xml version="1.0" encoding="UTF-8"?>. +// This line is strongly recommended, but not strictly required. +// It introduces the XML version and text encoding for the rest +// of the file. XML parsers are required to recognize UTF-8 and +// UTF-16. This parser only recognizes UTF-8 (for now?). +// +// * After the XML declaration comes an optional doctype declaration like +// <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" +// "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +// The parser should pass this information on to the client in some +// form, but does not. It discards such lines. +// +// * The XML declaration line is an instance of a more general tag +// called a processing instruction, XML's #pragma. The general form is +// <?target text?>, where target is a name (like "xml") specifying +// the intended recipient of the instruction, and text is the +// instruction itself. This XML parser keeps the <?xml ...?> declaration +// to itself but passes along other processing instructions using +// the ProcInst method. Processing instructions can appear anywhere +// in an XML document. Most clients will simply ignore them. +// +// * An XML comment can appear anywhere in an XML document. +// Comments have the form <!--text-->. The XML parser passes +// them along by calling the Comment method. Again, most clients +// will simply ignore them. +// +// * Text inside an XML element must be escaped to avoid looking like +// a start/end tag. Specifically, the characters < and & must be +// written as < and &. An alternate quoting mechanism is to +// use the construct <![CDATA[...]]>. The quoted text ... can contain +// < characters, but not the sequence ]]>. Ampersands must still be +// escaped. For some reason, the existence of the CDATA quoting mechanism +// infects the processing of ordinary unquoted text, which is not allowed +// to contain the literal sequence ]]>. Instead, it would be written +// escaped, as in ]]>. The parser hides all these considerations +// from the library client -- it reports all text, regardless of original +// form and already unescaped, using the Text method. +// +// * A revision to XML 1.0 introduced the concept of name spaces +// for attribute and tag names. A start tag with an attribute +// xmlns:prefix="URL" introduces `prefix' as a shorthand +// for the name space whose identifier is URL. Inside the element +// with that start tag, an element name or attribute prefix:foo +// (as in <prefix:foo prefix:bar="baz">) is understood to refer +// to name `foo' in the name space denoted by `URL'. Although +// this is a shorthand, there is no canonical expansion. Thus: +// +// <tag xmlns:foo="http://google.com/foo" xmlns:bar="http://google.com/bar"> +// <foo:red bar:attr="value">text1</foo:red> +// <bar:red>text2</bar:red> +// </tag> +// +// and +// +// <tag xmlns:bar="http://google.com/foo" xmlns:foo="http://google.com/bar"> +// <bar:red foo:attr="value">text1</bar:red> +// <foo:red>text2</foo:red> +// </tag> +// +// are equivalent XML documents, and there is no canonical form. +// +// The special attribute xmlns="URL" sets the default name space +// for unprefixed tags (but not attribute names) to URL. +// Thus: +// +// <tag xmlns="http://google.com/foo" xmlns:bar="http://google.com/bar"> +// <red bar:attr="value">text1</red> +// <bar:red>text2</bar:red> +// </tag> +// +// is another XML document equivalent to the first two, and +// +// <tag xmlns:bar="http://google.com/foo" xmlns="http://google.com/bar"> +// <bar:red attr="value">text1</bar:red> +// <red>text2</red> +// </tag> +// +// would be equivalent, except that `attr' in attr="value" has no +// associated name space, in contrast to the previous three where it +// is in the http://google.com/bar name space. +// +// The XML parser hides these details from the client by passing +// a Name struct (ns + name pair) for tag and attribute names. +// Tags and attributes without a name space have ns == "". +// +// References: +// Annotated XML spec: http://www.xml.com/axml/testaxml.htm +// XML name spaces: http://www.w3.org/TR/REC-xml-names/ + +package xml + +import ( + "io"; + "os"; +) + +// XML name, annotated with name space URL +type Name struct { + ns, name string; +} + +// XML attribute (name=value). +type Attr struct { + name Name; + value string; +} + +// XML Builder - methods client provides to Parser. +// Parser calls methods on builder as it reads and parses XML. +// If a builder method returns an error, the parse stops. +type Builder interface { + // Called when an element starts. + // Attr is list of attributes given in the tag. + // <name attr.name=attr.value attr1.name=attr1.value ...> + // <name attr.name=attr.value attr1.name=attr1.value ... /> + // xmlns and xmlns:foo attributes are handled internally + // and not passed through to StartElement. + StartElement(name Name, attr []Attr) os.Error; + + // Called when an element ends. + // </name> + // <name ... /> + EndElement(name Name) os.Error; + + // Called for non-empty character data string inside element. + // Can be called multiple times between elements. + // text + // <![CDATA[text]]> + Text(text []byte) os.Error; + + // Called when a comment is found in the XML. + // <!-- text --> + Comment(text []byte) os.Error; + + // Called for a processing instruction + // <?target text?> + ProcInst(target string, text []byte) os.Error; +} + +// Default builder. Implements no-op Builder methods. +// Embed this in your own Builders to handle the calls +// you don't care about (e.g., Comment, ProcInst). +type BaseBuilder struct { +} + +func (b *BaseBuilder) StartElement(name Name, attr []Attr) os.Error { + return nil; +} + +func (b *BaseBuilder) EndElement(name Name) os.Error { + return nil; +} + +func (b *BaseBuilder) Text(text []byte) os.Error { + return nil; +} + +func (b *BaseBuilder) Comment(text []byte) os.Error { + return nil; +} + +func (b *BaseBuilder) ProcInst(target string, text []byte) os.Error { + return nil; +} + +// XML Parser. Calls Builder methods as it parses. +func Parse(r io.Read, b Builder) os.Error { + return os.NewError("unimplemented"); +} + +// Channel interface to XML parser: create a new channel, +// go ParseTokens(r, c), and then read from the channel +// until TokenEnd. This variant has the benefit that +// the process reading the channel can be a recursive +// function instead of a set of callbacks, but it has the +// drawback that the channel interface cannot signal an +// error to cause the parser to stop early. + +// An XML parsing token. +const ( + TokenStartElement = 1 + iota; + TokenEndElement; + TokenText; + TokenComment; + TokenProcInst; + TokenEnd; +) + +type Token struct { + Kind int; // TokenStartElement, TokenEndElement, etc. + Name Name; // name (TokenStartElement, TokenEndElement) + Attr []Attr; // attributes (TokenStartElement) + Target string; // target (TokenProcessingInstruction) + Text []byte; // text (TokenCharData, TokenComment, etc.) + Err os.Error; // error (TokenEnd) +} + +type ChanBuilder chan Token; + +func (c ChanBuilder) StartElement(name Name, attr []Attr) os.Error { + var t Token; + t.Kind = TokenStartElement; + t.Name = name; + t.Attr = attr; + c <- t; + return nil; +} + +func (c ChanBuilder) EndElement(name Name) os.Error { + var t Token; + t.Kind = TokenEndElement; + t.Name = name; + c <- t; + return nil; +} + +func (c ChanBuilder) Text(text []byte) os.Error { + var t Token; + t.Kind = TokenText; + t.Text = text; + c <- t; + return nil; +} + +func (c ChanBuilder) Comment(text []byte) os.Error { + var t Token; + t.Kind = TokenComment; + t.Text = text; + c <- t; + return nil; +} + +func (c ChanBuilder) ProcInst(target string, text []byte) os.Error { + var t Token; + t.Kind = TokenProcInst; + t.Target = target; + t.Text = text; + c <- t; + return nil; +} + +func ParseToChan(r io.Read, c chan Token) { + var t Token; + t.Kind = TokenEnd; + t.Err = Parse(r, ChanBuilder(c)); + c <- t; +} + + +// scribbled notes based on XML spec. + +// document is +// xml decl? +// doctype decl? +// element +// +// if xml decl is present, must be first. after that, +// can have comments and procinsts scattered throughout, +// even after the element is done. +// +// xml decl is: +// +// <\?xml version='[a-zA-Z0-9_.:\-]+'( encoding='[A-Za-z][A-Za-z0-9._\-]*')? +// ( standalone='(yes|no)')? ?\?> +// +// spaces denote [ \r\t\n]+. +// written with '' above but can use "" too. +// +// doctype decl might as well be <!DOCTYPE[^>]*> +// +// procinst is <\?name( .*?)\?>. name cannot be [Xx][Mm][Ll]. +// +// comment is <!--(.*?)-->. +// +// tags are: +// <name( attrib)* ?> start tag +// <name( attrib)* ?/> combined start/end tag +// </name ?> end tag +// (the " ?" is an optional space, not a literal question mark.) +// +// plain text is [^<&]* except cannot contain "]]>". +// can also have escaped characters: +// &#[0-9]+; +// &#x[0-9A-Fa-f]+; +// &name; +// +// can use <![CDATA[.*?]]> to avoid escaping < characters. +// +// must rewrite \r and \r\n into \n in text. +// +// names are Unicode. valid chars listed below. +// +// attrib is name="value" or name='value'. +// can have spaces around =. +// attribute value text is [^<&"]* for appropriate ". +// can also use the &...; escape sequences above. +// cannot use <![CDATA[...]]>. +// +// xmlns attributes are name=value where name has form xmlns:name +// (i.e., xmlns:123 is not okay, because 123 is not a name; xmlns:a123 is ok). +// sub-name must not start with : either. +// +// name is first(second)*. +// +// first is +// +// 003A 04D0-04EB 0A59-0A5C 0C35-0C39 0F49-0F69 1E00-1E9B +// 0041-005A 04EE-04F5 0A5E 0C60-0C61 10A0-10C5 1EA0-1EF9 +// 005F 04F8-04F9 0A72-0A74 0C85-0C8C 10D0-10F6 1F00-1F15 +// 0061-007A 0531-0556 0A85-0A8B 0C8E-0C90 1100 1F18-1F1D +// 00C0-00D6 0559 0A8D 0C92-0CA8 1102-1103 1F20-1F45 +// 00D8-00F6 0561-0586 0A8F-0A91 0CAA-0CB3 1105-1107 1F48-1F4D +// 00F8-00FF 05D0-05EA 0A93-0AA8 0CB5-0CB9 1109 1F50-1F57 +// 0100-0131 05F0-05F2 0AAA-0AB0 0CDE 110B-110C 1F59 +// 0134-013E 0621-063A 0AB2-0AB3 0CE0-0CE1 110E-1112 1F5B +// 0141-0148 0641-064A 0AB5-0AB9 0D05-0D0C 113C 1F5D +// 014A-017E 0671-06B7 0ABD 0D0E-0D10 113E 1F5F-1F7D +// 0180-01C3 06BA-06BE 0AE0 0D12-0D28 1140 1F80-1FB4 +// 01CD-01F0 06C0-06CE 0B05-0B0C 0D2A-0D39 114C 1FB6-1FBC +// 01F4-01F5 06D0-06D3 0B0F-0B10 0D60-0D61 114E 1FBE +// 01FA-0217 06D5 0B13-0B28 0E01-0E2E 1150 1FC2-1FC4 +// 0250-02A8 06E5-06E6 0B2A-0B30 0E30 1154-1155 1FC6-1FCC +// 02BB-02C1 0905-0939 0B32-0B33 0E32-0E33 1159 1FD0-1FD3 +// 0386 093D 0B36-0B39 0E40-0E45 115F-1161 1FD6-1FDB +// 0388-038A 0958-0961 0B3D 0E81-0E82 1163 1FE0-1FEC +// 038C 0985-098C 0B5C-0B5D 0E84 1165 1FF2-1FF4 +// 038E-03A1 098F-0990 0B5F-0B61 0E87-0E88 1167 1FF6-1FFC +// 03A3-03CE 0993-09A8 0B85-0B8A 0E8A 1169 2126 +// 03D0-03D6 09AA-09B0 0B8E-0B90 0E8D 116D-116E 212A-212B +// 03DA 09B2 0B92-0B95 0E94-0E97 1172-1173 212E +// 03DC 09B6-09B9 0B99-0B9A 0E99-0E9F 1175 2180-2182 +// 03DE 09DC-09DD 0B9C 0EA1-0EA3 119E 3007 +// 03E0 09DF-09E1 0B9E-0B9F 0EA5 11A8 3021-3029 +// 03E2-03F3 09F0-09F1 0BA3-0BA4 0EA7 11AB 3041-3094 +// 0401-040C 0A05-0A0A 0BA8-0BAA 0EAA-0EAB 11AE-11AF 30A1-30FA +// 040E-044F 0A0F-0A10 0BAE-0BB5 0EAD-0EAE 11B7-11B8 3105-312C +// 0451-045C 0A13-0A28 0BB7-0BB9 0EB0 11BA 4E00-9FA5 +// 045E-0481 0A2A-0A30 0C05-0C0C 0EB2-0EB3 11BC-11C2 AC00-D7A3 +// 0490-04C4 0A32-0A33 0C0E-0C10 0EBD 11EB +// 04C7-04C8 0A35-0A36 0C12-0C28 0EC0-0EC4 11F0 +// 04CB-04CC 0A38-0A39 0C2A-0C33 0F40-0F47 11F9 +// +// second is first plus +// +// 002D 06DD-06DF 09E6-09EF 0B56-0B57 0D3E-0D43 0F3E +// 002E 06E0-06E4 0A02 0B66-0B6F 0D46-0D48 0F3F +// 0030-0039 06E7-06E8 0A3C 0B82-0B83 0D4A-0D4D 0F71-0F84 +// 00B7 06EA-06ED 0A3E 0BBE-0BC2 0D57 0F86-0F8B +// 02D0 06F0-06F9 0A3F 0BC6-0BC8 0D66-0D6F 0F90-0F95 +// 02D1 0901-0903 0A40-0A42 0BCA-0BCD 0E31 0F97 +// 0300-0345 093C 0A47-0A48 0BD7 0E34-0E3A 0F99-0FAD +// 0360-0361 093E-094C 0A4B-0A4D 0BE7-0BEF 0E46 0FB1-0FB7 +// 0387 094D 0A66-0A6F 0C01-0C03 0E47-0E4E 0FB9 +// 0483-0486 0951-0954 0A70-0A71 0C3E-0C44 0E50-0E59 20D0-20DC +// 0591-05A1 0962-0963 0A81-0A83 0C46-0C48 0EB1 20E1 +// 05A3-05B9 0966-096F 0ABC 0C4A-0C4D 0EB4-0EB9 3005 +// 05BB-05BD 0981-0983 0ABE-0AC5 0C55-0C56 0EBB-0EBC 302A-302F +// 05BF 09BC 0AC7-0AC9 0C66-0C6F 0EC6 3031-3035 +// 05C1-05C2 09BE 0ACB-0ACD 0C82-0C83 0EC8-0ECD 3099 +// 05C4 09BF 0AE6-0AEF 0CBE-0CC4 0ED0-0ED9 309A +// 0640 09C0-09C4 0B01-0B03 0CC6-0CC8 0F18-0F19 309D-309E +// 064B-0652 09C7-09C8 0B3C 0CCA-0CCD 0F20-0F29 30FC-30FE +// 0660-0669 09CB-09CD 0B3E-0B43 0CD5-0CD6 0F35 +// 0670 09D7 0B47-0B48 0CE6-0CEF 0F37 +// 06D6-06DC 09E2-09E3 0B4B-0B4D 0D02-0D03 0F39 + |