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