aboutsummaryrefslogtreecommitdiff
path: root/core/encoding/uuid/writing.odin
blob: 7acaa3cd710aa9601583dbde777f3da7cce45b39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package uuid

import "base:runtime"
import "core:io"
import "core:strconv"
import "core:strings"

/*
Write a UUID in the 8-4-4-4-12 format.

This procedure performs error checking with every byte written.

If you can guarantee beforehand that your stream has enough space to hold the
UUID (36 bytes), then it is better to use `unsafe_write` instead as that will
be faster.

Inputs:
- w: A writable stream.
- id: The identifier to convert.

Returns:
- error: An `io` error, if one occurred, otherwise `nil`.
*/
write :: proc(w: io.Writer, id: Identifier) -> (error: io.Error) #no_bounds_check {
	write_octet :: proc(w: io.Writer, octet: u8) -> io.Error #no_bounds_check {
		high_nibble := octet >> 4
		low_nibble := octet & 0xF

		io.write_byte(w, strconv.digits[high_nibble]) or_return
		io.write_byte(w, strconv.digits[low_nibble]) or_return
		return nil
	}

	for index in 0 ..< 4 {write_octet(w, id[index]) or_return}
	io.write_byte(w, '-') or_return
	for index in 4 ..< 6 {write_octet(w, id[index]) or_return}
	io.write_byte(w, '-') or_return
	for index in 6 ..< 8 {write_octet(w, id[index]) or_return}
	io.write_byte(w, '-') or_return
	for index in 8 ..< 10 {write_octet(w, id[index]) or_return}
	io.write_byte(w, '-') or_return
	for index in 10 ..< 16 {write_octet(w, id[index]) or_return}

	return nil
}

/*
Write a UUID in the 8-4-4-4-12 format.

This procedure performs no error checking on the underlying stream.

Inputs:
- w: A writable stream.
- id: The identifier to convert.
*/
unsafe_write :: proc(w: io.Writer, id: Identifier) #no_bounds_check {
	write_octet :: proc(w: io.Writer, octet: u8) #no_bounds_check {
		high_nibble := octet >> 4
		low_nibble := octet & 0xF

		io.write_byte(w, strconv.digits[high_nibble])
		io.write_byte(w, strconv.digits[low_nibble])
	}

	for index in 0 ..< 4 {write_octet(w, id[index])}
	io.write_byte(w, '-')
	for index in 4 ..< 6 {write_octet(w, id[index])}
	io.write_byte(w, '-')
	for index in 6 ..< 8 {write_octet(w, id[index])}
	io.write_byte(w, '-')
	for index in 8 ..< 10 {write_octet(w, id[index])}
	io.write_byte(w, '-')
	for index in 10 ..< 16 {write_octet(w, id[index])}
}

/*
Convert a UUID to a string in the 8-4-4-4-12 format.

*Allocates Using Provided Allocator*

Inputs:
- id: The identifier to convert.
- allocator: (default: context.allocator)
- loc: The caller location for debugging purposes (default: #caller_location)

Returns:
- str: The allocated and converted string.
- error: An optional allocator error if one occured, `nil` otherwise.
*/
to_string_allocated :: proc(
	id: Identifier,
	allocator := context.allocator,
	loc := #caller_location,
) -> (
	str: string,
	error: runtime.Allocator_Error,
) #optional_allocator_error {
	buf := make([]byte, EXPECTED_LENGTH, allocator, loc) or_return
	builder := strings.builder_from_bytes(buf[:])
	unsafe_write(strings.to_writer(&builder), id)
	return strings.to_string(builder), nil
}

/*
Convert a UUID to a string in the 8-4-4-4-12 format.

Inputs:
- id: The identifier to convert.
- buffer: A byte buffer to store the result. Must be at least 36 bytes large.
- loc: The caller location for debugging purposes (default: #caller_location)

Returns:
- str: The converted string which will be stored in `buffer`.
*/
to_string_buffer :: proc(
	id: Identifier,
	buffer: []byte,
	loc := #caller_location,
) -> (
	str: string,
) {
	assert(
		len(buffer) >= EXPECTED_LENGTH,
		"The buffer provided is not at least 36 bytes large.",
		loc,
	)
	builder := strings.builder_from_bytes(buffer)
	unsafe_write(strings.to_writer(&builder), id)
	return strings.to_string(builder)
}

to_string :: proc {
	to_string_allocated,
	to_string_buffer,
}