| | |
| | |
| | |
| |
|
| | |
| | package base64 |
| |
|
| | import ( |
| | "internal/byteorder" |
| | "io" |
| | "slices" |
| | "strconv" |
| | ) |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | type Encoding struct { |
| | encode [64]byte |
| | decodeMap [256]uint8 |
| | padChar rune |
| | strict bool |
| | } |
| |
|
| | const ( |
| | StdPadding rune = '=' |
| | NoPadding rune = -1 |
| | ) |
| |
|
| | 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' |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func NewEncoding(encoder string) *Encoding { |
| | if len(encoder) != 64 { |
| | panic("encoding alphabet is not 64-bytes long") |
| | } |
| |
|
| | e := new(Encoding) |
| | e.padChar = StdPadding |
| | copy(e.encode[:], encoder) |
| | copy(e.decodeMap[:], decodeMapInitialize) |
| |
|
| | for i := 0; i < len(encoder); i++ { |
| | |
| | |
| | |
| | switch { |
| | case encoder[i] == '\n' || encoder[i] == '\r': |
| | panic("encoding alphabet contains newline character") |
| | case e.decodeMap[encoder[i]] != invalidIndex: |
| | panic("encoding alphabet includes duplicate symbols") |
| | } |
| | e.decodeMap[encoder[i]] = uint8(i) |
| | } |
| | return e |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (enc Encoding) WithPadding(padding rune) *Encoding { |
| | switch { |
| | case padding < NoPadding || padding == '\r' || padding == '\n' || padding > 0xff: |
| | panic("invalid padding") |
| | case padding != NoPadding && enc.decodeMap[byte(padding)] != invalidIndex: |
| | panic("padding contained in alphabet") |
| | } |
| | enc.padChar = padding |
| | return &enc |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func (enc Encoding) Strict() *Encoding { |
| | enc.strict = true |
| | return &enc |
| | } |
| |
|
| | |
| | var StdEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") |
| |
|
| | |
| | |
| | var URLEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") |
| |
|
| | |
| | |
| | |
| | var RawStdEncoding = StdEncoding.WithPadding(NoPadding) |
| |
|
| | |
| | |
| | |
| | var RawURLEncoding = URLEncoding.WithPadding(NoPadding) |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func (enc *Encoding) Encode(dst, src []byte) { |
| | if len(src) == 0 { |
| | return |
| | } |
| | |
| | |
| | |
| | _ = enc.encode |
| |
|
| | di, si := 0, 0 |
| | n := (len(src) / 3) * 3 |
| | for si < n { |
| | |
| | val := uint(src[si+0])<<16 | uint(src[si+1])<<8 | uint(src[si+2]) |
| |
|
| | dst[di+0] = enc.encode[val>>18&0x3F] |
| | dst[di+1] = enc.encode[val>>12&0x3F] |
| | dst[di+2] = enc.encode[val>>6&0x3F] |
| | dst[di+3] = enc.encode[val&0x3F] |
| |
|
| | si += 3 |
| | di += 4 |
| | } |
| |
|
| | remain := len(src) - si |
| | if remain == 0 { |
| | return |
| | } |
| | |
| | val := uint(src[si+0]) << 16 |
| | if remain == 2 { |
| | val |= uint(src[si+1]) << 8 |
| | } |
| |
|
| | dst[di+0] = enc.encode[val>>18&0x3F] |
| | dst[di+1] = enc.encode[val>>12&0x3F] |
| |
|
| | switch remain { |
| | case 2: |
| | dst[di+2] = enc.encode[val>>6&0x3F] |
| | if enc.padChar != NoPadding { |
| | dst[di+3] = byte(enc.padChar) |
| | } |
| | case 1: |
| | if enc.padChar != NoPadding { |
| | dst[di+2] = byte(enc.padChar) |
| | dst[di+3] = byte(enc.padChar) |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | func (enc *Encoding) AppendEncode(dst, src []byte) []byte { |
| | n := enc.EncodedLen(len(src)) |
| | dst = slices.Grow(dst, n) |
| | enc.Encode(dst[len(dst):][:n], src) |
| | return dst[:len(dst)+n] |
| | } |
| |
|
| | |
| | func (enc *Encoding) EncodeToString(src []byte) string { |
| | buf := make([]byte, enc.EncodedLen(len(src))) |
| | enc.Encode(buf, src) |
| | return string(buf) |
| | } |
| |
|
| | type encoder struct { |
| | err error |
| | enc *Encoding |
| | w io.Writer |
| | buf [3]byte |
| | nbuf int |
| | out [1024]byte |
| | } |
| |
|
| | func (e *encoder) Write(p []byte) (n int, err error) { |
| | if e.err != nil { |
| | return 0, e.err |
| | } |
| |
|
| | |
| | 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:] |
| | if e.nbuf < 3 { |
| | return |
| | } |
| | e.enc.Encode(e.out[:], e.buf[:]) |
| | if _, e.err = e.w.Write(e.out[:4]); e.err != nil { |
| | return n, e.err |
| | } |
| | e.nbuf = 0 |
| | } |
| |
|
| | |
| | for len(p) >= 3 { |
| | nn := len(e.out) / 4 * 3 |
| | if nn > len(p) { |
| | nn = len(p) |
| | nn -= nn % 3 |
| | } |
| | e.enc.Encode(e.out[:], p[:nn]) |
| | if _, e.err = e.w.Write(e.out[0 : nn/3*4]); e.err != nil { |
| | return n, e.err |
| | } |
| | n += nn |
| | p = p[nn:] |
| | } |
| |
|
| | |
| | copy(e.buf[:], p) |
| | e.nbuf = len(p) |
| | n += len(p) |
| | return |
| | } |
| |
|
| | |
| | |
| | func (e *encoder) Close() error { |
| | |
| | if e.err == nil && e.nbuf > 0 { |
| | e.enc.Encode(e.out[:], e.buf[:e.nbuf]) |
| | _, e.err = e.w.Write(e.out[:e.enc.EncodedLen(e.nbuf)]) |
| | e.nbuf = 0 |
| | } |
| | return e.err |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func NewEncoder(enc *Encoding, w io.Writer) io.WriteCloser { |
| | return &encoder{enc: enc, w: w} |
| | } |
| |
|
| | |
| | |
| | func (enc *Encoding) EncodedLen(n int) int { |
| | if enc.padChar == NoPadding { |
| | return n/3*4 + (n%3*8+5)/6 |
| | } |
| | return (n + 2) / 3 * 4 |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | type CorruptInputError int64 |
| |
|
| | func (e CorruptInputError) Error() string { |
| | return "illegal base64 data at input byte " + strconv.FormatInt(int64(e), 10) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func (enc *Encoding) decodeQuantum(dst, src []byte, si int) (nsi, n int, err error) { |
| | |
| | var dbuf [4]byte |
| | dlen := 4 |
| |
|
| | |
| | _ = enc.decodeMap |
| |
|
| | for j := 0; j < len(dbuf); j++ { |
| | if len(src) == si { |
| | switch { |
| | case j == 0: |
| | return si, 0, nil |
| | case j == 1, enc.padChar != NoPadding: |
| | return si, 0, CorruptInputError(si - j) |
| | } |
| | dlen = j |
| | break |
| | } |
| | in := src[si] |
| | si++ |
| |
|
| | out := enc.decodeMap[in] |
| | if out != 0xff { |
| | dbuf[j] = out |
| | continue |
| | } |
| |
|
| | if in == '\n' || in == '\r' { |
| | j-- |
| | continue |
| | } |
| |
|
| | if rune(in) != enc.padChar { |
| | return si, 0, CorruptInputError(si - 1) |
| | } |
| |
|
| | |
| | switch j { |
| | case 0, 1: |
| | |
| | return si, 0, CorruptInputError(si - 1) |
| | case 2: |
| | |
| | |
| | for si < len(src) && (src[si] == '\n' || src[si] == '\r') { |
| | si++ |
| | } |
| | if si == len(src) { |
| | |
| | return si, 0, CorruptInputError(len(src)) |
| | } |
| | if rune(src[si]) != enc.padChar { |
| | |
| | return si, 0, CorruptInputError(si - 1) |
| | } |
| |
|
| | si++ |
| | } |
| |
|
| | |
| | for si < len(src) && (src[si] == '\n' || src[si] == '\r') { |
| | si++ |
| | } |
| | if si < len(src) { |
| | |
| | err = CorruptInputError(si) |
| | } |
| | dlen = j |
| | break |
| | } |
| |
|
| | |
| | val := uint(dbuf[0])<<18 | uint(dbuf[1])<<12 | uint(dbuf[2])<<6 | uint(dbuf[3]) |
| | dbuf[2], dbuf[1], dbuf[0] = byte(val>>0), byte(val>>8), byte(val>>16) |
| | switch dlen { |
| | case 4: |
| | dst[2] = dbuf[2] |
| | dbuf[2] = 0 |
| | fallthrough |
| | case 3: |
| | dst[1] = dbuf[1] |
| | if enc.strict && dbuf[2] != 0 { |
| | return si, 0, CorruptInputError(si - 1) |
| | } |
| | dbuf[1] = 0 |
| | fallthrough |
| | case 2: |
| | dst[0] = dbuf[0] |
| | if enc.strict && (dbuf[1] != 0 || dbuf[2] != 0) { |
| | return si, 0, CorruptInputError(si - 2) |
| | } |
| | } |
| |
|
| | return si, dlen - 1, err |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (enc *Encoding) AppendDecode(dst, src []byte) ([]byte, error) { |
| | |
| | n := len(src) |
| | for n > 0 && rune(src[n-1]) == enc.padChar { |
| | n-- |
| | } |
| | n = decodedLen(n, NoPadding) |
| |
|
| | dst = slices.Grow(dst, n) |
| | n, err := enc.Decode(dst[len(dst):][:n], src) |
| | return dst[:len(dst)+n], err |
| | } |
| |
|
| | |
| | |
| | |
| | func (enc *Encoding) DecodeString(s string) ([]byte, error) { |
| | dbuf := make([]byte, enc.DecodedLen(len(s))) |
| | n, err := enc.Decode(dbuf, []byte(s)) |
| | return dbuf[:n], err |
| | } |
| |
|
| | type decoder struct { |
| | err error |
| | readErr error |
| | enc *Encoding |
| | r io.Reader |
| | buf [1024]byte |
| | nbuf int |
| | out []byte |
| | outbuf [1024 / 4 * 3]byte |
| | } |
| |
|
| | func (d *decoder) Read(p []byte) (n int, err error) { |
| | |
| | if len(d.out) > 0 { |
| | n = copy(p, d.out) |
| | d.out = d.out[n:] |
| | return n, nil |
| | } |
| |
|
| | if d.err != nil { |
| | return 0, d.err |
| | } |
| |
|
| | |
| |
|
| | |
| | for d.nbuf < 4 && d.readErr == nil { |
| | nn := len(p) / 3 * 4 |
| | if nn < 4 { |
| | nn = 4 |
| | } |
| | if nn > len(d.buf) { |
| | nn = len(d.buf) |
| | } |
| | nn, d.readErr = d.r.Read(d.buf[d.nbuf:nn]) |
| | d.nbuf += nn |
| | } |
| |
|
| | if d.nbuf < 4 { |
| | if d.enc.padChar == NoPadding && d.nbuf > 0 { |
| | |
| | var nw int |
| | nw, d.err = d.enc.Decode(d.outbuf[:], d.buf[:d.nbuf]) |
| | d.nbuf = 0 |
| | d.out = d.outbuf[:nw] |
| | n = copy(p, d.out) |
| | d.out = d.out[n:] |
| | if n > 0 || len(p) == 0 && len(d.out) > 0 { |
| | return n, nil |
| | } |
| | if d.err != nil { |
| | return 0, d.err |
| | } |
| | } |
| | d.err = d.readErr |
| | if d.err == io.EOF && d.nbuf > 0 { |
| | d.err = io.ErrUnexpectedEOF |
| | } |
| | return 0, d.err |
| | } |
| |
|
| | |
| | nr := d.nbuf / 4 * 4 |
| | nw := d.nbuf / 4 * 3 |
| | if nw > len(p) { |
| | nw, d.err = d.enc.Decode(d.outbuf[:], d.buf[:nr]) |
| | d.out = d.outbuf[:nw] |
| | n = copy(p, d.out) |
| | d.out = d.out[n:] |
| | } else { |
| | n, d.err = d.enc.Decode(p, d.buf[:nr]) |
| | } |
| | d.nbuf -= nr |
| | copy(d.buf[:d.nbuf], d.buf[nr:]) |
| | return n, d.err |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { |
| | if len(src) == 0 { |
| | return 0, nil |
| | } |
| |
|
| | |
| | |
| | |
| | _ = enc.decodeMap |
| |
|
| | si := 0 |
| | for strconv.IntSize >= 64 && len(src)-si >= 8 && len(dst)-n >= 8 { |
| | src2 := src[si : si+8] |
| | if dn, ok := assemble64( |
| | enc.decodeMap[src2[0]], |
| | enc.decodeMap[src2[1]], |
| | enc.decodeMap[src2[2]], |
| | enc.decodeMap[src2[3]], |
| | enc.decodeMap[src2[4]], |
| | enc.decodeMap[src2[5]], |
| | enc.decodeMap[src2[6]], |
| | enc.decodeMap[src2[7]], |
| | ); ok { |
| | byteorder.BEPutUint64(dst[n:], dn) |
| | n += 6 |
| | si += 8 |
| | } else { |
| | var ninc int |
| | si, ninc, err = enc.decodeQuantum(dst[n:], src, si) |
| | n += ninc |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | } |
| |
|
| | for len(src)-si >= 4 && len(dst)-n >= 4 { |
| | src2 := src[si : si+4] |
| | if dn, ok := assemble32( |
| | enc.decodeMap[src2[0]], |
| | enc.decodeMap[src2[1]], |
| | enc.decodeMap[src2[2]], |
| | enc.decodeMap[src2[3]], |
| | ); ok { |
| | byteorder.BEPutUint32(dst[n:], dn) |
| | n += 3 |
| | si += 4 |
| | } else { |
| | var ninc int |
| | si, ninc, err = enc.decodeQuantum(dst[n:], src, si) |
| | n += ninc |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | } |
| |
|
| | for si < len(src) { |
| | var ninc int |
| | si, ninc, err = enc.decodeQuantum(dst[n:], src, si) |
| | n += ninc |
| | if err != nil { |
| | return n, err |
| | } |
| | } |
| | return n, err |
| | } |
| |
|
| | |
| | |
| | |
| | func assemble32(n1, n2, n3, n4 byte) (dn uint32, ok bool) { |
| | |
| | |
| | if n1|n2|n3|n4 == 0xff { |
| | return 0, false |
| | } |
| | return uint32(n1)<<26 | |
| | uint32(n2)<<20 | |
| | uint32(n3)<<14 | |
| | uint32(n4)<<8, |
| | true |
| | } |
| |
|
| | |
| | |
| | |
| | func assemble64(n1, n2, n3, n4, n5, n6, n7, n8 byte) (dn uint64, ok bool) { |
| | |
| | |
| | if n1|n2|n3|n4|n5|n6|n7|n8 == 0xff { |
| | return 0, false |
| | } |
| | return uint64(n1)<<58 | |
| | uint64(n2)<<52 | |
| | uint64(n3)<<46 | |
| | uint64(n4)<<40 | |
| | uint64(n5)<<34 | |
| | uint64(n6)<<28 | |
| | uint64(n7)<<22 | |
| | uint64(n8)<<16, |
| | true |
| | } |
| |
|
| | type newlineFilteringReader struct { |
| | wrapped io.Reader |
| | } |
| |
|
| | func (r *newlineFilteringReader) Read(p []byte) (int, error) { |
| | n, err := r.wrapped.Read(p) |
| | for n > 0 { |
| | offset := 0 |
| | for i, b := range p[:n] { |
| | if b != '\r' && b != '\n' { |
| | if i != offset { |
| | p[offset] = b |
| | } |
| | offset++ |
| | } |
| | } |
| | if offset > 0 { |
| | return offset, err |
| | } |
| | |
| | n, err = r.wrapped.Read(p) |
| | } |
| | return n, err |
| | } |
| |
|
| | |
| | func NewDecoder(enc *Encoding, r io.Reader) io.Reader { |
| | return &decoder{enc: enc, r: &newlineFilteringReader{r}} |
| | } |
| |
|
| | |
| | |
| | func (enc *Encoding) DecodedLen(n int) int { |
| | return decodedLen(n, enc.padChar) |
| | } |
| |
|
| | func decodedLen(n int, padChar rune) int { |
| | if padChar == NoPadding { |
| | |
| | return n/4*3 + n%4*6/8 |
| | } |
| | |
| | return n / 4 * 3 |
| | } |
| |
|