Skip to content
This repository
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 241 lines (219 sloc) 8.355 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
#include "parrot/parrot.h"
#define self (*attrs)
#define FPQ_DECL_ATTRS(SELF) Parrot_FixedPMCQueue_attributes * const attrs = PARROT_FIXEDPMCQUEUE(SELF)
#define FPQ_IDX_ADD(x, offset) (((x) + (offset)) % self.size)
#define FPQ_IDX_DIFF(x, y) (((x) + self.size - (y)) % self.size)
#define FPQ_IDX_INCR(x) ((x) = FPQ_IDX_ADD((x), 1))
#define FPQ_IDXF_INCR(f) FPQ_IDX_INCR(self.f)
#define FPQ_ITEMS() (self.items)
#define FPQ_EMPTY() (self.items == 0)
#define FPQ_FULL() (self.items == self.size)
#define FPQ_ALLOCATE(len) mem_allocate_n_typed(len, PMC *)
#define FPQ_REALLOCATE(p, len) mem_realloc_n_typed(p, len, PMC *)
#define FPQ_ITERATE(index,func) { \
        if (self.items == self.size) { \
            INTVAL index; \
            for (index = 0; index < self.size; index++) { \
                func; \
            } \
        } else \
        if (self.queue_start > self.queue_end) { \
            INTVAL index; \
            for (index = self.queue_start; index < self.size; index++) { \
                func; \
            } \
            for (index = 0; index < self.queue_end; index++) { \
                func; \
            } \
        } else { \
            INTVAL index; \
            for (index = self.queue_start; index < self.queue_end; index++) { \
                func; \
            } \
        } \
    }

/*
 * FixedPMCQueue is a fixed-length FIFO queue structure. It is optimized for
 * push/shift access. FPQ will be implemented as a ring buffer.
 */
