PreviousUpNext

15.4.1123  src/lib/std/src/posix-1003.1b/posix-io.pkg

## posix-io.pkg

# Compiled by:
#     src/lib/std/src/standard-core.sublib

# Package for POSIX 1003.1 primitive I/O operations
# This is a subpackage of the POSIX 1003.1 based
# 'Posix' package
#
#     src/lib/std/src/posix-1003.1b/posix-1003-1b.pkg

# An alternate portable (cross-platform)
# I/O interface is defined and implemented in:
#
#     src/lib/std/src/winix/winix-io.api
#     src/lib/std/src/posix/winix-io.pkg

stipulate
    package bio =  winix_base_data_file_io_driver_for_posix;                    # winix_base_data_file_io_driver_for_posix      is from   src/lib/std/src/io/winix-base-data-file-io-driver-for-posix.pkg
    package tio =  winix_base_text_file_io_driver_for_posix;                    # winix_base_text_file_io_driver_for_posix      is from   src/lib/std/src/io/winix-base-text-file-io-driver-for-posix.pkg
    #
    package iox =  io_exceptions;                                               # io_exceptions                                 is from   src/lib/std/src/io/io-exceptions.pkg
    #
    package hu  =       host_unt_guts;                                          # host_unt_guts                                 is from   src/lib/std/src/bind-sysword-32.pkg
    package hi  =       host_int;                                               # host_int                                      is from   src/lib/std/src/posix-1003.1b/posix-prelude.pkg
    package int =            int_guts;                                          # int_guts                                      is from   src/lib/std/src/bind-int-32.pkg
    package pos =  file_position_guts;                                          # file_position_guts                            is from   src/lib/std/src/bind-position-31.pkg
    package ci  =  mythryl_callable_c_library_interface;                        # mythryl_callable_c_library_interface          is from   src/lib/std/src/unsafe/mythryl-callable-c-library-interface.pkg

    package rus =     vector_slice_of_one_byte_unts;                            #    vector_slice_of_one_byte_unts              is from   src/lib/std/src/vector-slice-of-one-byte-unts.pkg
    package wus =  rw_vector_slice_of_one_byte_unts;                            # rw_vector_slice_of_one_byte_unts              is from   src/lib/std/src/rw-vector-slice-of-one-byte-unts.pkg

    package ru  =     vector_of_one_byte_unts;                                  #    vector_of_one_byte_unts                    is from   src/lib/std/src/vector-of-one-byte-unts.pkg
    package wu  =  rw_vector_of_one_byte_unts;                                  # rw_vector_of_one_byte_unts                    is from   src/lib/std/src/rw-vector-of-one-byte-unts.pkg

    package rcs =     vector_slice_of_chars;                                    #    vector_slice_of_chars                      is from   src/lib/std/src/vector-slice-of-chars.pkg
    package wcs =  rw_vector_slice_of_chars;                                    # rw_vector_slice_of_chars                      is from   src/lib/std/src/rw-vector-slice-of-chars.pkg

    package wc  =  rw_vector_of_chars;                                          # rw_vector_of_chars                            is from   src/lib/std/src/rw-vector-of-chars.pkg

    package pf  =   posix_file;                                                 # posix_file                                    is from   src/lib/std/src/posix-1003.1b/posix-file.pkg
