Skip to content

Commit 85f069b

Browse files
committed
Introduce bytebuf util
Add a ring-buffer for bytes. It will be useful for buffering audio. PR #3757 <Genymobile/scrcpy#3757>
1 parent 77eb14f commit 85f069b

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed

app/meson.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ src = [
3030
'src/version.c',
3131
'src/video_buffer.c',
3232
'src/util/acksync.c',
33+
'src/util/bytebuf.c',
3334
'src/util/file.c',
3435
'src/util/intmap.c',
3536
'src/util/intr.c',
@@ -254,6 +255,10 @@ if get_option('buildtype') == 'debug'
254255
['test_binary', [
255256
'tests/test_binary.c',
256257
]],
258+
['test_bytebuf', [
259+
'tests/test_bytebuf.c',
260+
'src/util/bytebuf.c',
261+
]],
257262
['test_cbuf', [
258263
'tests/test_cbuf.c',
259264
]],

app/src/util/bytebuf.c

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "bytebuf.h"
2+
3+
#include <assert.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
#include "util/log.h"
8+
9+
bool
10+
sc_bytebuf_init(struct sc_bytebuf *buf, size_t alloc_size) {
11+
assert(alloc_size);
12+
// sufficient, but use more for alignment.
13+
buf->data = malloc(alloc_size);
14+
if (!buf->data) {
15+
LOG_OOM();
16+
return false;
17+
}
18+
19+
buf->alloc_size = alloc_size;
20+
buf->head = 0;
21+
buf->tail = 0;
22+
23+
return true;
24+
}
25+
26+
void
27+
sc_bytebuf_destroy(struct sc_bytebuf *buf) {
28+
free(buf->data);
29+
}
30+
31+
void
32+
sc_bytebuf_read(struct sc_bytebuf *buf, uint8_t *to, size_t len) {
33+
assert(len);
34+
assert(len <= sc_bytebuf_read_available(buf));
35+
assert(buf->tail != buf->head); // the buffer could not be empty
36+
37+
size_t right_limit = buf->tail < buf->head ? buf->head : buf->alloc_size;
38+
size_t right_len = right_limit - buf->tail;
39+
if (len < right_len) {
40+
right_len = len;
41+
}
42+
memcpy(to, buf->data + buf->tail, right_len);
43+
44+
if (len > right_len) {
45+
memcpy(to + right_len, buf->data, len - right_len);
46+
}
47+
48+
buf->tail = (buf->tail + len) % buf->alloc_size;
49+
}
50+
51+
void
52+
sc_bytebuf_skip(struct sc_bytebuf *buf, size_t len) {
53+
assert(len);
54+
assert(len <= sc_bytebuf_read_available(buf));
55+
assert(buf->tail != buf->head); // the buffer could not be empty
56+
57+
buf->tail = (buf->tail + len) % buf->alloc_size;
58+
}
59+
60+
void
61+
sc_bytebuf_write(struct sc_bytebuf *buf, const uint8_t *from, size_t len) {
62+
assert(len);
63+
assert(len <= sc_bytebuf_write_available(buf));
64+
65+
size_t right_len = buf->alloc_size - buf->head;
66+
if (len < right_len) {
67+
right_len = len;
68+
}
69+
70+
memcpy(buf->data + buf->head, from, right_len);
71+
if (len > right_len) {
72+
memcpy(buf->data, from + right_len, len - right_len);
73+
}
74+
75+
buf->head = (buf->head + len) % buf->alloc_size;
76+
}

app/src/util/bytebuf.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#ifndef SC_BYTEBUF_H
2+
#define SC_BYTEBUF_H
3+
4+
#include "common.h"
5+
6+
#include <stdbool.h>
7+
#include <stdint.h>
8+
9+
struct sc_bytebuf {
10+
uint8_t *data;
11+
// The actual capacity is (allocated - 1) so that head == tail is
12+
// non-ambiguous
13+
size_t alloc_size;
14+
size_t head; // writter cursor
15+
size_t tail; // reader cursor
16+
// empty: tail == head
17+
// full: (tail + 1) % allocated == head
18+
};
19+
20+
bool
21+
sc_bytebuf_init(struct sc_bytebuf *buf, size_t alloc_size);
22+
23+
/**
24+
* Copy from the bytebuf to a user-provided array
25+
*
26+
* The caller must check that len <= sc_bytebuf_read_available() (it is an
27+
* error to attempt to read more bytes than available).
28+
*
29+
* This function is guaranteed not to write to buf->head.
30+
*/
31+
void
32+
sc_bytebuf_read(struct sc_bytebuf *buf, uint8_t *to, size_t len);
33+
34+
/**
35+
* Drop len bytes from the buffer
36+
*
37+
* The caller must check that len <= sc_bytebuf_read_available() (it is an
38+
* error to attempt to skip more bytes than available).
39+
*
40+
* This function is guaranteed not to change the head.
41+
*
42+
* It is equivalent to call sc_bytebuf_read() to some array and discard the
43+
* array (but more efficient since there is no copy).
44+
*/
45+
void
46+
sc_bytebuf_skip(struct sc_bytebuf *buf, size_t len);
47+
48+
/**
49+
* Copy the user-provided array to the bytebuf
50+
*
51+
* The caller must check that len <= sc_bytebuf_write_available() (it is an
52+
* error to write more bytes than the remaining available space).
53+
*
54+
* This function is guaranteed not to write to buf->tail.
55+
*/
56+
void
57+
sc_bytebuf_write(struct sc_bytebuf *buf, const uint8_t *from, size_t len);
58+
59+
/**
60+
* Return the number of bytes which can be read
61+
*
62+
* It is an error to read more bytes than available.
63+
*/
64+
static inline size_t
65+
sc_bytebuf_read_available(struct sc_bytebuf *buf) {
66+
return (buf->alloc_size + buf->head - buf->tail) % buf->alloc_size;
67+
}
68+
69+
/**
70+
* Return the number of bytes which can be written
71+
*
72+
* It is an error to write more bytes than available.
73+
*/
74+
static inline size_t
75+
sc_bytebuf_write_available(struct sc_bytebuf *buf) {
76+
return (buf->alloc_size + buf->tail - buf->head - 1) % buf->alloc_size;
77+
}
78+
79+
/**
80+
* Return the actual capacity of the buffer (read available + write available)
81+
*/
82+
static inline size_t
83+
sc_bytebuf_capacity(struct sc_bytebuf *buf) {
84+
return buf->alloc_size - 1;
85+
}
86+
87+
void
88+
sc_bytebuf_destroy(struct sc_bytebuf *buf);
89+
90+
#endif

app/tests/test_bytebuf.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include "common.h"
2+
3+
#include <assert.h>
4+
#include <string.h>
5+
6+
#include "util/bytebuf.h"
7+
8+
void test_bytebuf_simple(void) {
9+
struct sc_bytebuf buf;
10+
uint8_t data[20];
11+
12+
bool ok = sc_bytebuf_init(&buf, 20);
13+
assert(ok);
14+
15+
sc_bytebuf_write(&buf, (uint8_t *) "hello", sizeof("hello") - 1);
16+
assert(sc_bytebuf_read_available(&buf) == 5);
17+
18+
sc_bytebuf_read(&buf, data, 4);
19+
assert(!strncmp((char *) data, "hell", 4));
20+
21+
sc_bytebuf_write(&buf, (uint8_t *) " world", sizeof(" world") - 1);
22+
assert(sc_bytebuf_read_available(&buf) == 7);
23+
24+
sc_bytebuf_write(&buf, (uint8_t *) "!", 1);
25+
assert(sc_bytebuf_read_available(&buf) == 8);
26+
27+
sc_bytebuf_read(&buf, &data[4], 8);
28+
assert(sc_bytebuf_read_available(&buf) == 0);
29+
30+
data[12] = '\0';
31+
assert(!strcmp((char *) data, "hello world!"));
32+
assert(sc_bytebuf_read_available(&buf) == 0);
33+
34+
sc_bytebuf_destroy(&buf);
35+
}
36+
37+
void test_bytebuf_boundaries(void) {
38+
struct sc_bytebuf buf;
39+
uint8_t data[20];
40+
41+
bool ok = sc_bytebuf_init(&buf, 20);
42+
assert(ok);
43+
44+
sc_bytebuf_write(&buf, (uint8_t *) "hello ", sizeof("hello ") - 1);
45+
assert(sc_bytebuf_read_available(&buf) == 6);
46+
47+
sc_bytebuf_write(&buf, (uint8_t *) "hello ", sizeof("hello ") - 1);
48+
assert(sc_bytebuf_read_available(&buf) == 12);
49+
50+
sc_bytebuf_write(&buf, (uint8_t *) "hello ", sizeof("hello ") - 1);
51+
assert(sc_bytebuf_read_available(&buf) == 18);
52+
53+
sc_bytebuf_read(&buf, data, 9);
54+
assert(!strncmp((char *) data, "hello hel", 9));
55+
assert(sc_bytebuf_read_available(&buf) == 9);
56+
57+
sc_bytebuf_write(&buf, (uint8_t *) "world", sizeof("world") - 1);
58+
assert(sc_bytebuf_read_available(&buf) == 14);
59+
60+
sc_bytebuf_write(&buf, (uint8_t *) "!", 1);
61+
assert(sc_bytebuf_read_available(&buf) == 15);
62+
63+
sc_bytebuf_skip(&buf, 3);
64+
assert(sc_bytebuf_read_available(&buf) == 12);
65+
66+
sc_bytebuf_read(&buf, data, 12);
67+
data[12] = '\0';
68+
assert(!strcmp((char *) data, "hello world!"));
69+
assert(sc_bytebuf_read_available(&buf) == 0);
70+
71+
sc_bytebuf_destroy(&buf);
72+
}
73+
74+
int main(int argc, char *argv[]) {
75+
(void) argc;
76+
(void) argv;
77+
78+
test_bytebuf_simple();
79+
test_bytebuf_boundaries();
80+
81+
return 0;
82+
}

0 commit comments

Comments
 (0)