diff options
Diffstat (limited to 'src/pkg/encoding/base64/base64.go')
| -rw-r--r-- | src/pkg/encoding/base64/base64.go | 334 |
1 files changed, 334 insertions, 0 deletions
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; +} |
