1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package zstd
6
7// window stores up to size bytes of data.
8// It is implemented as a circular buffer:
9// sequential save calls append to the data slice until
10// its length reaches configured size and after that,
11// save calls overwrite previously saved data at off
12// and update off such that it always points at
13// the byte stored before others.
14type window struct {
15	size int
16	data []byte
17	off  int
18}
19
20// reset clears stored data and configures window size.
21func (w *window) reset(size int) {
22	b := w.data[:0]
23	if cap(b) < size {
24		b = make([]byte, 0, size)
25	}
26	w.data = b
27	w.off = 0
28	w.size = size
29}
30
31// len returns the number of stored bytes.
32func (w *window) len() uint32 {
33	return uint32(len(w.data))
34}
35
36// save stores up to size last bytes from the buf.
37func (w *window) save(buf []byte) {
38	if w.size == 0 {
39		return
40	}
41	if len(buf) == 0 {
42		return
43	}
44
45	if len(buf) >= w.size {
46		from := len(buf) - w.size
47		w.data = append(w.data[:0], buf[from:]...)
48		w.off = 0
49		return
50	}
51
52	// Update off to point to the oldest remaining byte.
53	free := w.size - len(w.data)
54	if free == 0 {
55		n := copy(w.data[w.off:], buf)
56		if n == len(buf) {
57			w.off += n
58		} else {
59			w.off = copy(w.data, buf[n:])
60		}
61	} else {
62		if free >= len(buf) {
63			w.data = append(w.data, buf...)
64		} else {
65			w.data = append(w.data, buf[:free]...)
66			w.off = copy(w.data, buf[free:])
67		}
68	}
69}
70
71// appendTo appends stored bytes between from and to indices to the buf.
72// Index from must be less or equal to index to and to must be less or equal to w.len().
73func (w *window) appendTo(buf []byte, from, to uint32) []byte {
74	dataLen := uint32(len(w.data))
75	from += uint32(w.off)
76	to += uint32(w.off)
77
78	wrap := false
79	if from > dataLen {
80		from -= dataLen
81		wrap = !wrap
82	}
83	if to > dataLen {
84		to -= dataLen
85		wrap = !wrap
86	}
87
88	if wrap {
89		buf = append(buf, w.data[from:]...)
90		return append(buf, w.data[:to]...)
91	} else {
92		return append(buf, w.data[from:to]...)
93	}
94}
95