From b6708b2dad4af458e69fde7fc596ecdce03280d0 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Tue, 5 May 2009 17:05:39 -0700 Subject: directory-per-package step 1: move files from lib/X.go to lib/X/X.go no substantive changes except: - new Makefiles, all auto-generated - go/src/lib/Makefile has been extensively edited R=rsc OCL=28310 CL=28310 --- src/lib/Makefile | 167 ++--- src/lib/bignum.go | 1464 --------------------------------------- src/lib/bignum/Makefile | 68 ++ src/lib/bignum/bignum.go | 1464 +++++++++++++++++++++++++++++++++++++++ src/lib/bignum/bignum_test.go | 482 +++++++++++++ src/lib/bignum_test.go | 482 ------------- src/lib/bufio.go | 518 -------------- src/lib/bufio/Makefile | 68 ++ src/lib/bufio/bufio.go | 518 ++++++++++++++ src/lib/bufio/bufio_test.go | 379 ++++++++++ src/lib/bufio_test.go | 379 ---------- src/lib/exec.go | 228 ------ src/lib/exec/Makefile | 68 ++ src/lib/exec/exec.go | 228 ++++++ src/lib/exec/exec_test.go | 51 ++ src/lib/exec_test.go | 51 -- src/lib/exvar.go | 202 ------ src/lib/exvar/Makefile | 68 ++ src/lib/exvar/exvar.go | 202 ++++++ src/lib/exvar/exvar_test.go | 80 +++ src/lib/exvar_test.go | 80 --- src/lib/flag.go | 486 ------------- src/lib/flag/Makefile | 68 ++ src/lib/flag/flag.go | 486 +++++++++++++ src/lib/flag/flag_test.go | 77 ++ src/lib/flag_test.go | 77 -- src/lib/log.go | 194 ------ src/lib/log/Makefile | 68 ++ src/lib/log/log.go | 194 ++++++ src/lib/log/log_test.go | 82 +++ src/lib/log_test.go | 82 --- src/lib/malloc.go | 24 - src/lib/malloc/Makefile | 68 ++ src/lib/malloc/malloc.go | 24 + src/lib/once.go | 46 -- src/lib/once/Makefile | 68 ++ src/lib/once/once.go | 46 ++ src/lib/once/once_test.go | 31 + src/lib/once_test.go | 31 - src/lib/path.go | 136 ---- src/lib/path/Makefile | 68 ++ src/lib/path/path.go | 136 ++++ src/lib/path/path_test.go | 136 ++++ src/lib/path_test.go | 136 ---- src/lib/rand.go | 775 --------------------- src/lib/rand/Makefile | 68 ++ src/lib/rand/rand.go | 775 +++++++++++++++++++++ src/lib/sort.go | 187 ----- src/lib/sort/Makefile | 68 ++ src/lib/sort/sort.go | 187 +++++ src/lib/sort/sort_test.go | 229 ++++++ src/lib/sort_test.go | 229 ------ src/lib/strings.go | 119 ---- src/lib/strings/Makefile | 68 ++ src/lib/strings/strings.go | 119 ++++ src/lib/strings/strings_test.go | 81 +++ src/lib/strings_test.go | 81 --- src/lib/testing.go | 135 ---- src/lib/testing/Makefile | 68 ++ src/lib/testing/testing.go | 135 ++++ src/lib/utf8.go | 290 -------- src/lib/utf8/Makefile | 68 ++ src/lib/utf8/utf8.go | 290 ++++++++ src/lib/utf8/utf8_test.go | 179 +++++ src/lib/utf8_test.go | 179 ----- src/lib/xml.go | 426 ------------ src/lib/xml/xml.go | 426 ++++++++++++ 67 files changed, 8074 insertions(+), 7119 deletions(-) delete mode 100755 src/lib/bignum.go create mode 100644 src/lib/bignum/Makefile create mode 100755 src/lib/bignum/bignum.go create mode 100644 src/lib/bignum/bignum_test.go delete mode 100644 src/lib/bignum_test.go delete mode 100644 src/lib/bufio.go create mode 100644 src/lib/bufio/Makefile create mode 100644 src/lib/bufio/bufio.go create mode 100644 src/lib/bufio/bufio_test.go delete mode 100644 src/lib/bufio_test.go delete mode 100644 src/lib/exec.go create mode 100644 src/lib/exec/Makefile create mode 100644 src/lib/exec/exec.go create mode 100644 src/lib/exec/exec_test.go delete mode 100644 src/lib/exec_test.go delete mode 100644 src/lib/exvar.go create mode 100644 src/lib/exvar/Makefile create mode 100644 src/lib/exvar/exvar.go create mode 100644 src/lib/exvar/exvar_test.go delete mode 100644 src/lib/exvar_test.go delete mode 100644 src/lib/flag.go create mode 100644 src/lib/flag/Makefile create mode 100644 src/lib/flag/flag.go create mode 100644 src/lib/flag/flag_test.go delete mode 100644 src/lib/flag_test.go delete mode 100644 src/lib/log.go create mode 100644 src/lib/log/Makefile create mode 100644 src/lib/log/log.go create mode 100644 src/lib/log/log_test.go delete mode 100644 src/lib/log_test.go delete mode 100644 src/lib/malloc.go create mode 100644 src/lib/malloc/Makefile create mode 100644 src/lib/malloc/malloc.go delete mode 100644 src/lib/once.go create mode 100644 src/lib/once/Makefile create mode 100644 src/lib/once/once.go create mode 100644 src/lib/once/once_test.go delete mode 100644 src/lib/once_test.go delete mode 100644 src/lib/path.go create mode 100644 src/lib/path/Makefile create mode 100644 src/lib/path/path.go create mode 100644 src/lib/path/path_test.go delete mode 100644 src/lib/path_test.go delete mode 100644 src/lib/rand.go create mode 100644 src/lib/rand/Makefile create mode 100644 src/lib/rand/rand.go delete mode 100644 src/lib/sort.go create mode 100644 src/lib/sort/Makefile create mode 100644 src/lib/sort/sort.go create mode 100644 src/lib/sort/sort_test.go delete mode 100644 src/lib/sort_test.go delete mode 100644 src/lib/strings.go create mode 100644 src/lib/strings/Makefile create mode 100644 src/lib/strings/strings.go create mode 100644 src/lib/strings/strings_test.go delete mode 100644 src/lib/strings_test.go delete mode 100644 src/lib/testing.go create mode 100644 src/lib/testing/Makefile create mode 100644 src/lib/testing/testing.go delete mode 100644 src/lib/utf8.go create mode 100644 src/lib/utf8/Makefile create mode 100644 src/lib/utf8/utf8.go create mode 100644 src/lib/utf8/utf8_test.go delete mode 100644 src/lib/utf8_test.go delete mode 100644 src/lib/xml.go create mode 100644 src/lib/xml/xml.go (limited to 'src') diff --git a/src/lib/Makefile b/src/lib/Makefile index f9c61f11d..97a33793f 100644 --- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -7,123 +7,126 @@ all: install GC=6g DIRS=\ + bignum\ + bufio\ container\ + exec\ + exvar\ + flag\ fmt\ go\ hash\ http\ io\ json\ + log\ + malloc\ math\ net\ + once\ os\ + path\ + rand\ reflect\ regexp\ + sort\ strconv\ + strings\ sync\ tabwriter\ template\ + testing\ time\ unicode\ - -FILES=\ - bignum\ - bufio\ - exec\ - exvar\ - flag\ - log\ - malloc\ - once\ - rand\ - sort\ - strings\ - testing\ utf8\ TEST=\ - bignum\ - bufio\ - exec\ - exvar\ - flag\ - log\ - once\ - sort\ - strings\ - utf8\ - -clean.dirs: $(addsuffix .dirclean, $(DIRS)) -install.dirs: $(addsuffix .dirinstall, $(DIRS)) -install.files: $(addsuffix .install, $(FILES)) -nuke.dirs: $(addsuffix .dirnuke, $(DIRS)) -test.files: $(addsuffix .test, $(TEST)) - -%.6: %.go - $(GC) $*.go - -%.test: %.6 - gotest $*_test.go + bignum \ + bufio \ + container \ + exec \ + exvar \ + flag \ + fmt \ + go \ + hash \ + 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: - rm -f $*.6 - -%.install: %.6 - 6ar grc $*.a $*.6 - mv $*.a $(GOROOT)/pkg/$*.a - rm -f $*.6 - -%.dirclean: +cd $* && make clean -%.dirinstall: +%.install: +cd $* && make install -%.dirnuke: +%.nuke: +cd $* && make nuke -clean.files: - rm -f 6.out *.6 +%.test: + +cd $* && make test -clean: clean.dirs clean.files +clean: clean.dirs -install: install.dirs install.files +install: install.dirs -nuke: nuke.dirs clean.files - rm -f $(GOROOT)/pkg/* +test: test.dirs -test: test.files +nuke: nuke.dirs + rm -rf $(GOROOT)/pkg/* # TODO: dependencies - should auto-generate -bignum.6: fmt.dirinstall -bufio.6: io.dirinstall os.dirinstall -exec.6: os.dirinstall strings.install -exvar.6: fmt.dirinstall http.dirinstall log.install strconv.dirinstall sync.dirinstall -flag.6: fmt.dirinstall os.dirinstall strconv.dirinstall -log.6: fmt.dirinstall io.dirinstall os.dirinstall time.dirinstall -path.6: io.dirinstall -once.6: sync.dirinstall -strings.6: utf8.install -testing.6: flag.install fmt.dirinstall - -fmt.dirinstall: io.dirinstall reflect.dirinstall strconv.dirinstall -go.dirinstall: regexp.dirinstall sort.install strconv.dirinstall strings.install \ - utf8.install unicode.dirinstall fmt.dirinstall -hash.dirinstall: os.dirinstall -http.dirinstall: bufio.install io.dirinstall net.dirinstall os.dirinstall path.install strings.install log.install -io.dirinstall: os.dirinstall sync.dirinstall -json.dirinstall: container.dirinstall fmt.dirinstall io.dirinstall math.dirinstall \ - strconv.dirinstall strings.install utf8.install +bignum.install: fmt.install +bufio.install: io.install os.install +exec.install: os.install strings.install +exvar.install: fmt.install http.install log.install strconv.install sync.install +flag.install: fmt.install os.install strconv.install +log.install: fmt.install io.install os.install time.install +path.install: io.install +once.install: sync.install +strings.install: utf8.install +testing.install: flag.install fmt.install + +fmt.install: io.install reflect.install strconv.install +go.install: regexp.install sort.install strconv.install strings.install \ + utf8.install unicode.install fmt.install +hash.install: os.install +http.install: bufio.install io.install net.install os.install path.install strings.install log.install +io.install: os.install sync.install +json.install: container.install fmt.install io.install math.install \ + strconv.install strings.install utf8.install # TODO(rsc): net is not supposed to depend on fmt or strings or strconv -net.dirinstall: fmt.dirinstall once.install os.dirinstall strconv.dirinstall strings.install -os.dirinstall: syscall.dirinstall once.install -regexp.dirinstall: os.dirinstall -reflect.dirinstall: strconv.dirinstall sync.dirinstall -strconv.dirinstall: math.dirinstall os.dirinstall utf8.install -sync.dirinstall: -syscall.dirinstall: sync.dirinstall -tabwriter.dirinstall: os.dirinstall io.dirinstall container.dirinstall -template.dirinstall: fmt.dirinstall io.dirinstall os.dirinstall reflect.dirinstall strings.install -time.dirinstall: once.install os.dirinstall io.dirinstall +net.install: fmt.install once.install os.install strconv.install strings.install +os.install: syscall.install once.install +regexp.install: os.install +reflect.install: strconv.install sync.install +strconv.install: math.install os.install utf8.install +sync.install: +syscall.install: sync.install +tabwriter.install: os.install io.install container.install +template.install: fmt.install io.install os.install reflect.install strings.install +time.install: once.install os.install io.install diff --git a/src/lib/bignum.go b/src/lib/bignum.go deleted file mode 100755 index b9ea66587..000000000 --- a/src/lib/bignum.go +++ /dev/null @@ -1,1464 +0,0 @@ -// Copyright 2009 The Go 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, 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)>>(_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/lib/bignum/Makefile b/src/lib/bignum/Makefile new file mode 100644 index 000000000..fae6ed647 --- /dev/null +++ b/src/lib/bignum/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/bignum.a + +packages: _obj$D/bignum.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/bignum.a $(GOROOT)/pkg$D/bignum.a diff --git a/src/lib/bignum/bignum.go b/src/lib/bignum/bignum.go new file mode 100755 index 000000000..b9ea66587 --- /dev/null +++ b/src/lib/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, 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)>>(_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/lib/bignum/bignum_test.go b/src/lib/bignum/bignum_test.go new file mode 100644 index 000000000..9351c2ebf --- /dev/null +++ b/src/lib/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< 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<> 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/lib/bignum_test.go b/src/lib/bignum_test.go deleted file mode 100644 index 9351c2ebf..000000000 --- a/src/lib/bignum_test.go +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2009 The Go 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< 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<> 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/lib/bufio.go b/src/lib/bufio.go deleted file mode 100644 index 23f559993..000000000 --- a/src/lib/bufio.go +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2009 The Go 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.Read or io.Write -// object, creating another object (BufRead or BufWrite) 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 -// - BufRead: ReadRune, UnreadRune ? -// could make ReadRune generic if we dropped UnreadRune -// - buffered output -// - would like to rename to Read, Write, but breaks -// embedding of these: would lose the Read, Write methods. - -const ( - defaultBufSize = 4096 -) - -// Errors introduced by this package. -var ( - PhaseError = os.NewError("phase error"); - BufferFull = os.NewError("buffer full"); - InternalError = os.NewError("bufio internal error"); - BadBufSize = os.NewError("bad bufio size"); - ShortWrite = os.NewError("short write"); -) - -func copySlice(dst []byte, src []byte) { - for i := 0; i < len(dst); i++ { - dst[i] = src[i] - } -} - - -// Buffered input. - -// BufRead implements buffering for an io.Read object. -type BufRead struct { - buf []byte; - rd io.Read; - r, w int; - err os.Error; - lastbyte int; -} - -// NewBufReadSize creates a new BufRead whose buffer has the specified size, -// which must be greater than zero. If the argument io.Read is already a -// BufRead with large enough size, it returns the underlying BufRead. -// It returns the BufRead and any error. -func NewBufReadSize(rd io.Read, size int) (*BufRead, os.Error) { - if size <= 0 { - return nil, BadBufSize - } - // Is it already a BufRead? - b, ok := rd.(*BufRead); - if ok && len(b.buf) >= size { - return b, nil - } - b = new(BufRead); - b.buf = make([]byte, size); - b.rd = rd; - b.lastbyte = -1; - return b, nil -} - -// NewBufRead returns a new BufRead whose buffer has the default size. -func NewBufRead(rd io.Read) *BufRead { - b, err := NewBufReadSize(rd, defaultBufSize); - if err != nil { - // cannot happen - defaultBufSize is a valid size - panic("bufio: NewBufRead: ", err.String()); - } - return b; -} - -//.fill reads a new chunk into the buffer. -func (b *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 - -// BufWrite implements buffering for an io.Write object. -type BufWrite struct { - err os.Error; - buf []byte; - n int; - wr io.Write; -} - -// NewBufWriteSize creates a new BufWrite whose buffer has the specified size, -// which must be greater than zero. If the argument io.Write is already a -// BufWrite with large enough size, it returns the underlying BufWrite. -// It returns the BufWrite and any error. -func NewBufWriteSize(wr io.Write, size int) (*BufWrite, os.Error) { - if size <= 0 { - return nil, BadBufSize - } - // Is it already a BufWrite? - b, ok := wr.(*BufWrite); - if ok && len(b.buf) >= size { - return b, nil - } - b = new(BufWrite); - b.buf = make([]byte, size); - b.wr = wr; - return b, nil -} - -// NewBufWrite returns a new BufWrite whose buffer has the default size. -func NewBufWrite(wr io.Write) *BufWrite { - b, err := NewBufWriteSize(wr, defaultBufSize); - if err != nil { - // cannot happen - defaultBufSize is valid size - panic("bufio: NewBufWrite: ", err.String()); - } - return b; -} - -// Flush writes any buffered data to the underlying io.Write. -func (b *BufWrite) Flush() os.Error { - if b.err != nil { - return b.err - } - n := 0; - for n < b.n { - m, e := b.wr.Write(b.buf[n:b.n]); - n += m; - if m == 0 && e == nil { - e = ShortWrite - } - if e != nil { - if 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 *BufWrite) Available() int { - return len(b.buf) - b.n -} - -// Buffered returns the number of bytes that have been written into the current buffer. -func (b *BufWrite) 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 *BufWrite) 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 *BufWrite) 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 - -// BufReadWrite stores (a pointer to) a BufRead and a BufWrite. -// It implements io.ReadWrite. -type BufReadWrite struct { - *BufRead; - *BufWrite; -} - -// NewBufReadWrite allocates a new BufReadWrite holding r and w. -func NewBufReadWrite(r *BufRead, w *BufWrite) *BufReadWrite { - return &BufReadWrite{r, w} -} - diff --git a/src/lib/bufio/Makefile b/src/lib/bufio/Makefile new file mode 100644 index 000000000..7cd095aa8 --- /dev/null +++ b/src/lib/bufio/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/bufio.a + +packages: _obj$D/bufio.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/bufio.a $(GOROOT)/pkg$D/bufio.a diff --git a/src/lib/bufio/bufio.go b/src/lib/bufio/bufio.go new file mode 100644 index 000000000..23f559993 --- /dev/null +++ b/src/lib/bufio/bufio.go @@ -0,0 +1,518 @@ +// Copyright 2009 The Go 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.Read or io.Write +// object, creating another object (BufRead or BufWrite) 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 +// - BufRead: ReadRune, UnreadRune ? +// could make ReadRune generic if we dropped UnreadRune +// - buffered output +// - would like to rename to Read, Write, but breaks +// embedding of these: would lose the Read, Write methods. + +const ( + defaultBufSize = 4096 +) + +// Errors introduced by this package. +var ( + PhaseError = os.NewError("phase error"); + BufferFull = os.NewError("buffer full"); + InternalError = os.NewError("bufio internal error"); + BadBufSize = os.NewError("bad bufio size"); + ShortWrite = os.NewError("short write"); +) + +func copySlice(dst []byte, src []byte) { + for i := 0; i < len(dst); i++ { + dst[i] = src[i] + } +} + + +// Buffered input. + +// BufRead implements buffering for an io.Read object. +type BufRead struct { + buf []byte; + rd io.Read; + r, w int; + err os.Error; + lastbyte int; +} + +// NewBufReadSize creates a new BufRead whose buffer has the specified size, +// which must be greater than zero. If the argument io.Read is already a +// BufRead with large enough size, it returns the underlying BufRead. +// It returns the BufRead and any error. +func NewBufReadSize(rd io.Read, size int) (*BufRead, os.Error) { + if size <= 0 { + return nil, BadBufSize + } + // Is it already a BufRead? + b, ok := rd.(*BufRead); + if ok && len(b.buf) >= size { + return b, nil + } + b = new(BufRead); + b.buf = make([]byte, size); + b.rd = rd; + b.lastbyte = -1; + return b, nil +} + +// NewBufRead returns a new BufRead whose buffer has the default size. +func NewBufRead(rd io.Read) *BufRead { + b, err := NewBufReadSize(rd, defaultBufSize); + if err != nil { + // cannot happen - defaultBufSize is a valid size + panic("bufio: NewBufRead: ", err.String()); + } + return b; +} + +//.fill reads a new chunk into the buffer. +func (b *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 *BufRead) 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 + +// BufWrite implements buffering for an io.Write object. +type BufWrite struct { + err os.Error; + buf []byte; + n int; + wr io.Write; +} + +// NewBufWriteSize creates a new BufWrite whose buffer has the specified size, +// which must be greater than zero. If the argument io.Write is already a +// BufWrite with large enough size, it returns the underlying BufWrite. +// It returns the BufWrite and any error. +func NewBufWriteSize(wr io.Write, size int) (*BufWrite, os.Error) { + if size <= 0 { + return nil, BadBufSize + } + // Is it already a BufWrite? + b, ok := wr.(*BufWrite); + if ok && len(b.buf) >= size { + return b, nil + } + b = new(BufWrite); + b.buf = make([]byte, size); + b.wr = wr; + return b, nil +} + +// NewBufWrite returns a new BufWrite whose buffer has the default size. +func NewBufWrite(wr io.Write) *BufWrite { + b, err := NewBufWriteSize(wr, defaultBufSize); + if err != nil { + // cannot happen - defaultBufSize is valid size + panic("bufio: NewBufWrite: ", err.String()); + } + return b; +} + +// Flush writes any buffered data to the underlying io.Write. +func (b *BufWrite) Flush() os.Error { + if b.err != nil { + return b.err + } + n := 0; + for n < b.n { + m, e := b.wr.Write(b.buf[n:b.n]); + n += m; + if m == 0 && e == nil { + e = ShortWrite + } + if e != nil { + if 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 *BufWrite) Available() int { + return len(b.buf) - b.n +} + +// Buffered returns the number of bytes that have been written into the current buffer. +func (b *BufWrite) 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 *BufWrite) 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 *BufWrite) 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 + +// BufReadWrite stores (a pointer to) a BufRead and a BufWrite. +// It implements io.ReadWrite. +type BufReadWrite struct { + *BufRead; + *BufWrite; +} + +// NewBufReadWrite allocates a new BufReadWrite holding r and w. +func NewBufReadWrite(r *BufRead, w *BufWrite) *BufReadWrite { + return &BufReadWrite{r, w} +} + diff --git a/src/lib/bufio/bufio_test.go b/src/lib/bufio/bufio_test.go new file mode 100644 index 000000000..00ab4a414 --- /dev/null +++ b/src/lib/bufio/bufio_test.go @@ -0,0 +1,379 @@ +// Copyright 2009 The Go 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"; +) + +// Should be in language! +func copy(p []byte, q []byte) { + for i := 0; i < len(p); i++ { + p[i] = q[i] + } +} + +// Reads from p. +type byteReader struct { + p []byte +} + +func newByteReader(p []byte) io.Read { + b := new(byteReader); + b.p = p; + return b +} + +func (b *byteReader) Read(p []byte) (int, os.Error) { + n := len(p); + if n > len(b.p) { + n = len(b.p) + } + copy(p[0:n], b.p[0:n]); + b.p = b.p[n:len(b.p)]; + return n, nil +} + + +// Reads from p but only returns half of what you asked for. +type halfByteReader struct { + p []byte +} + +func newHalfByteReader(p []byte) io.Read { + b := new(halfByteReader); + b.p = p; + return b +} + +func (b *halfByteReader) Read(p []byte) (int, os.Error) { + n := len(p)/2; + if n == 0 && len(p) > 0 { + n = 1 + } + if n > len(b.p) { + n = len(b.p) + } + copy(p[0:n], b.p[0:n]); + b.p = b.p[n:len(b.p)]; + return n, nil +} + +// Reads from a reader and rot13s the result. +type rot13Reader struct { + r io.Read +} + +func newRot13Reader(r io.Read) *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++ { + if 'a' <= p[i] && p[i] <= 'z' || 'A' <= p[i] && p[i] <= 'Z' { + if 'a' <= p[i] && p[i] <= 'm' || 'A' <= p[i] && p[i] <= 'M' { + p[i] += 13; + } else { + p[i] -= 13; + } + } + } + return n, nil +} + +type readMaker struct { + name string; + fn func([]byte) io.Read; +} +var readMakers = []readMaker { + readMaker{ "full", func(p []byte) io.Read { return newByteReader(p) } }, + readMaker{ "half", func(p []byte) io.Read { return newHalfByteReader(p) } }, +} + +// Call ReadLineString (which ends up calling everything else) +// to accumulate the text of a file. +func readLines(b *BufRead) 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 ReadByte to accumulate the text of a file +func readBytes(buf *BufRead) string { + var b [1000]byte; + nb := 0; + for { + c, e := buf.ReadByte(); + if e == io.ErrEOF { + break + } + if e != nil { + panic("GetBytes: "+e.String()) + } + b[nb] = c; + nb++; + } + // BUG return string(b[0:nb]) ? + return string(b[0:nb]) +} + +// Call Read to accumulate the text of a file +func reads(buf *BufRead, 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(*BufRead) string; +} +var bufreaders = []bufReader { + bufReader{ "1", func(b *BufRead) string { return reads(b, 1) } }, + bufReader{ "2", func(b *BufRead) string { return reads(b, 2) } }, + bufReader{ "3", func(b *BufRead) string { return reads(b, 3) } }, + bufReader{ "4", func(b *BufRead) string { return reads(b, 4) } }, + bufReader{ "5", func(b *BufRead) string { return reads(b, 5) } }, + bufReader{ "7", func(b *BufRead) 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 TestBufReadSimple(t *testing.T) { + b := NewBufRead(newByteReader(io.StringBytes("hello world"))); + if s := readBytes(b); s != "hello world" { + t.Errorf("simple hello world test failed: got %q", s); + } + + b = NewBufRead(newRot13Reader(newByteReader(io.StringBytes("hello world")))); + if s := readBytes(b); s != "uryyb jbeyq" { + t.Error("rot13 hello world test failed: got %q", s); + } +} + +func TestBufRead(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(textbytes); + buf, e := NewBufReadSize(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); + } + } + } + } + } +} + +type writeBuffer interface { + Write(p []byte) (int, os.Error); + GetBytes() []byte +} + +// Accumulates bytes into a byte array. +type byteWriter struct { + p []byte; + n int +} + +func newByteWriter() writeBuffer { + return new(byteWriter) +} + +func (w *byteWriter) Write(p []byte) (int, os.Error) { + if w.p == nil { + w.p = make([]byte, len(p)+100) + } else if w.n + len(p) >= len(w.p) { + newp := make([]byte, len(w.p)*2 + len(p)); + copy(newp[0:w.n], w.p[0:w.n]); + w.p = newp + } + copy(w.p[w.n:w.n+len(p)], p); + w.n += len(p); + return len(p), nil +} + +func (w *byteWriter) GetBytes() []byte { + return w.p[0:w.n] +} + +// Accumulates bytes written into a byte array +// but Write only takes half of what you give it. +// TODO: Could toss this -- Write() is not supposed to do that. +type halfByteWriter struct { + bw writeBuffer +} + +func newHalfByteWriter() writeBuffer { + w := new(halfByteWriter); + w.bw = newByteWriter(); + return w +} + +func (w *halfByteWriter) Write(p []byte) (int, os.Error) { + n := (len(p)+1) / 2; + // BUG return w.bw.Write(p[0:n]) + r, e := w.bw.Write(p[0:n]); + return r, e +} + +func (w *halfByteWriter) GetBytes() []byte { + return w.bw.GetBytes() +} + +type writeMaker struct { + name string; + fn func()writeBuffer; +} +func TestBufWrite(t *testing.T) { + var data [8192]byte; + + var writers = []writeMaker { + writeMaker{ "full", newByteWriter }, + writeMaker{ "half", newHalfByteWriter }, + }; + + for i := 0; i < len(data); i++ { + data[i] = byte(' '+ i%('~'-' ')); + } + for i := 0; i < len(bufsizes); i++ { + for j := 0; j < len(bufsizes); j++ { + for k := 0; k < len(writers); k++ { + 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. + + write := writers[k].fn(); + buf, e := NewBufWriteSize(write, bs); + context := fmt.Sprintf("write=%s nwrite=%d bufsize=%d", writers[k].name, nwrite, bs); + if e != nil { + t.Errorf("%s: NewBufWriteSize %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 := write.GetBytes(); + 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); + } + } + } + } + } +} + +func TestNewBufReadSizeIdempotent(t *testing.T) { + const BufSize = 1000; + b, err := NewBufReadSize(newByteReader(io.StringBytes("hello world")), BufSize); + if err != nil { + t.Error("NewBufReadSize create fail", err); + } + // Does it recognize itself? + b1, err2 := NewBufReadSize(b, BufSize); + if err2 != nil { + t.Error("NewBufReadSize #2 create fail", err2); + } + if b1 != b { + t.Error("NewBufReadSize did not detect underlying BufRead"); + } + // Does it wrap if existing buffer is too small? + b2, err3 := NewBufReadSize(b, 2*BufSize); + if err3 != nil { + t.Error("NewBufReadSize #3 create fail", err3); + } + if b2 == b { + t.Error("NewBufReadSize did not enlarge buffer"); + } +} + +func TestNewBufWriteSizeIdempotent(t *testing.T) { + const BufSize = 1000; + b, err := NewBufWriteSize(newByteWriter(), BufSize); + if err != nil { + t.Error("NewBufWriteSize create fail", err); + } + // Does it recognize itself? + b1, err2 := NewBufWriteSize(b, BufSize); + if err2 != nil { + t.Error("NewBufWriteSize #2 create fail", err2); + } + if b1 != b { + t.Error("NewBufWriteSize did not detect underlying BufWrite"); + } + // Does it wrap if existing buffer is too small? + b2, err3 := NewBufWriteSize(b, 2*BufSize); + if err3 != nil { + t.Error("NewBufWriteSize #3 create fail", err3); + } + if b2 == b { + t.Error("NewBufWriteSize did not enlarge buffer"); + } +} diff --git a/src/lib/bufio_test.go b/src/lib/bufio_test.go deleted file mode 100644 index 00ab4a414..000000000 --- a/src/lib/bufio_test.go +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2009 The Go 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"; -) - -// Should be in language! -func copy(p []byte, q []byte) { - for i := 0; i < len(p); i++ { - p[i] = q[i] - } -} - -// Reads from p. -type byteReader struct { - p []byte -} - -func newByteReader(p []byte) io.Read { - b := new(byteReader); - b.p = p; - return b -} - -func (b *byteReader) Read(p []byte) (int, os.Error) { - n := len(p); - if n > len(b.p) { - n = len(b.p) - } - copy(p[0:n], b.p[0:n]); - b.p = b.p[n:len(b.p)]; - return n, nil -} - - -// Reads from p but only returns half of what you asked for. -type halfByteReader struct { - p []byte -} - -func newHalfByteReader(p []byte) io.Read { - b := new(halfByteReader); - b.p = p; - return b -} - -func (b *halfByteReader) Read(p []byte) (int, os.Error) { - n := len(p)/2; - if n == 0 && len(p) > 0 { - n = 1 - } - if n > len(b.p) { - n = len(b.p) - } - copy(p[0:n], b.p[0:n]); - b.p = b.p[n:len(b.p)]; - return n, nil -} - -// Reads from a reader and rot13s the result. -type rot13Reader struct { - r io.Read -} - -func newRot13Reader(r io.Read) *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++ { - if 'a' <= p[i] && p[i] <= 'z' || 'A' <= p[i] && p[i] <= 'Z' { - if 'a' <= p[i] && p[i] <= 'm' || 'A' <= p[i] && p[i] <= 'M' { - p[i] += 13; - } else { - p[i] -= 13; - } - } - } - return n, nil -} - -type readMaker struct { - name string; - fn func([]byte) io.Read; -} -var readMakers = []readMaker { - readMaker{ "full", func(p []byte) io.Read { return newByteReader(p) } }, - readMaker{ "half", func(p []byte) io.Read { return newHalfByteReader(p) } }, -} - -// Call ReadLineString (which ends up calling everything else) -// to accumulate the text of a file. -func readLines(b *BufRead) 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 ReadByte to accumulate the text of a file -func readBytes(buf *BufRead) string { - var b [1000]byte; - nb := 0; - for { - c, e := buf.ReadByte(); - if e == io.ErrEOF { - break - } - if e != nil { - panic("GetBytes: "+e.String()) - } - b[nb] = c; - nb++; - } - // BUG return string(b[0:nb]) ? - return string(b[0:nb]) -} - -// Call Read to accumulate the text of a file -func reads(buf *BufRead, 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(*BufRead) string; -} -var bufreaders = []bufReader { - bufReader{ "1", func(b *BufRead) string { return reads(b, 1) } }, - bufReader{ "2", func(b *BufRead) string { return reads(b, 2) } }, - bufReader{ "3", func(b *BufRead) string { return reads(b, 3) } }, - bufReader{ "4", func(b *BufRead) string { return reads(b, 4) } }, - bufReader{ "5", func(b *BufRead) string { return reads(b, 5) } }, - bufReader{ "7", func(b *BufRead) 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 TestBufReadSimple(t *testing.T) { - b := NewBufRead(newByteReader(io.StringBytes("hello world"))); - if s := readBytes(b); s != "hello world" { - t.Errorf("simple hello world test failed: got %q", s); - } - - b = NewBufRead(newRot13Reader(newByteReader(io.StringBytes("hello world")))); - if s := readBytes(b); s != "uryyb jbeyq" { - t.Error("rot13 hello world test failed: got %q", s); - } -} - -func TestBufRead(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(textbytes); - buf, e := NewBufReadSize(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); - } - } - } - } - } -} - -type writeBuffer interface { - Write(p []byte) (int, os.Error); - GetBytes() []byte -} - -// Accumulates bytes into a byte array. -type byteWriter struct { - p []byte; - n int -} - -func newByteWriter() writeBuffer { - return new(byteWriter) -} - -func (w *byteWriter) Write(p []byte) (int, os.Error) { - if w.p == nil { - w.p = make([]byte, len(p)+100) - } else if w.n + len(p) >= len(w.p) { - newp := make([]byte, len(w.p)*2 + len(p)); - copy(newp[0:w.n], w.p[0:w.n]); - w.p = newp - } - copy(w.p[w.n:w.n+len(p)], p); - w.n += len(p); - return len(p), nil -} - -func (w *byteWriter) GetBytes() []byte { - return w.p[0:w.n] -} - -// Accumulates bytes written into a byte array -// but Write only takes half of what you give it. -// TODO: Could toss this -- Write() is not supposed to do that. -type halfByteWriter struct { - bw writeBuffer -} - -func newHalfByteWriter() writeBuffer { - w := new(halfByteWriter); - w.bw = newByteWriter(); - return w -} - -func (w *halfByteWriter) Write(p []byte) (int, os.Error) { - n := (len(p)+1) / 2; - // BUG return w.bw.Write(p[0:n]) - r, e := w.bw.Write(p[0:n]); - return r, e -} - -func (w *halfByteWriter) GetBytes() []byte { - return w.bw.GetBytes() -} - -type writeMaker struct { - name string; - fn func()writeBuffer; -} -func TestBufWrite(t *testing.T) { - var data [8192]byte; - - var writers = []writeMaker { - writeMaker{ "full", newByteWriter }, - writeMaker{ "half", newHalfByteWriter }, - }; - - for i := 0; i < len(data); i++ { - data[i] = byte(' '+ i%('~'-' ')); - } - for i := 0; i < len(bufsizes); i++ { - for j := 0; j < len(bufsizes); j++ { - for k := 0; k < len(writers); k++ { - 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. - - write := writers[k].fn(); - buf, e := NewBufWriteSize(write, bs); - context := fmt.Sprintf("write=%s nwrite=%d bufsize=%d", writers[k].name, nwrite, bs); - if e != nil { - t.Errorf("%s: NewBufWriteSize %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 := write.GetBytes(); - 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); - } - } - } - } - } -} - -func TestNewBufReadSizeIdempotent(t *testing.T) { - const BufSize = 1000; - b, err := NewBufReadSize(newByteReader(io.StringBytes("hello world")), BufSize); - if err != nil { - t.Error("NewBufReadSize create fail", err); - } - // Does it recognize itself? - b1, err2 := NewBufReadSize(b, BufSize); - if err2 != nil { - t.Error("NewBufReadSize #2 create fail", err2); - } - if b1 != b { - t.Error("NewBufReadSize did not detect underlying BufRead"); - } - // Does it wrap if existing buffer is too small? - b2, err3 := NewBufReadSize(b, 2*BufSize); - if err3 != nil { - t.Error("NewBufReadSize #3 create fail", err3); - } - if b2 == b { - t.Error("NewBufReadSize did not enlarge buffer"); - } -} - -func TestNewBufWriteSizeIdempotent(t *testing.T) { - const BufSize = 1000; - b, err := NewBufWriteSize(newByteWriter(), BufSize); - if err != nil { - t.Error("NewBufWriteSize create fail", err); - } - // Does it recognize itself? - b1, err2 := NewBufWriteSize(b, BufSize); - if err2 != nil { - t.Error("NewBufWriteSize #2 create fail", err2); - } - if b1 != b { - t.Error("NewBufWriteSize did not detect underlying BufWrite"); - } - // Does it wrap if existing buffer is too small? - b2, err3 := NewBufWriteSize(b, 2*BufSize); - if err3 != nil { - t.Error("NewBufWriteSize #3 create fail", err3); - } - if b2 == b { - t.Error("NewBufWriteSize did not enlarge buffer"); - } -} diff --git a/src/lib/exec.go b/src/lib/exec.go deleted file mode 100644 index c13bad3e0..000000000 --- a/src/lib/exec.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2009 The Go 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 uint64) (*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/lib/exec/Makefile b/src/lib/exec/Makefile new file mode 100644 index 000000000..2738143ba --- /dev/null +++ b/src/lib/exec/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/exec.a + +packages: _obj$D/exec.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/exec.a $(GOROOT)/pkg$D/exec.a diff --git a/src/lib/exec/exec.go b/src/lib/exec/exec.go new file mode 100644 index 000000000..c13bad3e0 --- /dev/null +++ b/src/lib/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 uint64) (*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/lib/exec/exec_test.go b/src/lib/exec/exec_test.go new file mode 100644 index 000000000..a1bb1f50e --- /dev/null +++ b/src/lib/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/lib/exec_test.go b/src/lib/exec_test.go deleted file mode 100644 index a1bb1f50e..000000000 --- a/src/lib/exec_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2009 The Go 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/lib/exvar.go b/src/lib/exvar.go deleted file mode 100644 index 9d2a172b7..000000000 --- a/src/lib/exvar.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2009 The Go 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) 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; -} - - -// 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); - v.m = make(map[string] Var); - 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/lib/exvar/Makefile b/src/lib/exvar/Makefile new file mode 100644 index 000000000..fa63fde50 --- /dev/null +++ b/src/lib/exvar/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/exvar.a + +packages: _obj$D/exvar.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/exvar.a $(GOROOT)/pkg$D/exvar.a diff --git a/src/lib/exvar/exvar.go b/src/lib/exvar/exvar.go new file mode 100644 index 000000000..9d2a172b7 --- /dev/null +++ b/src/lib/exvar/exvar.go @@ -0,0 +1,202 @@ +// Copyright 2009 The Go 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) 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; +} + + +// 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); + v.m = make(map[string] Var); + 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/lib/exvar/exvar_test.go b/src/lib/exvar/exvar_test.go new file mode 100644 index 000000000..28fbf3cf2 --- /dev/null +++ b/src/lib/exvar/exvar_test.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 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) + } +} diff --git a/src/lib/exvar_test.go b/src/lib/exvar_test.go deleted file mode 100644 index 28fbf3cf2..000000000 --- a/src/lib/exvar_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2009 The Go 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) - } -} diff --git a/src/lib/flag.go b/src/lib/flag.go deleted file mode 100644 index e66238f6d..000000000 --- a/src/lib/flag.go +++ /dev/null @@ -1,486 +0,0 @@ -// Copyright 2009 The Go 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 sys.Exit(1). -func Usage() { - if len(sys.Args) > 0 { - fmt.Fprintln(os.Stderr, "Usage of", sys.Args[0] + ":"); - } else { - fmt.Fprintln(os.Stderr, "Usage:"); - } - PrintDefaults(); - sys.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(sys.Args) { - return ""; - } - return sys.Args[i] -} - -// NArg is the number of arguments remaining after flags have been processed. -func NArg() int { - return len(sys.Args) - flags.first_arg -} - -// Args returns the non-flag command-line arguments. -func Args() []string { - return sys.Args[flags.first_arg:len(sys.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 := sys.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(sys.Args)-1 { - // value is the next arg - has_value = true; - index++; - value = sys.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(sys.Args); { - ok, next := flags.parseOne(i); - if next > 0 { - flags.first_arg = next; - i = next; - } - if !ok { - break - } - } -} diff --git a/src/lib/flag/Makefile b/src/lib/flag/Makefile new file mode 100644 index 000000000..596639121 --- /dev/null +++ b/src/lib/flag/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/flag.a + +packages: _obj$D/flag.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/flag.a $(GOROOT)/pkg$D/flag.a diff --git a/src/lib/flag/flag.go b/src/lib/flag/flag.go new file mode 100644 index 000000000..e66238f6d --- /dev/null +++ b/src/lib/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 sys.Exit(1). +func Usage() { + if len(sys.Args) > 0 { + fmt.Fprintln(os.Stderr, "Usage of", sys.Args[0] + ":"); + } else { + fmt.Fprintln(os.Stderr, "Usage:"); + } + PrintDefaults(); + sys.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(sys.Args) { + return ""; + } + return sys.Args[i] +} + +// NArg is the number of arguments remaining after flags have been processed. +func NArg() int { + return len(sys.Args) - flags.first_arg +} + +// Args returns the non-flag command-line arguments. +func Args() []string { + return sys.Args[flags.first_arg:len(sys.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 := sys.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(sys.Args)-1 { + // value is the next arg + has_value = true; + index++; + value = sys.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(sys.Args); { + ok, next := flags.parseOne(i); + if next > 0 { + flags.first_arg = next; + i = next; + } + if !ok { + break + } + } +} diff --git a/src/lib/flag/flag_test.go b/src/lib/flag/flag_test.go new file mode 100644 index 000000000..0d83fcf81 --- /dev/null +++ b/src/lib/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/lib/flag_test.go b/src/lib/flag_test.go deleted file mode 100644 index 0d83fcf81..000000000 --- a/src/lib/flag_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2009 The Go 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/lib/log.go b/src/lib/log.go deleted file mode 100644 index 34158c789..000000000 --- a/src/lib/log.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2009 The Go 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"; - "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.Write; // first destination for output - out1 io.Write; // second destination for output; may be nil - prefix string; // prefix to write at beginning of each line - flag int; // properties -} - -// NewLogger 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 NewLogger(out0, out1 io.Write, prefix string, flag int) *Logger { - return &Logger{out0, out1, prefix, flag} -} - -var ( - stdout = NewLogger(os.Stdout, nil, "", Lok|Ldate|Ltime); - stderr = NewLogger(os.Stderr, nil, "", Lok|Ldate|Ltime); - exit = NewLogger(os.Stderr, nil, "", Lexit|Ldate|Ltime); - crash = NewLogger(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 := sys.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: - sys.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 sys.Exit(1). -func Exit(v ...) { - exit.Output(2, fmt.Sprintln(v)) -} - -// Exitf is equivalent to Stderrf() followed by a call to sys.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/lib/log/Makefile b/src/lib/log/Makefile new file mode 100644 index 000000000..b135ec31f --- /dev/null +++ b/src/lib/log/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/log.a + +packages: _obj$D/log.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/log.a $(GOROOT)/pkg$D/log.a diff --git a/src/lib/log/log.go b/src/lib/log/log.go new file mode 100644 index 000000000..34158c789 --- /dev/null +++ b/src/lib/log/log.go @@ -0,0 +1,194 @@ +// Copyright 2009 The Go 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"; + "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.Write; // first destination for output + out1 io.Write; // second destination for output; may be nil + prefix string; // prefix to write at beginning of each line + flag int; // properties +} + +// NewLogger 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 NewLogger(out0, out1 io.Write, prefix string, flag int) *Logger { + return &Logger{out0, out1, prefix, flag} +} + +var ( + stdout = NewLogger(os.Stdout, nil, "", Lok|Ldate|Ltime); + stderr = NewLogger(os.Stderr, nil, "", Lok|Ldate|Ltime); + exit = NewLogger(os.Stderr, nil, "", Lexit|Ldate|Ltime); + crash = NewLogger(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 := sys.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: + sys.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 sys.Exit(1). +func Exit(v ...) { + exit.Output(2, fmt.Sprintln(v)) +} + +// Exitf is equivalent to Stderrf() followed by a call to sys.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/lib/log/log_test.go b/src/lib/log/log_test.go new file mode 100644 index 000000000..2d9e1ab30 --- /dev/null +++ b/src/lib/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.NewBufRead(r); + l := NewLogger(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/lib/log_test.go b/src/lib/log_test.go deleted file mode 100644 index 2d9e1ab30..000000000 --- a/src/lib/log_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2009 The Go 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.NewBufRead(r); - l := NewLogger(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/lib/malloc.go b/src/lib/malloc.go deleted file mode 100644 index 8e4397a0b..000000000 --- a/src/lib/malloc.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2009 The Go 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(uint64) *byte -func Free(*byte) -func GetStats() *Stats -func Lookup(*byte) (*byte, uintptr) -func GC() diff --git a/src/lib/malloc/Makefile b/src/lib/malloc/Makefile new file mode 100644 index 000000000..771896005 --- /dev/null +++ b/src/lib/malloc/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/malloc.a + +packages: _obj$D/malloc.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/malloc.a $(GOROOT)/pkg$D/malloc.a diff --git a/src/lib/malloc/malloc.go b/src/lib/malloc/malloc.go new file mode 100644 index 000000000..8e4397a0b --- /dev/null +++ b/src/lib/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(uint64) *byte +func Free(*byte) +func GetStats() *Stats +func Lookup(*byte) (*byte, uintptr) +func GC() diff --git a/src/lib/once.go b/src/lib/once.go deleted file mode 100644 index 6047df236..000000000 --- a/src/lib/once.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2009 The Go 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/lib/once/Makefile b/src/lib/once/Makefile new file mode 100644 index 000000000..7e700520d --- /dev/null +++ b/src/lib/once/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/once.a + +packages: _obj$D/once.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/once.a $(GOROOT)/pkg$D/once.a diff --git a/src/lib/once/once.go b/src/lib/once/once.go new file mode 100644 index 000000000..6047df236 --- /dev/null +++ b/src/lib/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/lib/once/once_test.go b/src/lib/once/once_test.go new file mode 100644 index 000000000..9506ff3d7 --- /dev/null +++ b/src/lib/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/lib/once_test.go b/src/lib/once_test.go deleted file mode 100644 index 9506ff3d7..000000000 --- a/src/lib/once_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2009 The Go 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/lib/path.go b/src/lib/path.go deleted file mode 100644 index a7e2c26c3..000000000 --- a/src/lib/path.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2009 The Go 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/lib/path/Makefile b/src/lib/path/Makefile new file mode 100644 index 000000000..020a66035 --- /dev/null +++ b/src/lib/path/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/path.a + +packages: _obj$D/path.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/path.a $(GOROOT)/pkg$D/path.a diff --git a/src/lib/path/path.go b/src/lib/path/path.go new file mode 100644 index 000000000..a7e2c26c3 --- /dev/null +++ b/src/lib/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/lib/path/path_test.go b/src/lib/path/path_test.go new file mode 100644 index 000000000..1238ac1cd --- /dev/null +++ b/src/lib/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/lib/path_test.go b/src/lib/path_test.go deleted file mode 100644 index 1238ac1cd..000000000 --- a/src/lib/path_test.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2009 The Go 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/lib/rand.go b/src/lib/rand.go deleted file mode 100644 index 2fd48629b..000000000 --- a/src/lib/rand.go +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright 2009 The Go 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 = 2147483647; - _Q = 44488; - _R = 3399; -) - -var ( - rng_cooked [_LEN]int64; // cooked random numbers - rng_vec [_LEN]int64; // current feedback register - rng_tap int; // index into vector - rng_feed int; // index into vector -) - -func seedrand(x int32) int32 { - // seed rng x[n+1] = 48271 * x[n] mod (2**31 - 1) - 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) << 20; - x = seedrand(x); - u ^= int64(x) << 10; - 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 an array of n ints, a pseudo-random permutation of the integers [0,n). -func Perm(n int) []int { - m := make([]int, n); - for i:=0; iMakefile + +D= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/rand.a + +packages: _obj$D/rand.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/rand.a $(GOROOT)/pkg$D/rand.a diff --git a/src/lib/rand/rand.go b/src/lib/rand/rand.go new file mode 100644 index 000000000..2fd48629b --- /dev/null +++ b/src/lib/rand/rand.go @@ -0,0 +1,775 @@ +// Copyright 2009 The Go 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 = 2147483647; + _Q = 44488; + _R = 3399; +) + +var ( + rng_cooked [_LEN]int64; // cooked random numbers + rng_vec [_LEN]int64; // current feedback register + rng_tap int; // index into vector + rng_feed int; // index into vector +) + +func seedrand(x int32) int32 { + // seed rng x[n+1] = 48271 * x[n] mod (2**31 - 1) + 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) << 20; + x = seedrand(x); + u ^= int64(x) << 10; + 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 an array 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 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/lib/sort/Makefile b/src/lib/sort/Makefile new file mode 100644 index 000000000..0a360ac4d --- /dev/null +++ b/src/lib/sort/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/sort.a + +packages: _obj$D/sort.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/sort.a $(GOROOT)/pkg$D/sort.a diff --git a/src/lib/sort/sort.go b/src/lib/sort/sort.go new file mode 100644 index 000000000..99ba0a0ef --- /dev/null +++ b/src/lib/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/lib/sort/sort_test.go b/src/lib/sort/sort_test.go new file mode 100644 index 000000000..1747daca6 --- /dev/null +++ b/src/lib/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<= 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<= 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 -} diff --git a/src/lib/strings/Makefile b/src/lib/strings/Makefile new file mode 100644 index 000000000..9372f4d16 --- /dev/null +++ b/src/lib/strings/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/strings.a + +packages: _obj$D/strings.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/strings.a $(GOROOT)/pkg$D/strings.a diff --git a/src/lib/strings/strings.go b/src/lib/strings/strings.go new file mode 100644 index 000000000..33adab249 --- /dev/null +++ b/src/lib/strings/strings.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. + +// 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)); + j := 0; + var size, rune int; + for i := 0; i < len(a); i++ { + rune, size = utf8.DecodeRuneInString(s, j); + a[i] = string(rune); + j += size; + } + 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 +} diff --git a/src/lib/strings/strings_test.go b/src/lib/strings/strings_test.go new file mode 100644 index 000000000..2cbf70b93 --- /dev/null +++ b/src/lib/strings/strings_test.go @@ -0,0 +1,81 @@ +// Copyright 2009 The Go 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); + } + } +} + diff --git a/src/lib/strings_test.go b/src/lib/strings_test.go deleted file mode 100644 index 2cbf70b93..000000000 --- a/src/lib/strings_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2009 The Go 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); - } - } -} - diff --git a/src/lib/testing.go b/src/lib/testing.go deleted file mode 100644 index 2f717d0e9..000000000 --- a/src/lib/testing.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2009 The Go 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"; -) - -// Report as tests are run; default is silent for success. -var chatty = flag.Bool("chatty", false, "chatty") - -// Insert tabs after newlines - but not the last one -func tabify(s string) string { - for i := 0; i < len(s) - 1; i++ { // -1 because if last char is newline, don't bother - if s[i] == '\n' { - return s[0:i+1] + "\t" + tabify(s[i+1:len(s)]); - } - } - 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 -} - -// 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; - sys.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 += tabify(fmt.Sprintf("\t" + format, args)); - l := len(t.errors); - if l > 0 && t.errors[l-1] != '\n' { - t.errors += "\n" - } -} - -// 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(); - ok := true; - if len(tests) == 0 { - println("testing: warning: no tests to run"); - } - for i := 0; i < len(tests); i++ { - 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"); - sys.Exit(1); - } - println("PASS"); -} diff --git a/src/lib/testing/Makefile b/src/lib/testing/Makefile new file mode 100644 index 000000000..f8adc5934 --- /dev/null +++ b/src/lib/testing/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/testing.a + +packages: _obj$D/testing.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/testing.a $(GOROOT)/pkg$D/testing.a diff --git a/src/lib/testing/testing.go b/src/lib/testing/testing.go new file mode 100644 index 000000000..2f717d0e9 --- /dev/null +++ b/src/lib/testing/testing.go @@ -0,0 +1,135 @@ +// Copyright 2009 The Go 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"; +) + +// Report as tests are run; default is silent for success. +var chatty = flag.Bool("chatty", false, "chatty") + +// Insert tabs after newlines - but not the last one +func tabify(s string) string { + for i := 0; i < len(s) - 1; i++ { // -1 because if last char is newline, don't bother + if s[i] == '\n' { + return s[0:i+1] + "\t" + tabify(s[i+1:len(s)]); + } + } + 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 +} + +// 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; + sys.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 += tabify(fmt.Sprintf("\t" + format, args)); + l := len(t.errors); + if l > 0 && t.errors[l-1] != '\n' { + t.errors += "\n" + } +} + +// 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(); + ok := true; + if len(tests) == 0 { + println("testing: warning: no tests to run"); + } + for i := 0; i < len(tests); i++ { + 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"); + sys.Exit(1); + } + println("PASS"); +} diff --git a/src/lib/utf8.go b/src/lib/utf8.go deleted file mode 100644 index 5ce59894b..000000000 --- a/src/lib/utf8.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2009 The Go 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, i int, n int) (rune, size int, short bool) { - if n < 1 { - return RuneError, 0, true; - } - c0 := s[i]; - - // 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[i+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[i+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[i+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, i int) bool { - rune, size, short := decodeRuneInStringInternal(s, i, len(s) - i); - 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, i int) (rune, size int) { - var short bool; - rune, size, short = decodeRuneInStringInternal(s, i, len(s) - i); - 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); - i += size; - } - } - return n; -} - diff --git a/src/lib/utf8/Makefile b/src/lib/utf8/Makefile new file mode 100644 index 000000000..2919ddb4e --- /dev/null +++ b/src/lib/utf8/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= + +O_arm=5 +O_amd64=6 +O_386=8 +OS=568vq + +O=$(O_$(GOARCH)) +GC=$(O)g -I_obj +CC=$(O)c -FVw +AS=$(O)a +AR=6ar + +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) $*.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$D/utf8.a + +packages: _obj$D/utf8.a + +install: packages + test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg$D + cp _obj$D/utf8.a $(GOROOT)/pkg$D/utf8.a diff --git a/src/lib/utf8/utf8.go b/src/lib/utf8/utf8.go new file mode 100644 index 000000000..5ce59894b --- /dev/null +++ b/src/lib/utf8/utf8.go @@ -0,0 +1,290 @@ +// Copyright 2009 The Go 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, i int, n int) (rune, size int, short bool) { + if n < 1 { + return RuneError, 0, true; + } + c0 := s[i]; + + // 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[i+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[i+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[i+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, i int) bool { + rune, size, short := decodeRuneInStringInternal(s, i, len(s) - i); + 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, i int) (rune, size int) { + var short bool; + rune, size, short = decodeRuneInStringInternal(s, i, len(s) - i); + 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); + i += size; + } + } + return n; +} + diff --git a/src/lib/utf8/utf8_test.go b/src/lib/utf8/utf8_test.go new file mode 100644 index 000000000..3ba5ee2b8 --- /dev/null +++ b/src/lib/utf8/utf8_test.go @@ -0,0 +1,179 @@ +// Copyright 2009 The Go 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 ( + "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 bytes(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 := bytes(m.str); + if !utf8.FullRune(b) { + t.Errorf("FullRune(%q) (rune %04x) = false, want true", b, m.rune); + } + s := "xx"+m.str; + if !utf8.FullRuneInString(s, 2) { + t.Errorf("FullRuneInString(%q, 2) (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 := "xxx"+string(b1); + if utf8.FullRuneInString(s1, 3) { + t.Errorf("FullRune(%q, 3) = true, want false", s1); + } + } +} + +func equalBytes(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; +} + +func TestEncodeRune(t *testing.T) { + for i := 0; i < len(utf8map); i++ { + m := utf8map[i]; + b := bytes(m.str); + var buf [10]byte; + n := utf8.EncodeRune(m.rune, &buf); + b1 := buf[0:n]; + if !equalBytes(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 := bytes(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 := "xx"+m.str; + rune, size = utf8.DecodeRuneInString(s, 2); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRune(%q, 2) = 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 = "x"+m.str+"\x00"; + rune, size = utf8.DecodeRuneInString(s, 1); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRuneInString(%q, 1) = 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 = "xxx"+m.str[0:len(m.str)-1]; + rune, size = utf8.DecodeRuneInString(s, 3); + if rune != RuneError || size != wantsize { + t.Errorf("DecodeRuneInString(%q, 3) = 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 = "xxxx"+string(b); + rune, size = utf8.DecodeRune(b); + if rune != RuneError || size != 1 { + t.Errorf("DecodeRuneInString(%q, 4) = 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(bytes(tt.in)); out != tt.out { + t.Errorf("RuneCount(%q) = %d, want %d", tt.in, out, tt.out); + } + } +} diff --git a/src/lib/utf8_test.go b/src/lib/utf8_test.go deleted file mode 100644 index 3ba5ee2b8..000000000 --- a/src/lib/utf8_test.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2009 The Go 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 ( - "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 bytes(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 := bytes(m.str); - if !utf8.FullRune(b) { - t.Errorf("FullRune(%q) (rune %04x) = false, want true", b, m.rune); - } - s := "xx"+m.str; - if !utf8.FullRuneInString(s, 2) { - t.Errorf("FullRuneInString(%q, 2) (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 := "xxx"+string(b1); - if utf8.FullRuneInString(s1, 3) { - t.Errorf("FullRune(%q, 3) = true, want false", s1); - } - } -} - -func equalBytes(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; -} - -func TestEncodeRune(t *testing.T) { - for i := 0; i < len(utf8map); i++ { - m := utf8map[i]; - b := bytes(m.str); - var buf [10]byte; - n := utf8.EncodeRune(m.rune, &buf); - b1 := buf[0:n]; - if !equalBytes(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 := bytes(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 := "xx"+m.str; - rune, size = utf8.DecodeRuneInString(s, 2); - if rune != m.rune || size != len(b) { - t.Errorf("DecodeRune(%q, 2) = 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 = "x"+m.str+"\x00"; - rune, size = utf8.DecodeRuneInString(s, 1); - if rune != m.rune || size != len(b) { - t.Errorf("DecodeRuneInString(%q, 1) = 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 = "xxx"+m.str[0:len(m.str)-1]; - rune, size = utf8.DecodeRuneInString(s, 3); - if rune != RuneError || size != wantsize { - t.Errorf("DecodeRuneInString(%q, 3) = 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 = "xxxx"+string(b); - rune, size = utf8.DecodeRune(b); - if rune != RuneError || size != 1 { - t.Errorf("DecodeRuneInString(%q, 4) = 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(bytes(tt.in)); out != tt.out { - t.Errorf("RuneCount(%q) = %d, want %d", tt.in, out, tt.out); - } - } -} diff --git a/src/lib/xml.go b/src/lib/xml.go deleted file mode 100644 index bd944337e..000000000 --- a/src/lib/xml.go +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright 2009 The Go 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 ..., or a combined start/end tag . -// The latter is identical in semantics to , -// 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 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 Google -// or Google. -// The former is an element with the text "Google" inside it. -// The latter is a element with that 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: -// -// -// Google -//
-// -// 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 -// . -// 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 -// -// 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 -// , 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 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 . 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 . 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 ) 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: -// -// -// text1 -// text2 -// -// -// and -// -// -// text1 -// text2 -// -// -// 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: -// -// -// text1 -// text2 -// -// -// is another XML document equivalent to the first two, and -// -// -// text1 -// text2 -// -// -// 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. - // - // - // 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. - // - // - EndElement(name Name) os.Error; - - // Called for non-empty character data string inside element. - // Can be called multiple times between elements. - // text - // - Text(text []byte) os.Error; - - // Called when a comment is found in the XML. - // - Comment(text []byte) os.Error; - - // Called for a processing instruction - // - 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 ]*> -// -// procinst is <\?name( .*?)\?>. name cannot be [Xx][Mm][Ll]. -// -// comment is . -// -// tags are: -// start tag -// combined start/end tag -// 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 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 . -// -// 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 - diff --git a/src/lib/xml/xml.go b/src/lib/xml/xml.go new file mode 100644 index 000000000..bd944337e --- /dev/null +++ b/src/lib/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 ..., or a combined start/end tag . +// The latter is identical in semantics to , +// 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 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 Google +// or Google. +// The former is an element with the text "Google" inside it. +// The latter is a element with that 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: +// +// +// Google +//
+// +// 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 +// . +// 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 +// +// 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 +// , 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 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 . 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 . 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 ) 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: +// +// +// text1 +// text2 +// +// +// and +// +// +// text1 +// text2 +// +// +// 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: +// +// +// text1 +// text2 +// +// +// is another XML document equivalent to the first two, and +// +// +// text1 +// text2 +// +// +// 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. + // + // + // 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. + // + // + EndElement(name Name) os.Error; + + // Called for non-empty character data string inside element. + // Can be called multiple times between elements. + // text + // + Text(text []byte) os.Error; + + // Called when a comment is found in the XML. + // + Comment(text []byte) os.Error; + + // Called for a processing instruction + // + 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 ]*> +// +// procinst is <\?name( .*?)\?>. name cannot be [Xx][Mm][Ll]. +// +// comment is . +// +// tags are: +// start tag +// combined start/end tag +// 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 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 . +// +// 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 + -- cgit v1.2.3