[gbinder] Added gbinder_writer_append_string16_utf16(). JB#43524
This function allows to write UTF-16 strings to Java parcel without converting UTF-16 to UTF-8 and then back to UTF-16. As a side effect, it also allows writing strings containing embedded NULL characters. This function can be used for adding QString contents to the request more efficiently.
This commit is contained in:
		@@ -86,6 +86,12 @@ gbinder_writer_append_string16_len(
 | 
			
		||||
    const char* utf8,
 | 
			
		||||
    gssize num_bytes);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_string16_utf16(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
    const gunichar2* utf16,
 | 
			
		||||
    gssize length); /* Since 1.0.17 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_string8(
 | 
			
		||||
    GBinderWriter* writer,
 | 
			
		||||
 
 | 
			
		||||
@@ -351,15 +351,36 @@ gbinder_writer_data_append_string16(
 | 
			
		||||
    gbinder_writer_data_append_string16_len(data, utf8, utf8? strlen(utf8) : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_null(
 | 
			
		||||
    GBinderWriterData* data)
 | 
			
		||||
{
 | 
			
		||||
    /* NULL string */
 | 
			
		||||
    gbinder_writer_data_append_int32(data, -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_empty(
 | 
			
		||||
    GBinderWriterData* data)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const gsize old_size = buf->len;
 | 
			
		||||
    guint16* ptr16;
 | 
			
		||||
 | 
			
		||||
    /* Empty string */
 | 
			
		||||
    g_byte_array_set_size(buf, old_size + 8);
 | 
			
		||||
    ptr16 = (guint16*)(buf->data + old_size);
 | 
			
		||||
    ptr16[0] = ptr16[1] = ptr16[2] = 0; ptr16[3] = 0xffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_len(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const char* utf8,
 | 
			
		||||
    gssize num_bytes)
 | 
			
		||||
{
 | 
			
		||||
    GByteArray* buf = data->bytes;
 | 
			
		||||
    const gsize old_size = buf->len;
 | 
			
		||||
 | 
			
		||||
    if (utf8) {
 | 
			
		||||
        const char* end = utf8;
 | 
			
		||||
 | 
			
		||||
@@ -370,6 +391,8 @@ gbinder_writer_data_append_string16_len(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (num_bytes > 0) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        const gsize old_size = buf->len;
 | 
			
		||||
        glong len = g_utf8_strlen(utf8, num_bytes);
 | 
			
		||||
        gsize padded_len = G_ALIGN4((len+1)*2);
 | 
			
		||||
        guint32* len_ptr;
 | 
			
		||||
@@ -407,14 +430,69 @@ gbinder_writer_data_append_string16_len(
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_len + 4);
 | 
			
		||||
    } else if (utf8) {
 | 
			
		||||
        /* Empty string */
 | 
			
		||||
        guint16* ptr16;
 | 
			
		||||
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + 8);
 | 
			
		||||
        ptr16 = (guint16*)(buf->data + old_size);
 | 
			
		||||
        ptr16[0] = ptr16[1] = ptr16[2] = 0; ptr16[3] = 0xffff;
 | 
			
		||||
        gbinder_writer_data_append_string16_empty(data);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* NULL string */
 | 
			
		||||
        gbinder_writer_data_append_int32(data, -1);
 | 
			
		||||
        gbinder_writer_data_append_string16_null(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_data_append_string16_utf16(
 | 
			
		||||
    GBinderWriterData* data,
 | 
			
		||||
    const gunichar2* utf16,
 | 
			
		||||
    gssize length)
 | 
			
		||||
{
 | 
			
		||||
    if (length < 0) {
 | 
			
		||||
        length = 0;
 | 
			
		||||
        if (utf16) {
 | 
			
		||||
            const guint16* ptr;
 | 
			
		||||
 | 
			
		||||
            /* Assume NULL terminated string */
 | 
			
		||||
            for (ptr = utf16; *ptr; ptr++);
 | 
			
		||||
            length = ptr - utf16;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (length > 0) {
 | 
			
		||||
        GByteArray* buf = data->bytes;
 | 
			
		||||
        const gsize old_size = buf->len;
 | 
			
		||||
        const gsize padded_size = G_ALIGN4((length + 1) * 2);
 | 
			
		||||
        guint32* len_ptr;
 | 
			
		||||
        gunichar2* utf16_ptr;
 | 
			
		||||
 | 
			
		||||
        /* Preallocate space */
 | 
			
		||||
        g_byte_array_set_size(buf, old_size + padded_size + 4);
 | 
			
		||||
        len_ptr = (guint32*)(buf->data + old_size);
 | 
			
		||||
        utf16_ptr = (gunichar2*)(len_ptr + 1);
 | 
			
		||||
 | 
			
		||||
        /* Actual length */
 | 
			
		||||
        *len_ptr = length;
 | 
			
		||||
 | 
			
		||||
        /* Characters */
 | 
			
		||||
        memcpy(utf16_ptr, utf16, 2 * length);
 | 
			
		||||
 | 
			
		||||
        /* Zero padding */
 | 
			
		||||
        memset(utf16_ptr + length, 0, padded_size - 2 * length);
 | 
			
		||||
    } else if (utf16) {
 | 
			
		||||
        /* Empty string */
 | 
			
		||||
        gbinder_writer_data_append_string16_empty(data);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* NULL string */
 | 
			
		||||
        gbinder_writer_data_append_string16_null(data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gbinder_writer_append_string16_utf16(
 | 
			
		||||
    GBinderWriter* self,
 | 
			
		||||
    const gunichar2* utf16,
 | 
			
		||||
    gssize length) /* Since 1.0.17 */
 | 
			
		||||
{
 | 
			
		||||
    GBinderWriterData* data = gbinder_writer_data(self);
 | 
			
		||||
 | 
			
		||||
    if (G_LIKELY(data)) {
 | 
			
		||||
        gbinder_writer_data_append_string16_utf16(data, utf16, length);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -74,6 +74,7 @@ test_null(
 | 
			
		||||
    gbinder_writer_append_string16(&writer, NULL);
 | 
			
		||||
    gbinder_writer_append_string16_len(NULL, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_string16_len(&writer, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_string16_utf16(NULL, NULL, 0);
 | 
			
		||||
    gbinder_writer_append_bool(NULL, FALSE);
 | 
			
		||||
    gbinder_writer_append_bool(&writer, FALSE);
 | 
			
		||||
    gbinder_writer_append_bytes(NULL, NULL, 0);
 | 
			
		||||
@@ -342,6 +343,75 @@ test_string16(
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * utf16
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
typedef struct test_utf16_data {
 | 
			
		||||
    const char* name;
 | 
			
		||||
    const gunichar2* in;
 | 
			
		||||
    gssize in_len;
 | 
			
		||||
    const guint8* out;
 | 
			
		||||
    gssize out_len;
 | 
			
		||||
} TestUtf16Data;
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_null[] = {
 | 
			
		||||
    TEST_INT32_BYTES(-1)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_empty[] = {
 | 
			
		||||
    TEST_INT32_BYTES(0),
 | 
			
		||||
    0x00, 0x00, 0xff, 0xff
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_x[] = {
 | 
			
		||||
    TEST_INT32_BYTES(1),
 | 
			
		||||
    TEST_INT16_BYTES('x'), 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const guint8 utf16_tests_data_xy[] = {
 | 
			
		||||
    TEST_INT32_BYTES(2),
 | 
			
		||||
    TEST_INT16_BYTES('x'), TEST_INT16_BYTES('y'),
 | 
			
		||||
    0x00, 0x00, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const gunichar2 utf16_tests_input_empty[] = { 0 };
 | 
			
		||||
static const gunichar2 utf16_tests_input_x[] = { 'x', 0 };
 | 
			
		||||
static const gunichar2 utf16_tests_input_xy[] = { 'x', 'y', 0 };
 | 
			
		||||
 | 
			
		||||
static const TestUtf16Data test_utf16_tests[] = {
 | 
			
		||||
    { "null", NULL, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_null) },
 | 
			
		||||
    { "empty", utf16_tests_input_empty, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_empty) },
 | 
			
		||||
    { "1", utf16_tests_input_x, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_x) },
 | 
			
		||||
    { "2", utf16_tests_input_xy, 1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_x) },
 | 
			
		||||
    { "3", utf16_tests_input_xy, -1,
 | 
			
		||||
      TEST_ARRAY_AND_SIZE(utf16_tests_data_xy) }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void
 | 
			
		||||
test_utf16(
 | 
			
		||||
    gconstpointer test_data)
 | 
			
		||||
{
 | 
			
		||||
    const TestUtf16Data* test = test_data;
 | 
			
		||||
    GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL);
 | 
			
		||||
    GBinderOutputData* data;
 | 
			
		||||
    GBinderWriter writer;
 | 
			
		||||
 | 
			
		||||
    gbinder_local_request_init_writer(req, &writer);
 | 
			
		||||
    gbinder_writer_append_string16_utf16(&writer, test->in, test->in_len);
 | 
			
		||||
    data = gbinder_local_request_data(req);
 | 
			
		||||
    g_assert(!gbinder_output_data_offsets(data));
 | 
			
		||||
    g_assert(!gbinder_output_data_buffers_size(data));
 | 
			
		||||
    g_assert(data->bytes->len == test->out_len);
 | 
			
		||||
    g_assert(!memcmp(data->bytes->data, test->out, test->out_len));
 | 
			
		||||
    gbinder_local_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*==========================================================================*
 | 
			
		||||
 * hidl_vec
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
@@ -745,41 +815,50 @@ test_byte_array(
 | 
			
		||||
 *==========================================================================*/
 | 
			
		||||
 | 
			
		||||
#define TEST_PREFIX "/writer/"
 | 
			
		||||
#define TEST_(t) TEST_PREFIX t
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
    guint i;
 | 
			
		||||
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "null", test_null);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "int32", test_int32);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "int64", test_int64);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "float", test_float);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "double", test_double);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "bool", test_bool);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "bytes", test_bytes);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "string8", test_string8);
 | 
			
		||||
    g_test_add_func(TEST_("null"), test_null);
 | 
			
		||||
    g_test_add_func(TEST_("int32"), test_int32);
 | 
			
		||||
    g_test_add_func(TEST_("int64"), test_int64);
 | 
			
		||||
    g_test_add_func(TEST_("float"), test_float);
 | 
			
		||||
    g_test_add_func(TEST_("double"), test_double);
 | 
			
		||||
    g_test_add_func(TEST_("bool"), test_bool);
 | 
			
		||||
    g_test_add_func(TEST_("bytes"), test_bytes);
 | 
			
		||||
    g_test_add_func(TEST_("string8"), test_string8);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_string16_tests); i++) {
 | 
			
		||||
        const TestString16Data* test = test_string16_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "string16/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("string16/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_string16);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_utf16_tests); i++) {
 | 
			
		||||
        const TestUtf16Data* test = test_utf16_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_("utf16/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_utf16);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_vec_tests); i++) {
 | 
			
		||||
        const TestHidlVecData* test = test_hidl_vec_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_vec/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_vec/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_vec);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "hidl_string/2strings", test_hidl_string2);
 | 
			
		||||
    g_test_add_func(TEST_("hidl_string/2strings"), test_hidl_string2);
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_string_tests); i++) {
 | 
			
		||||
        const TestHidlStringData* test = test_hidl_string_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_string/", test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_string/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_string);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
@@ -787,18 +866,17 @@ int main(int argc, char* argv[])
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < G_N_ELEMENTS(test_hidl_string_vec_tests); i++) {
 | 
			
		||||
        const TestHidlStringVecData* test = test_hidl_string_vec_tests + i;
 | 
			
		||||
        char* path = g_strconcat(TEST_PREFIX "hidl_string_vec/",
 | 
			
		||||
            test->name, NULL);
 | 
			
		||||
        char* path = g_strconcat(TEST_("hidl_string_vec/"), test->name, NULL);
 | 
			
		||||
 | 
			
		||||
        g_test_add_data_func(path, test, test_hidl_string_vec);
 | 
			
		||||
        g_free(path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "buffer", test_buffer);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "parent", test_parent);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "local_object", test_local_object);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "remote_object", test_remote_object);
 | 
			
		||||
    g_test_add_func(TEST_PREFIX "byte_array", test_byte_array);
 | 
			
		||||
    g_test_add_func(TEST_("buffer"), test_buffer);
 | 
			
		||||
    g_test_add_func(TEST_("parent"), test_parent);
 | 
			
		||||
    g_test_add_func(TEST_("local_object"), test_local_object);
 | 
			
		||||
    g_test_add_func(TEST_("remote_object"), test_remote_object);
 | 
			
		||||
    g_test_add_func(TEST_("byte_array"), test_byte_array);
 | 
			
		||||
    test_init(&test_opt, argc, argv);
 | 
			
		||||
    return g_test_run();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user