Find file
Fetching contributors…
Cannot retrieve contributors at this time
857 lines (556 sloc) 18.2 KB
/*
Copyright (C) 2001-2014, Parrot Foundation.
=head1 NAME
src/pmc/fixedpmcarray.pmc - FixedPMCArray PMC
=head1 DESCRIPTION
Fixed size array for PMCs only.
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 Vtable Methods
=over 4
=item C<void init_int(INTVAL size)>
Initializes the array.
=cut
*/
VTABLE void init_int(INTVAL size) :manual_wb {
if (size < 0)
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"illegal argument");
SELF.set_integer_native(size);
}
/*
=item C<void destroy()>
Destroys the array.
=cut
*/
VTABLE void destroy() :no_wb {
PMC ** array = PMC_array(SELF);
if (LIKELY(array))
mem_gc_free(INTERP, array);
}
/*
=item C<PMC *clone()>
Creates and returns a copy of the array.
=cut
*/
VTABLE PMC *clone() :no_wb {
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() :no_wb {
const INTVAL size = SELF.elements();
return (INTVAL)(size != 0);
}
/*
=item C<INTVAL elements()>
=cut
*/
VTABLE INTVAL elements() :no_wb {
UNUSED(INTERP)
return PMC_size(SELF);
}
/*
=item C<INTVAL get_integer()>
Returns the number of elements in the array.
=cut
*/
VTABLE INTVAL get_integer() :no_wb {
return SELF.elements();
}
/*
=item C<FLOATVAL get_number()>
Returns the number of elements in the array, as FLOATVAL.
You rather want to use C<elements()> instead.
=cut
*/
VTABLE FLOATVAL get_number() :no_wb {
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() :no_wb {
return Parrot_str_from_int(INTERP, SELF.elements());
}
VTABLE STRING *get_repr() :no_wb {
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) :no_wb {
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) :no_wb {
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) :no_wb {
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) :no_wb {
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) :no_wb {
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) :no_wb {
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) :no_wb {
PMC **data;
INTVAL size;
GET_ATTR_size(INTERP, SELF, size);
if (key < 0)
key += size;
if (key < 0 || key >= PMC_size(SELF))
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"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) :no_wb {
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_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"Can't resize");
if (!size)
return;
if (size < 0)
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"illegal argument");
PMC_size(SELF) = size;
data = mem_gc_allocate_n_typed(INTERP, size, PMC *);
for (i = 0; i < size; ++i) /* TODO: zero */
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) :manual_wb {
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) :manual_wb {
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) :manual_wb {
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) :manual_wb {
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);
PARROT_GC_WRITE_BARRIER(INTERP, SELF);
}
}
/*
=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) :manual_wb {
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) :manual_wb {
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;
INTVAL size;
GET_ATTR_size(INTERP, SELF, size);
if (UNLIKELY(key < 0))
key += size;
if (UNLIKELY(key < 0 || key >= size))
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"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) :manual_wb {
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);
PARROT_GC_WRITE_BARRIER(INTERP, SELF);
}
}
/*
=item C<INTVAL is_equal(PMC *value)>
The C<==> operation. Compares two array to hold equal elements.
=cut
*/
VTABLE INTVAL is_equal(PMC *value) :no_wb {
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() :no_wb {
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.
Unlike with ResizablePMCArray, and invalid index throws an out of bounds
exception.
=cut
*/
VTABLE INTVAL exists_keyed_int(INTVAL key) :no_wb {
PMC **data;
if (UNLIKELY(key < 0 || key >= PMC_size(SELF)))
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"index out of bounds");
data = PMC_array(SELF);
return !PMC_IS_NULL(data[key]);
}
VTABLE INTVAL exists_keyed(PMC *key) :no_wb {
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 out of bounds 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) :manual_wb {
const INTVAL elems0 = VTABLE_elements(INTERP, SELF);
/* start from end? */
if (UNLIKELY(offset < 0))
offset += elems0;
if (UNLIKELY(count < 0))
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"illegal argument");
if (UNLIKELY(offset < 0 || count + offset > PMC_size(SELF)))
Parrot_ex_throw_from_c_noargs(INTERP, EXCEPTION_OUT_OF_BOUNDS,
"index out of bounds");
/* TODO: use faster memmove as in ResizablePMCArray */
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) :no_wb {
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) :no_wb {
VTABLE_push_integer(INTERP, info, VTABLE_elements(INTERP, SELF));
}
VTABLE void thaw(PMC *info) :manual_wb {
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.
Unlike with ResizablePMCArray, and invalid index throws an out of bounds
exception.
=cut
*/
VTABLE INTVAL defined_keyed_int(INTVAL key) :no_wb {
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() :no_wb {
PMC ** const data = PMC_array(SELF);
if (LIKELY(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' :
*/