static void array_c_to_ruby(const gchar **elements, GITypeInfo *type_info, VALUE rb_array) { gint n_elements; gboolean fixed_size_p; gboolean zero_terminated_p; n_elements = g_type_info_get_array_length(type_info); fixed_size_p = g_type_info_get_array_fixed_size(type_info); zero_terminated_p = g_type_info_is_zero_terminated(type_info); if (n_elements != -1) { gint i; for (i = 0; i < n_elements; i++) { rb_ary_push(rb_array, CSTR2RVAL(elements[i])); } } else if (zero_terminated_p) { for (; *elements; elements++) { rb_ary_push(rb_array, CSTR2RVAL(*elements)); } } else { rb_raise(rb_eNotImpError, "TODO: GIArgument(array)[c] -> Ruby: " "zero-terminated: %s " "fixed-size: %s " "length: %d", zero_terminated_p ? "true" : "false", fixed_size_p ? "true" : "false", n_elements); } }
static void marshal_2lua_array (lua_State *L, GITypeInfo *ti, GIDirection dir, GIArrayType atype, GITransfer transfer, gpointer array, gssize size, int parent) { GITypeInfo *eti; gssize len = 0, esize; gint index, eti_guard; char *data = NULL; /* Avoid propagating return value marshaling flag to array elements. */ if (parent == LGI_PARENT_IS_RETVAL) parent = 0; /* First of all, find out the length of the array. */ if (atype == GI_ARRAY_TYPE_ARRAY) { if (array) { len = ((GArray *) array)->len; data = ((GArray *) array)->data; } } else { data = array; if (g_type_info_is_zero_terminated (ti)) len = -1; else { len = g_type_info_get_array_fixed_size (ti); if (len == -1) /* Length of the array is dynamic, get it from other argument. */ len = size; } } /* Get array element type info, wrap it in the guard so that we don't leak it. */ eti = g_type_info_get_param_type (ti, 0); lgi_gi_info_new (L, eti); eti_guard = lua_gettop (L); esize = array_get_elt_size (eti); /* Note that we ignore is_pointer check for uint8 type. Although it is not exactly correct, we probably would not handle uint8* correctly anyway, this is strange type to use, and moreover this is workaround for g-ir-scanner bug which might mark elements of uint8 arrays as gconstpointer, thus setting is_pointer=true on it. See https://github.com/pavouk/lgi/issues/57 */ if (g_type_info_get_tag (eti) == GI_TYPE_TAG_UINT8) { /* UINT8 arrays are marshalled as Lua strings. */ if (len < 0) len = data ? strlen(data) : 0; lua_pushlstring (L, data, len); } else { if (array == NULL) { /* NULL array is represented by empty table for C arrays, nil for other types. */ if (atype == GI_ARRAY_TYPE_C) lua_newtable (L); else lua_pushnil (L); lua_remove (L, eti_guard); return; } /* Create Lua table which will hold the array. */ lua_createtable (L, len > 0 ? len : 0, 0); /* Iterate through array elements. */ for (index = 0; len < 0 || index < len; index++) { /* Get value from specified index. */ GIArgument *eval = (GIArgument *) (data + index * esize); /* If the array is zero-terminated, terminate now and don't include NULL entry. */ if (len < 0 && eval->v_pointer == NULL) break; /* Store value into the table. */ lgi_marshal_2lua (L, eti, NULL, dir, (transfer == GI_TRANSFER_EVERYTHING) ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING, eval, parent, NULL, NULL); lua_rawseti (L, -2, index + 1); } } /* If needed, free the original array. */ if (transfer != GI_TRANSFER_NOTHING) { if (atype == GI_ARRAY_TYPE_ARRAY) g_array_free (array, TRUE); else g_free (array); } lua_remove (L, eti_guard); }
gboolean lgi_marshal_2c_caller_alloc (lua_State *L, GITypeInfo *ti, GIArgument *val, int pos) { gboolean handled = FALSE; switch (g_type_info_get_tag (ti)) { case GI_TYPE_TAG_INTERFACE: { GIBaseInfo *ii = g_type_info_get_interface (ti); GIInfoType type = g_base_info_get_type (ii); if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_UNION) { if (pos == 0) { lgi_type_get_repotype (L, G_TYPE_INVALID, ii); val->v_pointer = lgi_record_new (L, 1, FALSE); } handled = TRUE; } g_base_info_unref (ii); break; } case GI_TYPE_TAG_ARRAY: { if (g_type_info_get_array_type (ti) == GI_ARRAY_TYPE_C) { gpointer *array_guard; if (pos == 0) { gssize elt_size, size; /* Currently only fixed-size arrays are supported. */ elt_size = array_get_elt_size (g_type_info_get_param_type (ti, 0)); size = g_type_info_get_array_fixed_size (ti); g_assert (size > 0); /* Allocate underlying array. It is temporary, existing only for the duration of the call. */ array_guard = lgi_guard_create (L, (GDestroyNotify) g_array_unref); *array_guard = g_array_sized_new (FALSE, FALSE, elt_size, size); g_array_set_size (*array_guard, size); } else { /* Convert the allocated array into Lua table with contents. We have to do it in-place. */ /* Make sure that pos is absolute, so that stack shuffling below does not change the element it points to. */ if (pos < 0) pos += lua_gettop (L) + 1; /* Get GArray from the guard and unmarshal it as a full GArray into Lua. */ array_guard = lua_touserdata (L, pos); marshal_2lua_array (L, ti, GI_DIRECTION_OUT, GI_ARRAY_TYPE_ARRAY, GI_TRANSFER_EVERYTHING, *array_guard, -1, pos); /* Deactivate old guard, everything was marshalled into the newly created and marshalled table. */ *array_guard = NULL; /* Switch old value with the new data. */ lua_replace (L, pos); } handled = TRUE; } break; } default: break; } return handled; }
/* Marshalls array from Lua to C. Returns number of temporary elements pushed to the stack. */ static int marshal_2c_array (lua_State *L, GITypeInfo *ti, GIArrayType atype, gpointer *out_array, gssize *out_size, int narg, gboolean optional, GITransfer transfer) { GITypeInfo* eti; gssize objlen, esize; gint index, vals = 0, to_pop, eti_guard; GITransfer exfer = (transfer == GI_TRANSFER_EVERYTHING ? GI_TRANSFER_EVERYTHING : GI_TRANSFER_NOTHING); gboolean zero_terminated; GArray *array = NULL; /* Represent nil as NULL array. */ if (optional && lua_isnoneornil (L, narg)) { *out_size = 0; *out_array = NULL; } else { /* Get element type info, create guard for it. */ eti = g_type_info_get_param_type (ti, 0); lgi_gi_info_new (L, eti); eti_guard = lua_gettop (L); esize = array_get_elt_size (eti); /* Check the type. If this is C-array of byte-sized elements, we can try special-case and accept strings or buffers. */ *out_array = NULL; if (lua_type (L, narg) != LUA_TTABLE && esize == 1 && atype == GI_ARRAY_TYPE_C) { size_t size = 0; *out_array = lgi_udata_test (L, narg, LGI_BYTES_BUFFER); if (*out_array) size = lua_objlen (L, narg); else *out_array = (gpointer *) lua_tolstring (L, narg, &size); if (transfer != GI_TRANSFER_NOTHING) *out_array = g_memdup (*out_array, size); *out_size = size; } if (!*out_array) { /* Otherwise, we allow only tables. */ luaL_checktype (L, narg, LUA_TTABLE); /* Find out how long array should we allocate. */ zero_terminated = g_type_info_is_zero_terminated (ti); objlen = lua_objlen (L, narg); *out_size = g_type_info_get_array_fixed_size (ti); if (atype != GI_ARRAY_TYPE_C || *out_size < 0) *out_size = objlen; else if (*out_size < objlen) objlen = *out_size; /* Allocate the array and wrap it into the userdata guard, if needed. */ if (*out_size > 0 || zero_terminated) { array = g_array_sized_new (zero_terminated, TRUE, esize, *out_size); g_array_set_size (array, *out_size); *lgi_guard_create (L, (GDestroyNotify) (transfer == GI_TRANSFER_EVERYTHING ? array_detach : g_array_unref)) = array; vals = 1; } /* Iterate through Lua array and fill GArray accordingly. */ for (index = 0; index < objlen; index++) { lua_pushnumber (L, index + 1); lua_gettable (L, narg); /* Marshal element retrieved from the table into target array. */ to_pop = lgi_marshal_2c (L, eti, NULL, exfer, array->data + index * esize, -1, 0, NULL, NULL); /* Remove temporary element from the stack. */ lua_remove (L, - to_pop - 1); /* Remember that some more temp elements could be pushed. */ vals += to_pop; } /* Return either GArray or direct pointer to the data, according to the array type. */ *out_array = (atype == GI_ARRAY_TYPE_ARRAY || array == NULL) ? (void *) array : (void *) array->data; } lua_remove (L, eti_guard); } return vals; }
/** * _pygi_argument_to_array * @arg: The argument to convert * @array_length_policy: Closure for marshalling the array length argument when needed. * @user_data1: Generic user data passed to the array_length_policy. * @user_data2: Generic user data passed to the array_length_policy. * @type_info: The type info for @arg * @out_free_array: A return location for a gboolean that indicates whether * or not the wrapped GArray should be freed * * Make sure an array type argument is wrapped in a GArray. * * Note: This method can *not* be folded into _pygi_argument_to_object() because * arrays are special in the sense that they might require access to @args in * order to get the length. * * Returns: A GArray wrapping @arg. If @out_free_array has been set to TRUE then * free the array with g_array_free() without freeing the data members. * Otherwise don't free the array. */ GArray * _pygi_argument_to_array (GIArgument *arg, PyGIArgArrayLengthPolicy array_length_policy, void *user_data1, void *user_data2, GITypeInfo *type_info, gboolean *out_free_array) { GITypeInfo *item_type_info; gboolean is_zero_terminated; gsize item_size; gssize length; GArray *g_array; g_return_val_if_fail (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY, NULL); if (arg->v_pointer == NULL) { return NULL; } switch (g_type_info_get_array_type (type_info)) { case GI_ARRAY_TYPE_C: is_zero_terminated = g_type_info_is_zero_terminated (type_info); item_type_info = g_type_info_get_param_type (type_info, 0); item_size = _pygi_g_type_info_size (item_type_info); g_base_info_unref ( (GIBaseInfo *) item_type_info); if (is_zero_terminated) { length = g_strv_length (arg->v_pointer); } else { length = g_type_info_get_array_fixed_size (type_info); if (length < 0) { gint length_arg_pos; if (G_UNLIKELY (array_length_policy == NULL)) { g_critical ("Unable to determine array length for %p", arg->v_pointer); g_array = g_array_new (is_zero_terminated, FALSE, item_size); *out_free_array = TRUE; return g_array; } length_arg_pos = g_type_info_get_array_length (type_info); g_assert (length_arg_pos >= 0); length = array_length_policy (length_arg_pos, user_data1, user_data2); if (length < 0) { return NULL; } } } g_assert (length >= 0); g_array = g_array_new (is_zero_terminated, FALSE, item_size); g_free (g_array->data); g_array->data = arg->v_pointer; g_array->len = length; *out_free_array = TRUE; break; case GI_ARRAY_TYPE_ARRAY: case GI_ARRAY_TYPE_BYTE_ARRAY: /* Note: GByteArray is really just a GArray */ g_array = arg->v_pointer; *out_free_array = FALSE; break; case GI_ARRAY_TYPE_PTR_ARRAY: { GPtrArray *ptr_array = (GPtrArray*) arg->v_pointer; g_array = g_array_sized_new (FALSE, FALSE, sizeof(gpointer), ptr_array->len); g_array->data = (char*) ptr_array->pdata; g_array->len = ptr_array->len; *out_free_array = TRUE; break; } default: g_critical ("Unexpected array type %u", g_type_info_get_array_type (type_info)); g_array = NULL; break; } return g_array; }