encoding/cbor: initial package implementation

This commit is contained in:
Laytan Laats
2023-11-22 16:12:37 +01:00
parent 4c35633e01
commit 5533a327eb
12 changed files with 4067 additions and 51 deletions
+79 -49
View File
@@ -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_length := ((4 * length / 3) + 3) &~ 3
out := make([]byte, out_length, allocator)
out: strings.Builder
strings.builder_init(&out, 0, out_length, allocator) or_return
c0, c1, c2, block: int
ioerr := encode_into(strings.to_stream(&out), data, ENC_TBL)
assert(ioerr == nil)
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]) }
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0)
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)
return strings.to_string(out), nil
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
length := len(data)
if length == 0 {
return nil
}
encoded_length :: #force_inline proc(data: []byte) -> int {
length := len(data)
if length == 0 {
return 0
}
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
return ((4 * length / 3) + 3) &~ 3
}
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
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]) }
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]
#bounds_check { io.write_full(w, out[:]) or_return }
}
return
}
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
}
}