nativeaudio

audio playback for Go
Log | Files | Refs | README | LICENSE

commit 35931843c2f9d6dd8d1c7e50e4fcd654df5dcde1
parent 1c44b1ad59b8a59f377a2d7397b62bde4269dc80
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Thu, 18 May 2023 13:16:12 +0800

Merge commit '2717ef88f823bb149b803ef530c22d48202f9f94' as 'vendor/growable-buf'

Diffstat:
Avendor/growable-buf/.gitignore | 1+
Avendor/growable-buf/Makefile | 15+++++++++++++++
Avendor/growable-buf/README.md | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/growable-buf/UNLICENSE | 24++++++++++++++++++++++++
Avendor/growable-buf/buf.h | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avendor/growable-buf/tests.c | 209+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 453 insertions(+), 0 deletions(-)

diff --git a/vendor/growable-buf/.gitignore b/vendor/growable-buf/.gitignore @@ -0,0 +1 @@ +tests diff --git a/vendor/growable-buf/Makefile b/vendor/growable-buf/Makefile @@ -0,0 +1,15 @@ +.POSIX: +CFLAGS = -std=c99 -Wall -Wextra -O3 -g3 + +tests: tests.c buf.h + $(CC) $(LDFLAGS) $(CFLAGS) -o $@ tests.c $(LDLIBS) + +test: check +check: tests + ./tests + +bench: tests + ./tests 558d2750ae0e0887 + +clean: + rm -f tests diff --git a/vendor/growable-buf/README.md b/vendor/growable-buf/README.md @@ -0,0 +1,92 @@ +# Growable Memory Buffers for C99 + +This C99 header library provides a simple, portable interface to +growable buffers of homogeneous values of any type, similar to a +std::vector in C++. The user need not use any particular struct, and the +buffer need only initialize to NULL. + +Each "function" in the interface is actually a macro, and its usage is +reflected by these hypothetical prototypes: + +```c +/* Returns the number of elements in the buffer (for push and pop). +*/ +size_t buf_size(type *v); + +/* Returns the total capacity of the buffer. +*/ +size_t buf_capacity(type *v); + +/* Destroy and free the buffer, effectively resetting it. + * Potentially assigns a new V pointer. +*/ +void buf_free(type *v); + +/* Append an element E to the end of the buffer, growing if necessary. + * Potentially increases the capacity and assigns a new V pointer. +*/ +void buf_push(type *v, type e); + +/* Remove an element E from the end of the buffer. + * Neither the capacity nor the V pointer will change. Popping when the + * size is zero has undefined results. + */ +type buf_pop(type *v); + +/* Increase buffer capactity by N elements. + * Potentially assigns a new V pointer while also returning it. + */ +type *buf_grow(type *v, ptrdiff_t n); + +/* Set buffer capactity to exactly N elements. + * Potentially assigns a new V pointer while also returning it. A + * negative capacity has undefined results. + */ +type *buf_trunc(type *v, ptrdiff_t n); + +/* Set buffer size to zero. + * Only affects push and pop. The capacity and buffer contents are + * unchanged. + */ +void buf_clear(type *v); +``` + +Note: `buf_push()`, `buf_grow()`, `buf_trunc()`, and `buf_free()` may +change the buffer pointer, and any previously-taken pointers should be +considered invalidated. This has important consequences that must be +considered. + +The `BUF_INIT_CAPACITY` determines the initial capacity for buffers +receiving their first push. + +The `BUF_ABORT` macro is evaluated when the system runs out of memory. +It defaults to `abort()`, but you may override it to run your own abort +code instead. + +Example usage: + +```c +float *values = 0; + +/* Append 25 values */ +for (size_t i = 0; i < 25; i++) + buf_push(values, rand() / (float)RAND_MAX); + +/* Access 25 values using the normal [] operator */ +for (size_t i = 0; i < buf_size(values); i++) + printf("values[%zu] = %f\n", i, values[i]); + +/* Destroy/reset the buffer */ +buf_free(values); +``` + +## Purpose + +This library is inspired by [stb `stretchy_buffer.h`][sb]. The +difference is that it's written in C99 so that it doesn't need to rely +on undefined behavior. This does so by using a flexible array member and +the `offsetof()` macro. It also checks for integer overflows before +allocating any memory, making it safer. + + +[sb]: https://github.com/nothings/stb/blob/master/stretchy_buffer.h diff --git a/vendor/growable-buf/UNLICENSE b/vendor/growable-buf/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <http://unlicense.org/> diff --git a/vendor/growable-buf/buf.h b/vendor/growable-buf/buf.h @@ -0,0 +1,112 @@ +/* buf.h --- growable memory buffers for C99 + * This is free and unencumbered software released into the public domain. + * + * buf_size(v) : return the number of elements in the buffer (size_t) + * buf_capacity(v) : return the total capacity of the buffer (size_t) + * buf_free(v) : destroy and free the buffer + * buf_push(v, e) : append an element E to the end + * buf_pop(v) : remove and return an element E from the end + * buf_grow(v, n) : increase buffer capactity by (ptrdiff_t) N elements + * buf_trunc(v, n) : set buffer capactity to exactly (ptrdiff_t) N elements + * buf_clear(v, n) : set buffer size to 0 (for push/pop) + * + * Note: buf_push(), buf_grow(), buf_trunc(), and buf_free() may change + * the buffer pointer, and any previously-taken pointers should be + * considered invalidated. + * + * Example usage: + * + * float *values = 0; + * for (size_t i = 0; i < 25; i++) + * buf_push(values, rand() / (float)RAND_MAX); + * for (size_t i = 0; i < buf_size(values); i++) + * printf("values[%zu] = %f\n", i, values[i]); + * buf_free(values); + */ +#include <stddef.h> +#include <stdlib.h> + +#ifndef BUF_INIT_CAPACITY +# define BUF_INIT_CAPACITY 8 +#endif + +#ifndef BUF_ABORT +# define BUF_ABORT abort() +#endif + +struct buf { + size_t capacity; + size_t size; + char buffer[]; +}; + +#define buf_ptr(v) \ + ((struct buf *)((char *)(v) - offsetof(struct buf, buffer))) + +#define buf_free(v) \ + do { \ + if (v) { \ + free(buf_ptr((v))); \ + (v) = 0; \ + } \ + } while (0) + +#define buf_size(v) \ + ((v) ? buf_ptr((v))->size : 0) + +#define buf_capacity(v) \ + ((v) ? buf_ptr((v))->capacity : 0) + +#define buf_push(v, e) \ + do { \ + if (buf_capacity((v)) == buf_size((v))) { \ + (v) = buf_grow1(v, sizeof(*(v)), \ + !buf_capacity((v)) ? \ + BUF_INIT_CAPACITY : \ + buf_capacity((v))); \ + } \ + (v)[buf_ptr((v))->size++] = (e); \ + } while (0) + +#define buf_pop(v) \ + ((v)[--buf_ptr(v)->size]) + +#define buf_grow(v, n) \ + ((v) = buf_grow1((v), sizeof(*(v)), n)) + +#define buf_trunc(v, n) \ + ((v) = buf_grow1((v), sizeof(*(v)), n - buf_capacity(v))) + +#define buf_clear(v) \ + ((v) ? (buf_ptr((v))->size = 0) : 0) + + +static void * +buf_grow1(void *v, size_t esize, ptrdiff_t n) +{ + struct buf *p; + size_t max = (size_t)-1 - sizeof(struct buf); + if (v) { + p = buf_ptr(v); + if (n > 0 && p->capacity + n > max / esize) + goto fail; /* overflow */ + p = realloc(p, sizeof(struct buf) + esize * (p->capacity + n)); + if (!p) + goto fail; + p->capacity += n; + if (p->size > p->capacity) + p->size = p->capacity; + } else { + if ((size_t)n > max / esize) + goto fail; /* overflow */ + p = malloc(sizeof(struct buf) + esize * n); + if (!p) + goto fail; + p->capacity = n; + p->size = 0; + } + return p->buffer; +fail: + BUF_ABORT; + return 0; +} diff --git a/vendor/growable-buf/tests.c b/vendor/growable-buf/tests.c @@ -0,0 +1,209 @@ +#include <stdio.h> +#include <stdint.h> +#include <setjmp.h> + +static jmp_buf escape; + +static void +test_abort(void) +{ + longjmp(escape, 1); +} + +#define BUF_ABORT test_abort() +#include "buf.h" + +#if _WIN32 +# define C_RED(s) s +# define C_GREEN(s) s +#else +# define C_RED(s) "\033[31;1m" s "\033[0m" +# define C_GREEN(s) "\033[32;1m" s "\033[0m" +#endif + +#define TEST(s, x) \ + do { \ + if (x) { \ + printf(C_GREEN("PASS") " %s\n", s); \ + count_pass++; \ + } else { \ + printf(C_RED("FAIL") " %s\n", s); \ + count_fail++; \ + } \ + } while (0) + +static uint32_t +pcg32(uint64_t *s) +{ + uint64_t m = 0x5851f42d4c957f2d; + uint64_t a = 0x8b260b70b8e98891; + uint64_t p = *s; + uint32_t x = ((p >> 18) ^ p) >> 27; + uint32_t r = p >> 59; + *s = p * m + a; + return (x >> r) | (x << (-r & 31u)); +} + +static unsigned long +bench(uint64_t *rng) +{ + unsigned long r = 0; + uint32_t n = 1000000 + pcg32(rng) % 4000000; + float *buf = 0; + + for (uint32_t i = 0; i < n; i++) + buf_push(buf, pcg32(rng) / (double)UINT32_MAX); + + float threshold = pcg32(rng) / (double)UINT32_MAX; + for (uint32_t i = 0; i < n; i++) + r += buf[i] > threshold; + + buf_free(buf); + return r; +} + +#ifdef _WIN32 +#include <windows.h> +uint64_t +uepoch(void) +{ + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uint64_t tt = ft.dwHighDateTime; + tt <<= 32; + tt |= ft.dwLowDateTime; + tt /=10; + tt -= UINT64_C(11644473600000000); + return tt; +} +#else +#include <sys/time.h> +uint64_t +uepoch(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return 1000000LL * tv.tv_sec + tv.tv_usec; +} +#endif + +int +main(int argc, char **argv) +{ + /* Benchtest? */ + if (argc > 1) { + uint64_t rng = strtoull(argv[1], 0, 16); + unsigned long r = 0; + uint64_t start = uepoch(); + for (int i = 0; i < 300; i++) + r += bench(&rng); + double t = (uepoch() - start) / 1e6; + printf("%.6gs : actual %lu, expect 428859598\n", t, r); + return 0; + } + + /* volatile due to setjmp() / longjmp() */ + volatile int count_pass = 0; + volatile int count_fail = 0; + + if (setjmp(escape)) + abort(); + + /* initialization, buf_free() */ + float *a = 0; + TEST("capacity init", buf_capacity(a) == 0); + TEST("size init", buf_size(a) == 0); + buf_push(a, 1.3f); + TEST("size 1", buf_size(a) == 1); + TEST("value", a[0] == (float)1.3f); + buf_clear(a); + TEST("clear", buf_size(a) == 0); + TEST("clear not-free", a != 0); + buf_free(a); + TEST("free", a == 0); + + /* Clearing an NULL pointer is a no-op */ + buf_clear(a); + TEST("clear empty", buf_size(a) == 0); + TEST("clear no-op", a == 0); + + /* buf_push(), [] operator */ + long *ai = 0; + for (int i = 0; i < 10000; i++) + buf_push(ai, i); + TEST("size 10000", buf_size(ai) == 10000); + int match = 0; + for (int i = 0; i < (int)(buf_size(ai)); i++) + match += ai[i] == i; + TEST("match 10000", match == 10000); + buf_free(ai); + + /* buf_grow(), buf_trunc() */ + buf_grow(ai, 1000); + TEST("grow 1000", buf_capacity(ai) == 1000); + TEST("size 0 (grow)", buf_size(ai) == 0); + buf_trunc(ai, 100); + TEST("trunc 100", buf_capacity(ai) == 100); + buf_free(ai); + + /* buf_pop() */ + buf_push(a, 1.1); + buf_push(a, 1.2); + buf_push(a, 1.3); + buf_push(a, 1.4); + TEST("size 4", buf_size(a) == 4); + TEST("pop 3", buf_pop(a) == (float)1.4f); + buf_trunc(a, 3); + TEST("size 3", buf_size(a) == 3); + TEST("pop 2", buf_pop(a) == (float)1.3f); + TEST("pop 1", buf_pop(a) == (float)1.2f); + TEST("pop 0", buf_pop(a) == (float)1.1f); + TEST("size 0 (pop)", buf_size(a) == 0); + buf_free(a); + + /* Memory allocation failures */ + + volatile int aborted; + + { + int *volatile p = 0; + aborted = 0; + if (!setjmp(escape)) { + size_t max = (PTRDIFF_MAX - sizeof(struct buf)) / sizeof(*p) + 1; + buf_grow(p, max); + buf_grow(p, max); + } else { + aborted = 1; + } + buf_free(p); + TEST("out of memory", aborted); + } + + { + int *volatile p = 0; + aborted = 0; + if (!setjmp(escape)) { + buf_trunc(p, PTRDIFF_MAX); + } else { + aborted = 1; + } + buf_free(p); + TEST("overflow init", aborted); + } + + { + int *volatile p = 0; + aborted = 0; + if (!setjmp(escape)) { + buf_trunc(p, 1); /* force realloc() use next */ + buf_trunc(p, PTRDIFF_MAX); + } else { + aborted = 1; + } + buf_free(p); + TEST("overflow grow", aborted); + } + + printf("%d fail, %d pass\n", count_fail, count_pass); + return count_fail != 0; +}