herein

    package posix_io {
        #
        stipulate
            package open_mode
                #
                : (weak)   api { Open_Mode = O_RDONLY | O_WRONLY | O_RDWR; }
                =
                pf;
        herein
            include  open_mode;
        end;

        Sy_Unt = hu::Unt;
        Sy_Int = hi::Int;

    #    my op | = hu::bitwise_or;
    #    my op & = hu::bitwise_and;

        fun cfun  fun_name
            =
            ci::find_c_function { lib_name => "posix_io", fun_name };

        osval =  cfun "osval":  String -> Sy_Int;                                       # osval         def in    src/c/lib/posix-io/osval.c

        w_osval = hu::from_int o osval;

        fun fail (fct, msg)
            =
            raise exception FAIL ("POSIX_IO." + fct + ": " + msg);

        File_Descriptor =  pf::File_Descriptor;

        Process_Id =  posix_process::Process_Id;

        make_pipe' =   cfun "pipe":   Void -> (Sy_Int, Sy_Int);                         # pipe          def in    src/c/lib/posix-io/pipe.c
        #
        fun make_pipe ()
            =
            {   (make_pipe' ()) ->   (ifd, ofd);
                #
                { infd  =>  pf::int_to_fd  ifd,
                  outfd =>  pf::int_to_fd  ofd
                };
            };

        dup' =   cfun "dup":   Sy_Int -> Sy_Int ;                                       # dup           def in    src/c/lib/posix-io/dup.c
        #
        fun dup fd
            =
            pf::int_to_fd (dup' (pf::fd_to_int fd));

        dup2' =   cfun "dup2":   (Sy_Int, Sy_Int) -> Void;                              # dup2          def in    src/c/lib/posix-io/dup2.c
        #
        fun dup2 { old, new }
            =
            dup2'(pf::fd_to_int old, pf::fd_to_int new);


        close'  =   cfun "close":   Sy_Int -> Void;                                     # close         def in    src/c/lib/posix-io/close.c
        #
        fun close fd
            =
            close' (pf::fd_to_int fd);


        read'    =  cfun "read":    (Int, Int) -> ru::Vector;                           # read          def in    src/c/lib/posix-io/read.c
        readbuf' =  cfun "readbuf": (Int, wu::Rw_Vector, Int, Int) -> Int;              # readbuf       def in    src/c/lib/posix-io/readbuf.c


        fun read_as_vector { file_descriptor, max_bytes_to_read }
            = 
            {   if  (max_bytes_to_read < 0)     raise exception SIZE;   fi;
                #
                read' (pf::fd_to_int file_descriptor, max_bytes_to_read);
            };


        fun read_into_buffer {  file_descriptor => fd,  buffer  }
            =
            {   (wus::base  buffer)
                    ->
                    (buf, i, len);

                readbuf'  (pf::fd_to_int fd,  buf,  len,  i);
            };


        # Oddly, we nowhere call   cfun "write"   ==   src/c/lib/posix-io/write.c    The file should be used or deleted.  XXX BUGGO FIXME

        stipulate
                                    #   fd   buffer         nbytes offset
                                    #       --   -------------  ------ ------
            write_ro_slice' =  cfun "writebuf":  (Int,    ru::Vector, Int,   Int   ) -> Int;            # writebuf      def in   src/c/lib/posix-io/writebuf.c
            write_rw_slice' =  cfun "writebuf":  (Int, wu::Rw_Vector, Int,   Int   ) -> Int;            # writebuf      def in   src/c/lib/posix-io/writebuf.c
        herein

            fun write_rw_vector (fd, asl)                                               # This fn is exported to clients.
                =
                {   (wus::base  asl)
                        ->
                        (buf, i, len);

                    write_rw_slice'  (pf::fd_to_int fd,  buf,  len,  i);                # Write (to fd) 'len' bytes starting at &buf[i], via an atomic C write() call.
                };

            fun write_vector (fd, vsl)                                                  # This fn is exported to clients.
                =
                {   (rus::base  vsl)
                        ->
                        (buf, i, len);

                    write_ro_slice'  (pf::fd_to_int fd,  buf,  len,  i);                # Write (to fd) 'len' bytes starting at &buf[i], via an atomic C write() call.
                };
        end;

        Whence = SEEK_SET | SEEK_CUR | SEEK_END;

        seek_set =  osval "SEEK_SET";
        seek_cur =  osval "SEEK_CUR";
        seek_end =  osval "SEEK_END";

        fun wh_to_unt SEEK_SET => seek_set;
            wh_to_unt SEEK_CUR => seek_cur;
            wh_to_unt SEEK_END => seek_end;
        end;

        fun wh_from_unt  wh
            =
            if   (wh == seek_set ) SEEK_SET;
            elif (wh == seek_cur ) SEEK_CUR;
            elif (wh == seek_end ) SEEK_END;
            else                   fail ("whFromUnt", "unknown whence " + (int::to_string wh));
            fi;

        package fd {
            #
            stipulate
                package bit_flags = bit_flags_g ();                                     # bit_flags_g           is from   src/lib/std/src/bit-flags-g.pkg
            herein
                include bit_flags;
            end;

            cloexec = from_unt (w_osval "cloexec");
        };

        package flags {
            #
            stipulate
                package bit_flags =  bit_flags_g ();
            herein
                include bit_flags;
            end;

            append   = from_unt (w_osval "append");
            dsync    = from_unt (w_osval "dsync");
            nonblock = from_unt (w_osval "nonblock");
            rsync    = from_unt (w_osval "rsync");
            sync     = from_unt (w_osval "sync");
        };

        fcntl_d   =  cfun "fcntl_d"  :  (Sy_Int, Sy_Int) -> Sy_Int             ;        # fcntl_d       def in    src/c/lib/posix-io/fcntl_d.c
        fcntl_gfd =  cfun "fcntl_gfd":   Sy_Int          -> Sy_Unt             ;        # fcntl_gfd     def in    src/c/lib/posix-io/fcntl_gfd.c
        fcntl_sfd =  cfun "fcntl_sfd":  (Sy_Int, Sy_Unt) -> Void               ;        # fcntl_sfd     def in    src/c/lib/posix-io/fcntl_sfd.c
        fcntl_gfl =  cfun "fcntl_gfl":   Sy_Int          -> (Sy_Unt, Sy_Unt)   ;        # fcntl_gfl     def in    src/c/lib/posix-io/fcntl_gfl.c
        fcntl_sfl =  cfun "fcntl_sfl":  (Sy_Int, Sy_Unt) -> Void               ;        # fcntl_sfl     def in    src/c/lib/posix-io/fcntl_sfl.c

        fun dupfd { old, base }
            =
            pf::int_to_fd  (fcntl_d  (pf::fd_to_int  old,   pf::fd_to_int  base));

        fun getfd fd
            =
            fd::from_unt  (fcntl_gfd  (pf::fd_to_int  fd));

        fun setfd (fd, fl)
            =
            fcntl_sfd   (pf::fd_to_int  fd,   fd::to_unt  fl);

        fun getfl fd                                                    # "getfl" may be "get_flags", in particular ok_to_block (non/blocking mode) flag.
            =
            {   (fcntl_gfl  (pf::fd_to_int  fd))
                    ->
                    (status, omode);
                #
                ( flags::from_unt  status,
                  pf::omode_from_unt  omode
                );
            };

        fun setfl (fd, status)                                          # "setfl" may be "set_flags", in particular ok_to_block (non/blocking mode) flag.
            =
            fcntl_sfl  (pf::fd_to_int fd,  flags::to_unt status);

        Lock_Type
            =
            F_RDLCK | F_WRLCK | F_UNLCK;

        package flock {
            #
            Flock = FLOCK
                      { ltype:  Lock_Type,
                        whence: Whence,
                        start:  pos::Int,
                        len:    pos::Int,
                        pid:    Null_Or( Process_Id )
                      };

            fun flock fv = FLOCK fv;

            fun ltype  (FLOCK fv) =  fv.ltype;
            fun whence (FLOCK fv) =  fv.whence;
            fun start  (FLOCK fv) =  fv.start;
            fun len    (FLOCK fv) =  fv.len;
            fun pid    (FLOCK fv) =  fv.pid;
        };

        Flock_Rep
            =
            (Sy_Int, Sy_Int, tagged_int::Int, tagged_int::Int, Sy_Int);

        fcntl_l =  cfun "fcntl_l":  (Sy_Int, Sy_Int, Flock_Rep) -> Flock_Rep;                                           # fcntl_l       is from  src/c/lib/posix-io/fcntl_l.c

        f_getlk  = osval  "F_GETLK";
        f_setlk  = osval  "F_SETLK";
        f_setlkw = osval  "F_SETLKW";
        f_rdlck  = osval  "F_RDLCK";
        f_wrlck  = osval  "F_WRLCK";
        f_unlck  = osval  "F_UNLCK";

        fun flock_to_rep (flock::FLOCK { ltype, whence, start, len, ... } )
            =
            (ltype_of ltype, wh_to_unt whence, start, len, 0)
            where       
                fun ltype_of F_RDLCK => f_rdlck;
                    ltype_of F_WRLCK => f_wrlck;
                    ltype_of F_UNLCK => f_unlck;
                end;
            end;

        fun flock_from_rep (usepid, (ltype, whence, start, len, pid))           # 'ltype' may be 'locktype'.
            =
            flock::FLOCK
              { 
                ltype  =>  ltype_of  ltype,
                whence =>  wh_from_unt  whence,
                start,
                len,
                pid    =>  usepid  ??  THE (posix_process::PID pid)
                                   ::  NULL
              }
            where
                fun ltype_of  ltype
                    = 
                    if   (ltype == f_rdlck ) F_RDLCK;
                    elif (ltype == f_wrlck ) F_WRLCK;
                    elif (ltype == f_unlck ) F_UNLCK;
                    else                     fail ("flockFromRep", "unknown lock type " + (int::to_string ltype));
                    fi;
            end;


        fun getlk (fd, flock)
            =
            flock_from_rep (TRUE, fcntl_l (pf::fd_to_int fd, f_getlk, flock_to_rep flock));


        fun setlk (fd, flock)
            =
            flock_from_rep (FALSE, fcntl_l (pf::fd_to_int fd, f_setlk, flock_to_rep flock));


        fun setlkw (fd, flock)
            =
            flock_from_rep (FALSE, fcntl_l (pf::fd_to_int fd, f_setlkw, flock_to_rep flock));


        lseek' = cfun "lseek": (Sy_Int, tagged_int::Int, Sy_Int) -> tagged_int::Int;                            # lseek         def in    src/c/lib/posix-io/lseek.c


        fun lseek (fd, offset, whence)
            =
            lseek'(pf::fd_to_int fd, offset, wh_to_unt whence);


        fsync' =   cfun "fsync":  Sy_Int -> Void;                                                               # fsync         def in    src/c/lib/posix-io/fsync.c


        fun fsync fd
            =
            fsync' (pf::fd_to_int fd);



        # Making filereaders and filewriters
        # -- code moved here from winix-base-data-file-io-driver-for-posix.pkg
        #                     and winix-base-text-file-io-driver-for-posix.pkg

        fun announce s x y
            =
            {   # print "Posix: "; print (s: String); print "\n"; 
                #
                x y;
            };

        best_io_quantum = 4096;                                                                                 # Reading and writing 4KB at a time should be reasonably efficient.


        fun is_plain_file  fd
            =
            pf::stat::is_file (pf::fstat fd);


        fun make_file_position_fns (closed, fd)
            =
            if (not (is_plain_file fd))
                #
                { file_position   =>  REF (pos::from_int 0),
                  get_file_position    =>  NULL,
                  set_file_position    =>  NULL,
                  end_file_position    =>  NULL,
                  verify_file_position =>  NULL
                };

            else
                #
                file_position = REF (pos::from_int 0);

                fun get_file_position ()
                    =
                    *file_position;

                fun set_file_position p
                    =
                    {   if *closed    raise exception  iox::CLOSED_IO_STREAM;    fi;
                        #
                        file_position :=  announce "lseek" lseek (fd, p, SEEK_SET);
                    };

                fun end_file_position ()
                    =
                    {   if *closed  raise exception iox::CLOSED_IO_STREAM;  fi;
                        #
                        pf::stat::size (announce "fstat" pf::fstat fd);
                    };

                fun verify_file_position ()
                    =
                    {   current_position =  lseek (fd, pos::from_int 0, SEEK_CUR);
                        #
                        file_position :=  current_position;
                        #
                        current_position;
                    };

                ignore (verify_file_position ());

                { file_position,
                  get_file_position    =>  THE get_file_position,
                  set_file_position    =>  THE set_file_position,
                  end_file_position    =>  THE end_file_position,
                  verify_file_position =>  THE verify_file_position
                };

            fi;

        fun make_filereader
            { filereader_constructor,           # Either bio::FILEREADER
                                                # or     tio::FILEREADER -- core def in    src/lib/std/src/io/winix-base-file-io-driver-for-posix-g.pkg
              cvt_vec,
              cvt_arr_slice
            }
            { file_descriptor,  filename,  ok_to_block => initial_ok_to_block }
            =
            {   closed = REF FALSE;

                (make_file_position_fns (closed, file_descriptor))
                    ->
                    { file_position, get_file_position, set_file_position, end_file_position, verify_file_position };


                ok_to_block =  REF initial_ok_to_block;                                         # Hidden state shared by below fns.  We'll do nonblocking I/O whenever this is FALSE.

                fun blocking_on  () = { setfl (file_descriptor, flags::flags []);  ok_to_block := TRUE;  };
                fun blocking_off () = { setfl (file_descriptor, flags::nonblock);  ok_to_block := FALSE; };

                fun advance_file_position k
                    =
                    file_position :=  pos::(+) (*file_position, pos::from_int k);

                fun r_read_ro_vector  max_bytes_to_read
                    =
                    {   v =  announce "read"  read_as_vector  { file_descriptor,  max_bytes_to_read };
                        #
                        advance_file_position (ru::length v);

                        cvt_vec v;
                    };

                fun r_read_rw_vector arg
                    =
                    {   k =   announce "readBuf"   read_into_buffer  { file_descriptor,  buffer => cvt_arr_slice arg  };
                        #
                        advance_file_position k;

                        k;
                    };

                fun block_wrap f x
                    =
                    {   if   *closed              raise exception  iox::CLOSED_IO_STREAM;       fi;
                        if   (not *ok_to_block)   blocking_on ();                               fi;
                        f x;
                    };

                fun no_block_wrap f x
                    =
                    {   if   *closed              raise exception  iox::CLOSED_IO_STREAM;       fi;
                        if   *ok_to_block         blocking_off ();                              fi;

                        THE (f x)
                        except
                            (e as runtime::RUNTIME_EXCEPTION(_, THE cause))
                                =
                                if (cause == posix_error::again)   NULL;
                                else                               raise exception e;
                                fi;
                    };

                fun close_if_open ()
                    =
                    if (not *closed)
                        #
                        closed :=  TRUE;
                        #
                        announce "close"  close file_descriptor;
                    fi;

                stipulate
                    is_plain =  is_plain_file  file_descriptor;
                herein

                    fun avail ()                        # Number of bytes currently available to read.
                        =                               # This is usually just (file_length - file_position).
                        if *closed
                            #
                            THE 0;
                            #
                        elif  is_plain
                            #
                            THE (pos::to_int (pf::stat::size (pf::fstat file_descriptor) - *file_position));
                        else
                            NULL;
                        fi;
                end;

                filereader_constructor
                  {
                    filename,
                    best_io_quantum,
                    #
                    read_vector                => THE (   block_wrap r_read_ro_vector),
                    read_rw_vector             => THE (   block_wrap r_read_rw_vector),
                    #
                    read_vector_nonblocking    => THE (no_block_wrap r_read_ro_vector),
                    read_rw_vector_nonblocking => THE (no_block_wrap r_read_rw_vector),
                    #
                    blockx    => NULL,
                    can_readx => NULL,
                    #
                    avail,
                    #
                    get_file_position,
                    set_file_position,
                    #
                    end_file_position,
                    verify_file_position,
                    #
                    close         =>  close_if_open,
                    io_descriptor =>  THE (pf::fd_to_iod  file_descriptor)
                 };
            };

        fun make_filewriter
                #
                { filewriter_constructor,                                                       # Either bio::FILEWRITER (for binary files)
                                                                                                # or     tio::FILEWRITER (for text   files) -- see src/lib/std/src/io/winix-base-file-io-driver-for-posix-g.pkg
                  cvt_vec_slice,
                  cvt_arr_slice
                }
                #
                { file_descriptor, filename, ok_to_block => initial_ok_to_block, append_mode, best_io_quantum }
            =
            {   closed =  REF FALSE;
                #
                (make_file_position_fns (closed, file_descriptor))
                    ->
                    { file_position, get_file_position, set_file_position, end_file_position, verify_file_position };

                fun advance_file_position k
                    =
                    {   file_position := pos::(+) (*file_position, pos::from_int k);
                        #
                        k;
                    };

                ok_to_block =  REF initial_ok_to_block;                                         # Hidden state shared by below fns.  We'll do nonblocking I/O whenever this is FALSE.

                stipulate
                    append_flags =  flags::flags  (append_mode  ??  [flags::append] ::  NIL);
                herein
                    fun update_status ()
                        =
                        {   flgs =  if *ok_to_block                                    append_flags;
                                    else                flags::flags [flags::nonblock, append_flags];
                                    fi;

                            announce "setfl"   setfl (file_descriptor, flgs);
                        };
                end;

                fun ensure_open ()
                    =
                    if   *closed      raise exception iox::CLOSED_IO_STREAM;   fi;

                fun ensure_block x
                    =
                    if (*ok_to_block != x)
                        #
                        ok_to_block := x;
                        update_status();
                    fi;

                fun write_ro_vector' (fd, s) =     write_vector (fd, cvt_vec_slice s);
                fun write_rw_vector' (fd, s) =  write_rw_vector (fd, cvt_arr_slice s);

                fun put_ro_vector x =  advance_file_position (announce "put_ro_vector"  write_ro_vector' x);
                fun put_rw_vector x =  advance_file_position (announce "put_rw_vector"  write_rw_vector' x);

                fun write (put, block) arg
                    =
                    {   ensure_open ();
                        ensure_block block; 
                        put (file_descriptor, arg);
                    };

                fun handle_block writer arg
                    =
                    THE (writer arg)
                    except
                        (e as runtime::RUNTIME_EXCEPTION(_, THE cause))
                            =
                            if (cause == posix_error::again)   NULL;
                            else                               raise exception e;
                            fi;

                fun close_if_open ()
                    =
                    if (not *closed)
                        #
                        closed:=TRUE;
                        announce "close"   close file_descriptor;
                    fi;

                filewriter_constructor
                  {
                    filename,
                    best_io_quantum,
                    #
                    write_vector    =>  THE (write (put_ro_vector, TRUE)),
                    write_rw_vector =>  THE (write (put_rw_vector, TRUE)),
                    #
                    write_vector_nonblocking    =>  THE (handle_block (write (put_ro_vector, FALSE))),
                    write_rw_vector_nonblocking =>  THE (handle_block (write (put_rw_vector, FALSE))),
                    #
                    blockx     =>  NULL,
                    can_output =>  NULL,
                    #
                    get_file_position,
                    set_file_position,
                    #
                    end_file_position,
                    verify_file_position,
                    #
                    io_descriptor =>  THE (pf::fd_to_iod file_descriptor),
                    close         =>  close_if_open
                  };
            };

        stipulate
            fun c2w_vs cvs
                =
                {   (rcs::base  cvs)
                        ->
                        (cv, s, l);

                    wv =  byte::string_to_bytes cv;

                    rus::make_slice (wv, s, THE l);
                };

                                                                                # inline_t              is from   src/lib/core/init/built-in.pkg

            c2w_a =  inline_t::cast:  wc::Rw_Vector -> wu::Rw_Vector;
                #
                # Hack!!!  This only works because
                #                  wc::Rw_Vector and
                #                  wu::Rw_Vector
                # are really the same internally:

            fun c2w_as cas
                =
                {   (wcs::base cas)
                        ->
                        (ca, s, l);

                    wa =  c2w_a  ca;

                    wus::make_slice (wa, s, THE l);
                };
        herein

            make_data_filereader                                                        # "data" == "binary"
                =
                make_filereader
                  {
                    filereader_constructor      =>  bio::FILEREADER,
                    cvt_vec                     =>  fn v = v,
                    cvt_arr_slice               =>  fn s = s
                  };

            make_text_filereader
                =
                make_filereader
                  {
                    filereader_constructor      =>  tio::FILEREADER,
                    cvt_vec                     =>  byte::bytes_to_string,
                    cvt_arr_slice               =>  c2w_as
                  };

            make_data_filewriter                                                        # "data" == "binary"
                =
                make_filewriter
                  {
                    filewriter_constructor      =>  bio::FILEWRITER,
                    cvt_vec_slice               =>  fn s = s,
                    cvt_arr_slice               =>  fn s = s
                  };

            make_text_filewriter
                =
                make_filewriter
                  {
                    filewriter_constructor      =>  tio::FILEWRITER,
                    cvt_vec_slice               =>  c2w_vs,
                    cvt_arr_slice               =>  c2w_as
                  };

        end;                            # stipulate
    };                                  # package posix_io 
end;                                    # stipulate


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext