memfiles now uses winlean; changed the interface to raise EOS

This commit is contained in:
Araq 2011-11-05 12:16:49 +01:00
commit dc08a033d9
8 changed files with 123 additions and 70 deletions

View file

@ -632,7 +632,10 @@ proc newRodReader(modfilename: string, crc: TCrc32,
r.readerIndex = readerIndex r.readerIndex = readerIndex
r.filename = modfilename r.filename = modfilename
InitIdTable(r.syms) InitIdTable(r.syms)
if not open(r.memFile, modfilename): return nil try:
r.memFile = memfiles.open(modfilename)
except EOS:
return nil
# we terminate the file explicitely with ``\0``, so the cast to `cstring` # we terminate the file explicitely with ``\0``, so the cast to `cstring`
# is save: # is save:
r.s = cast[cstring](r.memFile.mem) r.s = cast[cstring](r.memFile.mem)

View file

@ -141,6 +141,10 @@ Generic Operating System Services
(also called *console*). The implementation simply uses ANSI escape (also called *console*). The implementation simply uses ANSI escape
sequences and does not depend on any other module. sequences and does not depend on any other module.
* `memfiles <memfiles.html>`_
This module provides support for memory mapped files (Posix's ``mmap``)
on the different operating systems.
Math libraries Math libraries
-------------- --------------

View file

@ -11,6 +11,9 @@
## ##
## This module implements operations for the built-in `seq`:idx: type ## This module implements operations for the built-in `seq`:idx: type
## which were inspired by functional programming languages. ## which were inspired by functional programming languages.
##
## **Note**: This interface will change as soon as the compiler supports
## closures and proper coroutines.
proc concat*[T](seqs: openarray[seq[T]]): seq[T] = proc concat*[T](seqs: openarray[seq[T]]): seq[T] =
## Takes several sequences' items and returns them inside of one sequence. ## Takes several sequences' items and returns them inside of one sequence.

View file

@ -1,39 +1,32 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2011 Andreas Rumpf # (c) Copyright 2011 Nimrod Contributors
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## :Authors: Zahary Karadjov, Andreas Rumpf
##
## This module provides support for `memory mapped files`:idx: ## This module provides support for `memory mapped files`:idx:
## (Posix's `mmap`:idx:) on the different operating systems. ## (Posix's `mmap`:idx:) on the different operating systems.
when defined(windows): when defined(windows):
import windows import winlean
elif defined(posix): elif defined(posix):
import posix import posix
else: else:
{.error: "the memfiles module is not supported yet on your operating system!".} {.error: "the memfiles module is not supported on your operating system!".}
## mem import os
## a pointer to the memory mapped file `f`. The pointer can be
## used directly to change the contents of the file, if `f` was opened
## with write access.
## size
## size of the memory mapped file `f`.
when defined(windows):
type Tsize = int64
else:
type Tsize = int
type type
TMemFile* = object {.pure.} ## represents a memory mapped file TMemFile* = object {.pure.} ## represents a memory mapped file
mem*: pointer # XXX: The compiler won't let me add comments on the next line mem*: pointer ## a pointer to the memory mapped file. The pointer
size*: Tsize ## can be used directly to change the contents of the
## file, if it was opened with write access.
size*: int ## size of the memory mapped file
when defined(windows): when defined(windows):
fHandle: int fHandle: int
@ -41,32 +34,31 @@ type
else: else:
handle: cint handle: cint
proc open*( proc open*(filename: string, mode: TFileMode = fmRead,
f : var TMemFile, mappedSize = -1, offset = 0, newFileSize = -1): TMemFile =
filename : string, ## opens a memory mapped file. If this fails, ``EOS`` is raised.
mode : TFileMode = fmRead, ## `newFileSize` can only be set if the file is not opened with ``fmRead``
mappedSize : int = -1, ## access. `mappedSize` and `offset` can be used to map only a slice of
offset : Tsize = 0, ## the file.
newFileSize : Tsize = -1 ): bool =
## open a memory mapped file `f`. Returns true for success.
# The file can be resized only when write mode is used # The file can be resized only when write mode is used:
assert newFileSize == -1 or mode != fmRead assert newFileSize == -1 or mode != fmRead
var readonly = mode == fmRead var readonly = mode == fmRead
template rollback = template rollback =
f.mem = nil result.mem = nil
f.size = 0 result.size = 0
when defined(windows): when defined(windows):
template fail(msg: expr) = template fail(msg: expr) =
rollback() rollback()
if f.fHandle != 0: discard CloseHandle(f.fHandle) if result.fHandle != 0: discard CloseHandle(result.fHandle)
if f.mapHandle != 0: discard CloseHandle(f.mapHandle) if result.mapHandle != 0: discard CloseHandle(result.mapHandle)
OSError()
# return false # return false
raise newException(EIO, msg) #raise newException(EIO, msg)
f.fHandle = CreateFileA( result.fHandle = CreateFileA(
filename, filename,
if readonly: GENERIC_READ else: GENERIC_ALL, if readonly: GENERIC_READ else: GENERIC_ALL,
FILE_SHARE_READ, FILE_SHARE_READ,
@ -75,7 +67,7 @@ proc open*(
if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY, if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY,
0) 0)
if f.fHandle == INVALID_HANDLE_VALUE: if result.fHandle == INVALID_HANDLE_VALUE:
fail "error opening file" fail "error opening file"
if newFileSize != -1: if newFileSize != -1:
@ -83,87 +75,83 @@ proc open*(
sizeHigh = int32(newFileSize shr 32) sizeHigh = int32(newFileSize shr 32)
sizeLow = int32(newFileSize and 0xffffffff) sizeLow = int32(newFileSize and 0xffffffff)
var status = SetFilePointer(f.fHandle, sizeLow, addr(sizeHigh), FILE_BEGIN) var status = SetFilePointer(result.fHandle, sizeLow, addr(sizeHigh),
FILE_BEGIN)
if (status == INVALID_SET_FILE_POINTER and GetLastError() != NO_ERROR) or if (status == INVALID_SET_FILE_POINTER and GetLastError() != NO_ERROR) or
(SetEndOfFile(f.fHandle) == 0): (SetEndOfFile(result.fHandle) == 0):
fail "error setting file size" fail "error setting file size"
f.mapHandle = CreateFileMapping( result.mapHandle = CreateFileMapping(
f.fHandle, nil, result.fHandle, nil,
if readonly: PAGE_READONLY else: PAGE_READWRITE, if readonly: PAGE_READONLY else: PAGE_READWRITE,
0, 0, nil) 0, 0, nil)
if f.mapHandle == 0: if result.mapHandle == 0:
fail "error creating mapping" fail "error creating mapping"
f.mem = MapViewOfFileEx( result.mem = MapViewOfFileEx(
f.mapHandle, result.mapHandle,
if readonly: FILE_MAP_READ else: FILE_MAP_WRITE, if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
int32(offset shr 32), int32(offset shr 32),
int32(offset and 0xffffffff), int32(offset and 0xffffffff),
if mappedSize == -1: 0 else: mappedSize, if mappedSize == -1: 0 else: mappedSize,
nil) nil)
if f.mem == nil: if result.mem == nil:
fail "error mapping view" fail "error mapping view"
var hi, low: int32 var hi, low: int32
low = GetFileSize(f.fHandle, addr(hi)) low = GetFileSize(result.fHandle, addr(hi))
if low == INVALID_FILE_SIZE: if low == INVALID_FILE_SIZE:
fail "error getting file size" fail "error getting file size"
else: else:
var fileSize = (int64(hi) shr 32) or low var fileSize = (int64(hi) shr 32) or low
f.size = if mappedSize != -1: min(fileSize, mappedSize) else: fileSize if mappedSize != -1: result.size = min(fileSize, mappedSize).int
else: result.size = fileSize.int
result = true
else: else:
template fail(msg: expr) = template fail(msg: expr) =
rollback() rollback()
if f.handle != 0: if result.handle != 0: discard close(result.handle)
discard close(f.handle) OSError()
# return false
raise newException(system.EIO, msg)
var flags = if readonly: O_RDONLY else: O_RDWR var flags = if readonly: O_RDONLY else: O_RDWR
if newFileSize != -1: if newFileSize != -1:
flags = flags or O_CREAT or O_TRUNC flags = flags or O_CREAT or O_TRUNC
f.handle = open(filename, flags) result.handle = open(filename, flags)
if f.handle == -1: if result.handle == -1:
# XXX: errno is supposed to be set here # XXX: errno is supposed to be set here
# Is there an exception that wraps it? # Is there an exception that wraps it?
fail "error opening file" fail "error opening file"
if newFileSize != -1: if newFileSize != -1:
if ftruncate(f.handle, newFileSize) == -1: if ftruncate(result.handle, newFileSize) == -1:
fail "error setting file size" fail "error setting file size"
if mappedSize != -1: if mappedSize != -1:
f.size = mappedSize result.size = mappedSize
else: else:
var stat: Tstat var stat: Tstat
if fstat(f.handle, stat) != -1: if fstat(result.handle, stat) != -1:
# XXX: Hmm, this could be unsafe # XXX: Hmm, this could be unsafe
# Why is mmap taking int anyway? # Why is mmap taking int anyway?
f.size = int(stat.st_size) result.size = int(stat.st_size)
else: else:
fail "error getting file size" fail "error getting file size"
f.mem = mmap( result.mem = mmap(
nil, nil,
f.size, result.size,
if readonly: PROT_READ else: PROT_READ or PROT_WRITE, if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
if readonly: MAP_PRIVATE else: MAP_SHARED, if readonly: MAP_PRIVATE else: MAP_SHARED,
f.handle, result.handle,
offset) offset)
if f.mem == cast[pointer](MAP_FAILED): if result.mem == cast[pointer](MAP_FAILED):
fail "file mapping failed" fail "file mapping failed"
result = true
proc close*(f: var TMemFile) = proc close*(f: var TMemFile) =
## closes the memory mapped file `f`. All changes are written back to the ## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access. ## file system, if `f` was opened with write access.
@ -189,6 +177,5 @@ proc close*(f: var TMemFile) =
else: else:
f.handle = 0 f.handle = 0
if error: if error: OSError()
raise newException(system.EIO, "error closing file")

View file

@ -7,15 +7,14 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## :Author: Zahary Karadjov (zah@github)
##
## This module implements the standard unit testing facilities such as ## This module implements the standard unit testing facilities such as
## suites, fixtures and test cases as well as facilities for combinatorial ## suites, fixtures and test cases as well as facilities for combinatorial
## and randomzied test case generation (not yet available) ## and randomzied test case generation (not yet available)
## and object mocking (not yet available) ## and object mocking (not yet available)
## ##
## It is loosely based on C++'s boost.test and Haskell's QuickTest ## It is loosely based on C++'s boost.test and Haskell's QuickTest
##
## Maintainer: Zahary Karadjov (zah@github)
##
import import
macros, terminal macros, terminal

View file

@ -12,6 +12,7 @@
type type
THandle* = int THandle* = int
LONG* = int
WINBOOL* = int32 WINBOOL* = int32
DWORD* = int32 DWORD* = int32
@ -65,6 +66,7 @@ const
DETACHED_PROCESS* = 8'i32 DETACHED_PROCESS* = 8'i32
SW_SHOWNORMAL* = 1'i32 SW_SHOWNORMAL* = 1'i32
INVALID_HANDLE_VALUE* = THANDLE(-1)
proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32", proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "CloseHandle".} importc: "CloseHandle".}
@ -143,6 +145,7 @@ const
FILE_ATTRIBUTE_HIDDEN* = 2'i32 FILE_ATTRIBUTE_HIDDEN* = 2'i32
FILE_ATTRIBUTE_READONLY* = 1'i32 FILE_ATTRIBUTE_READONLY* = 1'i32
FILE_ATTRIBUTE_SYSTEM* = 4'i32 FILE_ATTRIBUTE_SYSTEM* = 4'i32
FILE_ATTRIBUTE_TEMPORARY* = 256'i32
MAX_PATH* = 260 MAX_PATH* = 260
type type
@ -239,7 +242,8 @@ type
TInAddr* {.pure, final, importc: "IN_ADDR", header: "Winsock2.h".} = object TInAddr* {.pure, final, importc: "IN_ADDR", header: "Winsock2.h".} = object
s_addr*: int32 # IP address s_addr*: int32 # IP address
Tsockaddr_in* {.pure, final, importc: "SOCKADDR_IN", header: "Winsock2.h".} = object Tsockaddr_in* {.pure, final, importc: "SOCKADDR_IN",
header: "Winsock2.h".} = object
sin_family*: int16 sin_family*: int16
sin_port*: int16 # unsigned sin_port*: int16 # unsigned
sin_addr*: TInAddr sin_addr*: TInAddr
@ -248,7 +252,8 @@ type
Tin6_addr* {.pure, final, importc: "IN6_ADDR", header: "Winsock2.h".} = object Tin6_addr* {.pure, final, importc: "IN6_ADDR", header: "Winsock2.h".} = object
bytes*: array[0..15, char] bytes*: array[0..15, char]
Tsockaddr_in6* {.pure, final, importc: "SOCKADDR_IN6", header: "Winsock2.h".} = object Tsockaddr_in6* {.pure, final, importc: "SOCKADDR_IN6",
header: "Winsock2.h".} = object
sin6_family*: int16 sin6_family*: int16
sin6_port*: int16 # unsigned sin6_port*: int16 # unsigned
sin6_flowinfo*: int32 # unsigned sin6_flowinfo*: int32 # unsigned
@ -397,3 +402,54 @@ type
proc WaitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray, proc WaitForMultipleObjects*(nCount: DWORD, lpHandles: PWOHandleArray,
bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{. bWaitAll: WINBOOL, dwMilliseconds: DWORD): DWORD{.
stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
# for memfiles.nim:
const
GENERIC_READ* = 0x80000000'i32
GENERIC_ALL* = 0x10000000'i32
FILE_SHARE_READ* = 1'i32
CREATE_ALWAYS* = 2'i32
OPEN_EXISTING* = 3'i32
FILE_BEGIN* = 0'i32
INVALID_SET_FILE_POINTER* = -1'i32
NO_ERROR* = 0'i32
PAGE_READONLY* = 2'i32
PAGE_READWRITE* = 4'i32
FILE_MAP_READ* = 4'i32
FILE_MAP_WRITE* = 2'i32
INVALID_FILE_SIZE* = -1'i32
proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
lpSecurityAttributes: pointer,
dwCreationDisposition, dwFlagsAndAttributes: DWORD,
hTemplateFile: THANDLE): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileA".}
proc SetEndOfFile*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "SetEndOfFile".}
proc SetFilePointer*(hFile: THANDLE, lDistanceToMove: LONG,
lpDistanceToMoveHigh: ptr LONG,
dwMoveMethod: DWORD): DWORD {.
stdcall, dynlib: "kernel32", importc: "SetFilePointer".}
proc GetFileSize*(hFile: THANDLE, lpFileSizeHigh: ptr DWORD): DWORD{.stdcall,
dynlib: "kernel32", importc: "GetFileSize".}
proc MapViewOfFileEx*(hFileMappingObject: THANDLE, dwDesiredAccess: DWORD,
dwFileOffsetHigh, dwFileOffsetLow: DWORD,
dwNumberOfBytesToMap: DWORD,
lpBaseAddress: pointer): pointer{.
stdcall, dynlib: "kernel32", importc: "MapViewOfFileEx".}
proc CreateFileMapping*(hFile: THANDLE,
lpFileMappingAttributes: pointer,
flProtect, dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD, lpName: cstring): THANDLE {.
stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".}
proc UnmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "UnmapViewOfFile".}

View file

@ -106,6 +106,7 @@ Library Additions
- Added ``locks`` core module for more flexible locking support. - Added ``locks`` core module for more flexible locking support.
- Added ``irc`` module. - Added ``irc`` module.
- Added ``ftpclient`` module. - Added ``ftpclient`` module.
- Added ``memfiles`` module.
2011-07-10 Version 0.8.12 released 2011-07-10 Version 0.8.12 released

View file

@ -42,7 +42,7 @@ srcdoc: "impure/rdstdin;wrappers/zmq;wrappers/sphinx"
srcdoc: "pure/collections/tables;pure/collections/sets;pure/collections/lists" srcdoc: "pure/collections/tables;pure/collections/sets;pure/collections/lists"
srcdoc: "pure/collections/intsets;pure/collections/queues;pure/encodings" srcdoc: "pure/collections/intsets;pure/collections/queues;pure/encodings"
srcdoc: "pure/events;pure/collections/sequtils;pure/irc;ecmas/dom" srcdoc: "pure/events;pure/collections/sequtils;pure/irc;ecmas/dom"
srcdoc: "pure/ftpclient" srcdoc: "pure/ftpclient;pure/memfiles"
webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup" webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"