summary refs log tree commit diff stats
path: root/lib/pure/streams.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/streams.nim')
-rw-r--r--lib/pure/streams.nim244
1 files changed, 244 insertions, 0 deletions
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
new file mode 100644
index 000000000..238cba4ec
--- /dev/null
+++ b/lib/pure/streams.nim
@@ -0,0 +1,244 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2008 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides a stream interface and two implementations thereof:
+## the `PFileStream` and the `PStringStream` which implement the stream
+## interface for Nimrod file objects (`TFile`) and strings. Other modules
+## may provide other implementations for this standard stream interface.
+
+proc newEIO(msg: string): ref EIO =
+  new(result)
+  result.msg = msg
+
+type
+  PStream* = ref TStream
+  TStream* = object of TObject ## Stream interface that supports
+                               ## writing or reading.
+    close*: proc (s: PStream)
+    atEnd*: proc (s: PStream): bool
+    setPosition*: proc (s: PStream, pos: int)
+    getPosition*: proc (s: PStream): int
+    readData*: proc (s: PStream, buffer: pointer, bufLen: int): int
+    writeData*: proc (s: PStream, buffer: pointer, bufLen: int)
+
+proc write*[T](s: PStream, x: T) = 
+  ## generic write procedure. Writes `x` to the stream `s`. Implementation:
+  ##
+  ## .. code-block:: Nimrod
+  ##
+  ##     s.writeData(s, addr(x), sizeof(x))
+  var x = x
+  s.writeData(s, addr(x), sizeof(x))
+
+proc write*(s: PStream, x: string) = 
+  ## writes the string `x` to the the stream `s`. No length field or 
+  ## terminating zero is written.
+  s.writeData(s, cstring(x), x.len)
+
+proc read[T](s: PStream, result: var T) = 
+  ## generic write procedure. Reads `result` from the stream `s`.
+  if s.readData(s, addr(result), sizeof(T)) != sizeof(T):
+    raise newEIO("cannot read from stream")
+
+proc readChar*(s: PStream): char =
+  ## reads a char from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readBool*(s: PStream): bool = 
+  ## reads a bool from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readInt8*(s: PStream): int8 = 
+  ## reads an int8 from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readInt16*(s: PStream): int16 = 
+  ## reads an int16 from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readInt32*(s: PStream): int32 = 
+  ## reads an int32 from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readInt64*(s: PStream): int64 = 
+  ## reads an int64 from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readFloat32*(s: PStream): float32 = 
+  ## reads a float32 from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readFloat64*(s: PStream): float64 = 
+  ## reads a float64 from the stream `s`. Raises `EIO` if an error occured.
+  read(s, result)
+
+proc readStr*(s: PStream, length: int): string = 
+  ## reads a string of length `length` from the stream `s`. Raises `EIO` if 
+  ## an error occured.
+  result = newString(length)
+  var L = s.readData(s, addr(result[0]), length)
+  if L != length: setLen(result, L)
+
+proc readLine*(s: PStream): string =
+  ## Reads a line from a stream `s`. Note: This is not very efficient. Raises 
+  ## `EIO` if an error occured.
+  result = ""
+  while not s.atEnd(s): 
+    var c = readChar(s)
+    if c == '\c': 
+      c = readChar(s)
+      break
+    elif c == '\L': break
+    result.add(c)
+
+type
+  PStringStream* = ref TStringStream ## a stream that encapsulates a string
+  TStringStream* = object of TStream
+    data*: string
+    pos: int
+    
+proc ssAtEnd(s: PStringStream): bool = 
+  return s.pos >= s.data.len
+    
+proc ssSetPosition(s: PStringStream, pos: int) = 
+  s.pos = min(pos, s.data.len-1)
+
+proc ssGetPosition(s: PStringStream): int =
+  return s.pos
+
+proc ssReadData(s: PStringStream, buffer: pointer, bufLen: int): int =
+  result = min(bufLen, s.data.len - s.pos)
+  if result > 0: 
+    copyMem(buffer, addr(s.data[s.pos]), result)
+    inc(s.pos, result)
+
+proc ssWriteData(s: PStringStream, buffer: pointer, bufLen: int) = 
+  if bufLen > 0: 
+    setLen(s.data, s.data.len + bufLen)
+    copyMem(addr(s.data[s.pos]), buffer, bufLen)
+    inc(s.pos, bufLen)
+
+proc ssClose(s: PStringStream) =
+  s.data = nil
+
+proc newStringStream*(s: string = ""): PStringStream = 
+  ## creates a new stream from the string `s`.
+  new(result)
+  result.data = s
+  result.pos = 0
+  result.close = ssClose
+  result.atEnd = ssAtEnd
+  result.setPosition = ssSetPosition
+  result.getPosition = ssGetPosition
+  result.readData = ssReadData
+  result.writeData = ssWriteData
+
+type
+  PFileStream* = ref TFileStream ## a stream that encapsulates a `TFile`
+  TFileStream* = object of TStream
+    f: TFile
+
+proc fsClose(s: PFileStream) = closeFile(s.f)
+proc fsAtEnd(s: PFileStream): bool = return EndOfFile(s.f)
+proc fsSetPosition(s: PFileStream, pos: int) = setFilePos(s.f, pos)
+proc fsGetPosition(s: PFileStream): int = return int(getFilePos(s.f))
+
+proc fsReadData(s: PFileStream, buffer: pointer, bufLen: int): int = 
+  result = readBuffer(s.f, buffer, bufLen)
+  
+proc fsWriteData(s: PFileStream, buffer: pointer, bufLen: int) = 
+  if writeBuffer(s.f, buffer, bufLen) != bufLen: 
+    raise newEIO("cannot write to stream")
+
+proc newFileStream*(f: TFile): PFileStream = 
+  ## creates a new stream from the file `f`.
+  new(result)
+  result.f = f
+  result.close = fsClose
+  result.atEnd = fsAtEnd
+  result.setPosition = fsSetPosition
+  result.getPosition = fsGetPosition
+  result.readData = fsReadData
+  result.writeData = fsWriteData
+
+proc newFileStream*(filename: string, mode: TFileMode): PFileStream = 
+  ## creates a new stream from the file named `filename` with the mode `mode`.
+  ## If the file cannot be opened, nil is returned.
+  var f: TFile
+  if OpenFile(f, filename, mode): result = newFileStream(f)
+
+
+when true:
+  nil
+else:
+  type
+    TFileHandle* = cint ## Operating system file handle
+    PFileHandleStream* = ref TFileHandleStream
+    TFileHandleStream* = object of TStream
+      handle*: TFileHandle
+      pos: int
+
+  proc newEOS(msg: string): ref EOS =
+    new(result)
+    result.msg = msg
+
+  proc hsGetPosition(s: PFileHandleStream): int = 
+    return s.pos
+
+  when defined(windows):
+    # do not import windows as this increases compile times:
+    nil
+  else:
+    import posix
+    
+    proc hsSetPosition(s: PFileHandleStream, pos: int) = 
+      discard lseek(s.handle, pos, SEEK_SET)
+
+    proc hsClose(s: PFileHandleStream) = discard close(s.handle)
+    proc hsAtEnd(s: PFileHandleStream): bool = 
+      var pos = hsGetPosition(s)
+      var theEnd = lseek(s.handle, 0, SEEK_END)
+      result = pos >= theEnd
+      hsSetPosition(s, pos) # set position back
+
+    proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int = 
+      result = posix.read(s.handle, buffer, bufLen)
+      inc(s.pos, result)
+      
+    proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) = 
+      if posix.write(s.handle, buffer, bufLen) != bufLen: 
+        raise newEIO("cannot write to stream")
+      inc(s.pos, bufLen)
+
+  proc newFileHandleStream*(handle: TFileHandle): PFileHandleStream = 
+    new(result)
+    result.handle = handle
+    result.pos = 0
+    result.close = hsClose
+    result.atEnd = hsAtEnd
+    result.setPosition = hsSetPosition
+    result.getPosition = hsGetPosition
+    result.readData = hsReadData
+    result.writeData = hsWriteData
+
+  proc newFileHandleStream*(filename: string, 
+                            mode: TFileMode): PFileHandleStream = 
+    when defined(windows): 
+      nil
+    else:
+      var flags: cint
+      case mode
+      of fmRead:              flags = posix.O_RDONLY
+      of fmWrite:             flags = O_WRONLY or int(O_CREAT)
+      of fmReadWrite:         flags = O_RDWR or int(O_CREAT)
+      of fmReadWriteExisting: flags = O_RDWR
+      of fmAppend:            flags = O_WRONLY or int(O_CREAT) or O_APPEND
+      var handle = open(filename, flags)
+      if handle < 0: raise newEOS("posix.open() call failed")
+    result = newFileHandleStream(handle)