diff options
21 files changed, 676 insertions, 89 deletions
diff --git a/src/pkg/Make.deps b/src/pkg/Make.deps index 1114f5515..49862d2dc 100644 --- a/src/pkg/Make.deps +++ b/src/pkg/Make.deps @@ -1,7 +1,5 @@ archive/tar.install: bytes.install io.install os.install strconv.install strings.install asn1.install: fmt.install os.install reflect.install strconv.install strings.install time.install -base64.install: bytes.install io.install os.install strconv.install -base85.install: bytes.install io.install os.install strconv.install big.install: bignum.install: fmt.install bufio.install: io.install os.install strconv.install utf8.install @@ -19,13 +17,16 @@ crypto/hmac.install: crypto/md5.install crypto/sha1.install hash.install os.inst crypto/md5.install: hash.install os.install crypto/rc4.install: os.install strconv.install crypto/sha1.install: hash.install os.install -debug/binary.install: io.install math.install os.install reflect.install -debug/dwarf.install: debug/binary.install os.install strconv.install -debug/macho.install: bytes.install debug/binary.install debug/dwarf.install fmt.install io.install os.install strconv.install -debug/elf.install: debug/binary.install debug/dwarf.install fmt.install io.install os.install strconv.install -debug/gosym.install: debug/binary.install fmt.install os.install strconv.install strings.install +debug/dwarf.install: encoding/binary.install os.install strconv.install +debug/macho.install: bytes.install debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install +debug/elf.install: debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install +debug/gosym.install: encoding/binary.install fmt.install os.install strconv.install strings.install debug/proc.install: container/vector.install fmt.install io.install os.install runtime.install strconv.install strings.install sync.install syscall.install ebnf.install: container/vector.install go/scanner.install go/token.install os.install strconv.install unicode.install utf8.install +encoding/ascii85.install: bytes.install io.install os.install strconv.install +encoding/base64.install: bytes.install io.install os.install strconv.install +encoding/binary.install: io.install math.install os.install reflect.install +encoding/git85.install: bytes.install io.install os.install strconv.install exec.install: os.install strings.install exp/datafmt.install: bytes.install container/vector.install fmt.install go/scanner.install go/token.install io.install os.install reflect.install runtime.install strconv.install strings.install exp/eval.install: bignum.install fmt.install go/ast.install go/parser.install go/scanner.install go/token.install log.install os.install reflect.install runtime.install strconv.install strings.install diff --git a/src/pkg/Makefile b/src/pkg/Makefile index 8761558de..cd50bb92f 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -14,8 +14,6 @@ all: install DIRS=\ archive/tar\ asn1\ - base64\ - base85\ big\ bignum\ bufio\ @@ -33,13 +31,16 @@ DIRS=\ crypto/md5\ crypto/rc4\ crypto/sha1\ - debug/binary\ debug/dwarf\ debug/macho\ debug/elf\ debug/gosym\ debug/proc\ ebnf\ + encoding/ascii85\ + encoding/base64\ + encoding/binary\ + encoding/git85\ exec\ exp/datafmt\ exp/eval\ diff --git a/src/pkg/debug/dwarf/buf.go b/src/pkg/debug/dwarf/buf.go index 2d8211090..34880a5d5 100644 --- a/src/pkg/debug/dwarf/buf.go +++ b/src/pkg/debug/dwarf/buf.go @@ -7,7 +7,7 @@ package dwarf import ( - "debug/binary"; + "encoding/binary"; "os"; "strconv"; ) diff --git a/src/pkg/debug/dwarf/open.go b/src/pkg/debug/dwarf/open.go index f2cfa4c93..6fc34fed3 100644 --- a/src/pkg/debug/dwarf/open.go +++ b/src/pkg/debug/dwarf/open.go @@ -8,7 +8,7 @@ package dwarf import ( - "debug/binary"; + "encoding/binary"; "os"; ) diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index 0c5d6f317..0b5ff3fa1 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -6,8 +6,8 @@ package elf import ( - "debug/binary"; "debug/dwarf"; + "encoding/binary"; "fmt"; "io"; "os"; diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 01e638eea..9b756aea1 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -5,7 +5,7 @@ package elf import ( - "debug/binary"; + "encoding/binary"; "reflect"; "testing"; ) diff --git a/src/pkg/debug/gosym/pclntab.go b/src/pkg/debug/gosym/pclntab.go index 24c368616..8008ada83 100644 --- a/src/pkg/debug/gosym/pclntab.go +++ b/src/pkg/debug/gosym/pclntab.go @@ -8,7 +8,7 @@ package gosym -import "debug/binary" +import "encoding/binary" type LineTable struct { Data []byte; diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go index b531db6e0..7edbc0390 100644 --- a/src/pkg/debug/gosym/symtab.go +++ b/src/pkg/debug/gosym/symtab.go @@ -13,7 +13,7 @@ package gosym // and the Go format is the runtime source, specifically ../../runtime/symtab.c. import ( - "debug/binary"; + "encoding/binary"; "fmt"; "os"; "strconv"; diff --git a/src/pkg/debug/macho/file.go b/src/pkg/debug/macho/file.go index fee02fb27..67af39be5 100644 --- a/src/pkg/debug/macho/file.go +++ b/src/pkg/debug/macho/file.go @@ -10,8 +10,8 @@ package macho import ( "bytes"; - "debug/binary"; "debug/dwarf"; + "encoding/binary"; "fmt"; "io"; "os"; diff --git a/src/pkg/encoding/ascii85/Makefile b/src/pkg/encoding/ascii85/Makefile new file mode 100644 index 000000000..5a7bc176c --- /dev/null +++ b/src/pkg/encoding/ascii85/Makefile @@ -0,0 +1,11 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.$(GOARCH) + +TARG=encoding/ascii85 +GOFILES=\ + ascii85.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/base85/base85.go b/src/pkg/encoding/ascii85/ascii85.go index b8d250a17..27256eeca 100644 --- a/src/pkg/base85/base85.go +++ b/src/pkg/encoding/ascii85/ascii85.go @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package base85 implements radix 85 encoding/decoding. -package base85 +// Package ascii85 implements the ascii85 data encoding +// as used in the btoa tool and Adobe's PostScript and PDF document formats. +package ascii85 import ( "bytes"; @@ -18,12 +19,13 @@ import ( // Encode encodes src into at most MaxEncodedLen(len(src)) // bytes of dst, returning the actual number of bytes written. -// Encode implements the ascii85 encoding as used in the btoa -// tool and Adobe's PostScript and PDF document formats. // // The encoding handles 4-byte chunks, using a special encoding // for the last fragment, so Encode is not appropriate for use on // individual blocks of a large data stream. Use NewEncoder() instead. +// +// Often, ascii85-encoded data is wrapped in <~ and ~> symbols. +// Encode does not add these. func Encode(src, dst []byte) int { if len(src) == 0 { return 0; @@ -183,6 +185,8 @@ func (e CorruptInputError) String() string { // If src contains invalid ascii85 data, Decode will return the // number of bytes successfully written and a CorruptInputError. // Decode ignores space and control characters in src. +// Often, ascii85-encoded data is wrapped in <~ and ~> symbols. +// Decode expects these to have been stripped by the caller. // // If flush is true, Decode assumes that src represents the // end of the input stream and processes it completely rather diff --git a/src/pkg/base85/base85_test.go b/src/pkg/encoding/ascii85/ascii85_test.go index 01f329830..0264333ab 100644 --- a/src/pkg/base85/base85_test.go +++ b/src/pkg/encoding/ascii85/ascii85_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package base85 +package ascii85 import ( "bytes"; diff --git a/src/pkg/encoding/base64/Makefile b/src/pkg/encoding/base64/Makefile new file mode 100644 index 000000000..1afb0ebb8 --- /dev/null +++ b/src/pkg/encoding/base64/Makefile @@ -0,0 +1,11 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.$(GOARCH) + +TARG=encoding/base64 +GOFILES=\ + base64.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/encoding/base64/base64.go b/src/pkg/encoding/base64/base64.go new file mode 100644 index 000000000..b4daee3cc --- /dev/null +++ b/src/pkg/encoding/base64/base64.go @@ -0,0 +1,334 @@ +// Copyright 2009 The Go 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 base64 implements base64 encoding as specified by RFC 4648. +package base64 + +import ( + "bytes"; + "io"; + "os"; + "strconv"; +) + +/* + * Encodings + */ + +// An Encoding is a radix 64 encoding/decoding scheme, defined by a +// 64-character alphabet. The most common encoding is the "base64" +// encoding defined in RFC 4648 and used in MIME (RFC 2045) and PEM +// (RFC 1421). RFC 4648 also defines an alternate encoding, which is +// the standard encoding with - and _ substituted for + and /. +type Encoding struct { + encode string; + decodeMap [256]byte; +} + +const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" + +// NewEncoding returns a new Encoding defined by the given alphabet, +// which must be a 64-byte string. +func NewEncoding(encoder string) *Encoding { + e := new(Encoding); + e.encode = encoder; + for i := 0; i < len(e.decodeMap); i++ { + e.decodeMap[i] = 0xFF; + } + for i := 0; i < len(encoder); i++ { + e.decodeMap[encoder[i]] = byte(i); + } + return e; +} + +// StdEncoding is the standard base64 encoding, as defined in +// RFC 4648. +var StdEncoding = NewEncoding(encodeStd) + +// URLEncoding is the alternate base64 encoding defined in RFC 4648. +// It is typically used in URLs and file names. +var URLEncoding = NewEncoding(encodeURL) + +/* + * Encoder + */ + +// Encode encodes src using the encoding enc, writing +// EncodedLen(len(src)) bytes to dst. +// +// The encoding pads the output to a multiple of 4 bytes, +// so Encode is not appropriate for use on individual blocks +// of a large data stream. Use NewEncoder() instead. +func (enc *Encoding) Encode(src, dst []byte) { + if len(src) == 0 { + return; + } + + for len(src) > 0 { + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; + + // Unpack 4x 6-bit source blocks into a 4 byte + // destination quantum + switch len(src) { + default: + dst[3] |= src[2]&0x3F; + dst[2] |= src[2]>>6; + fallthrough; + case 2: + dst[2] |= (src[1]<<2)&0x3F; + dst[1] |= src[1]>>4; + fallthrough; + case 1: + dst[1] |= (src[0]<<4)&0x3F; + dst[0] |= src[0]>>2; + } + + // Encode 6-bit blocks using the base64 alphabet + for j := 0; j < 4; j++ { + dst[j] = enc.encode[dst[j]]; + } + + // Pad the final quantum + if len(src) < 3 { + dst[3] = '='; + if len(src) < 2 { + dst[2] = '='; + } + break; + } + + src = src[3:len(src)]; + dst = dst[4:len(dst)]; + } +} + +type encoder struct { + err os.Error; + enc *Encoding; + w io.Writer; + buf [3]byte; // buffered data waiting to be encoded + nbuf int; // number of bytes in buf + out [1024]byte; // output buffer +} + +func (e *encoder) Write(p []byte) (n int, err os.Error) { + if e.err != nil { + return 0, e.err; + } + + // Leading fringe. + if e.nbuf > 0 { + var i int; + for i = 0; i < len(p) && e.nbuf < 3; i++ { + e.buf[e.nbuf] = p[i]; + e.nbuf++; + } + n += i; + p = p[i:len(p)]; + if e.nbuf < 3 { + return; + } + e.enc.Encode(&e.buf, &e.out); + if _, e.err = e.w.Write(e.out[0:4]); e.err != nil { + return n, e.err; + } + e.nbuf = 0; + } + + // Large interior chunks. + for len(p) >= 3 { + nn := len(e.out)/4*3; + if nn > len(p) { + nn = len(p); + } + nn -= nn%3; + if nn > 0 { + e.enc.Encode(p[0:nn], &e.out); + if _, e.err = e.w.Write(e.out[0 : nn/3*4]); e.err != nil { + return n, e.err; + } + } + n += nn; + p = p[nn:len(p)]; + } + + // Trailing fringe. + for i := 0; i < len(p); i++ { + e.buf[i] = p[i]; + } + e.nbuf = len(p); + n += len(p); + return; +} + +// Close flushes any pending output from the encoder. +// It is an error to call Write after calling Close. +func (e *encoder) Close() os.Error { + // If there's anything left in the buffer, flush it out + if e.err == nil && e.nbuf > 0 { + e.enc.Encode(e.buf[0 : e.nbuf], &e.out); + e.nbuf = 0; + _, e.err = e.w.Write(e.out[0:4]); + } + return e.err; +} + +// NewEncoder returns a new base64 stream encoder. Data written to +// the returned writer will be encoded using enc and then written to w. +// Base64 encodings operate in 4-byte blocks; when finished +// writing, the caller must Close the returned encoder to flush any +// partially written blocks. +func NewEncoder(enc *Encoding, w io.Writer) io.WriteCloser { + return &encoder{enc: enc, w: w}; +} + +// EncodedLen returns the length in bytes of the base64 encoding +// of an input buffer of length n. +func (enc *Encoding) EncodedLen(n int) int { + return (n+2)/3*4; +} + +/* + * Decoder + */ + +type CorruptInputError int64 + +func (e CorruptInputError) String() string { + return "illegal base64 data at input byte" + strconv.Itoa64(int64(e)); +} + +// decode is like Decode but returns an additional 'end' value, which +// indicates if end-of-message padding was encountered and thus any +// additional data is an error. decode also assumes len(src)%4==0, +// since it is meant for internal use. +func (enc *Encoding) decode(src, dst []byte) (n int, end bool, err os.Error) { + for i := 0; i < len(src)/4 && !end; i++ { + // Decode quantum using the base64 alphabet + var dbuf [4]byte; + dlen := 4; + + dbufloop: + for j := 0; j < 4; j++ { + in := src[i*4 + j]; + if in == '=' && j >= 2 && i == len(src)/4 - 1 { + // We've reached the end and there's + // padding + if src[i*4 + 3] != '=' { + return n, false, CorruptInputError(i*4 + 2); + } + dlen = j; + end = true; + break dbufloop; + } + dbuf[j] = enc.decodeMap[in]; + if dbuf[j] == 0xFF { + return n, false, CorruptInputError(i*4 + j); + } + } + + // Pack 4x 6-bit source blocks into 3 byte destination + // quantum + switch dlen { + case 4: + dst[i*3 + 2] = dbuf[2]<<6 | dbuf[3]; + fallthrough; + case 3: + dst[i*3 + 1] = dbuf[1]<<4 | dbuf[2]>>2; + fallthrough; + case 2: + dst[i*3 + 0] = dbuf[0]<<2 | dbuf[1]>>4; + } + n += dlen-1; + } + + return n, end, nil; +} + +// Decode decodes src using the encoding enc. It writes at most +// DecodedLen(len(src)) bytes to dst and returns the number of bytes +// written. If src contains invalid base64 data, it will return the +// number of bytes successfully written and CorruptInputError. +func (enc *Encoding) Decode(src, dst []byte) (n int, err os.Error) { + if len(src)%4 != 0 { + return 0, CorruptInputError(len(src)/4*4); + } + + n, _, err = enc.decode(src, dst); + return; +} + +type decoder struct { + err os.Error; + enc *Encoding; + r io.Reader; + end bool; // saw end of message + buf [1024]byte; // leftover input + nbuf int; + out []byte; // leftover decoded output + outbuf [1024/4*3]byte; +} + +func (d *decoder) Read(p []byte) (n int, err os.Error) { + if d.err != nil { + return 0, d.err; + } + + // Use leftover decoded output from last read. + if len(d.out) > 0 { + n = bytes.Copy(p, d.out); + d.out = d.out[n:len(d.out)]; + return n, nil; + } + + // Read a chunk. + nn := len(p)/3*4; + if nn < 4 { + nn = 4; + } + if nn > len(d.buf) { + nn = len(d.buf); + } + nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf : nn], 4 - d.nbuf); + d.nbuf += nn; + if d.nbuf < 4 { + return 0, d.err; + } + + // Decode chunk into p, or d.out and then p if p is too small. + nr := d.nbuf / 4 * 4; + nw := d.nbuf / 4 * 3; + if nw > len(p) { + nw, d.end, d.err = d.enc.decode(d.buf[0:nr], &d.outbuf); + d.out = d.outbuf[0:nw]; + n = bytes.Copy(p, d.out); + d.out = d.out[n:len(d.out)]; + } else { + n, d.end, d.err = d.enc.decode(d.buf[0:nr], p); + } + d.nbuf -= nr; + for i := 0; i < d.nbuf; i++ { + d.buf[i] = d.buf[i+nr]; + } + + if d.err == nil { + d.err = err; + } + return n, d.err; +} + +// NewDecoder constructs a new base64 stream decoder. +func NewDecoder(enc *Encoding, r io.Reader) io.Reader { + return &decoder{enc: enc, r: r}; +} + +// DecodeLen returns the maximum length in bytes of the decoded data +// corresponding to n bytes of base64-encoded data. +func (enc *Encoding) DecodedLen(n int) int { + return n/4*3; +} diff --git a/src/pkg/encoding/base64/base64_test.go b/src/pkg/encoding/base64/base64_test.go new file mode 100644 index 000000000..54d2326f5 --- /dev/null +++ b/src/pkg/encoding/base64/base64_test.go @@ -0,0 +1,201 @@ +// Copyright 2009 The Go 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 base64 + +import ( + "bytes"; + "io"; + "os"; + "reflect"; + "strings"; + "testing"; +) + +type testpair struct { + decoded, encoded string; +} + +var pairs = []testpair{ + // RFC 3548 examples + testpair{"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"}, + testpair{"\x14\xfb\x9c\x03\xd9", "FPucA9k="}, + testpair{"\x14\xfb\x9c\x03", "FPucAw=="}, + + // RFC 4648 examples + testpair{"", ""}, + testpair{"f", "Zg=="}, + testpair{"fo", "Zm8="}, + testpair{"foo", "Zm9v"}, + testpair{"foob", "Zm9vYg=="}, + testpair{"fooba", "Zm9vYmE="}, + testpair{"foobar", "Zm9vYmFy"}, + + // Wikipedia examples + testpair{"sure.", "c3VyZS4="}, + testpair{"sure", "c3VyZQ=="}, + testpair{"sur", "c3Vy"}, + testpair{"su", "c3U="}, + testpair{"leasure.", "bGVhc3VyZS4="}, + testpair{"easure.", "ZWFzdXJlLg=="}, + testpair{"asure.", "YXN1cmUu"}, + testpair{"sure.", "c3VyZS4="}, +} + +var bigtest = testpair{ + "Twas brillig, and the slithy toves", + "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", +} + +func testEqual(t *testing.T, msg string, args ...) bool { + v := reflect.NewValue(args).(*reflect.StructValue); + v1 := v.Field(v.NumField() - 2); + v2 := v.Field(v.NumField() - 1); + if v1.Interface() != v2.Interface() { + t.Errorf(msg, args); + return false; + } + return true; +} + +func TestEncode(t *testing.T) { + for _, p := range pairs { + buf := make([]byte, StdEncoding.EncodedLen(len(p.decoded))); + StdEncoding.Encode(strings.Bytes(p.decoded), buf); + testEqual(t, "Encode(%q) = %q, want %q", p.decoded, string(buf), p.encoded); + } +} + +func TestEncoder(t *testing.T) { + for _, p := range pairs { + bb := &bytes.Buffer{}; + encoder := NewEncoder(StdEncoding, bb); + encoder.Write(strings.Bytes(p.decoded)); + encoder.Close(); + testEqual(t, "Encode(%q) = %q, want %q", p.decoded, bb.String(), p.encoded); + } +} + +func TestEncoderBuffering(t *testing.T) { + input := strings.Bytes(bigtest.decoded); + for bs := 1; bs <= 12; bs++ { + bb := &bytes.Buffer{}; + encoder := NewEncoder(StdEncoding, bb); + for pos := 0; pos < len(input); pos += bs { + end := pos+bs; + if end > len(input) { + end = len(input); + } + n, err := encoder.Write(input[pos:end]); + testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, os.Error(nil)); + testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos); + } + err := encoder.Close(); + testEqual(t, "Close gave error %v, want %v", err, os.Error(nil)); + testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, bb.String(), bigtest.encoded); + } +} + +func TestDecode(t *testing.T) { + for _, p := range pairs { + dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))); + count, end, err := StdEncoding.decode(strings.Bytes(p.encoded), dbuf); + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)); + testEqual(t, "Decode(%q) = length %v, want %v", p.encoded, count, len(p.decoded)); + if len(p.encoded) > 0 { + testEqual(t, "Decode(%q) = end %v, want %v", p.encoded, end, (p.encoded[len(p.encoded)-1] == '=')); + } + testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded); + } +} + +func TestDecoder(t *testing.T) { + for _, p := range pairs { + decoder := NewDecoder(StdEncoding, bytes.NewBufferString(p.encoded)); + dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))); + count, err := decoder.Read(dbuf); + if err != nil && err != os.EOF { + t.Fatal("Read failed", err); + } + testEqual(t, "Read from %q = length %v, want %v", p.encoded, count, len(p.decoded)); + testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded); + if err != os.EOF { + count, err = decoder.Read(dbuf); + } + testEqual(t, "Read from %q = %v, want %v", p.encoded, err, os.EOF); + } +} + +func TestDecoderBuffering(t *testing.T) { + for bs := 1; bs <= 12; bs++ { + decoder := NewDecoder(StdEncoding, bytes.NewBufferString(bigtest.encoded)); + buf := make([]byte, len(bigtest.decoded)+12); + var total int; + for total = 0; total < len(bigtest.decoded); { + n, err := decoder.Read(buf[total : total+bs]); + testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, os.Error(nil)); + total += n; + } + testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded); + } +} + +func TestDecodeCorrupt(t *testing.T) { + type corrupt struct { + e string; + p int; + } + examples := []corrupt{ + corrupt{"!!!!", 0}, + corrupt{"x===", 1}, + corrupt{"AA=A", 2}, + corrupt{"AAA=AAAA", 3}, + corrupt{"AAAAA", 4}, + corrupt{"AAAAAA", 4}, + }; + + for _, e := range examples { + dbuf := make([]byte, StdEncoding.DecodedLen(len(e.e))); + _, err := StdEncoding.Decode(strings.Bytes(e.e), dbuf); + switch err := err.(type) { + case CorruptInputError: + testEqual(t, "Corruption in %q at offset %v, want %v", e.e, int(err), e.p); + default: + t.Error("Decoder failed to detect corruption in", e); + } + } +} + +func TestBig(t *testing.T) { + n := 3*1000 + 1; + raw := make([]byte, n); + const alpha = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for i := 0; i < n; i++ { + raw[i] = alpha[i%len(alpha)]; + } + encoded := new(bytes.Buffer); + w := NewEncoder(StdEncoding, encoded); + nn, err := w.Write(raw); + if nn != n || err != nil { + t.Fatalf("Encoder.Write(raw) = %d, %v want %d, nil", nn, err, n); + } + err = w.Close(); + if err != nil { + t.Fatalf("Encoder.Close() = %v want nil", err); + } + decoded, err := io.ReadAll(NewDecoder(StdEncoding, encoded)); + if err != nil { + t.Fatalf("io.ReadAll(NewDecoder(...)): %v", err); + } + + if !bytes.Equal(raw, decoded) { + var i int; + for i = 0; i < len(decoded) && i < len(raw); i++ { + if decoded[i] != raw[i] { + break; + } + } + t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i); + } +} diff --git a/src/pkg/debug/binary/Makefile b/src/pkg/encoding/binary/Makefile index 4d681e104..69fd57c67 100644 --- a/src/pkg/debug/binary/Makefile +++ b/src/pkg/encoding/binary/Makefile @@ -4,7 +4,7 @@ include $(GOROOT)/src/Make.$(GOARCH) -TARG=debug/binary +TARG=encoding/binary GOFILES=\ binary.go\ diff --git a/src/pkg/debug/binary/binary.go b/src/pkg/encoding/binary/binary.go index 836a43df0..836a43df0 100644 --- a/src/pkg/debug/binary/binary.go +++ b/src/pkg/encoding/binary/binary.go diff --git a/src/pkg/debug/binary/binary_test.go b/src/pkg/encoding/binary/binary_test.go index a04684b72..a04684b72 100644 --- a/src/pkg/debug/binary/binary_test.go +++ b/src/pkg/encoding/binary/binary_test.go diff --git a/src/pkg/base85/Makefile b/src/pkg/encoding/git85/Makefile index 2dae5b124..10587743a 100644 --- a/src/pkg/base85/Makefile +++ b/src/pkg/encoding/git85/Makefile @@ -4,9 +4,8 @@ include $(GOROOT)/src/Make.$(GOARCH) -TARG=base85 +TARG=encoding/git85 GOFILES=\ - base85.go\ git.go\ include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/base85/git.go b/src/pkg/encoding/git85/git.go index 813645e86..2dcd4d877 100644 --- a/src/pkg/base85/git.go +++ b/src/pkg/encoding/git85/git.go @@ -2,19 +2,28 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package base85 +// Package git85 implements the radix 85 data encoding +// used in the GIT version control system. +package git85 import ( "bytes"; "io"; "os"; + "strconv"; ) -const gitEncode = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~" +type CorruptInputError int64 + +func (e CorruptInputError) String() string { + return "illegal git85 data at input byte" + strconv.Itoa64(int64(e)); +} + +const encode = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~" // The decodings are 1+ the actual value, so that the // default zero value can be used to mean "not valid". -var gitDecode = [256]uint8{ +var decode = [256]uint8{ '0': 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'A': 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, @@ -31,15 +40,15 @@ var gitDecode = [256]uint8{ '{': 82, 83, 84, 85 } -// GitEncode encodes src into GitEncodedLen(len(src)) +// Encode encodes src into EncodedLen(len(src)) // bytes of dst. As a convenience, it returns the number -// of bytes written to dst, but this value is always GitEncodedLen(len(src)). -// GitEncode implements the radix 85 encoding used in the +// of bytes written to dst, but this value is always EncodedLen(len(src)). +// Encode implements the radix 85 encoding used in the // Git version control tool. // // The encoding splits src into chunks of at most 52 bytes // and encodes each chunk on its own line. -func GitEncode(src, dst []byte) int { +func Encode(src, dst []byte) int { ndst := 0; for len(src) > 0 { n := len(src); @@ -58,7 +67,7 @@ func GitEncode(src, dst []byte) int { v |= uint32(src[i+j]) << uint(24 - j*8); } for j := 4; j >= 0; j-- { - dst[ndst+j] = gitEncode[v%85]; + dst[ndst+j] = encode[v%85]; v /= 85; } ndst += 5; @@ -70,8 +79,8 @@ func GitEncode(src, dst []byte) int { return ndst; } -// GitEncodedLen returns the length of an encoding of n source bytes. -func GitEncodedLen(n int) int { +// EncodedLen returns the length of an encoding of n source bytes. +func EncodedLen(n int) int { if n == 0 { return 0; } @@ -82,12 +91,12 @@ func GitEncodedLen(n int) int { var newline = []byte{'\n'} -// GitDecode decodes src into at most MaxGitDecodedLen(len(src)) +// Decode decodes src into at most MaxDecodedLen(len(src)) // bytes, returning the actual number of bytes written to dst. // -// If GitDecode encounters invalid input, it returns a CorruptInputError. +// If Decode encounters invalid input, it returns a CorruptInputError. // -func GitDecode(src, dst []byte) (n int, err os.Error) { +func Decode(src, dst []byte) (n int, err os.Error) { ndst := 0; nsrc := 0; for nsrc < len(src) { @@ -111,7 +120,7 @@ func GitDecode(src, dst []byte) (n int, err os.Error) { for i := 0; i < el; i += 5 { var v uint32; for j := 0; j < 5; j++ { - ch := gitDecode[line[i+j]]; + ch := decode[line[i+j]]; if ch == 0 { return ndst, CorruptInputError(nsrc+1+i+j); } @@ -133,7 +142,7 @@ func GitDecode(src, dst []byte) (n int, err os.Error) { return ndst, nil; } -func MaxGitDecodedLen(n int) int { +func MaxDecodedLen(n int) int { return n/5*4; } @@ -142,11 +151,11 @@ func MaxGitDecodedLen(n int) int { // The Git encoding operates on 52-byte blocks; when finished // writing, the caller must Close the returned encoder to flush any // partially written blocks. -func NewGitEncoder(w io.Writer) io.WriteCloser { - return &gitEncoder{w: w}; +func NewEncoder(w io.Writer) io.WriteCloser { + return &encoder{w: w}; } -type gitEncoder struct { +type encoder struct { w io.Writer; err os.Error; buf [52]byte; @@ -155,7 +164,7 @@ type gitEncoder struct { nout int; } -func (e *gitEncoder) Write(p []byte) (n int, err os.Error) { +func (e *encoder) Write(p []byte) (n int, err os.Error) { if e.err != nil { return 0, e.err; } @@ -172,7 +181,7 @@ func (e *gitEncoder) Write(p []byte) (n int, err os.Error) { if e.nbuf < 52 { return; } - nout := GitEncode(&e.buf, &e.out); + nout := Encode(&e.buf, &e.out); if _, e.err = e.w.Write(e.out[0:nout]); e.err != nil { return n, e.err; } @@ -186,7 +195,7 @@ func (e *gitEncoder) Write(p []byte) (n int, err os.Error) { nn = len(p)/52 * 52; } if nn > 0 { - nout := GitEncode(p[0:nn], &e.out); + nout := Encode(p[0:nn], &e.out); if _, e.err = e.w.Write(e.out[0:nout]); e.err != nil { return n, e.err; } @@ -204,22 +213,22 @@ func (e *gitEncoder) Write(p []byte) (n int, err os.Error) { return; } -func (e *gitEncoder) Close() os.Error { +func (e *encoder) Close() os.Error { // If there's anything left in the buffer, flush it out if e.err == nil && e.nbuf > 0 { - nout := GitEncode(e.buf[0:e.nbuf], &e.out); + nout := Encode(e.buf[0:e.nbuf], &e.out); e.nbuf = 0; _, e.err = e.w.Write(e.out[0:nout]); } return e.err; } -// NewGitDecoder returns a new Git base85 stream decoder. -func NewGitDecoder(r io.Reader) io.Reader { - return &gitDecoder{r: r}; +// NewDecoder returns a new Git base85 stream decoder. +func NewDecoder(r io.Reader) io.Reader { + return &decoder{r: r}; } -type gitDecoder struct { +type decoder struct { r io.Reader; err os.Error; readErr os.Error; @@ -230,7 +239,7 @@ type gitDecoder struct { off int64; } -func (d *gitDecoder) Read(p []byte) (n int, err os.Error) { +func (d *decoder) Read(p []byte) (n int, err os.Error) { if len(p) == 0 { return 0, nil; } @@ -257,12 +266,12 @@ func (d *gitDecoder) Read(p []byte) (n int, err os.Error) { nn, d.readErr = d.r.Read(d.buf[d.nbuf:len(d.buf)]); d.nbuf += nn; - // Send complete lines to GitDecode. + // Send complete lines to Decode. nl := bytes.LastIndex(d.buf[0:d.nbuf], newline); if nl < 0 { continue; } - nn, d.err = GitDecode(d.buf[0:nl+1], &d.outbuf); + nn, d.err = Decode(d.buf[0:nl+1], &d.outbuf); if e, ok := d.err.(CorruptInputError); ok { d.err = CorruptInputError(int64(e)+d.off); } diff --git a/src/pkg/base85/git_test.go b/src/pkg/encoding/git85/git_test.go index 916859942..e83e941f1 100644 --- a/src/pkg/base85/git_test.go +++ b/src/pkg/encoding/git85/git_test.go @@ -2,27 +2,43 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package base85 +package git85 import ( "bytes"; "io"; "os"; + "reflect"; "strings"; "testing"; ) +type testpair struct { + decoded, encoded string; +} + +func testEqual(t *testing.T, msg string, args ...) bool { + v := reflect.NewValue(args).(*reflect.StructValue); + v1 := v.Field(v.NumField() - 2); + v2 := v.Field(v.NumField() - 1); + if v1.Interface() != v2.Interface() { + t.Errorf(msg, args); + return false; + } + return true; +} + func TestGitTable(t *testing.T) { var saw [256]bool; - for i, c := range gitEncode { - if gitDecode[c] != uint8(i+1) { - t.Errorf("gitDecode['%c'] = %d, want %d", c, gitDecode[c], i+1); + for i, c := range encode { + if decode[c] != uint8(i+1) { + t.Errorf("decode['%c'] = %d, want %d", c, decode[c], i+1); } saw[c] = true; } for i, b := range saw { - if !b && gitDecode[i] != 0 { - t.Errorf("gitDecode[%d] = %d, want 0", i, gitDecode[i]); + if !b && decode[i] != 0 { + t.Errorf("decode[%d] = %d, want 0", i, decode[i]); } } } @@ -46,33 +62,33 @@ var gitPairs = []testpair{ var gitBigtest = gitPairs[len(gitPairs)-1]; -func TestGitEncode(t *testing.T) { +func TestEncode(t *testing.T) { for _, p := range gitPairs { - buf := make([]byte, GitEncodedLen(len(p.decoded))); - n := GitEncode(strings.Bytes(p.decoded), buf); + buf := make([]byte, EncodedLen(len(p.decoded))); + n := Encode(strings.Bytes(p.decoded), buf); if n != len(buf) { - t.Errorf("GitEncodedLen does not agree with GitEncode"); + t.Errorf("EncodedLen does not agree with Encode"); } buf = buf[0:n]; - testEqual(t, "GitEncode(%q) = %q, want %q", p.decoded, string(buf), p.encoded); + testEqual(t, "Encode(%q) = %q, want %q", p.decoded, string(buf), p.encoded); } } -func TestGitEncoder(t *testing.T) { +func TestEncoder(t *testing.T) { for _, p := range gitPairs { bb := &bytes.Buffer{}; - encoder := NewGitEncoder(bb); + encoder := NewEncoder(bb); encoder.Write(strings.Bytes(p.decoded)); encoder.Close(); - testEqual(t, "GitEncode(%q) = %q, want %q", p.decoded, bb.String(), p.encoded); + testEqual(t, "Encode(%q) = %q, want %q", p.decoded, bb.String(), p.encoded); } } -func TestGitEncoderBuffering(t *testing.T) { +func TestEncoderBuffering(t *testing.T) { input := strings.Bytes(gitBigtest.decoded); for bs := 1; bs <= 12; bs++ { bb := &bytes.Buffer{}; - encoder := NewGitEncoder(bb); + encoder := NewEncoder(bb); for pos := 0; pos < len(input); pos += bs { end := pos+bs; if end > len(input) { @@ -88,19 +104,19 @@ func TestGitEncoderBuffering(t *testing.T) { } } -func TestGitDecode(t *testing.T) { +func TestDecode(t *testing.T) { for _, p := range gitPairs { dbuf := make([]byte, 4*len(p.encoded)); - ndst, err := GitDecode(strings.Bytes(p.encoded), dbuf); - testEqual(t, "GitDecode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)); - testEqual(t, "GitDecode(%q) = ndst %v, want %v", p.encoded, ndst, len(p.decoded)); - testEqual(t, "GitDecode(%q) = %q, want %q", p.encoded, string(dbuf[0:ndst]), p.decoded); + ndst, err := Decode(strings.Bytes(p.encoded), dbuf); + testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, os.Error(nil)); + testEqual(t, "Decode(%q) = ndst %v, want %v", p.encoded, ndst, len(p.decoded)); + testEqual(t, "Decode(%q) = %q, want %q", p.encoded, string(dbuf[0:ndst]), p.decoded); } } -func TestGitDecoder(t *testing.T) { +func TestDecoder(t *testing.T) { for _, p := range gitPairs { - decoder := NewGitDecoder(bytes.NewBufferString(p.encoded)); + decoder := NewDecoder(bytes.NewBufferString(p.encoded)); dbuf, err := io.ReadAll(decoder); if err != nil { t.Fatal("Read failed", err); @@ -113,9 +129,9 @@ func TestGitDecoder(t *testing.T) { } } -func TestGitDecoderBuffering(t *testing.T) { +func TestDecoderBuffering(t *testing.T) { for bs := 1; bs <= 12; bs++ { - decoder := NewGitDecoder(bytes.NewBufferString(gitBigtest.encoded)); + decoder := NewDecoder(bytes.NewBufferString(gitBigtest.encoded)); buf := make([]byte, len(gitBigtest.decoded)+12); var total int; for total = 0; total < len(gitBigtest.decoded); { @@ -127,7 +143,7 @@ func TestGitDecoderBuffering(t *testing.T) { } } -func TestGitDecodeCorrupt(t *testing.T) { +func TestDecodeCorrupt(t *testing.T) { type corrupt struct { e string; p int; @@ -139,12 +155,12 @@ func TestGitDecodeCorrupt(t *testing.T) { for _, e := range examples { dbuf := make([]byte, 2*len(e.e)); - _, err := GitDecode(strings.Bytes(e.e), dbuf); + _, err := Decode(strings.Bytes(e.e), dbuf); switch err := err.(type) { case CorruptInputError: testEqual(t, "Corruption in %q at offset %v, want %v", e.e, int(err), e.p); default: - t.Error("GitDecoder failed to detect corruption in", e); + t.Error("Decoder failed to detect corruption in", e); } } } @@ -157,18 +173,18 @@ func TestGitBig(t *testing.T) { raw[i] = alpha[i%len(alpha)]; } encoded := new(bytes.Buffer); - w := NewGitEncoder(encoded); + w := NewEncoder(encoded); nn, err := w.Write(raw); if nn != n || err != nil { - t.Fatalf("GitEncoder.Write(raw) = %d, %v want %d, nil", nn, err, n); + t.Fatalf("Encoder.Write(raw) = %d, %v want %d, nil", nn, err, n); } err = w.Close(); if err != nil { - t.Fatalf("GitEncoder.Close() = %v want nil", err); + t.Fatalf("Encoder.Close() = %v want nil", err); } - decoded, err := io.ReadAll(NewGitDecoder(encoded)); + decoded, err := io.ReadAll(NewDecoder(encoded)); if err != nil { - t.Fatalf("io.ReadAll(NewGitDecoder(...)): %v", err); + t.Fatalf("io.ReadAll(NewDecoder(...)): %v", err); } if !bytes.Equal(raw, decoded) { @@ -178,6 +194,6 @@ func TestGitBig(t *testing.T) { break; } } - t.Errorf("GitDecode(GitEncode(%d-byte string)) failed at offset %d", n, i); + t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i); } } |