Files
libgbinder-radio/unit/common/test_gbinder_reader_writer.c

507 lines
12 KiB
C

/*
* Copyright (C) 2021 Jolla Ltd.
* Copyright (C) 2021 Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* any official policies, either expressed or implied.
*/
#include "test_gbinder.h"
#include <gutil_misc.h>
#include <gutil_idlepool.h>
typedef enum test_gbinder_data_type {
DATA_TYPE_BOOLEAN,
DATA_TYPE_INT32,
DATA_TYPE_BUFFER,
DATA_TYPE_LOCAL_OBJ
} DATA_TYPE;
typedef struct test_gbinder_data_item TestGBinderDataItem;
struct test_gbinder_data_item {
TestGBinderDataItem* next;
DATA_TYPE type;
union {
gboolean b;
gint32 i32;
struct {
void* buf;
gsize size;
} blob;
GBinderLocalObject* obj;
} data;
void (*destroy)(TestGBinderDataItem*);
};
struct test_gbinder_data {
guint32 refcount;
TestGBinderDataItem* items;
GUtilIdlePool* pool;
char* iface;
};
typedef struct test_gbinder_reader {
TestGBinderDataItem* item;
} TestGBinderReader;
typedef struct test_gbinder_writer {
TestGBinderData* data;
} TestGBinderWriter;
static inline TestGBinderReader* test_gbinder_reader_cast(GBinderReader* reader)
{ return (TestGBinderReader*)reader; }
static inline TestGBinderWriter* test_gbinder_writer_cast(GBinderWriter* writer)
{ return (TestGBinderWriter*)writer; }
static
void
test_gbinder_data_item_destroy_local_obj(
TestGBinderDataItem* item)
{
g_assert_cmpint(item->type, == ,DATA_TYPE_LOCAL_OBJ);
gbinder_local_object_unref(item->data.obj);
}
static
void
test_gbinder_data_item_destroy_buffer(
TestGBinderDataItem* item)
{
g_assert_cmpint(item->type, == ,DATA_TYPE_BUFFER);
g_free(item->data.blob.buf);
}
static
TestGBinderDataItem*
test_gbinder_data_item_new(
DATA_TYPE type)
{
TestGBinderDataItem* item = g_new0(TestGBinderDataItem, 1);
item->type = type;
return item;
}
static
void
test_gbinder_data_item_free(
TestGBinderDataItem* item)
{
if (item) {
test_gbinder_data_item_free(item->next);
if (item->destroy) {
item->destroy(item);
}
g_free(item);
}
}
static
void
test_gbinder_data_free(
TestGBinderData* data)
{
test_gbinder_data_item_free(data->items);
gutil_idle_pool_destroy(data->pool);
g_free(data->iface);
g_free(data);
}
static
guint
test_gbinder_data_count_buffers(
TestGBinderData* data)
{
TestGBinderDataItem* item;
guint n;
for (n = 0, item = data->items; item; item = item->next) {
if (item->type == DATA_TYPE_BUFFER) {
n++;
}
}
return n;
}
static
void
test_gbinder_data_append(
TestGBinderData* data,
TestGBinderDataItem* item)
{
TestGBinderDataItem* last = data->items;
if (last) {
while (last->next) {
last = last->next;
}
last->next = item;
} else {
data->items = item;
}
}
static
gsize
test_gbinder_data_item_size(
TestGBinderDataItem* item)
{
switch (item->type) {
case DATA_TYPE_BOOLEAN:
return sizeof(item->data.b);
case DATA_TYPE_INT32:
return sizeof(item->data.i32);
case DATA_TYPE_BUFFER:
return sizeof(item->data.blob);
case DATA_TYPE_LOCAL_OBJ:
return sizeof(item->data.obj);
}
return 0;
}
static
void*
test_gbinder_data_buffer(
TestGBinderData* data,
gsize* out_size)
{
gsize size = 0;
void* ptr = NULL;
if (data) {
TestGBinderDataItem* item;
GByteArray* buf = g_byte_array_new();
if (data->iface) {
gsize header_size = strlen(data->iface);
g_byte_array_append(buf, (void*)data->iface, header_size);
size += header_size;
}
for (item = data->items; item; item = item->next) {
gsize item_size = test_gbinder_data_item_size(item);
g_byte_array_append(buf, (void*)&item->data, item_size);
size += item_size;
}
ptr = g_byte_array_free(buf, FALSE);
}
if (out_size) *out_size = size;
return ptr;
}
static
gsize
test_gbinder_data_size(
TestGBinderData* data)
{
gsize size = 0;
if (data) {
TestGBinderDataItem* item;
if (data->iface) size += strlen(data->iface);
for (item = data->items; item; item = item->next) {
size += test_gbinder_data_item_size(item);
}
}
return size;
}
static
guint32
test_gbinder_date_replace_int32(
TestGBinderData* data,
gsize offset,
guint32 value)
{
if (data) {
gsize size = 0;
TestGBinderDataItem* item;
if (data->iface) size += strlen(data->iface);
for (item = data->items; item; item = item->next) {
if (size == offset) {
guint32 prev;
g_assert_cmpint(item->type, == ,DATA_TYPE_INT32);
prev = item->data.i32;
item->data.i32 = value;
return prev;
}
size += test_gbinder_data_item_size(item);
}
}
return 0;
}
/*==========================================================================*
* Internal API
*==========================================================================*/
TestGBinderData*
test_gbinder_data_new(
const char* iface)
{
TestGBinderData* data = g_new0(TestGBinderData, 1);
g_atomic_int_set(&data->refcount, 1);
data->iface = g_strdup(iface); /* Doubles as a request header */
return data;
}
TestGBinderData*
test_gbinder_data_ref(
TestGBinderData* data)
{
if (data) {
g_assert_cmpint(data->refcount, > ,0);
g_atomic_int_inc(&data->refcount);
}
return data;
}
void
test_gbinder_data_unref(
TestGBinderData* data)
{
if (data) {
g_assert_cmpint(data->refcount, > ,0);
if (g_atomic_int_dec_and_test(&data->refcount)) {
test_gbinder_data_free(data);
}
}
}
void
test_gbinder_data_init_reader(
TestGBinderData* data,
GBinderReader* reader)
{
memset(reader, 0, sizeof(*reader));
if (data) {
test_gbinder_reader_cast(reader)->item = data->items;
}
}
void
test_gbinder_data_init_writer(
TestGBinderData* data,
GBinderWriter* writer)
{
if (writer) {
memset(writer, 0, sizeof(*writer));
if (data) {
test_gbinder_writer_cast(writer)->data = data;
}
}
}
/*==========================================================================*
* libgbinder API
*==========================================================================*/
gboolean
gbinder_reader_read_uint32(
GBinderReader* reader,
guint32* value)
{
TestGBinderReader* self = test_gbinder_reader_cast(reader);
TestGBinderDataItem* item = self->item;
if (item && item->type == DATA_TYPE_INT32) {
if (value) {
*value = item->data.i32;
}
self->item = item->next;
return TRUE;
}
return FALSE;
}
gboolean
gbinder_reader_read_int32(
GBinderReader* reader,
gint32* value)
{
return gbinder_reader_read_uint32(reader, (guint32*)value);
}
const void*
gbinder_reader_read_hidl_struct1(
GBinderReader* reader,
gsize size)
{
TestGBinderReader* self = test_gbinder_reader_cast(reader);
TestGBinderDataItem* item = self->item;
if (item && item->type == DATA_TYPE_BUFFER &&
item->data.blob.size == size) {
self->item = item->next;
return item->data.blob.buf;
}
return NULL;
}
const void*
gbinder_reader_read_parcelable(
GBinderReader* reader,
gsize* size)
{
TestGBinderReader* self = test_gbinder_reader_cast(reader);
TestGBinderDataItem* item = self->item;
if (item && item->type == DATA_TYPE_BUFFER) {
if (size) {
*size = item->data.blob.size;
}
self->item = item->next;
return item->data.blob.buf;
}
return NULL;
}
GBinderRemoteObject*
gbinder_reader_read_object(
GBinderReader* reader)
{
TestGBinderReader* self = test_gbinder_reader_cast(reader);
TestGBinderDataItem* item = self->item;
if (item && item->type == DATA_TYPE_LOCAL_OBJ) {
self->item = item->next;
return test_gbinder_remote_object_new(item->data.obj);
}
return NULL;
}
const void*
gbinder_writer_get_data(
GBinderWriter* writer,
gsize* size)
{
TestGBinderWriter* self = test_gbinder_writer_cast(writer);
TestGBinderData* data = self->data;
void* buf = test_gbinder_data_buffer(data, size);
if (buf) {
if (!data->pool) {
data->pool = gutil_idle_pool_new();
}
gutil_idle_pool_add(data->pool, buf, g_free);
}
return buf;
}
gsize
gbinder_writer_bytes_written(
GBinderWriter* writer)
{
TestGBinderWriter* self = test_gbinder_writer_cast(writer);
return test_gbinder_data_size(self->data);
}
void
gbinder_writer_append_int32(
GBinderWriter* writer,
guint32 value)
{
TestGBinderWriter* self = test_gbinder_writer_cast(writer);
TestGBinderDataItem* item = test_gbinder_data_item_new(DATA_TYPE_INT32);
item->data.i32 = value;
test_gbinder_data_append(self->data, item);
}
void
gbinder_writer_overwrite_int32(
GBinderWriter* writer,
gsize offset,
gint32 value)
{
TestGBinderWriter* self = test_gbinder_writer_cast(writer);
test_gbinder_date_replace_int32(self->data, offset, value);
}
void
gbinder_writer_append_bool(
GBinderWriter* writer,
gboolean value)
{
TestGBinderWriter* self = test_gbinder_writer_cast(writer);
TestGBinderDataItem* item = test_gbinder_data_item_new(DATA_TYPE_BOOLEAN);
item->data.b = value;
test_gbinder_data_append(self->data, item);
}
guint
gbinder_writer_append_buffer_object(
GBinderWriter* writer,
const void* buf,
gsize size)
{
TestGBinderWriter* self = test_gbinder_writer_cast(writer);
TestGBinderDataItem* item = test_gbinder_data_item_new(DATA_TYPE_BUFFER);
const guint index = test_gbinder_data_count_buffers(self->data);
item->destroy = test_gbinder_data_item_destroy_buffer;
item->data.blob.buf = gutil_memdup(buf, size);
item->data.blob.size = size;
test_gbinder_data_append(self->data, item);
return index;
}
void
gbinder_writer_append_local_object(
GBinderWriter* writer,
GBinderLocalObject* obj)
{
TestGBinderWriter* self = test_gbinder_writer_cast(writer);
TestGBinderDataItem* item = test_gbinder_data_item_new(DATA_TYPE_LOCAL_OBJ);
item->data.obj = gbinder_local_object_ref(obj);
item->destroy = test_gbinder_data_item_destroy_local_obj;
test_gbinder_data_append(self->data, item);
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/