diff options
| author | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2026-02-14 13:57:02 +0100 |
|---|---|---|
| committer | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2026-02-14 13:57:02 +0100 |
| commit | 2622c1ab993cdc422fee96a99beb33c73dafa1d1 (patch) | |
| tree | 8584c6002ccfbd4fceb7af0845448686fb9d0553 | |
| parent | 4c9466e3ea6943911971c62dd57c59bba886cfef (diff) | |
Fix #6229
Fixes #6229 by adding `encode_upper` and `encode_upper_into_writer`.
Also updated the documentation to be more like the rest of `core`.
| -rw-r--r-- | core/encoding/hex/hex.odin | 124 |
1 files changed, 110 insertions, 14 deletions
diff --git a/core/encoding/hex/hex.odin b/core/encoding/hex/hex.odin index 318e52ace..c4726d9e9 100644 --- a/core/encoding/hex/hex.odin +++ b/core/encoding/hex/hex.odin @@ -1,35 +1,115 @@ // Encoding and decoding of hex-encoded binary, e.g. `0x23` -> `#`. package encoding_hex +import "base:runtime" import "core:io" import "core:strings" -encode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> []byte #no_bounds_check { - dst := make([]byte, len(src) * 2, allocator, loc) - for i, j := 0, 0; i < len(src); i += 1 { +/* +Encodes a byte slice into a lowercase hex sequence + +*Allocates Using Provided Allocator* + +Inputs: +- src: The `[]byte` to be hex-encoded +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +- res: The hex-encoded result +- err: An optional allocator error if one occured, `.None` otherwise +*/ +encode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (res: []byte, err: runtime.Allocator_Error) #optional_allocator_error { + res, err = make([]byte, len(src) * 2, allocator, loc) + #no_bounds_check for i, j := 0, 0; i < len(src); i += 1 { v := src[i] - dst[j] = HEXTABLE[v>>4] - dst[j+1] = HEXTABLE[v&0x0f] + res[j] = LOWER[v>>4] + res[j+1] = LOWER[v&0x0f] j += 2 } + return +} + +/* +Encodes a byte slice as a lowercase hex sequence into an `io.Writer` + +Inputs: +- dst: The `io.Writer` to encode into +- src: The `[]byte` to be hex-encoded + +Returns: +- err: An `io.Error` if one occured, `.None` otherwise +*/ +encode_into_writer :: proc(dst: io.Writer, src: []byte) -> (err: io.Error) { + for v in src { + io.write(dst, {LOWER[v>>4], LOWER[v&0x0f]}) or_return + } + return +} + +/* +Encodes a byte slice into an uppercase hex sequence - return dst +*Allocates Using Provided Allocator* + +Inputs: +- src: The `[]byte` to be hex-encoded +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +- res: The hex-encoded result +- err: An optional allocator error if one occured, `.None` otherwise +*/ +encode_upper :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (res: []byte, err: runtime.Allocator_Error) #optional_allocator_error { + res, err = make([]byte, len(src) * 2, allocator, loc) + #no_bounds_check for i, j := 0, 0; i < len(src); i += 1 { + v := src[i] + res[j] = UPPER[v>>4] + res[j+1] = UPPER[v&0x0f] + j += 2 + } + return } -encode_into_writer :: proc(dst: io.Writer, src: []byte) -> io.Error { +/* +Encodes a byte slice as an uppercase hex sequence into an `io.Writer` + +Inputs: +- dst: The `io.Writer` to encode into +- src: The `[]byte` to be hex-encoded + +Returns: +- err: An `io.Error` if one occured, `.None` otherwise +*/ +encode_upper_into_writer :: proc(dst: io.Writer, src: []byte) -> (err: io.Error) { for v in src { - io.write(dst, {HEXTABLE[v>>4], HEXTABLE[v&0x0f]}) or_return + io.write(dst, {UPPER[v>>4], UPPER[v&0x0f]}) or_return } - return nil + return } -decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (dst: []byte, ok: bool) #no_bounds_check { +/* +Decodes a hex sequence into a byte slice + +*Allocates Using Provided Allocator* + +Inputs: +- dst: The hex sequence decoded into bytes +- src: The `[]byte` to be hex-decoded +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +- ok: A bool, `true` if decoding succeeded, `false` otherwise +*/ +decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (dst: []byte, ok: bool) { if len(src) % 2 == 1 { return } dst = make([]byte, len(src) / 2, allocator, loc) - for i, j := 0, 1; j < len(src); j += 2 { + #no_bounds_check for i, j := 0, 1; j < len(src); j += 2 { p := src[j-1] q := src[j] @@ -43,8 +123,16 @@ decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_locat return dst, true } -// Decodes the given sequence into one byte. -// Should be called with one byte worth of the source, eg: 0x23 -> '#'. +/* +Decodes the first byte in a hex sequence to a byte + +Inputs: +- str: A hex-encoded `string`, e.g. `"0x23"` + +Returns: +- res: The decoded byte, e.g. `'#'` +- ok: A bool, `true` if decoding succeeded, `false` otherwise +*/ decode_sequence :: proc(str: string) -> (res: byte, ok: bool) { str := str if strings.has_prefix(str, "0x") || strings.has_prefix(str, "0X") { @@ -62,7 +150,7 @@ decode_sequence :: proc(str: string) -> (res: byte, ok: bool) { } @(private) -HEXTABLE := [16]byte { +LOWER := [16]byte { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', @@ -70,6 +158,14 @@ HEXTABLE := [16]byte { } @(private) +UPPER := [16]byte { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', +} + +@(private) hex_digit :: proc(char: byte) -> (u8, bool) { switch char { case '0' ..= '9': return char - '0', true |