Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

fuzion/sys/fileio.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature fuzion.sys.fileio
#
#  Author: Wael Youssfi (wael.youssfi@tokiwa.software)
#
# -----------------------------------------------------------------------

# fuzion.sys.fileio -- fileio presents basic features to handle File I/O operations
#

module fileio is

  # reads n bytes of a file opened as fd
  #
  # in case the outcome is an array, it may be shorter than n. this means the end of file
  # has been reached.
  #
  module read(fd i64, n u64) outcome (array u8) =>
    len := n.as_i32
    arr := fuzion.sys.internal_array_init u8 len
    res := read fd arr.data len

    if res < -1
      error "unspecified read error: {res}"
    else if res < 0
      []
    else
      buf := arr.as_array
      (buf.slice 0 res).as_array


  # intrinsic that fills an array u8 with the file bytes that is represented by the file descriptor
  #
  # returns:
  # 0 or greater on success
  # -1 at end of file
  # -2 for any other error
  #
  module read(
               # the file descriptor
               fd i64,
               # the internal array data representing the container for the bytes to be read from the file
               file_array fuzion.sys.Pointer,
               # the length of the array that represents the file bytes
               file_array_length i32) i32 => intrinsic


  # retrieves the file size in bytes and returns an outcome of error in case of an error
  #
  private get_file_size(
                        # the (relative or absolute) file name, using platform specific path separators
                        path String) outcome i64 =>
    md := fuzion.sys.internal_array_init i64 4
    match stats (fuzion.sys.c_string path) md.data
      TRUE  => md.as_array[0]
      FALSE => error "error getting file size"


  # writes the content of an array of bytes to a file opened as fd
  #
  # this might overwrite parts or all of an existing file.
  #
  module write(fd i64, content array u8) outcome unit =>
    res := write fd content.internal_array.data content.length

    if res = 0
      unit
    else
      error "write error: {res}"

  # intrinsic to write bytes (internal array data) in a file using the file descriptor
  # returns 0 in case of success, -1 in case of failure
  #
  private write(
                # the file descriptor
                fd i64,
                # the internal array data representing the content bytes to insert in file
                content fuzion.sys.Pointer,
                # the length of the internal array representing the content
                content_length i32) i32 => intrinsic


  # deletes the file/dir found in the path
  # returns unit as outcome in case of successful deletion and error in case of failure
  # if the targeted dir has content, then the return value will be error and the deletion will not take place
  #
  module delete(
                # the (relative or absolute) file name, using platform specific path separators
                path String) outcome unit =>
    arr := fuzion.sys.c_string path
    if delete arr unit
      unit
    else
      error "an error occurred while performing the delete operation on the following file/dir: \"$path\""

  # intrinsic that deletes the file/dir represented by the path returning TRUE in case of success
  # and FALSE in case of failure
  #
  private delete(
                 # the internal array data representing the file/dir path in bytes
                 path fuzion.sys.Pointer,
                 # dummy parameter to avoid duplicate feature name
                 _ unit) bool => intrinsic_constructor


  #  moves file/dir from an old path to a the new path
  # can rename the file/dir as well by changing the name of the old file/dir to a new name in the new_path
  # returns a unit type as outcome in case of success and error in case of failure
  #
  module move(
              # the old (relative or absolute) file name, using platform specific path separators
              old_path String,
              # the new (relative or absolute) file name, using platform specific path separators
              new_path String) outcome unit =>
    arr0 := fuzion.sys.c_string old_path
    arr1 := fuzion.sys.c_string new_path
    if move arr0 arr1 unit
      unit
    else
      error "an error occurred while performing the following move operation: \"$old_path\" --> \"$new_path\""

  # intrinsic that returns TRUE in case the move was successful and FALSE in case not
  #
  private move(
               # the internal array data representing the old file/dir path in bytes
               old_path fuzion.sys.Pointer,
               # the internal array data representing the new file/dir path in bytes
               new_path fuzion.sys.Pointer,
               # dummy parameter for overloading
               _ unit) bool => intrinsic_constructor


  # creates a directory using the specified path
  # parent directories in the path should exist otherwise, no creation will take place and an error will be the outcome
  #
  module create_dir(
                    # the (relative or absolute) dir name, using platform specific path separators
                    path String) outcome unit =>
    arr := fuzion.sys.c_string path
    if create_dir arr unit
      unit
    else
      error "an error occurred while creating the following directory: \"$path\""

  # intrinsic that returns TRUE in case of success or FALSE in case of failure during dir creation
  #
  private create_dir(
                     # the internal array data representing the dir path in bytes
                     path fuzion.sys.Pointer,
                     # dummy parameter to enable overloading
                     _ unit) bool => intrinsic_constructor


  # intrinsic that fills an array with some metadata of the file/dir provided by the path
  # returns TRUE in case the operation was successful and FALSE in case of failure
  # in case the path refers to a symbolic link it resolves it and returns info about the actual file
  #
  # in case an error is returned (the result of this feature is false), then the size field of
  # the meta_data array will contain the errno for the stat call.
  #
  module stats(
        # the internal array data representing the file/dir path in bytes
        path fuzion.sys.Pointer,
        # the internal array data representing the metadata fields [size in bytes, creation_time in seconds, regular file? 1 : 0, dir? 1 : 0]
        meta_data fuzion.sys.Pointer) bool => intrinsic_constructor

  # intrinsic that fills an array with some metadata of the file/dir provided by the path
  # returns TRUE in case the operation was successful and FALSE in case of failure
  # in case the path refers to a symbolic link it does not attempt to follow it and returns info about the link itself
  #
  # in case an error is returned (the result of this feature is false), then the size field of
  # the meta_data array will contain the errno for the lstat call.
  #
  module lstats(path fuzion.sys.Pointer, meta_data fuzion.sys.Pointer) bool => intrinsic # NYI behaves the same as stats in the interpreter


  # Opens an IO source using a Fuzion Pointer as path and an i8 flag to represent the opening method (Read: 0, Write: 1, Append: 2)
  # returns outcome i64 representing the file descriptor in success
  # returns an error in failure
  #
  module open(
              # a Fuzion Object represention the path for the source to be opened
              path String,
              # a flag to speicify the open method (Read: 0, Write: 1, Append: 2)
              flag i8) outcome i64 =>
    or := fuzion.sys.internal_array_init i64 2  # [file descriptor, error number]
    open (fuzion.sys.c_string path) or.data flag
    open_results := or.as_array
    if open_results[1] = i64 0
      open_results[0]
    else
      error "error number: {open_results[1]}"

  # intrinsic that fills a Fuzion object with the file descriptor and the error number from C back-end/ -1 in the interpreter
  # after opening the source represented by the path parameter
  #
  private open(
               # a Fuzion Pointer represention the path for the source to be opened
               path fuzion.sys.Pointer,
               # open_results[file descriptor, error number] as a Fuzion Pointer
               open_results fuzion.sys.Pointer,
               # opening flag (Read: 0, Write: 1, Append: 2)
               flag i8) unit => intrinsic


  # Closes an IO source using an i64 representing the source handler (file descriptor)
  # returns outcome unit in case of success and an error in case of failure
  #
  module close(
               # file descriptor
               fd i64) outcome unit =>
    closing_result := close fd unit
    if closing_result = i8 0
      unit
    else
      error "error number: $closing_result"

  # intrinsic that takes an i64 value that represents the file descriptor and closes the stream
  # returns an i8 to represent the result of the operation
  # 0 in case no errors occurred and the error number in case the operation failed in the C back-end/ -1 in the interpreter
  #
  private close(
                # file descriptor
                fd i64,
                # dummy variable to enable overload
                _ unit) i8 => intrinsic


  # seek offset in the stream represented by fd
  # returns an outcome i64 that represents the new offset
  # returns an error in case of failure
  #
  module seek(
              # file descriptor
              fd i64,
              # the offset to seek from the beginning of this file
              offset i64) outcome i64 =>
    iarr := fuzion.sys.internal_array_init i64 2
    seek fd offset iarr.data
    arr := iarr.as_array
    if arr[1] = i64 0
      arr[0]
    else
      error "error number: {arr[1]}"

  # intrinsic to set the file-pointer offset at which the next read or write occurs
  # the offset is measured from the beginning of the file indicated by the file descriptor
  # and fills a Fuzion object with the new offset
  # and the error number from the C back-end/ -1 in the interpreter
  #
  private seek(
               # file descriptor
               fd i64,
               # the offset to seek from the beginning of this file
               offset i64,
               # Array data [new file position, error number]
               seek_results fuzion.sys.Pointer) unit => intrinsic


  # returns the current file-pointer offset as an outcome i64,
  # the offset is measured from the beginning of the file indicated by the file descriptor
  # returns the current offset in success and error in failure
  #
  module file_position(
                       # file descriptor
                       fd i64) outcome i64 =>
    iarr := fuzion.sys.internal_array_init i64 2
    file_position fd iarr.data
    arr := iarr.as_array
    if arr[1] = i64 0
      arr[0]
    else
      error "error number: {arr[1]}"

  # intrinsic that fills a Fuzion object with the current file stream position
  # and the error number from C back-end/ -1 in the interpreter
  #
  private file_position(
                        # file descriptor
                        fd i64,
                        # Array data [new file position, error number]
                        position_results fuzion.sys.Pointer) unit => intrinsic


  # memory map a file
  #
  # res[0]: 0 on success, -1 on error
  #
  # return: allocated memory (same as fuzion.sys.internal_array_init.alloc)
  #
  # note: offset+size must not exceed file size.
  # note: returning allocated memory - instead of error code - simplifies handling in DFA.
  #
  module mmap(fd i64, offset, size i64, res fuzion.sys.Pointer) Pointer => intrinsic_constructor


  # close a memory mapped file
  #
  # return: 0 on success, -1 on error
  #
  module munmap(address fuzion.sys.Pointer, size i64) i32 => intrinsic


  # get a byte from a given mapped buffer
  #
  module mapped_buffer_get
    (# memory address obtained by `mmap`
     m fuzion.sys.Pointer,

     # index of the byte
     i i64
    ) u8 => intrinsic_constructor


  # set a byte of a given mapped buffer to a new value
  #
  module mapped_buffer_set
    (# memory address obtained by `mmap`
     m fuzion.sys.Pointer,

     # index of the byte
     i i64,

     # new value to be written at m[i]
     o u8
    ) unit => intrinsic


  # flush user-space buffers of file descriptor
  #
  # return: 0 on success everything else is an error
  #
  module flush(fd i64) i32 => intrinsic


  # open the directory given by path
  #
  # accepts as arguments a path (processed by fuzion.sys.c_string) and a pointer to
  # an array of two i64.
  #
  # after calling, the array will contain an integer that is intended to be used as an
  # opaque reference to the opened directory in its first field, and an integer that is
  # not 0 in case of an error in its second field.
  #
  module open_dir(path fuzion.sys.Pointer, open_results fuzion.sys.Pointer) unit => intrinsic


  # open the directory given by path
  #
  # returns an integer that is intended to be used as an opaque reference
  # to the opened directory, or an error if one was encountered.
  #
  module open_dir(path String) outcome i64 =>
    or := fuzion.sys.internal_array_init i64 2  # [file descriptor, error number]
    open_dir (fuzion.sys.c_string path) or.data
    open_results := or.as_array
    if open_results[1] = i64 0
      open_results[0]
    else
      error "error number: {open_results[1]}"


  # reads the next entry of the directory given by opaque integer reference
  # to an open directory
  #
  # returns the name of the next entry, or an error string on failure.
  #
  module read_dir(ptr i64) String => intrinsic


  # checks whether there is a next entry in the directory given by opaque
  # integer reference to an open directory
  #
  # true if the next call to read_dir should find a new entry, if false then
  # the next call to read_dir will return an error string.
  #
  module read_dir_has_next(ptr i64) bool => intrinsic


  # closes the directory given by opaque integer reference to an open
  # directory
  #
  # returns 0 on success, something else on failure.
  #
  module close_dir(ptr i64) i64 => intrinsic