preparations for proper memmap'ed files

This commit is contained in:
Araq 2011-10-22 11:08:52 +02:00
commit 235bd1c474
4 changed files with 105 additions and 32 deletions

View file

@ -68,12 +68,16 @@
# sym # sym
# ) # )
# #
# The data section MUST be the last section of the file, because processing
# stops immediately after ``DATA(`` and the rest is only loaded on demand
# by using mem'mapped a file.
#
# We now also do index compression, because an index always needs to be read. # We now also do index compression, because an index always needs to be read.
# #
import import
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
ropes, idents, crc, idgen, rodutils ropes, idents, crc, idgen, rodutils, memfiles
type type
TReasonForRecompile* = enum TReasonForRecompile* = enum
@ -104,26 +108,28 @@ type
offset*: int # readers use this offset*: int # readers use this
TRodReader* = object of TObject TRodReader* = object of TObject
pos*: int # position; used for parsing pos: int # position; used for parsing
s*: string # the whole file in memory; XXX mmap this! s: cstring # mmap'ed file contents
options*: TOptions options: TOptions
reason*: TReasonForRecompile reason: TReasonForRecompile
modDeps*: TStringSeq modDeps: TStringSeq
files*: TStringSeq files: TStringSeq
dataIdx*: int # offset of start of data section dataIdx: int # offset of start of data section
convertersIdx*: int # offset of start of converters section convertersIdx: int # offset of start of converters section
initIdx*, interfIdx*, compilerProcsIdx*, cgenIdx*: int initIdx, interfIdx, compilerProcsIdx: int
filename*: string filename: string
index*, imports*: TIndex index, imports: TIndex
readerIndex*: int readerIndex: int
line*: int # only used for debugging, but is always in the code line: int # only used for debugging, but is always in the code
moduleID*: int moduleID: int
syms*: TIdTable # already processed symbols syms: TIdTable # already processed symbols
memfile: TMemFile # unfortunately there is no point in time where we
# can close this! XXX
PRodReader* = ref TRodReader PRodReader* = ref TRodReader
const const
FileVersion* = "1019" # modify this if the rod-format changes! FileVersion* = "1022" # modify this if the rod-format changes!
var rodCompilerprocs*: TStrTable var rodCompilerprocs*: TStrTable
@ -541,14 +547,11 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
of "DATA": of "DATA":
r.dataIdx = r.pos + 2 # "(\10" r.dataIdx = r.pos + 2 # "(\10"
# We do not read the DATA section here! We read the needed objects on # We do not read the DATA section here! We read the needed objects on
# demand. # demand. And the DATA section comes last in the file, so we stop here:
skipSection(r) break
of "INIT": of "INIT":
r.initIdx = r.pos + 2 # "(\10" r.initIdx = r.pos + 2 # "(\10"
skipSection(r) skipSection(r)
of "CGEN":
r.cgenIdx = r.pos + 2
skipSection(r)
else: else:
MsgWriteln("skipping section: " & $r.pos) MsgWriteln("skipping section: " & $r.pos)
skipSection(r) skipSection(r)
@ -556,6 +559,12 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
inc(r.pos) inc(r.pos)
inc(r.line) inc(r.line)
proc startsWith(buf: cstring, token: string, pos = 0): bool =
var s = 0
while s < token.len and buf[pos+s] == token[s]: inc s
result = s == token.len
proc newRodReader(modfilename: string, crc: TCrc32, proc newRodReader(modfilename: string, crc: TCrc32,
readerIndex: int): PRodReader = readerIndex: int): PRodReader =
new(result) new(result)
@ -568,7 +577,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)
r.s = readFile(modfilename) if not open(r.memFile, modfilename): return nil
# we terminate the file explicitely with ``\0``, so the cast to `cstring`
# is save:
r.s = cast[cstring](r.memFile.mem)
if startsWith(r.s, "NIM:"): if startsWith(r.s, "NIM:"):
initIITable(r.index.tab) initIITable(r.index.tab)
initIITable(r.imports.tab) # looks like a ROD file initIITable(r.imports.tab) # looks like a ROD file

View file

@ -282,6 +282,11 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
result.add('@') result.add('@')
encodeVInt(ord(s.magic), result) encodeVInt(ord(s.magic), result)
if s.ast != nil: if s.ast != nil:
# we used to attempt to save space here by only storing a dummy AST if
# it is not necessary, but Nimrod's heavy compile-time evaluation features
# make that unfeasible nowadays:
encodeNode(w, s.info, s.ast, result)
when false:
var codeAst: PNode = nil var codeAst: PNode = nil
if not astNeeded(s): if not astNeeded(s):
codeAst = s.ast.sons[codePos] codeAst = s.ast.sons[codePos]
@ -458,6 +463,9 @@ proc writeRod(w: PRodWriter) =
f.write("DATA(" & rodNL) f.write("DATA(" & rodNL)
f.write(w.data) f.write(w.data)
f.write(')' & rodNL) f.write(')' & rodNL)
# write trailing zero which is necessary because we use memory mapped files
# for reading:
f.write("\0")
f.close() f.close()
#echo "interf: ", w.interf.len #echo "interf: ", w.interf.len

51
lib/pure/memfiles.nim Normal file
View file

@ -0,0 +1,51 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module provides support for `memory mapped files`:idx:
## (Posix's `mmap`:idx:) on the different operating systems.
## XXX Currently it is implemented with Nimrod's
## basic IO facilities and does not use any platform specific code!
## Oh and currently only ``fmRead`` is supported...
type
TMemFile* = object {.pure.}
file: TFile
buffer: pointer
fileLen: int
proc open*(f: var TMemFile, filename: string, mode: TFileMode = fmRead): bool =
## open a memory mapped file `f`. Returns true for success.
assert mode == fmRead
result = open(f.file, filename, mode)
var len = getFileSize(f.file)
if len < high(int):
f.fileLen = int(len)
f.buffer = alloc(f.fileLen)
if readBuffer(f.file, f.buffer, f.fileLen) != f.fileLen:
raise newException(EIO, "error while reading from file")
else:
raise newException(EIO, "file too big to fit in memory")
proc close*(f: var TMemFile) =
## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access.
dealloc(f.buffer)
close(f.file)
proc mem*(f: var TMemFile): pointer {.inline.} =
## retrives 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.
result = f.buffer
proc size*(f: var TMemFile): int {.inline.} =
## retrives the size of the memory mapped file `f`.
result = f.fileLen

View file

@ -11,7 +11,7 @@ Version 0.8.14
- thread local vs. global raiseHook() - thread local vs. global raiseHook()
- make pegs support a compile-time option and make c2nim use regexes instead - make pegs support a compile-time option and make c2nim use regexes instead
per default per default
- implement lib/pure/memfiles properly
incremental compilation incremental compilation
----------------------- -----------------------
@ -20,6 +20,8 @@ incremental compilation
- write test cases: needs test script support - write test cases: needs test script support
- test thread var - test thread var
- test method generation - test method generation
- test type converters
- test init sections
- test DLL interfacing! - test DLL interfacing!
- hallo.rod is missing initial statements: feature or bug? - hallo.rod is missing initial statements: feature or bug?
- fix remaining bugs - fix remaining bugs