Skip to content

Commit 0cee6f5

Browse files
committed
updated base64
1 parent 216af1d commit 0cee6f5

File tree

1 file changed

+52
-35
lines changed

1 file changed

+52
-35
lines changed

conversion/base64.go

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,100 @@
11
// base64.go
2-
// description: The base64 encoding algorithm as defined in the RFC4648 standard.
3-
// author: [Paul Leydier] (https://github.com/paul-leydier)
4-
// time complexity: O(n)
5-
// space complexity: O(n)
6-
// ref: https://datatracker.ietf.org/doc/html/rfc4648#section-4
7-
// ref: https://en.wikipedia.org/wiki/Base64
8-
// see base64_test.go
2+
// Description: Implements Base64 encoding and decoding as specified in the RFC4648 standard.
3+
// Time Complexity: O(n) - The encoding and decoding processes iterate through the input linearly.
4+
// Space Complexity: O(n) - The output size is proportional to the input size.
5+
// References:
6+
// - RFC4648 Base64 Encoding: https://datatracker.ietf.org/doc/html/rfc4648#section-4
7+
// - Wikipedia Base64 Overview: https://en.wikipedia.org/wiki/Base64
8+
// See also: base64_test.go for test cases and verification.
99

1010
package conversion
1111

1212
import (
13-
"strings" // Used for efficient string builder (more efficient than simply appending strings)
13+
"strings" // Provides an efficient way to build strings without unnecessary memory allocations.
1414
)
1515

1616
const Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
1717

18-
// Base64Encode encodes the received input bytes slice into a base64 string.
19-
// The implementation follows the RFC4648 standard, which is documented
20-
// at https://datatracker.ietf.org/doc/html/rfc4648#section-4
18+
// Base64Encode converts a byte slice into a Base64-encoded string.
19+
//
20+
// The encoding follows the RFC4648 standard, where every 3 bytes of input
21+
// are converted into 4 characters using a 6-bit mapping table. If the input
22+
// length is not a multiple of 3, padding is added using '='.
23+
//
24+
// Example:
25+
//
26+
// Input: "hello"
27+
// Output: "aGVsbG8="
2128
func Base64Encode(input []byte) string {
22-
var sb strings.Builder
23-
// If not 24 bits (3 bytes) multiple, pad with 0 value bytes, and with "=" for the output
29+
var sb strings.Builder // Efficient string concatenation
30+
31+
// Calculate padding required if the input length is not a multiple of 3.
2432
var padding string
2533
for i := len(input) % 3; i > 0 && i < 3; i++ {
2634
var zeroByte byte
27-
input = append(input, zeroByte)
28-
padding += "="
35+
input = append(input, zeroByte) // Append zero bytes to align the length
36+
padding += "=" // Add '=' padding to match the expected output length
2937
}
3038

31-
// encode 24 bits per 24 bits (3 bytes per 3 bytes)
39+
// Process input 3 bytes at a time, converting them into 4 Base64 characters
3240
for i := 0; i < len(input); i += 3 {
33-
// select 3 8-bit input groups, and re-arrange them into 4 6-bit groups
34-
// the literal 0x3F corresponds to the byte "0011 1111"
35-
// the operation "byte & 0x3F" masks the two left-most bits
41+
// Extract 3 bytes and split them into four 6-bit groups
42+
// Each byte contributes to multiple output characters
3643
group := [4]byte{
37-
input[i] >> 2,
38-
(input[i]<<4)&0x3F + input[i+1]>>4,
39-
(input[i+1]<<2)&0x3F + input[i+2]>>6,
40-
input[i+2] & 0x3F,
44+
input[i] >> 2, // First 6 bits
45+
(input[i]<<4)&0x3F + input[i+1]>>4, // Next 6 bits (spanning two bytes)
46+
(input[i+1]<<2)&0x3F + input[i+2]>>6, // Next 6 bits (spanning two bytes)
47+
input[i+2] & 0x3F, // Last 6 bits
4148
}
4249

43-
// translate each group into a char using the static map
50+
// Convert each 6-bit group to a Base64 character
4451
for _, b := range group {
45-
sb.WriteString(string(Alphabet[int(b)]))
52+
sb.WriteByte(Alphabet[b])
4653
}
4754
}
55+
4856
encoded := sb.String()
4957

50-
// Apply the output padding
58+
// Apply '=' padding if necessary
5159
encoded = encoded[:len(encoded)-len(padding)] + padding[:]
5260

5361
return encoded
5462
}
5563

56-
// Base64Decode decodes the received input base64 string into a byte slice.
57-
// The implementation follows the RFC4648 standard, which is documented
58-
// at https://datatracker.ietf.org/doc/html/rfc4648#section-4
64+
// Base64Decode converts a Base64-encoded string back into a byte slice.
65+
//
66+
// This function processes 4-character chunks from the input string, converting
67+
// them back into 3 original bytes. Padding ('=') characters at the end of the input
68+
// are ignored to restore the correct output length.
69+
//
70+
// Example:
71+
//
72+
// Input: "aGVsbG8="
73+
// Output: "hello"
5974
func Base64Decode(input string) []byte {
60-
padding := strings.Count(input, "=") // Number of bytes which will be ignored
75+
padding := strings.Count(input, "=") // Count padding characters, which affect output size
6176
var decoded []byte
6277

63-
// select 4 6-bit input groups, and re-arrange them into 3 8-bit groups
78+
// Process input in chunks of 4 Base64 characters at a time
6479
for i := 0; i < len(input); i += 4 {
65-
// translate each group into a byte using the static map
80+
// Convert each Base64 character back to its corresponding 6-bit value
6681
byteInput := [4]byte{
6782
byte(strings.IndexByte(Alphabet, input[i])),
6883
byte(strings.IndexByte(Alphabet, input[i+1])),
6984
byte(strings.IndexByte(Alphabet, input[i+2])),
7085
byte(strings.IndexByte(Alphabet, input[i+3])),
7186
}
7287

88+
// Reassemble original bytes from 6-bit groups
7389
group := [3]byte{
74-
byteInput[0]<<2 + byteInput[1]>>4,
75-
byteInput[1]<<4 + byteInput[2]>>2,
76-
byteInput[2]<<6 + byteInput[3],
90+
byteInput[0]<<2 + byteInput[1]>>4, // First byte
91+
byteInput[1]<<4 + byteInput[2]>>2, // Second byte
92+
byteInput[2]<<6 + byteInput[3], // Third byte
7793
}
7894

7995
decoded = append(decoded, group[:]...)
8096
}
8197

98+
// Remove extra bytes that were added due to padding
8299
return decoded[:len(decoded)-padding]
83100
}

0 commit comments

Comments
 (0)