507 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			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:
 | 
						|
 */
 |