diff options
author | Rob Pike <r@golang.org> | 2008-03-27 00:06:21 -0700 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2008-03-27 00:06:21 -0700 |
commit | 281b270ba26499019bad97700103f38f3df5f0d2 (patch) | |
tree | 4706e11209d99fc5b608582cf1fb7b735e1658b5 | |
parent | b9829f88caae9d67410240956843ea812d44fe22 (diff) | |
download | golang-281b270ba26499019bad97700103f38f3df5f0d2.tar.gz |
Basic formatted I/O library plus one support lib.
SVN=113977
-rw-r--r-- | src/lib/fmt.go | 484 | ||||
-rw-r--r-- | src/lib/sys.go | 16 |
2 files changed, 500 insertions, 0 deletions
diff --git a/src/lib/fmt.go b/src/lib/fmt.go new file mode 100644 index 000000000..715643432 --- /dev/null +++ b/src/lib/fmt.go @@ -0,0 +1,484 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmt + +/* + f := fmt.New(); + print f.d(1234).s("\n").str(); // create string, print it + f.d(-1234).s("\n").put(); // print string + f.ud(^0).putnl(); // print string with automatic newline +*/ + +import sys "sys" + +export Fmt, New; + +var ldigits [16]byte; // BUG: should be constants +var udigits [16]byte; // BUG: can they be strings? looks like trouble with buf[i] = digits[val]; +var inited bool; +var pows10 [160] double; + +type Fmt struct { + buf string; + wid int; + wid_present bool; + prec int; + prec_present bool; +} + +func (f *Fmt) clearflags() { + f.wid_present = false; + f.prec_present = false; +} + +func (f *Fmt) clearbuf() { + f.buf = ""; +} + +func (f *Fmt) init() { + f.clearbuf(); + f.clearflags(); + if inited { + return; + } + var i byte; + for i = 0; i < 10; i++ { + ldigits[i] = '0' + i; + udigits[i] = '0' + i; + } + for i = 0; i < 6; i++ { + ldigits[i+10] = 'a' + i; + udigits[i+10] = 'A' + i; + } + // BUG: should be done with initialization + var p double = 1.0; + for i = 0; i < 160; i++ { // BUG: len(pows10) + pows10[i] = p; + p *= 10.0; + } + inited = true; +} + +func New() *Fmt { + f := new(Fmt); + f.init(); + return f; +} + +func (f *Fmt) str() string { + s := f.buf; + f.clearbuf(); + f.clearflags(); + f.buf = ""; + return s; +} + +func (f *Fmt) put() { + print f.buf; + f.clearbuf(); + f.clearflags(); +} + +func (f *Fmt) putnl() { + print f.buf, "\n"; + f.clearbuf(); + f.clearflags(); +} + +func (f *Fmt) wp(w, p int) *Fmt { + f.wid_present = true; + f.wid = w; + f.prec_present = true; + f.prec = p; + return f; +} + +func (f *Fmt) p(p int) *Fmt { + f.prec_present = true; + f.prec = p; + return f; +} + +func (f *Fmt) w(x int) *Fmt { + f.wid_present = true; + f.wid = x; + return f; +} + +// append s to buf, padded on left (w > 0) or right (w < 0) +// padding is in bytes, not characters (agrees with ANSIC C, not Plan 9 C) +func (f *Fmt) pad(s string) { + if f.wid_present && f.wid != 0 { + left := true; + w := f.wid; + if w < 0 { + left = false; + w = -w; + } + w -= len(s); + if w > 0 { + if w > 64 { // BUG: should be able to use a const + w = 64; + } + var buf[64] byte; // BUG: should be able to allocate a size + for i := 0; i < w; i++ { + buf[i] = ' '; + } + if left { + s = string(buf)[0:w] + s; + } else { + s = s + string(buf)[0:w]; + } + } + } + f.buf = f.buf + s; // BUG: += should work +} + +// format val into buf, ending at buf[i]. (printing is easier right-to-left; +// that's why the bidi languages are right-to-left except for numbers. wait, +// never mind.) val is known to be unsigned. we could make things maybe +// marginally faster by splitting the 32-bit case out into a separate function +// but it's not worth the duplication, so val has 64 bits. +func putint(buf *[64]byte, i int, base, val uint64, digits *[16]byte) int { + for val >= base { + buf[i] = digits[val%base]; + i--; + val /= base; + } + buf[i] = digits[val]; + return i-1; +} + +// integer; interprets prec but not wid. +func (f *Fmt) integer(a int64, base uint, is_signed bool, digits *[16]byte) string { + var buf [64]byte; + negative := is_signed && a < 0; + if negative { + a = -a; + } + i := putint(&buf, 63, uint64(base), uint64(a), digits); + if f.prec_present { + for i > 0 && f.prec > (63-i) { + buf[i] = '0'; + i--; + } + } + if negative { + buf[i] = '-'; + i--; + } + return string(buf)[i+1:64]; +} + +// decimal +func (f *Fmt) d(a int32) *Fmt { + f.pad(f.integer(int64(a), 10, true, &ldigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) D(a int64) *Fmt { + f.pad(f.integer(a, 10, true, &ldigits)); + f.clearflags(); + return f; +} + +// unsigned decimal +func (f *Fmt) ud(a int32) *Fmt { + f.pad(f.integer(int64(uint32(a)), 10, false, &ldigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) uD(a int64) *Fmt { + f.pad(f.integer(a, 10, false, &ldigits)); + f.clearflags(); + return f; +} + +// hexdecimal +func (f *Fmt) x(a int32) *Fmt { + f.pad(f.integer(int64(a), 16, true, &ldigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) X(a int64) *Fmt { + f.pad(f.integer(a, 16, true, &ldigits)); + f.clearflags(); + return f; +} + +// unsigned hexdecimal +func (f *Fmt) ux(a int32) *Fmt { + f.pad(f.integer(int64(uint32(a)), 16, false, &ldigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) uX(a int64) *Fmt { + f.pad(f.integer(a, 16, false, &ldigits)); + f.clearflags(); + return f; +} + +// HEXADECIMAL +func (f *Fmt) Ux(a int32) *Fmt { + f.pad(f.integer(int64(a), 16, true, &udigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) UX(a int64) *Fmt { + f.pad(f.integer(a, 16, true, &udigits)); + f.clearflags(); + return f; +} + +// unsigned HEXADECIMAL +func (f *Fmt) uUx(a int32) *Fmt { + f.pad(f.integer(int64(uint32(a)), 16, false, &udigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) uUX(a int64) *Fmt { + f.pad(f.integer(a, 16, false, &udigits)); + f.clearflags(); + return f; +} + +// octal +func (f *Fmt) o(a int32) *Fmt { + f.pad(f.integer(int64(a), 8, true, &ldigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) O(a int64) *Fmt { + f.pad(f.integer(a, 8, true, &ldigits)); + f.clearflags(); + return f; +} + +// unsigned octal +func (f *Fmt) uo(a int32) *Fmt { + f.pad(f.integer(int64(uint32(a)), 8, false, &ldigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) uO(a int64) *Fmt { + f.pad(f.integer(a, 8, false, &ldigits)); + f.clearflags(); + return f; +} + +// binary +func (f *Fmt) b(a int32) *Fmt { + f.pad(f.integer(int64(uint32(a)), 2, false, &ldigits)); + f.clearflags(); + return f; +} + +func (f *Fmt) B(a int64) *Fmt { + f.pad(f.integer(a, 2, false, &ldigits)); + f.clearflags(); + return f; +} + +// character +func (f *Fmt) c(a int) *Fmt { + f.pad(string(a)); + f.clearflags(); + return f; +} + +// string +func (f *Fmt) s(s string) *Fmt { + if f.prec_present { + if f.prec < len(s) { + w := f.prec; // BUG: can't use f.prec in slice + s = s[0:w]; + } + } + f.pad(s); + f.clearflags(); + return f; +} + +func pow10(n int) double { + var d double; + npows10 := 160; // nelem(pows10); BUG: why not a const? + + neg := false; + if n < 0 { + if n < -307 { // DBL_MIN_10_EXP + return 0.; + } + neg = true; + n = -n; + }else if n > 308 { // DBL_MAX_10_EXP + return 1.79769e+308; // HUGE_VAL + } + + if n < npows10 { + d = pows10[n]; + } else { + d = pows10[npows10-1]; + for { + n -= npows10 - 1; + if n < npows10 { + d *= pows10[n]; + break; + } + d *= pows10[npows10 - 1]; + } + } + if neg { + return 1/d; + } + return d; +} + +func unpack(a double) (negative bool, exp int, num double) { + neg := a < 0; + if neg { + a = -a; + } + // find g,e such that a = g*10^e. + // guess 10-exponent using 2-exponent, then fine tune. + var g double; + var e2 int; + e2, g = sys.frexp(a); + e := int(e2 * .301029995663981); + g := a * pow10(-e); + for g < 1 { + e--; + g = a * pow10(-e); + } + for g >= 10 { + e++; + g = a * pow10(-e); + } + return neg, e, g; +} + +// double +func (f *Fmt) E(a double) *Fmt { + var negative bool; + var g double; + var exp int; + negative, exp, g = unpack(a); + prec := 6; + if f.prec_present { + prec = f.prec; + } + prec++; // one digit left of decimal + // multiply by 10^prec to get decimal places; put decimal after first digit + g *= pow10(prec); + s := f.integer(int64(g + .5), 10, true, &ldigits); // get the digits into a string + s = s[0:1] + "." + s[1:prec]; // insert a decimal point + // print exponent with leading 0 if appropriate. + es := New().p(2).integer(int64(exp), 10, true, &ldigits); + if exp > 0 { + es = "+" + es; // BUG: should do this with a fmt flag + } + s = s + "e" + es; + if negative { + s = "-" + s; + } + f.pad(s); + f.clearflags(); + return f; +} + +// double +func (f *Fmt) F(a double) *Fmt { + var negative bool; + var g double; + var exp int; + negative, exp, g = unpack(a); + if exp > 19 || exp < -19 { // too big for this sloppy code + return f.E(a); + } + prec := 6; + if f.prec_present { + prec = f.prec; + } + // prec is number of digits after decimal point + s := "NO"; + if exp >= 0 { + g *= pow10(exp); + gi := int64(g); + s = New().integer(gi, 10, true, &ldigits); + s = s + "."; + g -= double(gi); + s = s + New().p(prec).integer(int64(g*pow10(prec) + .5), 10, true, &ldigits); + } else { + g *= pow10(prec + exp); + s = "0." + New().p(prec).integer(int64(g + .5), 10, true, &ldigits); + } + if negative { + s = "-" + s; + } + f.pad(s); + f.clearflags(); + return f; +} + +// double +func (f *Fmt) G(a double) *Fmt { + f1 := New(); + f2 := New(); + if f.wid_present { + f1.w(f.wid); + f2.w(f.wid); + } + if f.prec_present { + f1.p(f.prec); + f2.p(f.prec); + } + efmt := f1.E(a).str(); + ffmt := f2.F(a).str(); + // ffmt can return e in my bogus world; don't trim trailing 0s if so. + f_is_e := false; + for i := 0; i < len(ffmt); i++ { + if ffmt[i] == 'e' { + f_is_e = true; + break; + } + } + if !f_is_e { + // strip trailing zeros + l := len(ffmt); + for ffmt[l-1]=='0' { + l--; + } + ffmt = ffmt[0:l]; + } + if len(efmt) < len(ffmt) { + f.pad(efmt); + } else { + f.pad(ffmt); + } + f.clearflags(); + return f; +} + +// float +func (x *Fmt) f(a float) *Fmt { + return x.F(double(a)); +} + +// float +func (x *Fmt) e(a float) *Fmt { + return x.E(double(a)); +} + +// float +func (x *Fmt) g(a float) *Fmt { + return x.G(double(a)); +} diff --git a/src/lib/sys.go b/src/lib/sys.go new file mode 100644 index 000000000..eddfd1fad --- /dev/null +++ b/src/lib/sys.go @@ -0,0 +1,16 @@ +// Copyright 2009 The Go 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 sys + +func modf(a double) (double, double); +func frexp(a double) (int, double); +func ldexp(double, int) double; + +func Inf(n int) double; +func NaN() double; +func isInf(arg double, n int) bool; + +export modf, frexp, ldexp +export NaN, isInf, Inf |