summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-byteutil/buffer.go
blob: 9e15c8adecd144c93bcec28ce6bf2a8c3f055d28 (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
package byteutil

import (
	"errors"
	"io"
	"unicode/utf8"
)

var (
	// ensure we conform to interfaces.
	_ interface {
		io.Writer
		io.ByteWriter
		WriteRune(rune) (int, error)
		io.StringWriter
		io.WriterAt
		WriteStringAt(string, int64) (int, error)
	} = (*Buffer)(nil)

	// ErrBeyondBufferLen is returned if .WriteAt() is attempted beyond buffer length.
	ErrBeyondBufferLen = errors.New("start beyond buffer length")
)

// Buffer is a simple wrapper around a byte slice.
type Buffer struct{ B []byte }

// WriteByte will append given byte to buffer, fulfilling io.ByteWriter.
func (buf *Buffer) WriteByte(c byte) error {
	buf.B = append(buf.B, c)
	return nil
}

// WriteRune will append given rune to buffer.
func (buf *Buffer) WriteRune(r rune) (int, error) {
	// Check for single-byte rune
	if r < utf8.RuneSelf {
		buf.B = append(buf.B, byte(r))
		return 1, nil
	}

	// Before-len
	l := len(buf.B)

	// Grow to max size rune
	buf.Grow(utf8.UTFMax)

	// Write encoded rune to buffer
	n := utf8.EncodeRune(buf.B[l:len(buf.B)], r)
	buf.B = buf.B[:l+n]

	return n, nil
}

// Write will append given byte slice to buffer, fulfilling io.Writer.
func (buf *Buffer) Write(b []byte) (int, error) {
	buf.B = append(buf.B, b...)
	return len(b), nil
}

// WriteString will append given string to buffer, fulfilling io.StringWriter.
func (buf *Buffer) WriteString(s string) (int, error) {
	buf.B = append(buf.B, s...)
	return len(s), nil
}

// WriteAt will append given byte slice to buffer at index 'start', fulfilling io.WriterAt.
func (buf *Buffer) WriteAt(b []byte, start int64) (int, error) {
	if start > int64(len(buf.B)) {
		return 0, ErrBeyondBufferLen
	}
	buf.Grow(len(b) - int(int64(len(buf.B))-start))
	return copy(buf.B[start:], b), nil
}

// WriteStringAt will append given string to buffer at index 'start'.
func (buf *Buffer) WriteStringAt(s string, start int64) (int, error) {
	if start > int64(len(buf.B)) {
		return 0, ErrBeyondBufferLen
	}
	buf.Grow(len(s) - int(int64(len(buf.B))-start))
	return copy(buf.B[start:], s), nil
}

// Len returns the length of the buffer's underlying byte slice.
func (buf *Buffer) Len() int {
	return len(buf.B)
}

// Cap returns the capacity of the buffer's underlying byte slice.
func (buf *Buffer) Cap() int {
	return cap(buf.B)
}

// Grow will increase the buffers length by 'sz', and the capacity by at least this.
func (buf *Buffer) Grow(sz int) {
	buf.Guarantee(sz)
	buf.B = buf.B[:len(buf.B)+sz]
}

// Guarantee will guarantee buffer containers at least 'sz' remaining capacity.
func (buf *Buffer) Guarantee(sz int) {
	if sz > cap(buf.B)-len(buf.B) {
		nb := make([]byte, 2*cap(buf.B)+sz)
		copy(nb, buf.B)
		buf.B = nb[:len(buf.B)]
	}
}

// Truncate will reduce the length of the buffer by 'n'.
func (buf *Buffer) Truncate(n int) {
	if n > len(buf.B) {
		n = len(buf.B)
	}
	buf.B = buf.B[:len(buf.B)-n]
}

// Reset will reset the buffer length to 0 (retains capacity).
func (buf *Buffer) Reset() {
	buf.B = buf.B[:0]
}

// String returns the underlying byte slice as a string. Please note
// this value is tied directly to the underlying byte slice, if you
// write to the buffer then returned string values will also change.
//
// To get an immutable string from buffered data, use string(buf.B).
func (buf *Buffer) String() string {
	return B2S(buf.B)
}

// Full returns the full capacity byteslice allocated for this buffer.
func (buf *Buffer) Full() []byte {
	return buf.B[0:cap(buf.B)]
}