obs-studio/libobs/callback/calldata.c

250 lines
5.6 KiB
C

/*
* Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string.h>
#include "../util/bmem.h"
#include "../util/base.h"
#include "calldata.h"
/*
* Uses a data stack. Probably more complex than it should be, but reduces
* fetching.
*
* Stack format is:
* [size_t param1_name_size]
* [char[] param1_name]
* [size_t param1_data_size]
* [uint8_t[] param1_data]
* [size_t param2_name_size]
* [char[] param2_name]
* [size_t param2_data_size]
* [uint8_t[] param2_data]
* [...]
* [size_t 0]
*
* Strings and string sizes always include the null terminator to allow for
* direct referencing.
*/
static inline void cd_serialize(uint8_t **pos, void *ptr, size_t size)
{
memcpy(ptr, *pos, size);
*pos += size;
}
static inline size_t cd_serialize_size(uint8_t **pos)
{
size_t size = 0;
memcpy(&size, *pos, sizeof(size_t));
*pos += sizeof(size_t);
return size;
}
static inline const char *cd_serialize_string(uint8_t **pos)
{
size_t size = cd_serialize_size(pos);
const char *str = (const char *)*pos;
*pos += size;
return (size != 0) ? str : NULL;
}
static bool cd_getparam(const calldata_t *data, const char *name, uint8_t **pos)
{
size_t name_size;
if (!data->size)
return false;
*pos = data->stack;
name_size = cd_serialize_size(pos);
while (name_size != 0) {
const char *param_name = (const char *)*pos;
size_t param_size;
*pos += name_size;
if (strcmp(param_name, name) == 0)
return true;
param_size = cd_serialize_size(pos);
*pos += param_size;
name_size = cd_serialize_size(pos);
}
*pos -= sizeof(size_t);
return false;
}
static inline void cd_copy_string(uint8_t **pos, const char *str, size_t len)
{
if (!len)
len = strlen(str) + 1;
memcpy(*pos, &len, sizeof(size_t));
*pos += sizeof(size_t);
memcpy(*pos, str, len);
*pos += len;
}
static inline void cd_copy_data(uint8_t **pos, const void *in, size_t size)
{
memcpy(*pos, &size, sizeof(size_t));
*pos += sizeof(size_t);
if (size) {
memcpy(*pos, in, size);
*pos += size;
}
}
static inline void cd_set_first_param(calldata_t *data, const char *name,
const void *in, size_t size)
{
uint8_t *pos;
size_t capacity;
size_t name_len = strlen(name) + 1;
capacity = sizeof(size_t) * 3 + name_len + size;
data->size = capacity;
if (capacity < 128)
capacity = 128;
data->capacity = capacity;
data->stack = bmalloc(capacity);
pos = data->stack;
cd_copy_string(&pos, name, name_len);
cd_copy_data(&pos, in, size);
memset(pos, 0, sizeof(size_t));
}
static inline bool cd_ensure_capacity(calldata_t *data, uint8_t **pos,
size_t new_size)
{
size_t offset;
size_t new_capacity;
if (new_size < data->capacity)
return true;
if (data->fixed) {
blog(LOG_ERROR, "Tried to go above fixed calldata stack size!");
return false;
}
offset = *pos - data->stack;
new_capacity = data->capacity * 2;
if (new_capacity < new_size)
new_capacity = new_size;
data->stack = brealloc(data->stack, new_capacity);
data->capacity = new_capacity;
*pos = data->stack + offset;
return true;
}
/* ------------------------------------------------------------------------- */
bool calldata_get_data(const calldata_t *data, const char *name, void *out,
size_t size)
{
uint8_t *pos;
size_t data_size;
if (!data || !name || !*name)
return false;
if (!cd_getparam(data, name, &pos))
return false;
data_size = cd_serialize_size(&pos);
if (data_size != size)
return false;
memcpy(out, pos, size);
return true;
}
void calldata_set_data(calldata_t *data, const char *name, const void *in,
size_t size)
{
uint8_t *pos = NULL;
if (!data || !name || !*name)
return;
if (!data->fixed && !data->stack) {
cd_set_first_param(data, name, in, size);
return;
}
if (cd_getparam(data, name, &pos)) {
size_t cur_size;
memcpy(&cur_size, pos, sizeof(size_t));
if (cur_size < size) {
size_t offset = size - cur_size;
size_t bytes = data->size;
if (!cd_ensure_capacity(data, &pos, bytes + offset))
return;
memmove(pos + offset, pos, bytes - (pos - data->stack));
data->size += offset;
} else if (cur_size > size) {
size_t offset = cur_size - size;
size_t bytes = data->size - offset;
memmove(pos, pos + offset, bytes - (pos - data->stack));
data->size -= offset;
}
cd_copy_data(&pos, in, size);
} else {
size_t name_len = strlen(name) + 1;
size_t offset = name_len + size + sizeof(size_t) * 2;
if (!cd_ensure_capacity(data, &pos, data->size + offset))
return;
data->size += offset;
cd_copy_string(&pos, name, 0);
cd_copy_data(&pos, in, size);
memset(pos, 0, sizeof(size_t));
}
}
bool calldata_get_string(const calldata_t *data, const char *name,
const char **str)
{
uint8_t *pos;
if (!data || !name || !*name)
return false;
if (!cd_getparam(data, name, &pos))
return false;
*str = cd_serialize_string(&pos);
return true;
}