# New Ticket Created by   
# Please include the string:  [perl #41874]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=41874 >


Attached is an extended implementation of the mmap io layer. It adds the
write and seek funcitons to the layer.

It can be tested with the attached pir programm. It needs also a little
modification in io_unix.c, this comes in a separate bug report.
Index: src/io/io_mmap.c
===================================================================
--- src/io/io_mmap.c	(Revision 17524)
+++ src/io/io_mmap.c	(Arbeitskopie)
@@ -44,6 +44,36 @@
 
 /*
 
+=item C<static INTVAL
+flags_to_mmap(INTVAL flags)>
+
+Returns a UNIX-specific interpretation of C<flags> suitable for passing
+to C<mmap()> in C<PIO_mmap_open()> and C<PIO_mmap_seek()> respectively.
+
+=cut
+
+*/
+
+static INTVAL
+flags_to_mmap(INTVAL flags)
+{
+    INTVAL oflags;
+    oflags = 0;
+    if ((flags & (PIO_F_WRITE | PIO_F_READ)) == (PIO_F_WRITE | PIO_F_READ)) {
+        oflags |= PROT_READ | PROT_WRITE;
+    }
+    else if (flags & PIO_F_WRITE) {
+        oflags |= PROT_WRITE;
+    }
+    else if (flags & PIO_F_READ) {
+        oflags |= PROT_READ;
+    }
+
+    return oflags;
+}
+
+/*
+
 =item C<static ParrotIO *
 PIO_mmap_open(Interp *interp, ParrotIOLayer *layer,
                const char *path, INTVAL flags)>
@@ -60,34 +90,51 @@
 {
     ParrotIO *io;
     ParrotIOLayer *l = PIO_DOWNLAYER(layer);
+    INTVAL oflags;
 
     if (!l) {
         l = interp->piodata->default_stack;
         if (!strcmp(l->name, "buf"))
             l = PIO_DOWNLAYER(l);
     }
+
+    if (flags & PIO_F_WRITE) 
+        flags |= PIO_F_READ; /* can't mmap PROT_WRITE and PROT_SHRAED if not */ 
+
     io = PIO_open_down(interp, l, path, flags);
     if (!io) {
         /* error creating IO stream */
         return NULL;
     }
-#ifdef PARROT_HAS_HEADER_SYSMMAN
-    {
-        /* XXX assume fstat exists too
-        */
-        struct stat statbuf;
-        int status;
-        PIOOFF_T file_size;
 
-        status = fstat(io->fd, &statbuf);
-        file_size = statbuf.st_size;
-        /* TODO verify flags */
-        io->b.startb = mmap(0, file_size, PROT_READ, MAP_SHARED, io->fd, 0);
-        io->b.size = (size_t)file_size;  /* XXX */
-        io->b.endb = io->b.startb + io->b.size;
-        io->b.flags |= PIO_BF_MMAP;
+    /* XXX assume fstat exists too */
+    struct stat statbuf;
+    int status;
+
+    status = fstat(io->fd, &statbuf);
+
+    io->fpos = io->lpos = 0;
+    io->fsize = statbuf.st_size;
+
+    oflags = flags_to_mmap(flags);
+    io->b.startb = mmap(0, io->fsize, oflags & (PROT_WRITE | PROT_READ), MAP_SHARED, io->fd, 0);
+
+    if ( MAP_FAILED == io->b.startb ) { 
+        if (errno == EINVAL)
+            io->b.size = 0;
+        else {
+            PIO_close_down(interp, l, io);
+            return NULL;
+        }
     }
-#endif
+    else 
+        io->b.size = io->fsize;
+    
+
+    io->b.next = io->b.startb;
+    io->b.endb = io->b.startb + io->b.size;
+    io->b.flags |= PIO_BF_MMAP;
+
     return io;
 }
 
@@ -114,9 +161,9 @@
     if (!(io->b.flags & PIO_BF_MMAP))
         return PIO_read_down(interp, PIO_DOWNLAYER(layer), io, buf);
 
-    if (*buf == NULL) {
+    if (*buf == NULL)
         *buf = new_string_header(interp, 0);
-    }
+    
     s = *buf;
     /* TODO create string_free API for reusing string headers */
     if (s->strstart && PObj_sysmem_TEST(s))
@@ -124,6 +171,7 @@
     PObj_get_FLAGS(s) |= PObj_external_FLAG;
     PObj_bufstart(s) = s->strstart = io->b.startb + io->fpos;
     len = s->bufused ? s->bufused : io->b.size;
+    io->lpos = io->fpos;
     io->fpos += len;
     PObj_buflen(s) = s->strlen = len;
     return len;
