Skip to content

Commit 81e8897

Browse files
committed
Refactoring of the decompression buffer
1 parent ab726d7 commit 81e8897

File tree

5 files changed

+207
-46
lines changed

5 files changed

+207
-46
lines changed

include/lsp-plug.in/resource/buffer.h

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ namespace lsp
109109
{
110110
public:
111111
uint8_t *data; // Buffer data (2 x capacity)
112-
ssize_t head; // Head of the buffer
113-
ssize_t tail; // Buffer tail
114-
ssize_t cap; // Buffer capacity
112+
uint32_t length; // Actual size of buffer
113+
uint32_t head; // Head of the buffer
114+
uint32_t cap; // Buffer capacity
115115

116116
public:
117117
explicit dbuffer_t();
@@ -121,10 +121,38 @@ namespace lsp
121121
void destroy();
122122

123123
public:
124+
/**
125+
* Extract data from buffer
126+
* @param dst destination pointer to store result
127+
* @param offset relative offset of the subsequence in the buffer to the last byte store in the buffer
128+
* @param count number of bytes to extract
129+
* @return status of operation (error on buffer underflow)
130+
*/
131+
status_t extract(void *dst, size_t offset, size_t count);
132+
133+
/**
134+
* Append multiple bytes to the buffer
135+
* @param src data to append to the buffer
136+
* @param count number of bytes to append
137+
*/
124138
void append(const void *src, ssize_t count);
139+
140+
/**
141+
* Append single byte to the buffer
142+
* @param v byt to append
143+
*/
125144
void append(uint8_t v);
145+
146+
/**
147+
* Clear buffer state
148+
*/
126149
void clear();
127-
inline size_t size() const { return tail - head; }
150+
151+
/**
152+
* Get size of data currently stored in the buffer
153+
* @return size of data currently stored in the buffer
154+
*/
155+
inline size_t size() const { return length; }
128156

129157
} duffer_t;
130158

src/main/resource/Decompressor.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ namespace lsp
137137
// Need to allocate data?
138138
if ((sReplay.cap < count) || (sReplay.data == NULL))
139139
{
140-
size_t cap = align_size(count, BUFFER_QUANTITY);
140+
size_t cap = align_size(lsp_max(sReplay.cap + (sReplay.cap >> 1), count), BUFFER_QUANTITY);
141141
uint8_t *ptr = reinterpret_cast<uint8_t *>(realloc(sReplay.data, cap));
142142
if (ptr == NULL)
143143
return STATUS_NO_MEM;
@@ -147,7 +147,10 @@ namespace lsp
147147
}
148148

149149
// Copy data to replay buffer
150-
memcpy(sReplay.data, &sBuffer.data[sBuffer.head + off], count);
150+
status_t res = sBuffer.extract(sReplay.data, off, count);
151+
if (res != STATUS_OK)
152+
return res;
153+
151154
sReplay.off = 0;
152155
sReplay.size = count;
153156
sReplay.rep = rep;
@@ -235,7 +238,7 @@ namespace lsp
235238

