@@ -7,40 +7,56 @@ import "core:io"
77
88Builder_Flush_Proc :: #type proc (b: ^Builder) -> (do_reset: bool )
99
10+ /*
11+ dynamic byte buffer / string builder with helper procedures
12+ the dynamic array is wrapped inside the struct to be more opaque
13+ you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly
14+ */
1015Builder :: struct {
1116 buf: [dynamic ]byte ,
1217}
1318
19+ // return a builder, default length 0 / cap 16 are done through make
1420make_builder_none :: proc (allocator := context .allocator) -> Builder {
1521 return Builder{buf=make ([dynamic ]byte , allocator)}
1622}
1723
24+ // return a builder, with a set length `len` and cap 16 byte buffer
1825make_builder_len :: proc (len: int , allocator := context .allocator) -> Builder {
1926 return Builder{buf=make ([dynamic ]byte , len, allocator)}
2027}
2128
29+ // return a builder, with a set length `len` byte buffer and a custom `cap`
2230make_builder_len_cap :: proc (len, cap: int , allocator := context .allocator) -> Builder {
2331 return Builder{buf=make ([dynamic ]byte , len, cap, allocator)}
2432}
2533
34+ // overload simple `make_builder_*` with or without len / cap parameters
2635make_builder :: proc {
2736 make_builder_none,
2837 make_builder_len,
2938 make_builder_len_cap,
3039}
3140
41+ // initialize a builder, default length 0 / cap 16 are done through make
42+ // replaces the existing `buf`
3243init_builder_none :: proc (b: ^Builder, allocator := context .allocator) {
3344 b.buf = make ([dynamic ]byte , allocator)
3445}
3546
47+ // initialize a builder, with a set length `len` and cap 16 byte buffer
48+ // replaces the existing `buf`
3649init_builder_len :: proc (b: ^Builder, len: int , allocator := context .allocator) {
3750 b.buf = make ([dynamic ]byte , len, allocator)
3851}
3952
53+ // initialize a builder, with a set length `len` byte buffer and a custom `cap`
54+ // replaces the existing `buf`
4055init_builder_len_cap :: proc (b: ^Builder, len, cap: int , allocator := context .allocator) {
4156 b.buf = make ([dynamic ]byte , len, cap, allocator)
4257}
4358
59+ // overload simple `init_builder_*` with or without len / ap parameters
4460init_builder :: proc {
4561 init_builder_none,
4662 init_builder_len,
@@ -76,30 +92,42 @@ _builder_stream_vtable := &io.Stream_VTable{
7692 },
7793}
7894
95+ // return an `io.Stream` from a builder
7996to_stream :: proc (b: ^Builder) -> io.Stream {
8097 return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
8198}
99+
100+ // return an `io.Writer` from a builder
82101to_writer :: proc (b: ^Builder) -> io.Writer {
83102 return io.to_writer (to_stream (b))
84103}
85104
86-
87-
88-
105+ // delete and clear the builder byte buffer content
89106destroy_builder :: proc (b: ^Builder) {
90107 delete (b.buf)
91108 clear (&b.buf)
92109}
93110
111+ // reserve the builfer byte buffer to a specific cap, when it's higher than before
94112grow_builder :: proc (b: ^Builder, cap: int ) {
95113 reserve (&b.buf, cap)
96114}
97115
116+ // clear the builder byte buffer content
98117reset_builder :: proc (b: ^Builder) {
99118 clear (&b.buf)
100119}
101120
102-
121+ /*
122+ create an empty builder with the same slice length as its cap
123+ uses the `mem.nil_allocator` to avoid allocation and keep a fixed length
124+ used in `fmt.bprint*`
125+
126+ bytes: [8]byte // <-- gets filled
127+ builder := strings.builder_from_slice(bytes[:])
128+ strings.write_byte(&builder, 'a') -> "a"
129+ strings.write_byte(&builder, 'b') -> "ab"
130+ */
103131builder_from_slice :: proc (backing: []byte ) -> Builder {
104132 s := transmute (mem.Raw_Slice)backing
105133 d := mem.Raw_Dynamic_Array{
@@ -112,39 +140,80 @@ builder_from_slice :: proc(backing: []byte) -> Builder {
112140 buf = transmute ([dynamic ]byte )d,
113141 }
114142}
143+
144+ // cast the builder byte buffer to a string and return it
115145to_string :: proc (b: Builder) -> string {
116146 return string (b.buf[:])
117147}
118148
149+ // return the length of the builder byte buffer
119150builder_len :: proc (b: Builder) -> int {
120151 return len (b.buf)
121152}
153+
154+ // return the cap of the builder byte buffer
122155builder_cap :: proc (b: Builder) -> int {
123156 return cap (b.buf)
124157}
158+
159+ // returns the space left in the builder byte buffer to use up
125160builder_space :: proc (b: Builder) -> int {
126- return max ( cap (b.buf), len (b.buf), 0 )
161+ return cap (b.buf) - len (b.buf)
127162}
128163
164+ /*
165+ appends a byte to the builder, returns the append diff
166+
167+ builder := strings.make_builder()
168+ strings.write_byte(&builder, 'a') // 1
169+ strings.write_byte(&builder, 'b') // 1
170+ strings.write_byte(&builder, 'c') // 1
171+ fmt.println(strings.to_string(builder)) // -> abc
172+ */
129173write_byte :: proc (b: ^Builder, x: byte ) -> (n: int ) {
130174 n0 := len (b.buf)
131175 append (&b.buf, x)
132176 n1 := len (b.buf)
133177 return n1-n0
134178}
135179
180+ /*
181+ appends a slice of bytes to the builder, returns the append diff
182+
183+ builder := strings.make_builder()
184+ bytes := [?]byte { 'a', 'b', 'c' }
185+ strings.write_bytes(&builder, bytes[:]) // 3
186+ fmt.println(strings.to_string(builder)) // -> abc
187+ */
136188write_bytes :: proc (b: ^Builder, x: []byte ) -> (n: int ) {
137189 n0 := len (b.buf)
138190 append (&b.buf, ..x)
139191 n1 := len (b.buf)
140192 return n1-n0
141193}
142194
195+ /*
196+ appends a single rune into the builder, returns written rune size and an `io.Error`
197+
198+ builder := strings.make_builder()
199+ strings.write_rune_builder(&builder, 'ä') // 2 None
200+ strings.write_rune_builder(&builder, 'b') // 1 None
201+ strings.write_rune_builder(&builder, 'c') // 1 None
202+ fmt.println(strings.to_string(builder)) // -> äbc
203+ */
143204write_rune_builder :: proc (b: ^Builder, r: rune ) -> (int , io.Error) {
144205 return io.write_rune (to_writer (b), r)
145206}
146207
208+ /*
209+ appends a quoted rune into the builder, returns written size
147210
211+ builder := strings.make_builder()
212+ strings.write_string(&builder, "abc") // 3
213+ strings.write_quoted_rune_builder(&builder, 'ä') // 4
214+ strings.write_string(&builder, "abc") // 3
215+ fmt.println(strings.to_string(builder)) // -> abc'ä'abc
216+ */
148217write_quoted_rune_builder :: proc (b: ^Builder, r: rune ) -> (n: int ) {
149218 return write_quoted_rune (to_writer (b), r)
150219}
@@ -155,7 +224,7 @@ _write_byte :: proc(w: io.Writer, c: byte) -> int {
155224 return 1 if err == nil else 0
156225}
157226
158-
227+ // writer append a quoted rune into the byte buffer, return the written size
159228write_quoted_rune :: proc (w: io.Writer, r: rune ) -> (n: int ) {
160229 quote := byte (' \' ' )
161230 n += _write_byte (w, quote)
@@ -173,50 +242,75 @@ write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
173242 return
174243}
175244
176-
245+ // overload for `write_string_*` variants
177246write_string :: proc {
178247 write_string_builder,
179248 write_string_writer,
180249}
181250
251+ /*
252+ appends a string to the builder, return the written byte size
253+
254+ builder := strings.make_builder()
255+ strings.write_string(&builder, "a") // 1
256+ strings.write_string(&builder, "bc") // 2
257+ strings.write_string(&builder, "xyz") // 3
258+ fmt.println(strings.to_string(builder)) // -> abcxyz
259+ */
182260write_string_builder :: proc (b: ^Builder, s: string ) -> (n: int ) {
183261 return write_string_writer (to_writer (b), s)
184262}
185263
264+ // appends a string to the writer
186265write_string_writer :: proc (w: io.Writer, s: string ) -> (n: int ) {
187266 n, _ = io.write (w, transmute ([]byte )s)
188267 return
189268}
190269
191-
192-
193-
270+ // pops and returns the last byte in the builder
271+ // returns 0 when the builder is empty
194272pop_byte :: proc (b: ^Builder) -> (r: byte ) {
195273 if len (b.buf) == 0 {
196274 return 0
197275 }
276+
198277 r = b.buf[len (b.buf)-1 ]
199278 d := cast (^mem.Raw_Dynamic_Array)&b.buf
200279 d.len = max (d.len-1 , 0 )
201280 return
202281}
203282
283+ // pops the last rune in the builder and returns the popped rune and its rune width
284+ // returns 0, 0 when the builder is empty
204285pop_rune :: proc (b: ^Builder) -> (r: rune , width: int ) {
286+ if len (b.buf) == 0 {
287+ return 0 , 0
288+ }
289+
205290 r, width = utf8.decode_last_rune (b.buf[:])
206291 d := cast (^mem.Raw_Dynamic_Array)&b.buf
207292 d.len = max (d.len-width, 0 )
208293 return
209294}
210295
211-
212296@(private)
213297DIGITS_LOWER := " 0123456789abcdefx"
214298
299+ // overload for `write_quoted_string_*` variants
215300write_quoted_string :: proc {
216301 write_quoted_string_builder,
217302 write_quoted_string_writer,
218303}
219304
305+ /*
306+ append a quoted string into the builder, return the written byte size
307+
308+ builder := strings.make_builder()
309+ strings.write_quoted_string(&builder, "a") // 3
310+ strings.write_quoted_string(&builder, "bc", '\'') // 4
311+ strings.write_quoted_string(&builder, "xyz") // 5
312+ fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
313+ */
220314write_quoted_string_builder :: proc (b: ^Builder, str: string , quote: byte = ' "' ) -> (n: int ) {
221315 n, _ = io.write_quoted_string (to_writer (b), str, quote)
222316 return
@@ -228,11 +322,13 @@ write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"')
228322 return
229323}
230324
325+ // overload for `write_encoded_rune_*`
231326write_encoded_rune :: proc {
232327 write_encoded_rune_builder,
233328 write_encoded_rune_writer,
234329}
235330
331+ // appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
236332write_encoded_rune_builder :: proc (b: ^Builder, r: rune , write_quote := true ) -> (n: int ) {
237333 n, _ = io.write_encoded_rune (to_writer (b), r, write_quote)
238334 return
@@ -244,12 +340,15 @@ write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) ->
244340 return
245341}
246342
247-
343+ // overload for `write_escaped_rune_*`
248344write_escaped_rune :: proc {
249345 write_escaped_rune_builder,
250346 write_escaped_rune_writer,
251347}
252348
349+ // appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
350+ // when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
351+ // `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
253352write_escaped_rune_builder :: proc (b: ^Builder, r: rune , quote: byte , html_safe := false ) -> (n: int ) {
254353 n, _ = io.write_escaped_rune (to_writer (b), r, quote, html_safe)
255354 return
@@ -261,21 +360,26 @@ write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe
261360 return
262361}
263362
264-
363+ // writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
265364write_u64 :: proc (b: ^Builder, i: u64 , base: int = 10 ) -> (n: int ) {
266365 buf: [32 ]byte
267366 s := strconv.append_bits (buf[:], i, base, false , 64 , strconv.digits, nil )
268367 return write_string (b, s)
269368}
369+
370+ // writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters
270371write_i64 :: proc (b: ^Builder, i: i64 , base: int = 10 ) -> (n: int ) {
271372 buf: [32 ]byte
272373 s := strconv.append_bits (buf[:], u64 (i), base, true , 64 , strconv.digits, nil )
273374 return write_string (b, s)
274375}
275376
377+ // writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters
276378write_uint :: proc (b: ^Builder, i: uint , base: int = 10 ) -> (n: int ) {
277379 return write_u64 (b, u64 (i), base)
278380}
381+
382+ // writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters
279383write_int :: proc (b: ^Builder, i: int , base: int = 10 ) -> (n: int ) {
280384 return write_i64 (b, i64 (i), base)
281385}
0 commit comments