// Copyright 2009 The Go 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 base64import ()/* * 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 /.typeEncodingstruct { encode [64]byte// mapping of symbol index to symbol byte value decodeMap [256]uint8// mapping of symbol byte value to symbol index padChar rune strict bool}const (StdPaddingrune = '='// Standard padding characterNoPaddingrune = -1// No padding)const ( decodeMapInitialize = "" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" invalidIndex = '\xff')// NewEncoding returns a new padded Encoding defined by the given alphabet,// which must be a 64-byte string that contains unique byte values and// does not contain the padding character or CR / LF ('\r', '\n').// The alphabet is treated as a sequence of byte values// without any special treatment for multi-byte UTF-8.// The resulting Encoding uses the default padding character ('='),// which may be changed or disabled via [Encoding.WithPadding].func ( string) *Encoding {iflen() != 64 {panic("encoding alphabet is not 64-bytes long") } := new(Encoding) .padChar = StdPaddingcopy(.encode[:], )copy(.decodeMap[:], decodeMapInitialize)for := 0; < len(); ++ {// Note: While we document that the alphabet cannot contain // the padding character, we do not enforce it since we do not know // if the caller intends to switch the padding from StdPadding later.switch {case [] == '\n' || [] == '\r':panic("encoding alphabet contains newline character")case .decodeMap[[]] != invalidIndex:panic("encoding alphabet includes duplicate symbols") } .decodeMap[[]] = uint8() }return}// WithPadding creates a new encoding identical to enc except// with a specified padding character, or [NoPadding] to disable padding.// The padding character must not be '\r' or '\n',// must not be contained in the encoding's alphabet,// must not be negative, and must be a rune equal or below '\xff'.// Padding characters above '\x7f' are encoded as their exact byte value// rather than using the UTF-8 representation of the codepoint.func ( Encoding) ( rune) *Encoding {switch {case < NoPadding || == '\r' || == '\n' || > 0xff:panic("invalid padding")case != NoPadding && .decodeMap[byte()] != invalidIndex:panic("padding contained in alphabet") } .padChar = return &}// Strict creates a new encoding identical to enc except with// strict decoding enabled. In this mode, the decoder requires that// trailing padding bits are zero, as described in RFC 4648 section 3.5.//// Note that the input is still malleable, as new line characters// (CR and LF) are still ignored.func ( Encoding) () *Encoding { .strict = truereturn &}// StdEncoding is the standard base64 encoding, as defined in RFC 4648.varStdEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")// URLEncoding is the alternate base64 encoding defined in RFC 4648.// It is typically used in URLs and file names.varURLEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_")// RawStdEncoding is the standard raw, unpadded base64 encoding,// as defined in RFC 4648 section 3.2.// This is the same as [StdEncoding] but omits padding characters.varRawStdEncoding = StdEncoding.WithPadding(NoPadding)// RawURLEncoding is the unpadded alternate base64 encoding defined in RFC 4648.// It is typically used in URLs and file names.// This is the same as [URLEncoding] but omits padding characters.varRawURLEncoding = URLEncoding.WithPadding(NoPadding)/* * Encoder */// Encode encodes src using the encoding enc,// writing [Encoding.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 ( *Encoding) (, []byte) {iflen() == 0 {return }// enc is a pointer receiver, so the use of enc.encode within the hot // loop below means a nil check at every operation. Lift that nil check // outside of the loop to speed up the encoder. _ = .encode , := 0, 0 := (len() / 3) * 3for < {// Convert 3x 8bit source bytes into 4 bytes := uint([+0])<<16 | uint([+1])<<8 | uint([+2]) [+0] = .encode[>>18&0x3F] [+1] = .encode[>>12&0x3F] [+2] = .encode[>>6&0x3F] [+3] = .encode[&0x3F] += 3 += 4 } := len() - if == 0 {return }// Add the remaining small block := uint([+0]) << 16if == 2 { |= uint([+1]) << 8 } [+0] = .encode[>>18&0x3F] [+1] = .encode[>>12&0x3F]switch {case2: [+2] = .encode[>>6&0x3F]if .padChar != NoPadding { [+3] = byte(.padChar) }case1:if .padChar != NoPadding { [+2] = byte(.padChar) [+3] = byte(.padChar) } }}// AppendEncode appends the base64 encoded src to dst// and returns the extended buffer.func ( *Encoding) (, []byte) []byte { := .EncodedLen(len()) = slices.Grow(, ) .Encode([len():][:], )return [:len()+]}// EncodeToString returns the base64 encoding of src.func ( *Encoding) ( []byte) string { := make([]byte, .EncodedLen(len())) .Encode(, )returnstring()}type encoder struct { err 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 ( *encoder) ( []byte) ( int, error) {if .err != nil {return0, .err }// Leading fringe.if .nbuf > 0 {varintfor = 0; < len() && .nbuf < 3; ++ { .buf[.nbuf] = [] .nbuf++ } += = [:]if .nbuf < 3 {return } .enc.Encode(.out[:], .buf[:])if _, .err = .w.Write(.out[:4]); .err != nil {return , .err } .nbuf = 0 }// Large interior chunks.forlen() >= 3 { := len(.out) / 4 * 3if > len() { = len() -= % 3 } .enc.Encode(.out[:], [:])if _, .err = .w.Write(.out[0 : /3*4]); .err != nil {return , .err } += = [:] }// Trailing fringe.copy(.buf[:], ) .nbuf = len() += len()return}// Close flushes any pending output from the encoder.// It is an error to call Write after calling Close.func ( *encoder) () error {// If there's anything left in the buffer, flush it outif .err == nil && .nbuf > 0 { .enc.Encode(.out[:], .buf[:.nbuf]) _, .err = .w.Write(.out[:.enc.EncodedLen(.nbuf)]) .nbuf = 0 }return .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 ( *Encoding, io.Writer) io.WriteCloser {return &encoder{enc: , w: }}// EncodedLen returns the length in bytes of the base64 encoding// of an input buffer of length n.func ( *Encoding) ( int) int {if .padChar == NoPadding {return /3*4 + (%3*8+5)/6// minimum # chars at 6 bits per char }return ( + 2) / 3 * 4// minimum # 4-char quanta, 3 bytes each}/* * Decoder */typeCorruptInputErrorint64func ( CorruptInputError) () string {return"illegal base64 data at input byte " + strconv.FormatInt(int64(), 10)}// decodeQuantum decodes up to 4 base64 bytes. The received parameters are// the destination buffer dst, the source buffer src and an index in the// source buffer si.// It returns the number of bytes read from src, the number of bytes written// to dst, and an error, if any.func ( *Encoding) (, []byte, int) (, int, error) {// Decode quantum using the base64 alphabetvar [4]byte := 4// Lift the nil check outside of the loop. _ = .decodeMapfor := 0; < len(); ++ {iflen() == {switch {case == 0:return , 0, nilcase == 1, .padChar != NoPadding:return , 0, CorruptInputError( - ) } = break } := [] ++ := .decodeMap[]if != 0xff { [] = continue }if == '\n' || == '\r' { --continue }ifrune() != .padChar {return , 0, CorruptInputError( - 1) }// We've reached the end and there's paddingswitch {case0, 1:// incorrect paddingreturn , 0, CorruptInputError( - 1)case2:// "==" is expected, the first "=" is already consumed. // skip over newlinesfor < len() && ([] == '\n' || [] == '\r') { ++ }if == len() {// not enough paddingreturn , 0, CorruptInputError(len()) }ifrune([]) != .padChar {// incorrect paddingreturn , 0, CorruptInputError( - 1) } ++ }// skip over newlinesfor < len() && ([] == '\n' || [] == '\r') { ++ }if < len() {// trailing garbage = CorruptInputError() } = break }// Convert 4x 6bit source bytes into 3 bytes := uint([0])<<18 | uint([1])<<12 | uint([2])<<6 | uint([3]) [2], [1], [0] = byte(>>0), byte(>>8), byte(>>16)switch {case4: [2] = [2] [2] = 0fallthroughcase3: [1] = [1]if .strict && [2] != 0 {return , 0, CorruptInputError( - 1) } [1] = 0fallthroughcase2: [0] = [0]if .strict && ([1] != 0 || [2] != 0) {return , 0, CorruptInputError( - 2) } }return , - 1, }// AppendDecode appends the base64 decoded src to dst// and returns the extended buffer.// If the input is malformed, it returns the partially decoded src and an error.// New line characters (\r and \n) are ignored.func ( *Encoding) (, []byte) ([]byte, error) {// Compute the output size without padding to avoid over allocating. := len()for > 0 && rune([-1]) == .padChar { -- } = decodedLen(, NoPadding) = slices.Grow(, ) , := .Decode([len():][:], )return [:len()+], }// DecodeString returns the bytes represented by the base64 string s.// If the input is malformed, it returns the partially decoded data and// [CorruptInputError]. New line characters (\r and \n) are ignored.func ( *Encoding) ( string) ([]byte, error) { := make([]byte, .DecodedLen(len())) , := .Decode(, []byte())return [:], }type decoder struct { err error readErr error// error from r.Read enc *Encoding r io.Reader buf [1024]byte// leftover input nbuf int out []byte// leftover decoded output outbuf [1024 / 4 * 3]byte}func ( *decoder) ( []byte) ( int, error) {// Use leftover decoded output from last read.iflen(.out) > 0 { = copy(, .out) .out = .out[:]return , nil }if .err != nil {return0, .err }// This code assumes that d.r strips supported whitespace ('\r' and '\n').// Refill buffer.for .nbuf < 4 && .readErr == nil { := len() / 3 * 4if < 4 { = 4 }if > len(.buf) { = len(.buf) } , .readErr = .r.Read(.buf[.nbuf:]) .nbuf += }if .nbuf < 4 {if .enc.padChar == NoPadding && .nbuf > 0 {// Decode final fragment, without padding.varint , .err = .enc.Decode(.outbuf[:], .buf[:.nbuf]) .nbuf = 0 .out = .outbuf[:] = copy(, .out) .out = .out[:]if > 0 || len() == 0 && len(.out) > 0 {return , nil }if .err != nil {return0, .err } } .err = .readErrif .err == io.EOF && .nbuf > 0 { .err = io.ErrUnexpectedEOF }return0, .err }// Decode chunk into p, or d.out and then p if p is too small. := .nbuf / 4 * 4 := .nbuf / 4 * 3if > len() { , .err = .enc.Decode(.outbuf[:], .buf[:]) .out = .outbuf[:] = copy(, .out) .out = .out[:] } else { , .err = .enc.Decode(, .buf[:]) } .nbuf -= copy(.buf[:.nbuf], .buf[:])return , .err}// Decode decodes src using the encoding enc. It writes at most// [Encoding.DecodedLen](len(src)) bytes to dst and returns the number of bytes// written. The caller must ensure that dst is large enough to hold all// the decoded data. If src contains invalid base64 data, it will return the// number of bytes successfully written and [CorruptInputError].// New line characters (\r and \n) are ignored.func ( *Encoding) (, []byte) ( int, error) {iflen() == 0 {return0, nil }// Lift the nil check outside of the loop. enc.decodeMap is directly // used later in this function, to let the compiler know that the // receiver can't be nil. _ = .decodeMap := 0forstrconv.IntSize >= 64 && len()- >= 8 && len()- >= 8 { := [ : +8]if , := assemble64( .decodeMap[[0]], .decodeMap[[1]], .decodeMap[[2]], .decodeMap[[3]], .decodeMap[[4]], .decodeMap[[5]], .decodeMap[[6]], .decodeMap[[7]], ); {binary.BigEndian.PutUint64([:], ) += 6 += 8 } else {varint , , = .decodeQuantum([:], , ) += if != nil {return , } } }forlen()- >= 4 && len()- >= 4 { := [ : +4]if , := assemble32( .decodeMap[[0]], .decodeMap[[1]], .decodeMap[[2]], .decodeMap[[3]], ); {binary.BigEndian.PutUint32([:], ) += 3 += 4 } else {varint , , = .decodeQuantum([:], , ) += if != nil {return , } } }for < len() {varint , , = .decodeQuantum([:], , ) += if != nil {return , } }return , }// assemble32 assembles 4 base64 digits into 3 bytes.// Each digit comes from the decode map, and will be 0xff// if it came from an invalid character.func assemble32(, , , byte) ( uint32, bool) {// Check that all the digits are valid. If any of them was 0xff, their // bitwise OR will be 0xff.if ||| == 0xff {return0, false }returnuint32()<<26 |uint32()<<20 |uint32()<<14 |uint32()<<8,true}// assemble64 assembles 8 base64 digits into 6 bytes.// Each digit comes from the decode map, and will be 0xff// if it came from an invalid character.func assemble64(, , , , , , , byte) ( uint64, bool) {// Check that all the digits are valid. If any of them was 0xff, their // bitwise OR will be 0xff.if ||||||| == 0xff {return0, false }returnuint64()<<58 |uint64()<<52 |uint64()<<46 |uint64()<<40 |uint64()<<34 |uint64()<<28 |uint64()<<22 |uint64()<<16,true}type newlineFilteringReader struct { wrapped io.Reader}func ( *newlineFilteringReader) ( []byte) (int, error) { , := .wrapped.Read()for > 0 { := 0for , := range [:] {if != '\r' && != '\n' {if != { [] = } ++ } }if > 0 {return , }// Previous buffer entirely whitespace, read again , = .wrapped.Read() }return , }// NewDecoder constructs a new base64 stream decoder.func ( *Encoding, io.Reader) io.Reader {return &decoder{enc: , r: &newlineFilteringReader{}}}// DecodedLen returns the maximum length in bytes of the decoded data// corresponding to n bytes of base64-encoded data.func ( *Encoding) ( int) int {returndecodedLen(, .padChar)}func decodedLen( int, rune) int {if == NoPadding {// Unpadded data may end with partial block of 2-3 characters.return /4*3 + %4*6/8 }// Padded base64 should always be a multiple of 4 characters in length.return / 4 * 3}
The pages are generated with Goldsv0.7.3. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.