// Copyright 2011 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 base32 implements base32 encoding as specified by RFC 4648.
package base32import ()/* * Encodings */// An Encoding is a radix 32 encoding/decoding scheme, defined by a// 32-character alphabet. The most common is the "base32" encoding// introduced for SASL GSSAPI and standardized in RFC 4648.// The alternate "base32hex" encoding is used in DNSSEC.typeEncodingstruct { encode [32]byte// mapping of symbol index to symbol byte value decodeMap [256]uint8// mapping of symbol byte value to symbol index padChar rune}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 32-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() != 32 {panic("encoding alphabet is not 32-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}// StdEncoding is the standard base32 encoding, as defined in RFC 4648.varStdEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")// HexEncoding is the “Extended Hex Alphabet” defined in RFC 4648.// It is typically used in DNS.varHexEncoding = NewEncoding("0123456789ABCDEFGHIJKLMNOPQRSTUV")// 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 &}/* * 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 8 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() / 5) * 5for < {// Combining two 32 bit loads allows the same code to be used // for 32 and 64 bit platforms. := uint32([+0])<<24 | uint32([+1])<<16 | uint32([+2])<<8 | uint32([+3]) := <<8 | uint32([+4]) [+0] = .encode[(>>27)&0x1F] [+1] = .encode[(>>22)&0x1F] [+2] = .encode[(>>17)&0x1F] [+3] = .encode[(>>12)&0x1F] [+4] = .encode[(>>7)&0x1F] [+5] = .encode[(>>2)&0x1F] [+6] = .encode[(>>5)&0x1F] [+7] = .encode[()&0x1F] += 5 += 8 }// Add the remaining small block := len() - if == 0 {return }// Encode the remaining bytes in reverse order. := uint32(0)switch {case4: |= uint32([+3]) [+6] = .encode[<<3&0x1F] [+5] = .encode[>>2&0x1F]fallthroughcase3: |= uint32([+2]) << 8 [+4] = .encode[>>7&0x1F]fallthroughcase2: |= uint32([+1]) << 16 [+3] = .encode[>>12&0x1F] [+2] = .encode[>>17&0x1F]fallthroughcase1: |= uint32([+0]) << 24 [+1] = .encode[>>22&0x1F] [+0] = .encode[>>27&0x1F] }// Pad the final quantumif .padChar != NoPadding { := ( * 8 / 5) + 1for := ; < 8; ++ { [+] = byte(.padChar) } }}// AppendEncode appends the base32 encoded src to dst// and returns the extended buffer.func ( *Encoding) (, []byte) []byte { := .EncodedLen(len()) = slices.Grow(, ) .Encode([len():][:], )return [:len()+]}// EncodeToString returns the base32 encoding of src.func ( *Encoding) ( []byte) string { := make([]byte, .EncodedLen(len())) .Encode(, )returnstring()}type encoder struct { err error enc *Encoding w io.Writer buf [5]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 < 5; ++ { .buf[.nbuf] = [] .nbuf++ } += = [:]if .nbuf < 5 {return } .enc.Encode(.out[0:], .buf[0:])if _, .err = .w.Write(.out[0:8]); .err != nil {return , .err } .nbuf = 0 }// Large interior chunks.forlen() >= 5 { := len(.out) / 8 * 5if > len() { = len() -= % 5 } .enc.Encode(.out[0:], [0:])if _, .err = .w.Write(.out[0 : /5*8]); .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[0:], .buf[0:.nbuf]) := .enc.EncodedLen(.nbuf) .nbuf = 0 _, .err = .w.Write(.out[0:]) }return .err}// NewEncoder returns a new base32 stream encoder. Data written to// the returned writer will be encoded using enc and then written to w.// Base32 encodings operate in 5-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 base32 encoding// of an input buffer of length n.func ( *Encoding) ( int) int {if .padChar == NoPadding {return /5*8 + (%5*8+4)/5 }return ( + 4) / 5 * 8}/* * Decoder */typeCorruptInputErrorint64func ( CorruptInputError) () string {return"illegal base32 data at input byte " + strconv.FormatInt(int64(), 10)}// 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. This method assumes that src has been// stripped of all supported whitespace ('\r' and '\n').func ( *Encoding) (, []byte) ( int, bool, error) {// Lift the nil check outside of the loop. _ = .decodeMap := 0 := len()forlen() > 0 && ! {// Decode quantum using the base32 alphabetvar [8]byte := 8for := 0; < 8; {iflen() == 0 {if .padChar != NoPadding {// We have reached the end and are missing paddingreturn , false, CorruptInputError( - len() - ) }// We have reached the end and are not expecting any padding , = , truebreak } := [0] = [1:]if == byte(.padChar) && >= 2 && len() < 8 {// We've reached the end and there's paddingiflen()+ < 8-1 {// not enough paddingreturn , false, CorruptInputError() }for := 0; < 8-1-; ++ {iflen() > && [] != byte(.padChar) {// incorrect paddingreturn , false, CorruptInputError( - len() + - 1) } } , = , true// 7, 5 and 2 are not valid padding lengths, and so 1, 3 and 6 are not // valid dlen values. See RFC 4648 Section 6 "Base 32 Encoding" listing // the five valid padding lengths, and Section 9 "Illustrations and // Examples" for an illustration for how the 1st, 3rd and 6th base32 // src bytes do not yield enough information to decode a dst byte.if == 1 || == 3 || == 6 {return , false, CorruptInputError( - len() - 1) }break } [] = .decodeMap[]if [] == 0xFF {return , false, CorruptInputError( - len() - 1) } ++ }// Pack 8x 5-bit source blocks into 5 byte destination // quantumswitch {case8: [+4] = [6]<<5 | [7] ++fallthroughcase7: [+3] = [4]<<7 | [5]<<2 | [6]>>3 ++fallthroughcase5: [+2] = [3]<<4 | [4]>>1 ++fallthroughcase4: [+1] = [1]<<6 | [2]<<1 | [3]>>4 ++fallthroughcase2: [+0] = [0]<<3 | [1]>>2 ++ } += 5 }return , , nil}// 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. If src contains invalid base32 data, it will return the// number of bytes successfully written and [CorruptInputError].// Newline characters (\r and \n) are ignored.func ( *Encoding) (, []byte) ( int, error) { := make([]byte, len()) := stripNewlines(, ) , _, = .decode(, [:])return}// AppendDecode appends the base32 decoded src to dst// and returns the extended buffer.// If the input is malformed, it returns the partially decoded src and an error.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 base32 string s.func ( *Encoding) ( string) ([]byte, error) { := []byte() := stripNewlines(, ) , , := .decode(, [:])return [:], }type decoder struct { err 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 / 8 * 5]byte}func readEncodedData( io.Reader, []byte, int, bool) ( int, error) {for < && == nil {varint , = .Read([:]) += }// data was read, less than min bytes could be readif < && > 0 && == io.EOF { = io.ErrUnexpectedEOF }// no data was read, the buffer already contains some data // when padding is disabled this is not an error, as the message can be of // any lengthif && < 8 && == 0 && == io.EOF { = io.ErrUnexpectedEOF }return}func ( *decoder) ( []byte) ( int, error) {// Use leftover decoded output from last read.iflen(.out) > 0 { = copy(, .out) .out = .out[:]iflen(.out) == 0 {return , .err }return , nil }if .err != nil {return0, .err }// Read a chunk. := (len() + 4) / 5 * 8if < 8 { = 8 }if > len(.buf) { = len(.buf) }// Minimum amount of bytes that needs to be read each cyclevarintvarboolif .enc.padChar == NoPadding { = 1 = false } else { = 8 - .nbuf = true } , .err = readEncodedData(.r, .buf[.nbuf:], , ) .nbuf += if .nbuf < {return0, .err }if > 0 && .end {return0, CorruptInputError(0) }// Decode chunk into p, or d.out and then p if p is too small.varintif .enc.padChar == NoPadding { = .nbuf } else { = .nbuf / 8 * 8 } := .enc.DecodedLen(.nbuf)if > len() { , .end, = .enc.decode(.outbuf[0:], .buf[0:]) .out = .outbuf[0:] = copy(, .out) .out = .out[:] } else { , .end, = .enc.decode(, .buf[0:]) } .nbuf -= for := 0; < .nbuf; ++ { .buf[] = .buf[+] }if != nil && (.err == nil || .err == io.EOF) { .err = }iflen(.out) > 0 {// We cannot return all the decoded bytes to the caller in this // invocation of Read, so we return a nil error to ensure that Read // will be called again. The error stored in d.err, if any, will be // returned with the last set of decoded bytes.return , nil }return , .err}type newlineFilteringReader struct { wrapped io.Reader}// stripNewlines removes newline characters and returns the number// of non-newline characters copied to dst.func stripNewlines(, []byte) int { := 0for , := range {if == '\r' || == '\n' {continue } [] = ++ }return}func ( *newlineFilteringReader) ( []byte) (int, error) { , := .wrapped.Read()for > 0 { := [0:] := stripNewlines(, )if != nil || > 0 {return , }// Previous buffer entirely whitespace, read again , = .wrapped.Read() }return , }// NewDecoder constructs a new base32 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 base32-encoded data.func ( *Encoding) ( int) int {returndecodedLen(, .padChar)}func decodedLen( int, rune) int {if == NoPadding {return /8*5 + %8*5/8 }return / 8 * 5}
The pages are generated with Goldsv0.7.0-preview. (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.