1 /* 2 * Copyright 2024 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package base64x 18 19 import ( 20 `encoding/base64` 21 ) 22 23 // An Encoding is a radix 64 encoding/decoding scheme, defined by a 24 // 64-character alphabet. The most common encoding is the "base64" 25 // encoding defined in RFC 4648 and used in MIME (RFC 2045) and PEM 26 // (RFC 1421). RFC 4648 also defines an alternate encoding, which is 27 // the standard encoding with - and _ substituted for + and /. 28 type Encoding int 29 30 const ( 31 _MODE_URL = 1 << 0 32 _MODE_RAW = 1 << 1 33 _MODE_AVX2 = 1 << 2 34 _MODE_JSON = 1 << 3 35 ) 36 37 // StdEncoding is the standard base64 encoding, as defined in 38 // RFC 4648. 39 const StdEncoding Encoding = 0 40 41 // URLEncoding is the alternate base64 encoding defined in RFC 4648. 42 // It is typically used in URLs and file names. 43 const URLEncoding Encoding = _MODE_URL 44 45 // RawStdEncoding is the standard raw, unpadded base64 encoding, 46 // as defined in RFC 4648 section 3.2. 47 // 48 // This is the same as StdEncoding but omits padding characters. 49 const RawStdEncoding Encoding = _MODE_RAW 50 51 // RawURLEncoding is the unpadded alternate base64 encoding defined in RFC 4648. 52 // It is typically used in URLs and file names. 53 // 54 // This is the same as URLEncoding but omits padding characters. 55 const RawURLEncoding Encoding = _MODE_RAW | _MODE_URL 56 57 // JSONStdEncoding is the StdEncoding and encoded as JSON string as RFC 8259. 58 const JSONStdEncoding Encoding = _MODE_JSON; 59 60 var ( 61 archFlags = 0 62 ) 63 64 /** Encoder Functions **/ 65 66 // Encode encodes src using the specified encoding, writing 67 // EncodedLen(len(src)) bytes to out. 68 // 69 // The encoding pads the output to a multiple of 4 bytes, 70 // so Encode is not appropriate for use on individual blocks 71 // of a large data stream. 72 // 73 // If out is not large enough to contain the encoded result, 74 // it will panic. 75 func (self Encoding) Encode(out []byte, src []byte) { 76 if len(src) != 0 { 77 if buf := out[:0:len(out)]; self.EncodedLen(len(src)) <= len(out) { 78 self.EncodeUnsafe(&buf, src) 79 } else { 80 panic("encoder output buffer is too small") 81 } 82 } 83 } 84 85 // EncodeUnsafe behaves like Encode, except it does NOT check if 86 // out is large enough to contain the encoded result. 87 // 88 // It will also update the length of out. 89 func (self Encoding) EncodeUnsafe(out *[]byte, src []byte) { 90 b64encode(out, &src, int(self) | archFlags) 91 } 92 93 // EncodeToString returns the base64 encoding of src. 94 func (self Encoding) EncodeToString(src []byte) string { 95 nbs := len(src) 96 ret := make([]byte, 0, self.EncodedLen(nbs)) 97 98 /* encode in native code */ 99 self.EncodeUnsafe(&ret, src) 100 return mem2str(ret) 101 } 102 103 // EncodedLen returns the length in bytes of the base64 encoding 104 // of an input buffer of length n. 105 func (self Encoding) EncodedLen(n int) int { 106 if (self & _MODE_RAW) == 0 { 107 return (n + 2) / 3 * 4 108 } else { 109 return (n * 8 + 5) / 6 110 } 111 } 112 113 /** Decoder Functions **/ 114 115 // Decode decodes src using the encoding enc. It writes at most 116 // DecodedLen(len(src)) bytes to out and returns the number of bytes 117 // written. If src contains invalid base64 data, it will return the 118 // number of bytes successfully written and base64.CorruptInputError. 119 // 120 // New line characters (\r and \n) are ignored. 121 // 122 // If out is not large enough to contain the encoded result, 123 // it will panic. 124 func (self Encoding) Decode(out []byte, src []byte) (int, error) { 125 if len(src) == 0 { 126 return 0, nil 127 } else if buf := out[:0:len(out)]; self.DecodedLen(len(src)) <= len(out) { 128 return self.DecodeUnsafe(&buf, src) 129 } else { 130 panic("decoder output buffer is too small") 131 } 132 } 133 134 // DecodeUnsafe behaves like Decode, except it does NOT check if 135 // out is large enough to contain the decoded result. 136 // 137 // It will also update the length of out. 138 func (self Encoding) DecodeUnsafe(out *[]byte, src []byte) (int, error) { 139 if n := b64decode(out, mem2addr(src), len(src), int(self) | archFlags); n >= 0 { 140 return n, nil 141 } else { 142 return 0, base64.CorruptInputError(-n - 1) 143 } 144 } 145 146 // DecodeString returns the bytes represented by the base64 string s. 147 func (self Encoding) DecodeString(s string) ([]byte, error) { 148 src := str2mem(s) 149 ret := make([]byte, 0, self.DecodedLen(len(s))) 150 151 /* decode into the allocated buffer */ 152 if _, err := self.DecodeUnsafe(&ret, src); err != nil { 153 return nil, err 154 } else { 155 return ret, nil 156 } 157 } 158 159 // DecodedLen returns the maximum length in bytes of the decoded data 160 // corresponding to n bytes of base64-encoded data. 161 func (self Encoding) DecodedLen(n int) int { 162 if (self & _MODE_RAW) == 0 { 163 return n / 4 * 3 164 } else { 165 return n * 6 / 8 166 } 167 } 168