236239
if (offset < sBuffer.size())
237240
{
238-
// REPLAY
241+
// REPLAY BUFFER
239242
// Length
240243
if ((res = read_uint(&length, 5, 5)) != STATUS_OK)
241244
return res;
@@ -255,7 +258,7 @@ namespace lsp
255258
}
256259
else
257260
{
258-
// OCTET
261+
// EMIT OCTET
259262
// Repeat
260263
if ((res = read_uint(&rep, 0, 4)) != STATUS_OK)
261264
return res;

src/main/runtime/buffer.cpp

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ namespace lsp
7979

8080
void cbuffer_t::append(const void *src, size_t count)
8181
{
82-
const uint8_t *v = reinterpret_cast<const uint8_t *>(src);
82+
const uint8_t *v = static_cast<const uint8_t *>(src);
8383
if (count >= cap)
8484
{
8585
// Cleanup root index and reset head
@@ -130,7 +130,7 @@ namespace lsp
130130

131131
size_t cbuffer_t::lookup(size_t *out, const void *src, size_t avail)
132132
{
133-
const uint8_t *v = reinterpret_cast<const uint8_t *>(src);
133+
const uint8_t *v = static_cast<const uint8_t *>(src);
134134
const size_t dmax = size();
135135

136136
size_t len = 0;
@@ -187,8 +187,8 @@ namespace lsp
187187
dbuffer_t::dbuffer_t()
188188
{
189189
data = NULL;
190+
length = 0;
190191
head = 0;
191-
tail = 0;
192192
cap = 0;
193193
}
194194

@@ -199,14 +199,14 @@ namespace lsp
199199

200200
status_t dbuffer_t::init(size_t capacity)
201201
{
202-
size_t dbuf = capacity * 2 * sizeof(uint8_t);
202+
size_t dbuf = capacity * sizeof(uint8_t);
203203
uint8_t *ptr = static_cast<uint8_t *>(realloc(data, dbuf));
204204
if (ptr == NULL)
205205
return STATUS_NO_MEM;
206206

207207
data = ptr;
208+
length = 0;
208209
head = 0;
209-
tail = 0;
210210
cap = capacity;
211211

212212
return STATUS_OK;
@@ -217,55 +217,73 @@ namespace lsp
217217
if (data != NULL)
218218
free(data);
219219
data = NULL;
220+
length = 0;
220221
head = 0;
221-
tail = 0;
222222
cap = 0;
223223
}
224224

225225
void dbuffer_t::append(const void *src, ssize_t count)
226226
{
227-
const uint8_t *v = reinterpret_cast<const uint8_t *>(src);
228-
ssize_t avail = ((cap << 1) - tail);
229-
if (count < avail)
227+
const uint8_t *v = static_cast<const uint8_t *>(src);
228+
if (count >= cap)
230229
{
231-
memcpy(&data[tail], v, count * sizeof(uint8_t));
232-
tail += count;
233-
head = lsp_max(head, tail - cap);
230+
// Replace data in the buffer
231+
memcpy(data, &v[count - cap], cap * sizeof(uint8_t));
232+
length = cap;
233+
head = 0;
234+
return;
234235
}
235-
else if (count < cap)
236+
237+
// Copy data to the buffer
238+
size_t ohead = head;
239+
head = (head + count) % cap;
240+
if (head < ohead)
236241
{
237-
ssize_t head = tail + count - cap;
238-
memmove(data, &data[head], tail - head);
239-
memcpy(&data[tail - head], v, count);
242+
const size_t split = cap - ohead;
243+
memcpy(&data[ohead], v, split * sizeof(uint8_t));
244+
memcpy(&data[0], &v[split], head * sizeof(uint8_t));
240245
}
241246
else
242-
{
243-
memcpy(data, &v[count - cap], cap * sizeof(uint8_t));
244-
head = 0;
245-
tail = cap;
246-
}
247+
memcpy(&data[ohead], v, count * sizeof(uint8_t));
248+
249+
length = lsp_min(length + count, cap);
247250
}
248251

249-
void dbuffer_t::append(uint8_t v)
252+
status_t dbuffer_t::extract(void *dst, size_t offset, size_t count)
250253
{
251-
// Shift buffer if needed
252-
if (tail >= (cap << 1))
254+
if (offset >= length)
255+
return STATUS_UNDERFLOW;
256+
257+
const size_t shift = offset + 1;
258+
if (count > shift)
259+
return STATUS_UNDERFLOW;
260+
261+
uint8_t *dptr = static_cast<uint8_t *>(dst);
262+
uint32_t soff = (head + cap - shift) % cap;
263+
264+
if ((soff + count) > cap)
253265
{
254-
memmove(data, &data[cap], cap * sizeof(uint8_t));
255-
head -= cap;
256-
tail -= cap;
266+
const size_t split = cap - soff;
267+
memcpy(dptr, &data[soff], split * sizeof(uint8_t));
268+
memcpy(&dptr[split], data, (count - split) * sizeof(uint8_t));
257269
}
270+
else
271+
memcpy(dptr, &data[soff], count * sizeof(uint8_t));
258272

259-
// Append byte
260-
data[tail] = v; // Data byte
261-
++tail;
262-
head = lsp_max(head, tail - cap);
273+
return STATUS_OK;
274+
}
275+
276+
void dbuffer_t::append(uint8_t v)
277+
{
278+
data[head] = v;
279+
head = (head + 1) % cap;
280+
length = lsp_min(length + 1, cap);
263281
}
264282

265283
void dbuffer_t::clear()
266284
{
285+
length = 0;
267286
head = 0;
268-
tail = 0;
269287
}
270288

271289
} /* namespace resource */

src/test/utest/resource/cbuffer.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ using namespace lsp;
2626

2727
UTEST_BEGIN("runtime.resource", cbuffer)
2828

29-
void test_buffer()
29+
UTEST_MAIN
3030
{
3131
resource::cbuffer_t buf;
3232
size_t offset = 0, length = 0;
@@ -170,11 +170,6 @@ UTEST_BEGIN("runtime.resource", cbuffer)
170170
UTEST_ASSERT(offset == 7);
171171
}
172172

173-
UTEST_MAIN
174-
{
175-
test_buffer();
176-
}
177-
178173
UTEST_END
179174

180175

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (C) 2025 Linux Studio Plugins Project <https://lsp-plug.in/>
3+
* (C) 2025 Vladimir Sadovnikov <[email protected]>
4+
*
5+
* This file is part of lsp-runtime-lib
6+
* Created on: 25 июл. 2025 г.
7+
*
8+
* lsp-runtime-lib is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* any later version.
12+
*
13+
* lsp-runtime-lib is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public License
19+
* along with lsp-runtime-lib. If not, see <https://www.gnu.org/licenses/>.
20+
*/
21+
22+
#include <lsp-plug.in/test-fw/utest.h>
23+
#include <lsp-plug.in/resource/buffer.h>
24+
25+
using namespace lsp;
26+
27+
UTEST_BEGIN("runtime.resource", dbuffer)
28+
29+
UTEST_MAIN
30+
{
31+
resource::dbuffer_t buf;
32+
uint8_t tmp[0x20];
33+
34+
printf("Testing decompression buffer");
35+
36+
UTEST_ASSERT(buf.init(0x20) == STATUS_OK);
37+
38+
// Check that we can not extract data from empty buffer
39+
// buffer state: ???? ???? ???? ???? ???? ???? ???? ????
40+
// ^
41+
UTEST_ASSERT(buf.size() == 0);
42+
UTEST_ASSERT(buf.extract(tmp, 0, 1) == STATUS_UNDERFLOW);
43+
44+
// Append single byte
45+
// buffer state: a??? ???? ???? ???? ???? ???? ???? ????
46+
// ^
47+
buf.append('a');
48+
UTEST_ASSERT(buf.size() == 1);
49+
UTEST_ASSERT(buf.extract(tmp, 0, 1) == STATUS_OK);
50+
UTEST_ASSERT(tmp[0] == 'a');
51+
UTEST_ASSERT(buf.extract(tmp, 0, 2) == STATUS_UNDERFLOW);
52+
UTEST_ASSERT(buf.extract(tmp, 1, 1) == STATUS_UNDERFLOW);
53+
54+
// Append small sequence of bytes
55+
// buffer state: abcd efgh ???? ???? ???? ???? ???? ????
56+
// ^
57+
buf.append("bcdefgh", 7);
58+
UTEST_ASSERT(buf.size() == 8);
59+
UTEST_ASSERT(buf.extract(tmp, 0, 1) == STATUS_OK);
60+
UTEST_ASSERT(tmp[0] == 'h');
61+
UTEST_ASSERT(buf.extract(tmp, 6, 1) == STATUS_OK);
62+
UTEST_ASSERT(tmp[0] == 'b');
63+
UTEST_ASSERT(buf.extract(tmp, 7, 8) == STATUS_OK);
64+
UTEST_ASSERT(memcmp(tmp, "abcdefgh", 8) == 0);
65+
UTEST_ASSERT(buf.extract(tmp, 3, 4) == STATUS_OK);
66+
UTEST_ASSERT(memcmp(tmp, "efgh", 4) == 0);
67+
UTEST_ASSERT(buf.extract(tmp, 3, 8) == STATUS_UNDERFLOW);
68+
UTEST_ASSERT(buf.extract(tmp, 7, 9) == STATUS_UNDERFLOW);
69+
70+
// Append yet another small sequence of bytes
71+
// buffer state: abcd efgh abc0 1234 ???? ???? ???? ????
72+
// ^
73+
buf.append("abc01234", 8);
74+
UTEST_ASSERT(buf.size() == 16);
75+
UTEST_ASSERT(buf.extract(tmp, 7, 1) == STATUS_OK);
76+
UTEST_ASSERT(tmp[0] == 'a');
77+
UTEST_ASSERT(buf.extract(tmp, 0, 1) == STATUS_OK);
78+
UTEST_ASSERT(tmp[0] == '4');
79+
UTEST_ASSERT(buf.extract(tmp, 7, 8) == STATUS_OK);
80+
UTEST_ASSERT(memcmp(tmp, "abc01234", 8) == 0);
81+
UTEST_ASSERT(buf.extract(tmp, 15, 16) == STATUS_OK);
82+
UTEST_ASSERT(memcmp(tmp, "abcdefghabc01234", 16) == 0);
83+
84+
// Append more data to the buffer
85+
// buffer state: abcd efgh abc0 1234 0123 4567 abcd efg?
86+
// ^
87+
buf.append("01234567abcdefg", 15);
88+
UTEST_ASSERT(buf.size() == 31);
89+
UTEST_ASSERT(buf.extract(tmp, 0, 1) == STATUS_OK);
90+
UTEST_ASSERT(tmp[0] == 'g');
91+
UTEST_ASSERT(buf.extract(tmp, 7, 8) == STATUS_OK);
92+
UTEST_ASSERT(memcmp(tmp, "7abcdefg", 8) == 0);
93+
UTEST_ASSERT(buf.extract(tmp, 15, 16) == STATUS_OK);
94+
UTEST_ASSERT(memcmp(tmp, "401234567abcdefg", 16) == 0);
95+
96+
// Fill the buffer with overlap
97+
// buffer state: 0123 4567 abc0 1234 0123 4567 abcd efgh
98+
// ^
99+
buf.append("h01234567", 9);
100+
UTEST_ASSERT(buf.size() == 32);
101+
UTEST_ASSERT(buf.extract(tmp, 31, 32) == STATUS_OK);
102+
UTEST_ASSERT(memcmp(tmp, "abc0123401234567abcdefgh01234567", 32) == 0);
103+
UTEST_ASSERT(buf.extract(tmp, 7, 8) == STATUS_OK);
104+
UTEST_ASSERT(memcmp(tmp, "01234567", 8) == 0);
105+
106+
// Fully overwrite the buffer
107+
// buffer state: ABCD EFGH IJKL MNOP QRST UVWX YZ01 2345
108+
// ^
109+
buf.append("blablablaABCDEFGHIJKLMNOPQRSTUVWXYZ012345", 41);
110+
UTEST_ASSERT(buf.size() == 32);
111+
UTEST_ASSERT(buf.extract(tmp, 31, 32) == STATUS_OK);
112+
UTEST_ASSERT(memcmp(tmp, "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345", 32) == 0);
113+
}
114+
115+
UTEST_END
116+
117+

0 commit comments

Comments
 (0)