aboutsummaryrefslogtreecommitdiff
path: root/core/encoding/base64
diff options
context:
space:
mode:
authorLaytan Laats <laytanlaats@hotmail.com>2023-11-22 16:12:37 +0100
committerLaytan Laats <laytanlaats@hotmail.com>2024-03-04 17:25:34 +0100
commit5533a327eb0f526cbebbe71124620fcbb0bc0649 (patch)
tree6aadf7d275703ef574533ac1e1d348374a411cde /core/encoding/base64
parent4c35633e0147b481dd7b2352d6bdb603f78c6dc7 (diff)
encoding/cbor: initial package implementation
Diffstat (limited to 'core/encoding/base64')
-rw-r--r--core/encoding/base64/base64.odin124
1 files changed, 77 insertions, 47 deletions
diff --git a/core/encoding/base64/base64.odin b/core/encoding/base64/base64.odin
index cf2ea1c12..793f22c57 100644
--- a/core/encoding/base64/base64.odin
+++ b/core/encoding/base64/base64.odin
@@ -1,5 +1,9 @@
package base64
+import "core:io"
+import "core:mem"
+import "core:strings"
+
// @note(zh): Encoding utility for Base64
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
@@ -39,59 +43,85 @@ DEC_TABLE := [128]int {
49, 50, 51, -1, -1, -1, -1, -1,
}
-encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
- length := len(data)
- if length == 0 {
- return ""
- }
+encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> (encoded: string, err: mem.Allocator_Error) #optional_allocator_error {
+ out_length := encoded_length(data)
+ if out_length == 0 {
+ return
+ }
+
+ out: strings.Builder
+ strings.builder_init(&out, 0, out_length, allocator) or_return
+
+ ioerr := encode_into(strings.to_stream(&out), data, ENC_TBL)
+ assert(ioerr == nil)
+
+ return strings.to_string(out), nil
+}
+
+encoded_length :: #force_inline proc(data: []byte) -> int {
+ length := len(data)
+ if length == 0 {
+ return 0
+ }
+
+ return ((4 * length / 3) + 3) &~ 3
+}
- out_length := ((4 * length / 3) + 3) &~ 3
- out := make([]byte, out_length, allocator)
+encode_into :: proc(w: io.Writer, data: []byte, ENC_TBL := ENC_TABLE) -> (err: io.Error) #no_bounds_check {
+ length := len(data)
+ if length == 0 {
+ return
+ }
- c0, c1, c2, block: int
+ c0, c1, c2, block: int
- for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
- c0, c1, c2 = int(data[i]), -1, -1
+ for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
+ c0, c1, c2 = int(data[i]), -1, -1
- if i + 1 < length { c1 = int(data[i + 1]) }
- if i + 2 < length { c2 = int(data[i + 2]) }
+ if i + 1 < length { c1 = int(data[i + 1]) }
+ if i + 2 < length { c2 = int(data[i + 2]) }
- block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0)
+ block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0)
+
+ out: [4]byte
+ out[0] = ENC_TBL[block >> 18 & 63]
+ out[1] = ENC_TBL[block >> 12 & 63]
+ out[2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63]
+ out[3] = c2 == -1 ? PADDING : ENC_TBL[block & 63]
- out[d] = ENC_TBL[block >> 18 & 63]
- out[d + 1] = ENC_TBL[block >> 12 & 63]
- out[d + 2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63]
- out[d + 3] = c2 == -1 ? PADDING : ENC_TBL[block & 63]
- }
- return string(out)
+ #bounds_check { io.write_full(w, out[:]) or_return }
+ }
+ return
}
-decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
- length := len(data)
- if length == 0 {
- return nil
- }
-
- pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0
- out_length := ((length * 6) >> 3) - pad_count
- out := make([]byte, out_length, allocator)
-
- c0, c1, c2, c3: int
- b0, b1, b2: int
-
- for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
- c0 = DEC_TBL[data[i]]
- c1 = DEC_TBL[data[i + 1]]
- c2 = DEC_TBL[data[i + 2]]
- c3 = DEC_TBL[data[i + 3]]
-
- b0 = (c0 << 2) | (c1 >> 4)
- b1 = (c1 << 4) | (c2 >> 2)
- b2 = (c2 << 6) | c3
-
- out[j] = byte(b0)
- out[j + 1] = byte(b1)
- out[j + 2] = byte(b2)
- }
- return out
+decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> (out: []byte, err: mem.Allocator_Error) #optional_allocator_error {
+ #no_bounds_check {
+ length := len(data)
+ if length == 0 {
+ return
+ }
+
+ pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0
+ out_length := ((length * 6) >> 3) - pad_count
+ out = make([]byte, out_length, allocator) or_return
+
+ c0, c1, c2, c3: int
+ b0, b1, b2: int
+
+ for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
+ c0 = DEC_TBL[data[i]]
+ c1 = DEC_TBL[data[i + 1]]
+ c2 = DEC_TBL[data[i + 2]]
+ c3 = DEC_TBL[data[i + 3]]
+
+ b0 = (c0 << 2) | (c1 >> 4)
+ b1 = (c1 << 4) | (c2 >> 2)
+ b2 = (c2 << 6) | c3
+
+ out[j] = byte(b0)
+ out[j + 1] = byte(b1)
+ out[j + 2] = byte(b2)
+ }
+ return
+ }
}