pmclass FixedPMCQueue dynpmc auto_attrs provides queue {
    ATTR INTVAL size;
    ATTR PMC **storage;
    ATTR INTVAL queue_start;
    ATTR INTVAL queue_end;
    ATTR INTVAL items;

    /* Initialize the PMC */
    VTABLE void init() {
        PObj_custom_mark_destroy_SETALL(SELF);
        /* All fields in attrs should be null/0, so no worries about
           initialization here. */
    }

    /* Destroy the PMC. Free allocated storage */
    VTABLE void destroy() {
        FPQ_DECL_ATTRS(SELF);
        if (self.storage)
           mem_sys_free(self.storage);
    }

    /* Mark the PMC for GC. */
    VTABLE void mark() {
        FPQ_DECL_ATTRS(SELF);
        PMC ** const s = self.storage;
        if (s)
            FPQ_ITERATE(i, Parrot_gc_mark_PMC_alive(INTERP, s[i]));
    }

    VTABLE void freeze(PMC *info) {
    }

    VTABLE void thaw(PMC *info) {
    }

    VTABLE void visit(PMC *info) {
        FPQ_DECL_ATTRS(SELF);
        PMC ** const storage = self.storage;
        if (storage)
            FPQ_ITERATE(i, VISIT_PMC(INTERP, info, storage[i]));
    }

    /* Size the queue. Cannot shrink the queue smaller than the number of items
       currently in the queue. */
    VTABLE void set_integer_native(INTVAL newsize) {
        FPQ_DECL_ATTRS(SELF);
        const INTVAL start = self.queue_start;
        const INTVAL end = self.queue_end;
        const INTVAL size = self.size;
        const INTVAL items = FPQ_ITEMS();

        if (newsize < items)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "FixedPMCQueue: Cannot shrink size with items in queue");
        if (newsize != size) {
            if (start == 0) {
                FPQ_REALLOCATE(self.storage, newsize);
                self.size = newsize;
            } else {
                PMC ** const oldstorage = self.storage;
                PMC ** newstorage = FPQ_ALLOCATE(newsize);
                if ( start > end ) {
                    mem_sys_memmove( &newstorage[0], &oldstorage[start], (size - start) * sizeof(PMC*));
                    mem_sys_memmove( &newstorage[size - start], &oldstorage[0], end * sizeof(PMC*));
                } else {
                    mem_sys_memmove( &newstorage[0], &oldstorage[start], (end - start) * sizeof(PMC*));
                }
                if (oldstorage)
                    mem_sys_free(oldstorage);
                self.storage = newstorage;
                self.size = newsize;
                self.queue_start = 0;
                self.queue_end = items;
            }
        }
    }

    /* Push an item onto the queue */
    VTABLE void push_pmc(PMC *item) {
        FPQ_DECL_ATTRS(SELF);

        if (FPQ_FULL())
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    "FixedPMCQueue: Pushed too many items");
        self.storage[self.queue_end] = item;
        FPQ_IDXF_INCR(queue_end);
        FPQ_ITEMS()++;
    }

    /* Pull an item off the queue */
    VTABLE PMC* shift_pmc() {
        FPQ_DECL_ATTRS(SELF);
        PMC * first_item;

        if (FPQ_EMPTY())
            return PMCNULL;
        first_item = self.storage[self.queue_start];
        FPQ_ITEMS()--;
        FPQ_IDXF_INCR(queue_start);
        return first_item;
    }

    /* Is the queue non-empty? */
    VTABLE INTVAL get_bool() {
        FPQ_DECL_ATTRS(SELF);
        return !FPQ_EMPTY();
    }

    /* Number of items in the queue */
    VTABLE INTVAL elements() {
        FPQ_DECL_ATTRS(SELF);
        return FPQ_ITEMS();
    }
    VTABLE INTVAL get_integer() {
        FPQ_DECL_ATTRS(SELF);
        return FPQ_ITEMS();
    }

    VTABLE INTVAL isnull() {
        FPQ_DECL_ATTRS(SELF);
        return self.size <= 0;
    }

    /* Total amount of allocated storage slots in the queue */
    METHOD capacity() {
        FPQ_DECL_ATTRS(SELF);
        PMC * result;
        if (self.size <= 0)
            result = PMCNULL;
        else {
            result = Parrot_pmc_new(INTERP,
                                    Parrot_get_ctx_HLL_type(INTERP,
                                                            enum_class_Integer));
            VTABLE_set_integer_native(INTERP, result, self.size);
        }
        RETURN(PMC* result);
    }

    /* Convert the queue to a FixedPMCArray. The array will have the same
       length as the queue, all additional spaces will contain PMCNULL */
    METHOD to_array() {
        FPQ_DECL_ATTRS(SELF);
        PMC * newarray = Parrot_pmc_new(INTERP, enum_class_FixedPMCArray);
        FPQ_ATTRS_CONST(attrs) = FPQ_GET_ATTRS(SELF);
        const INTVAL size = attrs->size;
        const INTVAL items = attrs->items;
        const INTVAL start = attrs->queue_start;
        const INTVAL end = attrs->queue_end;
        PMC ** const s = attrs->storage;
        INTVAL i = 0, ptr = start;
        /* TODO: A little bit of an issue here. set_integer_native on FPA
                 allocates storage, but it's that number that is returned later
                 on FPA.get_integer, so the results are off. For a real faithful
                 representation, we should return the same memory layout as we
                 have here (including unpopulated null areas), but for now we
                 need to truncate. When Parrot's core array types become sane,
                 fix this. */
        VTABLE_set_integer_native(INTERP, newarray, items);
        while (ptr != end) {
            VTABLE_set_pmc_keyed_int(INTERP, newarray, i, s[ptr]);
            i++;
            ptr = FPQ_NEXT_IDX(ptr, size);
        }
        RETURN(PMC* newarray);
    }

    /* Get the total size of the queue in memory, in bytes, including the PMC
       structure allocation size */
    METHOD total_mem_size() {
        FPQ_DECL_ATTRS(SELF);
        INTVAL const storage_size = self.size * sizeof(PMC *);
        INTVAL const struct_size = sizeof(PMC) + sizeof(Parrot_FixedPMCQueue_attributes);
        INTVAL const total_size = storage_size + struct_size;
        RETURN(INTVAL total_size);
    }

    /* Clear the queue, optionally resizing it */
    METHOD clear(INTVAL newsize :optional, INTVAL has_size :opt_flag) {
        FPQ_DECL_ATTRS(SELF);
        INTVAL const size = self.size;
        if (has_size) {
            if (newsize <= 0)
                Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    "FixedPMCQueue: Cannot set to zero or negative size");
            if (self.storage)
                mem_sys_free(self.storage);
            self.storage = FPQ_ALLOCATE(newsize);
            self.size = newsize;
        }
        self.queue_start = 0;
        self.queue_end = 0;
        FPQ_ITEMS() = 0;
    }
}

/*
 * Local variables:
 * c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
Something went wrong with that request. Please try again.