# New Ticket Created by Steve Fink # Please include the string: [perl #17039] # in the subject line of all future correspondence about this issue. # <URL: http://rt.perl.org/rt2/Ticket/Display.html?id=17039 >
Here is the new PMC I keep babbling about. Before I commit it, any comments? Like, does anybody think this should be named differently? It's really a dequeue (double-ended queue), meaning that push, pop, shift, and unshift are all constant-time operations, but arbitrary indexed lookup is theoretically linear-time. (It's linear in the number of 256-element chunks, so in practice it's usually fast.) It is implemented as a modification of rxstack.c. It is still a circular, doubly-linked list of chunks containing a fairly large array of integers, only now the first chunk can be partial as well as the last, in order to efficiently support shift/unshift. The length is cached in the first node. Also, these chunks are now Buffer subclasses, so they are completely managed by GC (rxstack uses mem_sys_allocate). -- attachment 1 ------------------------------------------------------ url: http://rt.perl.org/rt2/attach/36692/29583/f4c51f/intarray.patch
Index: MANIFEST =================================================================== RCS file: /cvs/public/parrot/MANIFEST,v retrieving revision 1.212 diff -p -u -a -r1.212 MANIFEST --- MANIFEST 5 Sep 2002 17:56:58 -0000 1.212 +++ MANIFEST 5 Sep 2002 19:17:21 -0000 @@ -23,6 +23,7 @@ classes/coroutine.pmc classes/csub.pmc classes/default.pmc classes/genclass.pl +classes/intarray.pmc classes/intqueue.pmc classes/key.pmc classes/multiarray.pmc @@ -206,6 +207,7 @@ include/parrot/exceptions.h include/parrot/global_setup.h include/parrot/hash.h include/parrot/headers.h +include/parrot/intarray.h include/parrot/interp_guts.h include/parrot/interpreter.h include/parrot/io.h @@ -235,6 +237,7 @@ include/parrot/sub.h include/parrot/trace.h include/parrot/unicode.h include/parrot/warnings.h +intarray.c interpreter.c io.ops io/TODO @@ -590,12 +593,14 @@ t/op/time.t t/op/trans.t t/pmc/array.t t/pmc/boolean.t +t/pmc/intarray.t t/pmc/perlarray.t t/pmc/perlhash.t t/pmc/perlstring.t t/pmc/pmc.t t/pmc/sub.t t/src/basic.t +t/src/intarray.t test_main.c tools/dev/check_source_standards.pl tools/dev/genrpt.pl Index: core.ops =================================================================== RCS file: /cvs/public/parrot/core.ops,v retrieving revision 1.206 diff -p -u -a -r1.206 core.ops --- core.ops 5 Sep 2002 15:03:23 -0000 1.206 +++ core.ops 5 Sep 2002 19:17:29 -0000 @@ -3428,6 +3428,146 @@ inline op restoreall() { ############################################################################### +=head2 Fast access ops + +The fast access ops are shortcuts to common operations implemented in PMCs. + +=over 4 + +=cut + +######################################## + +=item B<push>(in PMC, in INT) + +=item B<push>(in PMC, in NUM) + +=item B<push>(in PMC, in STR) + +=item B<push>(in PMC, in PMC) + +Push $2 onto the end of the aggregate PMC $1, if that operation is defined. + +=cut + +inline op push (in PMC, in INT) { + $1->vtable->push_integer(interpreter, $1, $2); + goto NEXT(); +} + +inline op push (in PMC, in NUM) { + $1->vtable->push_float(interpreter, $1, $2); + goto NEXT(); +} + +inline op push (in PMC, in STR) { + $1->vtable->push_string(interpreter, $1, $2); + goto NEXT(); +} + +inline op push (in PMC, in PMC) { + $1->vtable->push_pmc(interpreter, $1, $2); + goto NEXT(); +} + +=item B<pop>(out INT, in PMC) + +=item B<pop>(out NUM, in PMC) + +=item B<pop>(out STR, in PMC) + +=item B<pop>(out PMC, in PMC) + +Pop off last entry in the aggregate $2, placing the result in $1. + +=cut + +inline op pop (out INT, in PMC) { + $1 = $2->vtable->pop_integer(interpreter, $2); + goto NEXT(); +} + +inline op pop (out NUM, in PMC) { + $1 = $2->vtable->pop_float(interpreter, $2); + goto NEXT(); +} + +inline op pop (out STR, in PMC) { + $1 = $2->vtable->pop_string(interpreter, $2); + goto NEXT(); +} + +inline op pop (out PMC, in PMC) { + $1 = $2->vtable->pop_pmc(interpreter, $2); + goto NEXT(); +} + +=item B<unshift>(in PMC, in INT) + +=item B<unshift>(in PMC, in NUM) + +=item B<unshift>(in PMC, in STR) + +=item B<unshift>(in PMC, in PMC) + +Unshift $2 onto the end of the aggregate PMC $1, if that operation is defined. + +=cut + +inline op unshift (in PMC, in INT) { + $1->vtable->unshift_integer(interpreter, $1, $2); + goto NEXT(); +} + +inline op unshift (in PMC, in NUM) { + $1->vtable->unshift_float(interpreter, $1, $2); + goto NEXT(); +} + +inline op unshift (in PMC, in STR) { + $1->vtable->unshift_string(interpreter, $1, $2); + goto NEXT(); +} + +inline op unshift (in PMC, in PMC) { + $1->vtable->unshift_pmc(interpreter, $1, $2); + goto NEXT(); +} + +=item B<shift>(out INT, in PMC) + +=item B<shift>(out NUM, in PMC) + +=item B<shift>(out STR, in PMC) + +=item B<shift>(out PMC, in PMC) + +Shift off last entry in the aggregate $2, placing the result in $1. + +=cut + +inline op shift (out INT, in PMC) { + $1 = $2->vtable->shift_integer(interpreter, $2); + goto NEXT(); +} + +inline op shift (out NUM, in PMC) { + $1 = $2->vtable->shift_float(interpreter, $2); + goto NEXT(); +} + +inline op shift (out STR, in PMC) { + $1 = $2->vtable->shift_string(interpreter, $2); + goto NEXT(); +} + +inline op shift (out PMC, in PMC) { + $1 = $2->vtable->shift_pmc(interpreter, $2); + goto NEXT(); +} + +############################################################################### + =head2 Control flow The control flow opcodes check conditions and manage program flow. Index: intarray.c =================================================================== RCS file: intarray.c diff -N intarray.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ intarray.c 5 Sep 2002 19:17:30 -0000 @@ -0,0 +1,361 @@ +/* intarrays.c + * Copyright: (When this is determined...it will go here) + * CVS Info + * $Id: rxstacks.c,v 1.8 2002/08/22 20:08:24 dan Exp $ + * Overview: + * Regex stack handling routines for Parrot + * Data Structure and Algorithms: + * + * The basic data structure is a variant of a doubly-linked list + * of 'chunks', where a chunk is a Buffer header subclass + * containing the link pointers and other metadata for the chunk. + * As expected from it being a Buffer header, the .bufstart field + * points to the actual array of INTVALs. The handle used by + * external code for one of these IntArrays is just a pointer to a + * chunk, always called 'array' in this code. + * + * For now, all of the chunks are fixed-length in size. (That + * could easily be changed, at the cost of another integer in each + * header.) + * + * Notice that I said 'variant' of a doubly-linked list. That is + * because if you start at 'array' and follow prev pointers, you + * will loop through all the used nodes of the list, as usual. But + * if you follow next pointers instead, you might find a spare + * node hanging off the last node in the list (the last node is + * always array->prev, so if there is a spare node, it will be at + * array->prev->next. If no spare exists, then + * array->prev->next==array.) + * + * The first node in the list may be partly full; the intermediate + * nodes are always completely full; and the last node may be + * partly full. Each node has a .start field, giving the offset of + * the first valid element (always zero except for possibly the + * first node), and a .end field, giving one past the offset of + * the last valid element (always equal to INTARRAY_CHUNK_SIZE + * except for possibly the last node). + * + * To make it concrete, let's walk through some sample operations. + * To push onto the end of the array, first find the last chunk: + * array->prev. Then if chunk->end < INTARRAY_CHUNK_SIZE, there is + * space to fit another element and so just stick it in. If not, + * we must add a chunk to the end of the list. If there is a + * spare, just link it fully into the list (forming a conventional + * doubly-linked list). Otherwise, create a new chunk and link it + * fully into the list. Easy enough. + * + * To pop something off the end, first go to the end chunk + * (array->prev). Pop off an element and decrement .end if the + * chunk is nonempty. If it is empty, make that last chunk into + * the spare (discarding the previous spare). Then go to the + * previous chunk, which is guaranteed to have .end set to + * INTARRAY_CHUNK_SIZE, and return data[.end--]. + * + * The length of the array is always cached in the overall header + * chunk. If an operation changes which chunk is the header (i.e., + * shift or unshift), then the length is copied to the new header. + * + * Invariants: + * + * There is always space in array->prev to insert an element. + * + * The 'array' chunk is never empty unless the entire list is + * empty. + * + * In combination, the above invariants imply that the various + * operations are implemented as: + * + * push: write element, push a new chunk if necessary + * pop: check to see if we have to back up a chunk, read element + * shift: read element, discard chunk and advance if necessary + * unshift: unshift a chunk if necessary, write element + * + * History: + * Notes: + * References: */ + +#include "parrot/parrot.h" +#include "parrot/intarray.h" + +IntArray* +intarray_new(Interp *interpreter) +{ + IntArray* array; + + interpreter->DOD_block_level++; + array = (IntArray *) new_bufferlike_header(interpreter, sizeof(*array)); + array->start = 0; + array->end = 0; + array->length = 0; + array->next = array; + array->prev = array; + array->buffer.bufstart = NULL; + interpreter->GC_block_level++; + Parrot_allocate(interpreter, (Buffer*) array, + INTARRAY_CHUNK_SIZE * sizeof(INTVAL)); + interpreter->DOD_block_level--; + interpreter->GC_block_level--; + return array; +} + +PMC* +intarray_mark(Interp* interpreter, IntArray* array, PMC* last) +{ + IntArray_Chunk* chunk = (IntArray_Chunk*) array; + do { + buffer_lives((Buffer *) chunk); + chunk = chunk->next; + } while (chunk != (IntArray_Chunk*) array); + + return last; +} + +void +intarray_dump(FILE* fp, IntArray* array, int verbose) +{ + IntArray_Chunk* chunk = (IntArray_Chunk*) array; + IntArray_Chunk* lastChunk = array->prev; + if (fp == NULL) fp = stderr; /* Useful for calling from gdb */ + + if (verbose) fprintf(fp, "LIST[%d]: ", (int) chunk->length); + + while (1) { + int i; + + if (verbose) + fprintf(fp, "[%d..%d] ", (int) chunk->start, (int) chunk->end-1); + + for (i = chunk->start; i < chunk->end; i++) { + INTVAL* entries = (INTVAL*) chunk->buffer.bufstart; + fprintf(fp, INTVAL_FMT " ", entries[i]); + } + if (chunk == lastChunk) break; + chunk = chunk->next; + } + + fprintf(fp, "\n"); +} + +static void +add_chunk(Interp* interpreter, IntArray* array) +{ + IntArray_Chunk* chunk = array->prev; + + if (chunk->next == array) { + /* Need to add a new chunk */ + IntArray_Chunk* new_chunk = intarray_new(interpreter); + new_chunk->next = array; + new_chunk->prev = chunk; + chunk->next = new_chunk; + array->prev = new_chunk; + } + else { + /* Reuse the spare chunk we kept */ + array->prev = chunk->next; + } +} + +static void +push_chunk(Interp* interpreter, IntArray* array) +{ + add_chunk(interpreter, array); + array->prev->start = 0; + array->prev->end = 0; +} + +static void +unshift_chunk(Interp* interpreter, IntArray* array) +{ + add_chunk(interpreter, array); + array->prev->start = INTARRAY_CHUNK_SIZE; + array->prev->end = INTARRAY_CHUNK_SIZE; +} + +void +intarray_push(Interp *interpreter, IntArray* array, INTVAL data) +{ + IntArray_Chunk* chunk = (IntArray *) array->prev; + INTVAL length = array->length + 1; + + ((INTVAL*)chunk->buffer.bufstart)[chunk->end++] = data; + + /* Add on a new chunk if necessary */ + if (chunk->end == INTARRAY_CHUNK_SIZE) + push_chunk(interpreter, array); + + array->length = length; +} + +void +intarray_unshift(Interp *interpreter, IntArray** array, INTVAL data) +{ + IntArray_Chunk* chunk = (IntArray_Chunk *) *array; + INTVAL length = chunk->length + 1; + INTVAL offset; + + /* Add on a new chunk if necessary */ + if (chunk->start == 0) { + unshift_chunk(interpreter, *array); + chunk = chunk->prev; + *array = chunk; + } + + ((INTVAL*)chunk->buffer.bufstart)[--chunk->start] = data; + + (*array)->length = length; +} + +INTVAL +intarray_shift(Interp *interpreter, IntArray** array) +{ + IntArray_Chunk* chunk = (IntArray_Chunk *) *array; + INTVAL length = chunk->length - 1; + INTVAL value; + + if (chunk->start >= chunk->end) { + internal_exception(OUT_OF_BOUNDS, "No entries on array!\n"); + return 0; + } + + value = ((INTVAL*)chunk->buffer.bufstart)[chunk->start++]; + + if (chunk->start >= chunk->end) { + /* Just walked off the end of the initial chunk. Make initial + * chunk into the spare. */ + chunk->next->prev = chunk->prev; + chunk->prev->next = chunk; + *array = chunk->next; + } + + (*array)->length = length; + + return value; +} + +INTVAL +intarray_pop(Interp *interpreter, IntArray* array) +{ + IntArray_Chunk* chunk = (IntArray *) array->prev; + INTVAL length = array->length - 1; + + /* We may have an empty chunk at the end of the list */ + if (chunk->start >= chunk->end) { + /* Discard this chunk by making it the spare. */ + chunk->prev->next = chunk; + chunk->next = array; + array->prev = chunk->prev; + chunk = chunk->prev; + } + + /* Quick sanity check */ + if (chunk->end == chunk->start) { + internal_exception(OUT_OF_BOUNDS, "No entries on array!\n"); + return 0; + } + + array->length = length; + + /* Decrement end and return the value */ + return ((INTVAL*)chunk->buffer.bufstart)[--chunk->end]; +} + +static IntArray_Chunk* +find_chunk(Interp* interpreter, IntArray* array, INTVAL idx) +{ + IntArray_Chunk* chunk = array; + UNUSED(interpreter); + + /* Possible optimization: start from the closer end of the chunk list */ + + /* Find the chunk containing the requested element */ + while (idx >= chunk->end - chunk->start) { + idx -= chunk->end - chunk->start; + chunk = chunk->next; + } + + return chunk; +} + +INTVAL +intarray_get(Interp* interpreter, IntArray* array, INTVAL idx) +{ + IntArray_Chunk* chunk; + INTVAL length = array->length; + + if (idx >= length || -idx > length) { + internal_exception(OUT_OF_BOUNDS, + "Invalid index, must be " INTVAL_FMT ".." INTVAL_FMT, + -length, length-1); + return 0; + } + + if (idx < 0) idx += length; + + chunk = find_chunk(interpreter, array, idx); + + if (idx >= array->end - array->start) idx -= array->end - array->start; + idx = idx % INTARRAY_CHUNK_SIZE; + + return ((INTVAL*)chunk->buffer.bufstart)[idx + chunk->start]; +} + +static void +intarray_extend(Interp* interpreter, IntArray* array, INTVAL length) +{ + IntArray_Chunk* chunk = array->prev; + INTVAL to_add = length - array->length; + + while (to_add > 0) { + INTVAL available = INTARRAY_CHUNK_SIZE - chunk->end; + INTVAL end; + + /* Zero out all newly added elements */ + end = (to_add <= available) ? chunk->end + to_add : INTARRAY_CHUNK_SIZE; + memset(&((INTVAL*)chunk->buffer.bufstart)[chunk->end], + 0, + sizeof(INTVAL) * (end - chunk->end)); + to_add -= end - chunk->end; + chunk->end = end; + + if (to_add > 0) push_chunk(interpreter, array); + + chunk = chunk->next; + } + + assert(length >= array->length); + array->length = length; +} + +void +intarray_assign(Interp* interpreter, IntArray* array, INTVAL idx, INTVAL val) +{ + IntArray_Chunk* chunk; + INTVAL length = array->length; + + if (idx < -length) { + internal_exception(OUT_OF_BOUNDS, + "Invalid index, must be " INTVAL_FMT ".." INTVAL_FMT, + -length, length-1); + return; + } + + if (idx < 0) + idx += length; + else if (idx >= length) { + intarray_extend(interpreter, array, idx + 1); + } + + chunk = find_chunk(interpreter, array, idx); + + ((INTVAL*)chunk->buffer.bufstart)[idx] = val; +} + +/* + * Local variables: + * c-indentation-style: bsd + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: +*/ Index: include/parrot/intarray.h =================================================================== RCS file: include/parrot/intarray.h diff -N include/parrot/intarray.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ include/parrot/intarray.h 5 Sep 2002 19:17:30 -0000 @@ -0,0 +1,71 @@ +/* stacks.h + * Copyright: (When this is determined...it will go here) + * CVS Info + * $Id: rxstacks.h,v 1.4 2002/08/22 20:08:59 dan Exp $ + * Overview: + * Integer array routines. Constant-time push, pop, length operations. + * Fast linear-time lookup (requires walking through the chunks). + * All memory is managed by the Parrot garbage collector. + * Data Structure and Algorithms: + * History: + * Notes: + * References: + */ + +#if !defined(PARROT_INTARRAY_H_GUARD) +#define PARROT_INTARRAY_H_GUARD + +#include "parrot/parrot.h" + +/* Number of entries (NOT bytes) in each chunk of the array */ +#define INTARRAY_CHUNK_SIZE 256 + +typedef struct IntArray_chunk_t IntArray_Chunk; + +struct IntArray_chunk_t { + Buffer buffer; /* This struct is a Buffer header subclass! */ + INTVAL length; /* Only valid for the "head" chunk */ + INTVAL start; + INTVAL end; + IntArray_Chunk* next; + IntArray_Chunk* prev; +}; + +typedef IntArray_Chunk IntArray; + +PMC* intarray_mark(Interp*, IntArray*, PMC* last); + +IntArray *intarray_new(Interp*); + +static INTVAL intarray_length(Interp* interpreter, IntArray* array) +{ + UNUSED(interpreter); + return array->length; +} + +void intarray_assign(Interp*, IntArray*, INTVAL idx, INTVAL val); + +void intarray_push(Interp*, IntArray*, INTVAL); + +INTVAL intarray_pop(Interp*, IntArray*); + +void intarray_unshift(Interp*, IntArray**, INTVAL); + +INTVAL intarray_shift(Interp *, IntArray**); + +INTVAL intarray_get(Interp*, IntArray*, INTVAL idx); + +/* Debugging use only */ +void intarray_dump(FILE* fp, IntArray* array, int verbose); + +#endif + +/* + * Local variables: + * c-indentation-style: bsd + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: +*/ Index: t/pmc/intarray.t =================================================================== RCS file: t/pmc/intarray.t diff -N t/pmc/intarray.t --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ t/pmc/intarray.t 5 Sep 2002 19:17:30 -0000 @@ -0,0 +1,147 @@ +#! perl -w + +use Parrot::Test tests => 2; +use Test::More; + +output_is(<<'CODE', <<'OUTPUT', "creation"); + new P0, .IntArray + set I0, P0 + print "Created IntArray with " + print I0 + print " elements to start with.\n" + end +CODE +Created IntArray with 0 elements to start with. +OUTPUT + +output_is(<<'CODE', <<'OUTPUT', "aerobics"); + new P0, .IntArray + set I10, 10000 + + set I1, 0 + set I0, 0 +buildup: + ge I0, I10, postBuildUp + push P0, I1 + add I1, 1 # Push P0, I1++ + push P0, I1 + add I1, 1 # Push P0, I1++ + push P0, I1 + add I1, 1 # Push P0, I1++ + + pop I2, P0 + mul I3, I0, 3 + add I3, 2 + ne I2, I3, errFirstPop # fail if pop != I0 * 3 + 2 + + pop I2, P0 + mul I3, I0, 3 + add I3, 1 + ne I2, I3, errSecondPop # fail if pop != I0 * 3 + 1 + + set I2, P0 + add I3, I0, 1 + ne I2, I3, errBuildLen # fail if length != I0 + 1 + + add I0, 1 + branch buildup +postBuildUp: + + set I0, 0 +checkBuildUpLeft: + ge I0, I10, postCheckBuildUpLeft + set I2, P0[I0] + mul I3, I0, 3 + ne I2, I3, errLeftGet + add I0, 1 + branch checkBuildUpLeft +postCheckBuildUpLeft: + + mul I0, I10, -1 +checkBuildUpRight: + ge I0, 0, postCheckBuildUpRight + set I2, P0[I0] + add I3, I0, I10 + mul I3, 3 + ne I2, I3, errRightGet + add I0, 1 + branch checkBuildUpRight +postCheckBuildUpRight: + + mul I0, I10, 30 + push P0, I0 + + set I0, I10 +tearDown: + le I0, 0, postTearDown + pop I2, P0 + mul I3, I0, 30 + ne I2, I3, errTearCap + + pop I2, P0 + sub I3, I0, 1 + mul I3, 3 + ne I2, I3, errTearInner + + mul I3, 10 + push P0, I3 + + set I2, P0 + ne I2, I0, errTearLength + + sub I0, 1 + branch tearDown +postTearDown: + + pop I2, P0 + ne I2, 0, errLast + + print "I need a shower.\n" + end +errFirstPop: + print "FAILED: first pop\n" + bsr info + end +errSecondPop: + print "FAILED: second pop\n" + bsr info + end +errBuildLen: + print "FAILED: buildup length\n" + bsr info + end +errLeftGet: + print "FAILED: left get\n" + bsr info + end +errRightGet: + print "FAILED: right get\n" + bsr info + end +errTearCap: + print "FAILED: tear down cap\n" + bsr info + end +errTearInner: + print "FAILED: tear down inner\n" + bsr info + end +errTearLength: + print "FAILED: tear down length\n" + bsr info + end +errLast: + print "FAILED: last element (zero)\n" + set I3, 0 + bsr info + end +info: + print "Found: " + print I2 + print "\nWanted: " + print I3 + print "\n" + ret +CODE +I need a shower. +OUTPUT Index: t/src/intarray.t =================================================================== RCS file: t/src/intarray.t diff -N t/src/intarray.t --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ t/src/intarray.t 5 Sep 2002 19:17:31 -0000 @@ -0,0 +1,296 @@ +#! perl -w + +use Parrot::Test tests => 4; + +c_output_is(<<'CODE', <<'OUTPUT', "creation"); + #include <stdio.h> + #include "parrot/parrot.h" + #include "parrot/embed.h" + + int main(int argc, char* argv[]) { + int x; + IntArray* array; + + Interp* interpreter = Parrot_new(); + if (interpreter == NULL) return 1; + Parrot_init(interpreter, (void*) &interpreter); + + array = intarray_new(interpreter); + if (array == NULL) return 1; + + intarray_push(interpreter, array, 42); + x = (int) intarray_get(interpreter, array, 0); + + printf("The answer is %d.\n", x); + return 0; + } +CODE +The answer is 42. +OUTPUT + +c_output_is(<<'CODE', <<'OUTPUT', "array aerobics"); + #include <stdio.h> + #include "parrot/parrot.h" + #include "parrot/embed.h" + + const char* aerobics() + { + int x = 0; + int N = 10000; /* Number of iterations */ + int i; + IntArray* array; + + Interp* interpreter = Parrot_new(); + if (interpreter == NULL) return "create interpreter"; + Parrot_init(interpreter, (void*) &interpreter); + + array = intarray_new(interpreter); + if (array == NULL) return "create array"; + + /* Push 3, then pop 2. Repeat N times. */ + for (i = 0; i < N; i++) { + intarray_push(interpreter, array, x++); + intarray_push(interpreter, array, x++); + intarray_push(interpreter, array, x++); + if (intarray_pop(interpreter, array) != i * 3 + 2) + return "build-up first pop"; + if (intarray_pop(interpreter, array) != i * 3 + 1) + return "build-up second pop"; + if (intarray_length(interpreter, array) != i + 1) + return "build-up length"; + } + + /* Check array_get for all values */ + for (i = 0; i < N; i++) { + if (intarray_get(interpreter, array, i) != i * 3) + return "get from left"; + } + + /* Check array_get for all values, from the right */ + for (i = -N; i < 0; i++) { + if (intarray_get(interpreter, array, i) != (i + N) * 3) + return "get from right"; + } + + /* Set up the receding run */ + intarray_push(interpreter, array, N * 3 * 10); + + /* Pop 2, then push 1. Repeat N times. */ + for (i = N; i > 0; i--) { + if (intarray_pop(interpreter, array) != i * 3 * 10) + return "tear down cap"; + if (intarray_pop(interpreter, array) != (i - 1) * 3) + return "tear down inner"; + intarray_push(interpreter, array, (i - 1) * 3 * 10); + if (intarray_length(interpreter, array) != i) + return "tear down length"; + } + + /* And the final element is... */ + if (intarray_pop(interpreter, array) != 0) + return "last survivor"; + + printf("I need a shower.\n"); + + return 0; + } + + int main(int argc, char* argv[]) { + const char* failure = aerobics(); + if (failure == NULL) return 0; + printf("Failed: %s\n", failure); + return 1; + } +CODE +I need a shower. +OUTPUT + +c_output_is(<<'CODE', <<'OUTPUT', "step aerobics"); + #include <stdio.h> + #include "parrot/parrot.h" + #include "parrot/embed.h" + + const char* aerobics(Interp* interpreter, IntArray* array, int ground) + { + int x = 0; + int N = 1000; /* Number of iterations */ + int i; + static char msg[2000]; + + /* Push 3, then pop 2. Repeat N times. */ + for (i = 0; i < N; i++) { + intarray_push(interpreter, array, x++); + intarray_push(interpreter, array, x++); + intarray_push(interpreter, array, x++); + if (intarray_pop(interpreter, array) != i * 3 + 2) + return "build-up first pop"; + if (intarray_pop(interpreter, array) != i * 3 + 1) + return "build-up second pop"; + if (intarray_length(interpreter, array)-ground != i + 1) + return "build-up length"; + } + + /* Check array_get for all values */ + for (i = 0; i < N; i++) { + if (intarray_get(interpreter, array, i+ground) != i * 3) { + sprintf(msg, "get from left: wanted %d, got %d", + i * 3, intarray_get(interpreter, array, i)); + return msg; + } + } + + /* Check array_get for all values, from the right */ + for (i = -N; i < 0; i++) { + if (intarray_get(interpreter, array, i) != (i + N) * 3) + return "get from right"; + } + + /* Set up the receding run */ + intarray_push(interpreter, array, N * 3 * 10); + + /* Pop 2, then push 1. Repeat N times. */ + for (i = N; i > 0; i--) { + if (intarray_pop(interpreter, array) != i * 3 * 10) + return "tear down cap"; + if (intarray_pop(interpreter, array) != (i - 1) * 3) + return "tear down inner"; + intarray_push(interpreter, array, (i - 1) * 3 * 10); + if (intarray_length(interpreter, array)-ground != i) + return "tear down length"; + } + + /* And the final element is... */ + if (intarray_pop(interpreter, array) != 0) + return "last survivor"; + } + + int main(int argc, char* argv[]) { + int i; + const char* failure; + + IntArray* array; + Interp* interpreter; + + interpreter = Parrot_new(); + if (interpreter == NULL) return 1; + Parrot_init(interpreter, (void*) &interpreter); + + array = intarray_new(interpreter); + if (array == NULL) return 1; + + printf("Step 1: 0\n"); + if ((failure = aerobics(interpreter, array, 0)) != NULL) { + printf("Failed: %s\n", failure); + return 1; + } + + printf("Step 2: 1\n"); + intarray_push(interpreter, array, 42); + if ((failure = aerobics(interpreter, array, 1)) != NULL) { + printf("Failed: %s\n", failure); + return 1; + } + + printf("Step 3: 2\n"); + intarray_unshift(interpreter, &array, -42); + if ((failure = aerobics(interpreter, array, 2)) != NULL) { + printf("Failed: %s\n", failure); + return 1; + } + + printf("Step 4: 255\n"); + intarray_assign(interpreter, array, 254, -1); + if ((failure = aerobics(interpreter, array, 255)) != NULL) { + printf("Failed: %s\n", failure); + return 1; + } + + printf("Step 5: 256\n"); + intarray_unshift(interpreter, &array, -3); + if ((failure = aerobics(interpreter, array, 256)) != NULL) { + printf("Failed: %s\n", failure); + return 1; + } + + printf("Step 6: 257\n"); + intarray_unshift(interpreter, &array, -2); + if ((failure = aerobics(interpreter, array, 257)) != NULL) { + printf("Failed: %s\n", failure); + return 1; + } + + printf("Done.\n"); + return 0; + } +CODE +Step 1: 0 +Step 2: 1 +Step 3: 2 +Step 4: 255 +Step 5: 256 +Step 6: 257 +Done. +OUTPUT + +c_output_is(<<'CODE', <<'OUTPUT', "yoyo"); + #include <stdio.h> + #include "parrot/parrot.h" + #include "parrot/embed.h" + + void yoyo(Interp* interpreter, IntArray** arrayP, int size) + { + int i; + int x = 0; + int distance = 1000; + + /* Set up the yoyo */ + for (i = 0; i < size; i++) { + intarray_push(interpreter, *arrayP, x++); + } + + /* Flick it out */ + for (i = 0; i < distance; i++) { + intarray_push(interpreter, *arrayP, x++); + if (intarray_get(interpreter, *arrayP, -1) != i + size) + printf("Out get failed on i=%d\n", i); + if (intarray_shift(interpreter, arrayP) != i) + printf("Out shift failed on i=%d\n", i); + } + + /* Suck it back */ + for (i = 0; i < distance + 10; i++) { + intarray_unshift(interpreter, arrayP, x++); + intarray_pop(interpreter, *arrayP); + } + + /* Clean up the yoyo */ + for (i = 0; i < size; i++) { + intarray_pop(interpreter, *arrayP); + } + } + + int main(int argc, char* argv[]) { + int i; + const char* failure; + + IntArray* array; + Interp* interpreter; + + interpreter = Parrot_new(); + if (interpreter == NULL) return 1; + Parrot_init(interpreter, (void*) &interpreter); + + array = intarray_new(interpreter); + if (array == NULL) return 1; + + for (i = 0; i < INTARRAY_CHUNK_SIZE * 2.5; i++) { + yoyo(interpreter, &array, i); + } + + printf("Done.\n"); + } +CODE +Done. +OUTPUT + +1; Index: config/gen/makefiles/root.in =================================================================== RCS file: /cvs/public/parrot/config/gen/makefiles/root.in,v retrieving revision 1.36 diff -p -u -a -r1.36 root.in --- config/gen/makefiles/root.in 5 Sep 2002 17:58:32 -0000 1.36 +++ config/gen/makefiles/root.in 5 Sep 2002 19:17:32 -0000 @@ -71,7 +71,7 @@ $(INC)/global_setup.h $(INC)/vtable.h $( $(INC)/oplib/core_ops_prederef.h $(INC)/runops_cores.h $(INC)/trace.h \ $(INC)/pmc.h $(INC)/key.h $(INC)/hash.h $(INC)/resources.h \ $(INC)/core_pmcs.h $(INC)/platform.h ${cg_h} \ -$(INC)/interp_guts.h $(INC)/rx.h $(INC)/rxstacks.h \ +$(INC)/interp_guts.h $(INC)/rx.h $(INC)/rxstacks.h $(INC)/intarray.h \ $(INC)/embed.h $(INC)/warnings.h $(INC)/misc.h $(INC)/pmc.h \ $(INC)/key.h $(INC)/hash.h $(INC)/smallobject.h $(INC)/headers.h $(INC)/dod.h \ $(INC)/method_util.h @@ -96,7 +96,7 @@ INTERP_O_FILES = exceptions$(O) global_s packfile$(O) stacks$(O) string$(O) sub$(O) encoding$(O) \ chartype$(O) runops_cores$(O) trace$(O) pmc$(O) key$(O) hash$(O) \ core_pmcs$(O) platform$(O) ${jit_o} \ - resources$(O) rx$(O) rxstacks$(O) \ + resources$(O) rx$(O) rxstacks$(O) intarray$(O) \ embed$(O) warnings$(O) misc$(O) ${cg_o} \ packout$(O) byteorder$(O) debug$(O) smallobject$(O) \ headers$(O) dod$(O) method_util$(O) @@ -326,6 +326,8 @@ pmc$(O) : $(GENERAL_H_FILES) hash$(O) : $(GENERAL_H_FILES) +intarray$(O) : $(GENERAL_H_FILES) + jit$(O) : $(GENERAL_H_FILES) ${jit_h} $(INC)/jit_emit.h jit_cpu$(O): $(GENERAL_H_FILES) ${jit_h} $(INC)/jit_emit.h @@ -343,6 +345,8 @@ resources$(O) : $(GENERAL_H_FILES) platform$(O) : $(GENERAL_H_FILES) core_pmcs$(O) : $(GENERAL_H_FILES) + +trace$(O) : $(GENERAL_H_FILES) debug$(O) : $(GENERAL_H_FILES) $(INC)/debug.h Index: classes/intarray.pmc =================================================================== RCS file: classes/intarray.pmc diff -N classes/intarray.pmc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ classes/intarray.pmc 5 Sep 2002 19:24:00 -0000 @@ -0,0 +1,173 @@ +/* intarray.pmc + * Copyright: (When this is determined...it will go here) + * CVS Info + * $Id: perlarray.pmc,v 1.43 2002/08/19 23:15:20 tom Exp $ + * Overview: + * These are the vtable functions for the IntArray class + * Data Structure and Algorithms: + * History: + * Notes: + * References: + */ + +#include "parrot/parrot.h" + +#define THROW_UNSUPPORTED internal_exception(INTERP_ERROR, "Operation not +supported\n") + +pmclass IntArray { + + INTVAL type () { + return enum_class_PerlArray; + } + + STRING* name() { + return whoami; + } + + PMC* clone () { + THROW_UNSUPPORTED; + return NULL; + } + + void init () { + SELF->data = intarray_new(INTERP); + SELF->cache.int_val = 0; + SELF->flags |= PMC_custom_mark_FLAG; + } + + /* The end_of_used_list parameter is passed into the mark_used + * function of the garbage collector. */ + PMC* mark (PMC *end_of_used_list) { + return intarray_mark(INTERP, (IntArray *) SELF->data, + end_of_used_list); + } + + void set_integer_keyed_int (INTVAL* key, INTVAL value) { + if (!key) return; + intarray_assign(INTERP, (IntArray*) SELF->data, *key, value); + }; + + void set_integer_keyed (PMC* key, INTVAL value) { + INTVAL ix; + + if (!key) return; + + ix = key_integer(INTERP, key); + intarray_assign(INTERP, (IntArray*) SELF->data, ix, value); + }; + + + void set_number_keyed_int (INTVAL* key, FLOATVAL value) { + INTVAL ix; + + if (!key) return; + + intarray_assign(INTERP, (IntArray*) SELF->data, *key, (INTVAL) value); + }; + + void set_number_keyed (PMC* key, FLOATVAL value) { + INTVAL ix; + + if (!key) return; + + ix = key_integer(INTERP, key); + intarray_assign(INTERP, (IntArray*) SELF->data, ix, (INTVAL) value); + }; + + void set_string_keyed_int (INTVAL* key, STRING* value) { + THROW_UNSUPPORTED; + }; + + void set_string_keyed (PMC* key, STRING* value) { + THROW_UNSUPPORTED; + }; + + void set_pmc_keyed_int (INTVAL* key, PMC* src, INTVAL* src_key) { + INTVAL ix; + INTVAL value; + + if (!key) return; + + value = src->vtable->get_integer_keyed_int(INTERP, src, src_key); + intarray_assign(INTERP, (IntArray*) SELF->data, *key, value); + }; + + void set_pmc_keyed (PMC* key, PMC* src, PMC* src_key) { + INTVAL ix; + INTVAL value; + + if (!key) return; + + ix = key_integer(INTERP, key); + + value = src->vtable->get_integer_keyed(INTERP, src, src_key); + intarray_assign(INTERP, (IntArray*) SELF->data, ix, value); + }; + + INTVAL get_integer () { + return intarray_length(INTERP, (IntArray*) SELF->data); + }; + + INTVAL get_integer_keyed_int (INTVAL* key) { + return intarray_get(INTERP, (IntArray*) SELF->data, *key); + }; + + INTVAL get_integer_keyed (PMC* key) { + INTVAL ix; + if (!key) return 0; + ix = key_integer(INTERP, key); + return intarray_get(INTERP, (IntArray*) SELF->data, ix); + } + + FLOATVAL get_number_keyed_int (INTVAL* key) { + THROW_UNSUPPORTED; + return 0.0; + } + + FLOATVAL get_number_keyed (PMC* key) { + THROW_UNSUPPORTED; + return 0.0; + } + + STRING* get_string_keyed_int (INTVAL* key) { + THROW_UNSUPPORTED; + return NULL; + } + + STRING* get_string_keyed (PMC* key) { + THROW_UNSUPPORTED; + return NULL; + } + + PMC* get_pmc_keyed_int (INTVAL* key) { + THROW_UNSUPPORTED; + return NULL; + } + + PMC* get_pmc_keyed (PMC* key) { + THROW_UNSUPPORTED; + return NULL; + } + + void push_integer (INTVAL value) { + intarray_push(INTERP, (IntArray*) SELF->data, value); + } + + void push_float (FLOATVAL value) { + intarray_push(INTERP, (IntArray*) SELF->data, (INTVAL) value); + } + + INTVAL pop_integer() { + return intarray_pop(INTERP, (IntArray*) SELF->data); + } + + INTVAL pop_integer_keyed(PMC* key) { + THROW_UNSUPPORTED; + return 0; + } + + INTVAL pop_integer_keyed_int(INTVAL* key) { + THROW_UNSUPPORTED; + return 0; + } +} Index: include/parrot/parrot.h =================================================================== RCS file: /cvs/public/parrot/include/parrot/parrot.h,v retrieving revision 1.48 diff -p -u -a -r1.48 parrot.h --- include/parrot/parrot.h 23 Aug 2002 13:46:21 -0000 1.48 +++ include/parrot/parrot.h 5 Sep 2002 19:27:09 -0000 @@ -174,6 +180,7 @@ typedef void (*funcptr_t)(void); #include "parrot/pmc.h" #include "parrot/events.h" #include "parrot/stacks.h" +#include "parrot/intarray.h" #include "parrot/smallobject.h" #include "parrot/headers.h" #include "parrot/dod.h"