/*
Copyright (C) 2001-2011, Parrot Foundation.
=head1 NAME
src/pmc/fixedpmcarray.pmc - fixed size array for PMCs only
=head1 DESCRIPTION
This class, FixedPMCArray, implements an array of fixed size which stores PMCs.
It puts things into Integer, Float, or String PMCs as appropriate
=head2 Note
The flag C<PObj_private0_FLAG> is used in the C<NameSpace> PMC and should
never be set for user arrays.
=head2 Functions
=over 4
=cut
*/
#define PMC_size(x) ((Parrot_FixedPMCArray_attributes *)PMC_data(x))->size
#define PMC_array(x) ((Parrot_FixedPMCArray_attributes *)PMC_data(x))->pmc_array
/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
PARROT_DOES_NOT_RETURN
static void cannot_autovivify_nested(PARROT_INTERP)
__attribute__nonnull__(1);
#define ASSERT_ARGS_cannot_autovivify_nested __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
PARROT_ASSERT_ARG(interp))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
/* HEADERIZER END: static */
pmclass FixedPMCArray auto_attrs provides array {
ATTR INTVAL size; /* number of elements in the array */
ATTR PMC **pmc_array; /* pointer to PMC array */
/*
=item C<METHOD sort(PMC *cmp_func)>
Sort this array, optionally using the provided cmp_func
=cut
*/
METHOD sort(PMC *cmp_func :optional) {
const INTVAL n = SELF.elements();
if (n > 1) {
/* XXX Workaround for TT #218 */
if (PObj_is_object_TEST(SELF)) {
PMC * const parent = SELF.get_attr_str(CONST_STRING(INTERP, "proxy"));
Parrot_pcc_invoke_method_from_c_args(INTERP, parent, CONST_STRING(INTERP, "sort"), "P->", cmp_func);
}
else
Parrot_util_quicksort(INTERP, (void **)PMC_array(SELF), n, cmp_func, "PP->I");
}
RETURN(PMC *SELF);
}
/*
=item C<METHOD reverse()>
Reverse the contents of the array.
=cut
*/
METHOD reverse() {
INTVAL n = SELF.elements();
if (n > 1) {
PMC *val;
PMC **data = PMC_array(SELF);
INTVAL i;
for (i = 0; i <= --n; i++) {
val = data[i];
data[i] = data[n];
data[n] = val;
}
}
}
/*
=back
=head2 Methods
=over 4
=item C<void init_int(INTVAL size)>
Initializes the array.
=cut
*/
VTABLE void init_int(INTVAL size) {
if (size < 0)
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
_("FixedPMCArray: Cannot set array size to a negative number (%d)"), size);
SELF.set_integer_native(size);
}
/*
=item C<void destroy()>
Destroys the array.
=cut
*/
VTABLE void destroy() {
if (PMC_array(SELF))
mem_gc_free(INTERP, PMC_array(SELF));
}
/*
=item C<PMC *clone()>
Creates and returns a copy of the array.
=cut
*/
VTABLE PMC *clone() {
PMC * const dest = Parrot_pmc_new(INTERP, SELF->vtable->base_type);
const INTVAL size = PMC_size(SELF);
if (size) {
PMC_size(dest) = size;
PMC_array(dest) = mem_gc_allocate_n_typed(INTERP, size, PMC *);
mem_copy_n_typed(PMC_array(dest), PMC_array(SELF), size, PMC *);
PObj_custom_mark_destroy_SETALL(dest);
}
return dest;
}
/*
=item C<INTVAL get_bool()>
Returns whether the array has any elements (meaning been initialized, for a
fixed sized array).
=cut
*/
VTABLE INTVAL get_bool() {
const INTVAL size = SELF.elements();
return (INTVAL)(size != 0);
}
/*
=item C<INTVAL elements()>
=cut
*/
VTABLE INTVAL elements() {
return PMC_size(SELF);
}
/*
=item C<INTVAL get_integer()>
Returns the number of elements in the array.
=cut
*/
VTABLE INTVAL get_integer() {
return SELF.elements();
}
/*
=item C<FLOATVAL get_number()>
Returns the number of elements in the array.
=cut
*/
VTABLE FLOATVAL get_number() {
const INTVAL e = SELF.elements();
return (FLOATVAL)e;
}
/*
=item C<STRING *get_string()>
Returns the number of elements in the array as a Parrot string. (??? -leo)
=item C<STRING *get_repr()>
Returns a string representation of the array contents.
TT #1229: implement freeze/thaw and use that instead.
=cut
*/
VTABLE STRING *get_string() {
return Parrot_str_from_int(INTERP, SELF.elements());
}
VTABLE STRING *get_repr() {
STRING *res = CONST_STRING(INTERP, "(");
const INTVAL n = VTABLE_elements(INTERP, SELF);
INTVAL i;
for (i = 0; i < n; ++i) {
PMC * const val = SELF.get_pmc_keyed_int(i);
if (i > 0)
res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, ", "));
res = Parrot_str_concat(INTERP, res, VTABLE_get_repr(INTERP, val));
}
res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, ")"));
return res;
}
/*
=item C<INTVAL get_integer_keyed_int(INTVAL key)>
Returns the integer value of the element at index C<key>.
=cut
*/
VTABLE INTVAL get_integer_keyed_int(INTVAL key) {
PMC * const tempPMC = SELF.get_pmc_keyed_int(key);
if (PMC_IS_NULL(tempPMC))
return 0;
return VTABLE_get_integer(INTERP, tempPMC);
}
/*
=item C<INTVAL get_integer_keyed(PMC *key)>
Returns the integer value of the element at index C<*key>.
=cut
*/
VTABLE INTVAL get_integer_keyed(PMC *key) {
PMC * const tempPMC = SELF.get_pmc_keyed(key);
return VTABLE_get_integer(INTERP, tempPMC);
}
/*
=item C<FLOATVAL get_number_keyed_int(INTVAL key)>
Returns the floating-point value of the element at index C<key>.
=cut
*/
VTABLE FLOATVAL get_number_keyed_int(INTVAL key) {
PMC * const tempPMC = SELF.get_pmc_keyed_int(key);
return VTABLE_get_number(INTERP, tempPMC);
}
/*
=item C<FLOATVAL get_number_keyed(PMC *key)>
Returns the floating-point value of the element at index C<*key>.
=cut
*/
VTABLE FLOATVAL get_number_keyed(PMC *key) {
PMC * const tempPMC = SELF.get_pmc_keyed(key);
return VTABLE_get_number(INTERP, tempPMC);
}
/*
=item C<STRING *get_string_keyed_int(INTVAL key)>
Returns the Parrot string value of the element at index C<key>.
=cut
*/
VTABLE STRING *get_string_keyed_int(INTVAL key) {
PMC * const retval = SELF.get_pmc_keyed_int(key);
if (PMC_IS_NULL(retval))
return CONST_STRING(INTERP, "");
return VTABLE_get_string(INTERP, retval);
}
/*
=item C<STRING *get_string_keyed(PMC *key)>
Returns the Parrot string value of the element at index C<*key>.
=cut
*/
VTABLE STRING *get_string_keyed(PMC *key) {
PMC * const tempPMC = SELF.get_pmc_keyed(key);
return VTABLE_get_string(INTERP, tempPMC);
}
/*
=item C<PMC *get_pmc_keyed_int(INTVAL key)>
Returns the PMC value of the element at index C<key>.
=cut
*/
VTABLE PMC *get_pmc_keyed_int(INTVAL key) {
PMC **data;
if (key < 0 || key >= PMC_size(SELF))
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
_("FixedPMCArray: index out of bounds!"));
data = PMC_array(SELF);
return data[key];
}
/*
=item C<PMC *get_pmc_keyed(PMC *key)>
Returns the PMC value of the element at index C<*key>.
=cut
*/
VTABLE PMC *get_pmc_keyed(PMC *key) {
const INTVAL k = VTABLE_get_integer(INTERP, key);
PMC * const nextkey = Parrot_key_next(INTERP, key);
PMC *box;
if (!nextkey)
return SELF.get_pmc_keyed_int(k);
box = SELF.get_pmc_keyed_int(k);
/* TT #1561, return NULL early if we must autovivify. */
if (PMC_IS_NULL(box))
return PMCNULL;
return VTABLE_get_pmc_keyed(INTERP, box, nextkey);
}
/*
=item C<void set_integer_native(INTVAL size)>
Sizes the array to C<size> elements. Can't be used to resize an
array.
=cut
*/
VTABLE void set_integer_native(INTVAL size) {
int i;
PMC **data;
if (PMC_size(SELF) && size)
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
_("FixedPMCArray: Can't resize!"));
if (!size)
return;
if (size < 0)
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
_("FixedPMCArray: Cannot set array size to a negative number"));
PMC_size(SELF) = size;
data = mem_gc_allocate_n_typed(INTERP, size, PMC *);
for (i = 0; i < size; ++i)
data[i] = PMCNULL;
PObj_custom_mark_destroy_SETALL(SELF);
PMC_array(SELF) = data;
}
/*
=item C<void set_integer_keyed_int(INTVAL key, INTVAL value)>
Sets the integer value of the element at index C<key> to C<value>.
=cut
*/
VTABLE void set_integer_keyed_int(INTVAL key, INTVAL value) {
PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
enum_class_Integer));
VTABLE_set_integer_native(INTERP, val, value);
SELF.set_pmc_keyed_int(key, val);
}
/*
=item C<void set_integer_keyed(PMC *key, INTVAL value)>
Sets the integer value of the element at index C<key> to C<value>.
=cut
*/
VTABLE void set_integer_keyed(PMC *key, INTVAL value) {
PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
enum_class_Integer));
VTABLE_set_integer_native(INTERP, val, value);
/* Let set_pmc_keyed worry about multi keys */
SELF.set_pmc_keyed(key, val);
}
/*
=item C<void set_number_keyed_int(INTVAL key, FLOATVAL value)>
Sets the floating-point value of the element at index C<key> to
C<value>.
=cut
*/
VTABLE void set_number_keyed_int(INTVAL key, FLOATVAL value) {
PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
enum_class_Float));
VTABLE_set_number_native(INTERP, val, value);
SELF.set_pmc_keyed_int(key, val);
}
/*
=item C<void set_number_keyed(PMC *key, FLOATVAL value)>
Sets the floating-point value of the element at index C<key> to
C<value>.
=cut
*/
VTABLE void set_number_keyed(PMC *key, FLOATVAL value) {
const INTVAL k = VTABLE_get_integer(INTERP, key);
PMC * const nextkey = Parrot_key_next(INTERP, key);
if (nextkey == NULL) {
SELF.set_number_keyed_int(k, value);
}
else {
PMC * const box = SELF.get_pmc_keyed_int(k);
if (PMC_IS_NULL(box))
cannot_autovivify_nested(INTERP);
VTABLE_set_number_keyed(INTERP, box, nextkey, value);
}
}
/*
=item C<void set_string_keyed_int(INTVAL key, STRING *value)>
Sets the Parrot string value of the element at index C<key> to C<value>.
=cut
*/
VTABLE void set_string_keyed_int(INTVAL key, STRING *value) {
PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
enum_class_String));
VTABLE_set_string_native(INTERP, val, value);
SELF.set_pmc_keyed_int(key, val);
}
/*
=item C<void set_string_keyed(PMC *key, STRING *value)>
Sets the string value of the element at index C<key> to
C<value>.
=cut
*/
VTABLE void set_string_keyed(PMC *key, STRING *value) {
PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
enum_class_String));
VTABLE_set_string_native(INTERP, val, value);
/* Let set_pmc_keyed worry about multi keys */
SELF.set_pmc_keyed(key, val);
}
/*
=item C<void set_pmc_keyed_int(INTVAL key, PMC *src)>
Sets the PMC value of the element at index C<key> to C<*src>.
=cut
*/
VTABLE void set_pmc_keyed_int(INTVAL key, PMC *src) {
PMC **data;
if (key < 0 || key >= PMC_size(SELF))
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
_("FixedPMCArray: index out of bounds!"));
data = PMC_array(SELF);
data[key] = src;
}
/*
=item C<void set_pmc_keyed(PMC *key, PMC *value)>
Sets the PMC at index C<key> to C<value>.
=cut
*/
VTABLE void set_pmc_keyed(PMC *key, PMC *value) {
const INTVAL k = VTABLE_get_integer(INTERP, key);
PMC * const nextkey = Parrot_key_next(INTERP, key);
if (!nextkey) {
SELF.set_pmc_keyed_int(k, value);
}
else {
PMC * const box = SELF.get_pmc_keyed_int(k);
if (PMC_IS_NULL(box))
cannot_autovivify_nested(INTERP);
VTABLE_set_pmc_keyed(INTERP, box, nextkey, value);
}
}
/*
=item C<INTVAL is_equal(PMC *value)>
The C<==> operation. Compares two array to hold equal elements.
=cut
*/
VTABLE INTVAL is_equal(PMC *value) {
INTVAL j, n;
if (value->vtable->base_type != SELF->vtable->base_type)
return 0;
n = SELF.elements();
if (VTABLE_elements(INTERP, value) != n)
return 0;
for (j = 0; j < n; ++j) {
PMC * const item1 = SELF.get_pmc_keyed_int(j);
PMC * const item2 = VTABLE_get_pmc_keyed_int(INTERP, value, j);
if (item1 == item2)
continue;
if (item1->vtable->base_type == enum_class_Null
|| item2->vtable->base_type == enum_class_Null)
return 0;
if (!VTABLE_is_equal(INTERP, item1, item2))
return 0;
}
return 1;
}
/*
=item C<PMC *get_iter()>
Return a new iterator for SELF.
=cut
*/
VTABLE PMC *get_iter() {
return Parrot_pmc_new_init(INTERP, enum_class_ArrayIterator, SELF);
}
/*
=item C<INTVAL exists_keyed_int(INTVAL key)>
=item C<INTVAL exists_keyed_int(PMC *key)>
Returns TRUE is the element at C<key> exists; otherwise returns false.
=cut
*/
VTABLE INTVAL exists_keyed_int(INTVAL key) {
PMC **data;
if (key < 0 || key >= PMC_size(SELF))
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
_("FixedPMCArray: index out of bounds!"));
data = PMC_array(SELF);
return !PMC_IS_NULL(data[key]);
}
VTABLE INTVAL exists_keyed(PMC *key) {
const INTVAL ix = VTABLE_get_integer(INTERP, key);
return SELF.exists_keyed_int(ix);
}
/*
=item C<void splice(PMC *value, INTVAL offset, INTVAL count)>
Replaces C<count> elements starting at C<offset> with the elements in C<value>.
If C<count> is 0 then the elements in C<value> will be inserted after
C<offset>.
This throws an exception if any of the spliced in values are out of the range
of this array.
=cut
*/
VTABLE void splice(PMC *value, INTVAL offset, INTVAL count) {
if (count + offset > PMC_size(SELF))
Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
_("FixedPMCArray: index out of bounds!"));
for (count--; count >= 0; --count) {
VTABLE_set_pmc_keyed_int(INTERP, SELF, offset + count, value);
}
}
/*
=item C<void visit(PMC *info)>
This is used by freeze/thaw to visit the contents of the array.
C<*info> is the visit info, (see F<include/parrot/pmc_freeze.h>).
=item C<void freeze(PMC *info)>
Used to archive the array.
=item C<void thaw(PMC *info)>
Used to unarchive the array.
=cut
*/
VTABLE void visit(PMC *info) {
INTVAL i;
const INTVAL n = VTABLE_elements(INTERP, SELF);
PMC **pos = PMC_array(SELF);
for (i = 0; i < n; ++i, ++pos) {
VISIT_PMC(INTERP, info, *pos);
}
SUPER(info);
}
VTABLE void freeze(PMC *info) {
VTABLE_push_integer(INTERP, info, VTABLE_elements(INTERP, SELF));
}
VTABLE void thaw(PMC *info) {
SELF.init_int(VTABLE_shift_integer(INTERP, info));
}
/*
=item C<INTVAL defined_keyed_int(INTVAL key)>
Returns TRUE is the element at C<key> is defined; otherwise returns false.
=cut
*/
VTABLE INTVAL defined_keyed_int(INTVAL key) {
PMC * const val = SELF.get_pmc_keyed_int(key);
if (PMC_IS_NULL(val))
return 0;
return VTABLE_defined(INTERP, val);
}
/*
=item C<void mark(void)>
Mark the array.
=cut
*/
VTABLE void mark() {
PMC ** const data = PMC_array(SELF);
if (data) {
INTVAL i;
for (i = PMC_size(SELF) - 1; i >= 0; --i)
Parrot_gc_mark_PMC_alive(INTERP, data[i]);
}
}
}
/*
=back
=head1 Auxiliary functions
=over 4
=item C<static void cannot_autovivify_nested(PARROT_INTERP)>
Throw exception when trying to autovivify nested arrays
=cut
*/
PARROT_DOES_NOT_RETURN
static void
cannot_autovivify_nested(PARROT_INTERP)
{
ASSERT_ARGS(cannot_autovivify_nested)
Parrot_ex_throw_from_c_args(interp, NULL,
EXCEPTION_INVALID_OPERATION,
"Cannot autovivify nested arrays");
}
/*
=back
=head1 SEE ALSO
F<docs/pdds/pdd17_basic_types.pod>.
=cut
*/
/*
* Local variables:
* c-file-style: "parrot"
* End:
* vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
*/