/* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
/* Arrays containing non-basic types as non-pointers need to be treated
* specially. Prime example: GValue *values = g_new0 (GValue, n);
*/
static gboolean
_need_struct_value_semantics (GIArrayType array_type, GITypeInfo *param_info, GITypeTag param_tag)
{
gboolean is_flat, need_struct_value_semantics;
is_flat =
/* is a raw array, and ... */
(GI_ARRAY_TYPE_C == array_type || GI_ARRAY_TYPE_ARRAY == array_type) &&
/* ... contains a compound type, and... */
!G_TYPE_TAG_IS_BASIC (param_tag) &&
/* ... contains non-pointers */
!g_type_info_is_pointer (param_info);
need_struct_value_semantics = is_flat;
if (GI_TYPE_TAG_INTERFACE == param_tag) {
/* FIXME: Try to use the invocation info here to avoid getting
* the interface info again? */
GIBaseInfo *interface_info = g_type_info_get_interface (param_info);
switch (g_base_info_get_type (interface_info)) {
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
need_struct_value_semantics = FALSE;
default:
break;
}
g_base_info_unref (interface_info);
}
return need_struct_value_semantics;
}
static void
_free_raw_array (gpointer raw_array)
{
dwarn ("%p\n", raw_array);
g_free (raw_array);
}
static void
_free_array (GArray *array)
{
dwarn ("%p\n", array);
g_array_free (array, TRUE);
}
static void
_free_ptr_array (GPtrArray *array)
{
dwarn ("%p\n", array);
g_ptr_array_free (array, TRUE);
}
static void
_free_byte_array (GByteArray *array)
{
dwarn ("%p\n", array);
g_byte_array_free (array, TRUE);
}
/* This may call Perl code (via arg_to_sv), so it needs to be wrapped with
* PUTBACK/SPAGAIN by the caller. */
static SV *
array_to_sv (GITypeInfo *info,
gpointer pointer,
GITransfer transfer,
GPerlI11nInvocationInfo *iinfo)
{
GIArrayType array_type;
gpointer array = NULL, elements = NULL;
GITypeInfo *param_info;
GITypeTag param_tag;
gsize item_size;
GITransfer item_transfer;
gboolean need_struct_value_semantics;
gssize length = -1, i;
AV *av;
dwarn ("pointer %p\n", pointer);
if (pointer == NULL) {
return &PL_sv_undef;
}
array_type = g_type_info_get_array_type (info);
#define GET_LENGTH_AND_ELEMENTS(type, len_field, data_field) { \
array = pointer; \
length = ((type *) array)->len_field; \
elements = ((type *) array)->data_field; }
switch (array_type) {
case GI_ARRAY_TYPE_C:
array = pointer;
elements = pointer;
if (g_type_info_is_zero_terminated (info)) {
length = g_strv_length (elements);
} else {
length = g_type_info_get_array_fixed_size (info);
if (length < 0) {
SV *conversion_sv;
gint length_pos = g_type_info_get_array_length (info);
g_assert (iinfo && iinfo->aux_args);
conversion_sv = arg_to_sv (&(iinfo->aux_args[length_pos]),
&(iinfo->arg_types[length_pos]),
GI_TRANSFER_NOTHING, NULL);
length = SvIV (conversion_sv);
SvREFCNT_dec (conversion_sv);
}
}
break;
case GI_ARRAY_TYPE_ARRAY:
GET_LENGTH_AND_ELEMENTS (GArray, len, data);
break;
case GI_ARRAY_TYPE_PTR_ARRAY:
GET_LENGTH_AND_ELEMENTS (GPtrArray, len, pdata);
break;
case GI_ARRAY_TYPE_BYTE_ARRAY:
GET_LENGTH_AND_ELEMENTS (GByteArray, len, data);
break;
default:
ccroak ("Unhandled array type %d", array_type);
}
#undef GET_LENGTH_AND_ELEMENTS
if (length < 0) {
ccroak ("Could not determine the length of the array");
}
/* FIXME: What about an array containing arrays of strings, where the
* outer array is GI_TRANSFER_EVERYTHING but the inner arrays are
* GI_TRANSFER_CONTAINER? */
item_transfer = transfer == GI_TRANSFER_EVERYTHING
? GI_TRANSFER_EVERYTHING
: GI_TRANSFER_NOTHING;
param_info = g_type_info_get_param_type (info, 0);
param_tag = g_type_info_get_tag (param_info);
item_size = size_of_type_info (param_info);
av = newAV ();
need_struct_value_semantics =
_need_struct_value_semantics (array_type, param_info, param_tag);
dwarn ("type %d, array %p, elements %p\n",
array_type, array, elements);
dwarn ("length %"G_GSSIZE_FORMAT", item size %"G_GSIZE_FORMAT", param_info %p, param_tag %d (%s)\n",
length,
item_size,
param_info,
param_tag,
g_type_tag_to_string (param_tag));
for (i = 0; i < length; i++) {
GIArgument arg;
SV *value;
gpointer element = elements + ((gsize) i) * item_size;
dwarn (" element %"G_GSSIZE_FORMAT": %p\n", i, element);
if (need_struct_value_semantics) {
raw_to_arg (&element, &arg, param_info);
} else {
raw_to_arg (element, &arg, param_info);
}
value = arg_to_sv (&arg, param_info, item_transfer, iinfo);
if (value)
av_push (av, value);
}
if (transfer >= GI_TRANSFER_CONTAINER) {
switch (array_type) {
case GI_ARRAY_TYPE_C:
_free_raw_array (array);
break;
case GI_ARRAY_TYPE_ARRAY:
_free_array (array);
break;
case GI_ARRAY_TYPE_PTR_ARRAY:
_free_ptr_array (array);
break;
case GI_ARRAY_TYPE_BYTE_ARRAY:
_free_byte_array (array);
break;
}
}
g_base_info_unref ((GIBaseInfo *) param_info);
dwarn (" -> AV %p of length %"G_GSIZE_FORMAT"\n",
av, av_len (av) + 1);
return newRV_noinc ((SV *) av);
}
static gpointer
sv_to_array (GITransfer transfer,
GITypeInfo *type_info,
SV *sv,
GPerlI11nInvocationInfo *iinfo)
{
AV *av;
GIArrayType array_type;
GITransfer item_transfer;
GITypeInfo *param_info;
GITypeTag param_tag;
gint length_pos;
gsize i, length;
GPerlI11nArrayInfo *array_info = NULL;
gpointer array = NULL;
gpointer return_array;
GFunc return_array_free_func;
gboolean is_zero_terminated = FALSE;
gsize item_size;
gboolean need_struct_value_semantics;
dwarn ("sv %p\n", sv);
/* Add an array info entry even before the undef check so that the
* corresponding length arg is set to zero later by
* _handle_automatic_arg. */
length_pos = g_type_info_get_array_length (type_info);
if (length_pos >= 0) {
array_info = g_new0 (GPerlI11nArrayInfo, 1);
array_info->length_pos = length_pos;
array_info->length = 0;
iinfo->array_infos = g_slist_prepend (iinfo->array_infos, array_info);
}
if (!gperl_sv_is_defined (sv))
return NULL;
if (!gperl_sv_is_array_ref (sv))
ccroak ("need an array ref to convert to GArray");
array_type = g_type_info_get_array_type (type_info);
av = (AV *) SvRV (sv);
item_transfer = transfer == GI_TRANSFER_CONTAINER
? GI_TRANSFER_NOTHING
: transfer;
param_info = g_type_info_get_param_type (type_info, 0);
param_tag = g_type_info_get_tag (param_info);
dwarn ("type %d, param_info %p, param_tag %d (%s), transfer %d\n",
array_type,
param_info,
param_tag,
g_type_tag_to_string (param_tag),
transfer);
need_struct_value_semantics =
_need_struct_value_semantics (array_type, param_info, param_tag);
is_zero_terminated = g_type_info_is_zero_terminated (type_info);
item_size = size_of_type_info (param_info);
length = (gsize) (av_len (av) + 1); /* av_len always returns at least -1 */
switch (array_type) {
case GI_ARRAY_TYPE_C:
case GI_ARRAY_TYPE_ARRAY:
array = g_array_sized_new (is_zero_terminated, FALSE, item_size, length);
break;
case GI_ARRAY_TYPE_PTR_ARRAY:
array = g_ptr_array_sized_new (length);
g_ptr_array_set_size (array, length);
break;
case GI_ARRAY_TYPE_BYTE_ARRAY:
array = g_byte_array_sized_new (length);
g_byte_array_set_size (array, length);
break;
}
for (i = 0; i < length; i++) {
SV **svp;
svp = av_fetch (av, i, 0);
dwarn (" element %"G_GSIZE_FORMAT": svp = %p\n", i, svp);
if (svp && gperl_sv_is_defined (*svp)) {
GIArgument arg;
/* FIXME: Is it OK to always allow undef here? */
sv_to_arg (*svp, &arg, NULL, param_info,
item_transfer, TRUE, NULL);
switch (array_type) {
case GI_ARRAY_TYPE_C:
case GI_ARRAY_TYPE_ARRAY:
if (need_struct_value_semantics) {
/* Copy from the memory area pointed to by
* arg.v_pointer. */
g_array_insert_vals (array, i, arg.v_pointer, 1);
} else {
/* Copy from &arg, i.e. the memory area that is
* arg. */
g_array_insert_val (array, i, arg);
}
break;
case GI_ARRAY_TYPE_PTR_ARRAY:
((GPtrArray *) array)->pdata[i] = arg.v_pointer;
break;
case GI_ARRAY_TYPE_BYTE_ARRAY:
((GByteArray *) array)->data[i] = arg.v_uint8;
break;
}
}
}
if (length_pos >= 0) {
array_info->length = length;
}
return_array = array;
return_array_free_func = NULL;
switch (array_type) {
case GI_ARRAY_TYPE_C:
return_array = g_array_free (array, FALSE);
return_array_free_func = (GFunc) _free_raw_array;
break;
case GI_ARRAY_TYPE_ARRAY:
return_array_free_func = (GFunc) _free_array;
break;
case GI_ARRAY_TYPE_PTR_ARRAY:
return_array_free_func = (GFunc) _free_ptr_array;
break;
case GI_ARRAY_TYPE_BYTE_ARRAY:
return_array_free_func = (GFunc) _free_byte_array;
break;
}
if (GI_TRANSFER_NOTHING == transfer) {
free_after_call (iinfo, return_array_free_func, return_array);
}
g_base_info_unref ((GIBaseInfo *) param_info);
dwarn (" -> array %p of length %"G_GSIZE_FORMAT"\n",
return_array, length);
return return_array;
}