@@ -131,7 +179,128 @@
 
 /*
 
+=item C<static PIOOFF_T
+PIO_mmap_seek(Interp *interp, ParrotIOLayer *l, ParrotIO *io,
+               PIOOFF_T offset, INTVAL whence)>
+
+The mmap layer's C<Seek> function.
+
+=cut
+
+*/
+
+static PIOOFF_T
+PIO_mmap_seek(Interp *interp, ParrotIOLayer *l, ParrotIO *io,
+               PIOOFF_T offset, INTVAL whence)
+{
+    PIOOFF_T newpos;
+
+    switch (whence) {
+        case SEEK_SET:
+          newpos = offset;
+          break;
+        case SEEK_CUR:
+          newpos = io->b.next - io->b.startb + offset;
+          break;
+        case SEEK_END:
+          newpos = io->b.endb - io->b.startb; 
+          break;
+        default:
+          /* XXX: somehow report the illegal whence value */
+          return -1;
+    }
+
+    if (PIO_seek_down(interp, PIO_DOWNLAYER(l), io, newpos, whence) == -1) { 
+        return -1;
+    }
+
+    if (newpos <= io->b.size)
+        io->b.next = io->b.startb + newpos;
+    else {
+        if ( io->b.size > 0 ) {
+            if ( -1 == munmap(io->b.startb, io->b.size) ) 
+                return -1;
+        }
+
+
+        if (!write(io->fd, "", 1))
+            fprintf(stderr, "write error\n");
+        else {
+            INTVAL oflags;
+            oflags = flags_to_mmap(io->flags);
+            io->b.startb = mmap(0, io->fsize, oflags & (PROT_WRITE | PROT_READ), MAP_SHARED, 
+                                   io->fd, 0);
+
+            if ( MAP_FAILED == io->b.startb)
+                return -1;
+            
+            io->b.endb = io->b.startb + io->fsize;
+            io->b.next = io->b.startb + newpos;
+            io->b.size = io->fsize;
+        }
+    }
+
+    return io->b.next - io->b.startb;
+}
+
+static size_t
+PIO_mmap_write(Interp *interp, ParrotIOLayer *l, ParrotIO *io, STRING *s)
+{
+    long avail;
+    void * const buffer = s->strstart;
+    size_t len = s->bufused;
+
+    if (len <= 0) return 0;
+
+    avail = io->b.endb - io->b.next + len;
+
+    if (avail < 0) {
+        if (PIO_mmap_seek(interp, l, io, -avail, SEEK_CUR) == -1) 
+            return -1;
+        else if (!write(io->fd, "", 1)) {
+            /* is not required that lseek grows the file. Forcing */
+            return -1;
+        }
+        if ( munmap(io->b.startb, io->b.size) == -1)
+            return -1;
+        
+        if ( (io->b.startb = mmap(0, io->fsize, PROT_WRITE | PROT_READ, MAP_SHARED, io->fd, 0) ) 
+              == MAP_FAILED ) 
+            return -1;
+    }
+
+    memcpy(io->b.next, buffer, len);
+    io->b.next += len;
+
+    return (size_t)len;
+}
+
+
+/*
 =item C<static INTVAL
+PIO_mmap_flush(Interp *interp, ParrotIOLayer *layer, ParrotIO *io)>
+
+At lowest layer all we can do for C<flush> is to ask the kernel to
+C<sync()>.
+
+XXX: Is it necessary to C<sync()> here?
+
+=cut
+
+*/
+
+static INTVAL
+PIO_mmap_flush(Interp *interp, ParrotIOLayer *layer, ParrotIO *io)
+{
+    UNUSED(interp);
+    UNUSED(layer);
+
+    return msync(io->b.startb + io->fpos, 0, MS_SYNC); /* XXX: MS_ASYNC? */ 
+}
+
+/*
+
+=item C<static INTVAL
 PIO_mmap_close(Interp *interp, ParrotIOLayer *layer, ParrotIO *io)>
 
 Closes C<*io>'s file descriptor.
@@ -145,9 +314,9 @@
 {
     INTVAL ret = -1;
 
-    if (io->fd >= 0) {
+    if (io->fd >= 0) 
         ret = PIO_close_down(interp, PIO_DOWNLAYER(layer), io);
-    }
+
     return ret;
 }
 
@@ -163,13 +332,13 @@
     PIO_null_open_async,
     PIO_null_fdopen,
     PIO_mmap_close,
-    PIO_null_write,
+    PIO_mmap_write,
     PIO_null_write_async,
     PIO_mmap_read,
     PIO_null_read_async,
-    PIO_null_flush,
+    PIO_mmap_flush,
     PIO_null_peek,
-    PIO_null_seek,
+    PIO_mmap_seek,
     PIO_null_tell,
     PIO_null_setbuf,
     PIO_null_setlinebuf,
Index: src/io/io.c
===================================================================
--- src/io/io.c	(Revision 17524)
+++ src/io/io.c	(Arbeitskopie)
@@ -15,7 +15,7 @@
 
 This file implements the generic functionality. Specific layers are in
 separate files: F<src/io/io_buf.c>, F<src/io/io_stdio.c>, F<src/io/io_unix.c>,
-F<src/io/io_win32.c>, and F<src/io/io_layers.c>.
+F<src/io/io_win32.c>, F<src/io/io_mmap.c> and F<src/io/io_layers.c>.
 
 The C<ParrotIO> PMC provides the class-based interface that is used in
 Parrot ops. The C<ParrotIO struct> is defined in F<src/io/io_private.h>.
 
# Copyright (C) 2001-2006, The Perl Foundation.
# $Id: io.pir 12835 2006-05-30 13:32:26Z coke $

=head1 NAME

examples/pir/io.pir - IO Example

=head1 SYNOPSIS

    % ./parrot examples/pir/io.pir

=head1 DESCRIPTION

Simple open/seek/write/close on a file. After the file is written it is read in 
again
and printed to STDOUT.
You should check where the file is going to be before you run this.

=cut

.sub 'example' :main
    .local string test_fn 
    test_fn = "tmp_example_io.tmp"
    
    .local pmc pio, cl
    cl = getclass "ParrotIO"
    # slurp the file into memory
    pio = cl."open"(test_fn, ">", "mmap")

#    P0 = open test_fn, ">", "mmap"
    seek pio, 300, 0
    # 64bit version of seek with high 32bits = 0
    #seek IO, P0, 0, 400, 0
    print pio, "test3\n"
    print pio, "test4\n"
    print pio, "test5\n"
    seek pio, 0, 0
    print pio, "test1\n"
    print pio, "test2\n"
    close pio

    pio = cl."open"(test_fn, "<", "mmap")
    S0 = read pio, 1024 
    print S0

    # now clean up after ourselves.
    P1 = new "OS"
    P1."rm"(test_fn)

.end

=head1 SEE ALSO

F<examples/io>.

=cut

Reply via email to