summaryrefslogtreecommitdiff
path: root/vendor/github.com/dsoprea/go-utility/v2/filesystem/seekable_buffer.go
blob: 5d41bb5df1b1894080a66f09302fc1c4f83da5c2 (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
137
138
139
140
141
142
143
144
145
146
package rifs

import (
	"io"
	"os"

	"github.com/dsoprea/go-logging"
)

// SeekableBuffer is a simple memory structure that satisfies
// `io.ReadWriteSeeker`.
type SeekableBuffer struct {
	data     []byte
	position int64
}

// NewSeekableBuffer is a factory that returns a `*SeekableBuffer`.
func NewSeekableBuffer() *SeekableBuffer {
	data := make([]byte, 0)

	return &SeekableBuffer{
		data: data,
	}
}

// NewSeekableBufferWithBytes is a factory that returns a `*SeekableBuffer`.
func NewSeekableBufferWithBytes(originalData []byte) *SeekableBuffer {
	data := make([]byte, len(originalData))
	copy(data, originalData)

	return &SeekableBuffer{
		data: data,
	}
}

func len64(data []byte) int64 {
	return int64(len(data))
}

// Bytes returns the underlying slice.
func (sb *SeekableBuffer) Bytes() []byte {
	return sb.data
}

// Len returns the number of bytes currently stored.
func (sb *SeekableBuffer) Len() int {
	return len(sb.data)
}

// Write does a standard write to the internal slice.
func (sb *SeekableBuffer) Write(p []byte) (n int, err error) {
	defer func() {
		if state := recover(); state != nil {
			err = log.Wrap(state.(error))
		}
	}()

	// The current position we're already at is past the end of the data we
	// actually have. Extend our buffer up to our current position.
	if sb.position > len64(sb.data) {
		extra := make([]byte, sb.position-len64(sb.data))
		sb.data = append(sb.data, extra...)
	}

	positionFromEnd := len64(sb.data) - sb.position
	tailCount := positionFromEnd - len64(p)

	var tailBytes []byte
	if tailCount > 0 {
		tailBytes = sb.data[len64(sb.data)-tailCount:]
		sb.data = append(sb.data[:sb.position], p...)
	} else {
		sb.data = append(sb.data[:sb.position], p...)
	}

	if tailBytes != nil {
		sb.data = append(sb.data, tailBytes...)
	}

	dataSize := len64(p)
	sb.position += dataSize

	return int(dataSize), nil
}

// Read does a standard read against the internal slice.
func (sb *SeekableBuffer) Read(p []byte) (n int, err error) {
	defer func() {
		if state := recover(); state != nil {
			err = log.Wrap(state.(error))
		}
	}()

	if sb.position >= len64(sb.data) {
		return 0, io.EOF
	}

	n = copy(p, sb.data[sb.position:])
	sb.position += int64(n)

	return n, nil
}

// Truncate either chops or extends the internal buffer.
func (sb *SeekableBuffer) Truncate(size int64) (err error) {
	defer func() {
		if state := recover(); state != nil {
			err = log.Wrap(state.(error))
		}
	}()

	sizeInt := int(size)
	if sizeInt < len(sb.data)-1 {
		sb.data = sb.data[:sizeInt]
	} else {
		new := make([]byte, sizeInt-len(sb.data))
		sb.data = append(sb.data, new...)
	}

	return nil
}

// Seek does a standard seek on the internal slice.
func (sb *SeekableBuffer) Seek(offset int64, whence int) (n int64, err error) {
	defer func() {
		if state := recover(); state != nil {
			err = log.Wrap(state.(error))
		}
	}()

	if whence == os.SEEK_SET {
		sb.position = offset
	} else if whence == os.SEEK_END {
		sb.position = len64(sb.data) + offset
	} else if whence == os.SEEK_CUR {
		sb.position += offset
	} else {
		log.Panicf("seek whence is not valid: (%d)", whence)
	}

	if sb.position < 0 {
		sb.position = 0
	}

	return sb.position, nil
}