version 0.7.6

This commit is contained in:
Andreas Rumpf 2009-04-22 15:55:27 +02:00
commit e792940f52
106 changed files with 8394 additions and 10083 deletions

View file

@ -1,13 +1,20 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Low level allocator for Nimrod.
# TODO:
# - eliminate "used" field
# - make searching for block O(1)
proc raiseOutOfMem {.noinline.} =
assert false
quit(1)
# ------------ platform specific chunk allocation code -----------------------
@ -15,20 +22,14 @@ when defined(posix):
const # XXX: make these variables for portability?
PROT_READ = 1 # page can be read
PROT_WRITE = 2 # page can be written
PROT_EXEC = 4 # page can be executed
PROT_NONE = 0 # page can not be accessed
MAP_SHARED = 1 # Share changes
MAP_PRIVATE = 2 # Changes are private
MAP_TYPE = 0xf # Mask for type of mapping
MAP_FIXED = 0x10 # Interpret addr exactly
MAP_ANONYMOUS = 0x20 # don't use a file
MAP_GROWSDOWN = 0x100 # stack-like segment
MAP_DENYWRITE = 0x800 # ETXTBSY
MAP_EXECUTABLE = 0x1000 # mark it as an executable
MAP_LOCKED = 0x2000 # pages are locked
MAP_NORESERVE = 0x4000 # don't check for reservations
MAP_PRIVATE = 2 # Changes are private
when defined(linux):
const MAP_ANONYMOUS = 0x20 # don't use a file
elif defined(macosx):
const MAP_ANONYMOUS = 0x1000
else:
const MAP_ANONYMOUS = 0 # other operating systems may not know about this
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
off: int): pointer {.header: "<sys/mman.h>".}
@ -42,7 +43,7 @@ when defined(posix):
raiseOutOfMem()
proc osDeallocPages(p: pointer, size: int) {.inline} =
munmap(p, len)
munmap(p, size)
elif defined(windows):
const
@ -51,20 +52,27 @@ elif defined(windows):
MEM_TOP_DOWN = 0x100000
PAGE_READWRITE = 0x04
MEM_DECOMMIT = 0x4000
MEM_RELEASE = 0x8000
proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
flProtect: int32): pointer {.
header: "<windows.h>", stdcall.}
proc VirtualFree(lpAddress: pointer, dwSize: int,
dwFreeType: int32) {.header: "<windows.h>", stdcall.}
proc osAllocPages(size: int): pointer {.inline.} =
result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE)
if result == nil: raiseOutOfMem()
proc osDeallocPages(p: pointer, size: int) {.inline.} =
nil
proc osDeallocPages(p: pointer, size: int) {.inline.} =
# according to Microsoft, 0 is the only correct value here:
VirtualFree(p, 0, MEM_RELEASE)
else:
{.error: "Port GC to your platform".}
{.error: "Port memory manager to your platform".}
# --------------------- end of non-portable code -----------------------------
@ -78,59 +86,79 @@ else:
# Guess the page size of the system; if it is the
# wrong value, performance may be worse (this is not
# for sure though), but GC still works; must be a power of two!
when defined(linux) or defined(windows) or defined(macosx):
const
PageShift = 12
PageSize = 1 shl PageShift # on 32 bit systems 4096
else:
{.error: "unkown page size".}
const
PageShift = if sizeof(pointer) == 4: 12 else: 13
PageSize = 1 shl PageShift # on 32 bit systems 4096
PageMask = PageSize-1
SmallChunkSize = PageSize # * 4
MemAlignment = sizeof(pointer)*2 # minimal memory block that can be allocated
BitsPerUnit = sizeof(int)*8
# a "unit" is a word, i.e. 4 bytes
# on a 32 bit system; I do not use the term "word" because under 32-bit
# Windows it is sometimes only 16 bits
MemAlign = 8 # minimal memory block that can be allocated
BitsPerPage = PageSize div MemAlignment
UnitsPerPage = BitsPerPage div BitsPerUnit
# how many units do we need to describe a page:
BitsPerPage = PageSize div MemAlign
UnitsPerPage = BitsPerPage div (sizeof(int)*8)
# how many ints do we need to describe a page:
# on 32 bit systems this is only 16 (!)
smallRequest = PageSize div 4
ChunkOsReturn = 1024 # in pages
ChunkOsReturn = 64 * PageSize
InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
debugMemMan = true # we wish to debug the memory manager...
type
PChunkDesc = ptr TChunkDesc
TChunkDesc {.final, pure.} = object
key: TAddress # address at bit 0
next: PChunkDesc
bits: array[0..127, int] # a bit vector
PChunkDescArray = ptr array[0..1000_000, PChunkDesc]
TChunkSet {.final, pure.} = object
counter, max: int
head: PChunkDesc
data: PChunkDescArray
when sizeof(int) == 4:
type THalfWord = int16
else:
type THalfWord = int32
# Compile time options:
coalescRight = true
coalescLeft = true
const
TrunkShift = 9
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and divisible by 64
TrunkMask = BitsPerTrunk - 1
IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
IntMask = 1 shl IntShift - 1
type
PTrunk = ptr TTrunk
TTrunk {.final.} = object
next: PTrunk # all nodes are connected with this pointer
key: int # start address at bit 0
bits: array[0..IntsPerTrunk-1, int] # a bit vector
TTrunkBuckets = array[0..1023, PTrunk]
TIntSet {.final.} = object
data: TTrunkBuckets
type
TAlignType = float
TFreeCell {.final, pure.} = object
zeroField: pointer # type info nil means cell is not used
next: ptr TFreeCell # next free cell in chunk
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
zeroField: pointer # nil means cell is not used (overlaid with typ field)
PChunk = ptr TChunk
TChunk {.final, pure.} = object
size: int # lowest two bits are used for merging:
# bit 0: chunk to the left is accessible and free
# bit 1: chunk to the right is accessible and free
len: int # for small object allocation
prev, next: PChunk # chunks of the same (or bigger) size
#len, used: THalfWord # index of next to allocate cell
PChunk = ptr TBaseChunk
PBigChunk = ptr TBigChunk
PSmallChunk = ptr TSmallChunk
TBaseChunk {.pure.} = object
prevSize: int # size of previous chunk; for coalescing
size: int # if < PageSize it is a small chunk
used: bool # later will be optimized into prevSize...
TSmallChunk = object of TBaseChunk
next, prev: PSmallChunk # chunks of the same size
freeList: ptr TFreeCell
data: float # a float for alignment purposes
free: int # how many bytes remain
acc: int # accumulator for small object allocation
data: TAlignType # start of usable memory
TBigChunk = object of TBaseChunk # not necessarily > PageSize!
next: PBigChunk # chunks of the same (or bigger) size
prev: PBigChunk
data: TAlignType # start of usable memory
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
proc roundup(x, v: int): int {.inline.} = return ((-x) and (v-1)) +% x
@ -147,240 +175,325 @@ assert(roundup(15, 8) == 16)
type
PLLChunk = ptr TLLChunk
TLLChunk {.pure.} = object ## *low-level* chunk
size: int
when sizeof(int) == 4:
align: int
size: int # remaining size
acc: int # accumulator
TAllocator {.final, pure.} = object
llmem: PLLChunk
UsedPagesCount, FreePagesCount, maxPagesCount: int
freeSmallChunks: array[0..smallRequest div MemAlign-1, PChunk]
freeBigChunks: array[0..ChunkOsReturn-1, PChunk]
currMem, maxMem: int # currently and maximum used memory size (allocated from OS)
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
chunkStarts: TIntSet
proc incCurrMem(a: var TAllocator, bytes: int) {.inline.} =
inc(a.currMem, bytes)
proc decCurrMem(a: var TAllocator, bytes: int) {.inline.} =
a.maxMem = max(a.maxMem, a.currMem)
dec(a.currMem, bytes)
proc getMaxMem(a: var TAllocator): int =
# Since we update maxPagesCount only when freeing pages,
# maxPagesCount may not be up to date. Thus we use the
# maximum of these both values here:
return max(a.currMem, a.maxMem)
var
allocator: TAllocator
proc llAlloc(a: var TAllocator, size: int): pointer =
# *low-level* alloc for the memory managers data structures. Deallocation
# is never done.
assert(size <= PageSize-8)
if a.llmem.size + size > PageSize:
a.llmem = osGetPages(PageSize)
inc(a.gUsedPages)
a.llmem.size = 8
result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.size)
inc(llmem.size, size)
if a.llmem == nil or size > a.llmem.size:
var request = roundup(size+sizeof(TLLChunk), PageSize)
a.llmem = cast[PLLChunk](osAllocPages(request))
incCurrMem(a, request)
a.llmem.size = request - sizeof(TLLChunk)
a.llmem.acc = sizeof(TLLChunk)
result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.acc)
dec(a.llmem.size, size)
inc(a.llmem.acc, size)
zeroMem(result, size)
const
InitChunkSetSize = 1024 # must be a power of two!
proc ChunkSetInit(s: var TChunkSet) =
s.data = cast[PChunkDescArray](llAlloc(InitChunkSetSize * sizeof(PChunkDesc)))
s.max = InitChunkSetSize-1
s.counter = 0
s.head = nil
proc ChunkSetGet(t: TChunkSet, key: TAddress): PChunkDesc =
var h = cast[int](key) and t.max
while t.data[h] != nil:
if t.data[h].key == key: return t.data[h]
h = nextTry(h, t.max)
return nil
proc ChunkSetRawInsert(t: TChunkSet, data: PChunkDescArray,
desc: PChunkDesc) =
var h = cast[int](desc.key) and t.max
while data[h] != nil:
assert(data[h] != desc)
h = nextTry(h, t.max)
assert(data[h] == nil)
data[h] = desc
proc ChunkSetEnlarge(t: var TChunkSet) =
var oldMax = t.max
t.max = ((t.max+1)*2)-1
var n = cast[PChunkDescArray](llAlloc((t.max + 1) * sizeof(PChunkDescArray)))
for i in 0 .. oldmax:
if t.data[i] != nil:
ChunkSetRawInsert(t, n, t.data[i])
tlsf_free(t.data)
t.data = n
proc ChunkSetPut(t: var TChunkSet, key: TAddress): PChunkDesc =
var h = cast[int](key) and t.max
while true:
var x = t.data[h]
if x == nil: break
if x.key == key: return x
h = nextTry(h, t.max)
if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
ChunkSetEnlarge(t)
inc(t.counter)
h = cast[int](key) and t.max
while t.data[h] != nil: h = nextTry(h, t.max)
assert(t.data[h] == nil)
# the new page descriptor goes into result
result = cast[PChunkDesc](llAlloc(sizeof(TChunkDesc)))
result.next = t.head
result.key = key
t.head = result
t.data[h] = result
# ---------- slightly higher level procs --------------------------------------
proc in_Operator(s: TChunkSet, cell: PChunk): bool =
var u = cast[TAddress](cell)
var t = ChunkSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlignment
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
else:
result = false
proc incl(s: var TCellSet, cell: PCell) =
var u = cast[TAddress](cell)
var t = ChunkSetPut(s, u shr PageShift)
u = (u %% PageSize) /% MemAlignment
t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or
(1 shl (u %% BitsPerUnit))
proc excl(s: var TCellSet, cell: PCell) =
var u = cast[TAddress](cell)
var t = ChunkSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlignment
t.bits[u /% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and
not (1 shl (u %% BitsPerUnit)))
iterator elements(t: TChunkSet): PChunk {.inline.} =
# while traversing it is forbidden to add pointers to the tree!
var r = t.head
while r != nil:
var i = 0
while i <= high(r.bits):
var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
# modifying operations are not allowed during traversation
var j = 0
while w != 0: # test all remaining bits for zero
if (w and 1) != 0: # the bit is set!
yield cast[PCell]((r.key shl PageShift) or # +%
(i*%BitsPerUnit+%j) *% MemAlignment)
inc(j)
w = w shr 1
inc(i)
r = r.next
# ------------- chunk management ----------------------------------------------
proc removeChunk(a: var TAllocator, c: PChunk) {.inline.} =
if c.prev != nil: c.prev.next = c.next
if c.next != nil: c.next.prev = c.prev
if a.freeChunks[c.size div PageSize] == c:
a.freeChunks[c.size div PageSize] = c.next
proc addChunk(a: var TAllocator, c: PChunk) {.inline.} =
var s = abs(c.size) div PageSize
proc IntSetGet(t: TIntSet, key: int): PTrunk =
var it = t.data[key and high(t.data)]
while it != nil:
if it.key == key: return it
it = it.next
result = nil
proc IntSetPut(t: var TIntSet, key: int): PTrunk =
result = IntSetGet(t, key)
if result == nil:
result = cast[PTrunk](llAlloc(allocator, sizeof(result^)))
result.next = t.data[key and high(t.data)]
t.data[key and high(t.data)] = result
result.key = key
proc Contains(s: TIntSet, key: int): bool =
var t = IntSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
else:
result = false
proc Incl(s: var TIntSet, key: int) =
var t = IntSetPut(s, key shr TrunkShift)
var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
proc Excl(s: var TIntSet, key: int) =
var t = IntSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
(1 shl (u and IntMask))
proc ContainsOrIncl(s: var TIntSet, key: int): bool =
var t = IntSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
if not result:
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
(1 shl (u and IntMask))
else:
Incl(s, key)
result = false
# ------------- chunk management ----------------------------------------------
proc pageIndex(c: PChunk): int {.inline.} =
result = cast[TAddress](c) shr PageShift
proc pageIndex(p: pointer): int {.inline.} =
result = cast[TAddress](p) shr PageShift
proc pageAddr(p: pointer): PChunk {.inline.} =
result = cast[PChunk](cast[TAddress](p) and not PageMask)
assert(Contains(allocator.chunkStarts, pageIndex(result)))
var lastSize = PageSize
proc requestOsChunks(a: var TAllocator, size: int): PBigChunk =
incCurrMem(a, size)
result = cast[PBigChunk](osAllocPages(size))
assert((cast[TAddress](result) and PageMask) == 0)
result.next = nil
result.prev = nil
result.used = false
result.size = size
# update next.prevSize:
var nxt = cast[TAddress](result) +% size
assert((nxt and PageMask) == 0)
var next = cast[PChunk](nxt)
if pageIndex(next) in a.chunkStarts:
#echo("Next already allocated!")
next.prevSize = size
# set result.prevSize:
var prv = cast[TAddress](result) -% lastSize
assert((nxt and PageMask) == 0)
var prev = cast[PChunk](prv)
if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
#echo("Prev already allocated!")
result.prevSize = lastSize
else:
result.prevSize = 0 # unknown
lastSize = size # for next request
proc freeOsChunks(a: var TAllocator, p: pointer, size: int) =
# update next.prevSize:
var c = cast[PChunk](p)
var nxt = cast[TAddress](p) +% c.size
assert((nxt and PageMask) == 0)
var next = cast[PChunk](nxt)
if pageIndex(next) in a.chunkStarts:
next.prevSize = 0 # XXX used
excl(a.chunkStarts, pageIndex(p))
osDeallocPages(p, size)
decCurrMem(a, size)
proc isAccessible(p: pointer): bool {.inline.} =
result = Contains(allocator.chunkStarts, pageIndex(p))
proc ListAdd[T](head: var T, c: T) {.inline.} =
assert c.prev == nil
assert c.next == nil
c.next = head
if head != nil:
assert head.prev == nil
head.prev = c
head = c
proc ListRemove[T](head: var T, c: T) {.inline.} =
if c == head:
head = c.next
assert c.prev == nil
if head != nil: head.prev = nil
else:
assert c.prev != nil
c.prev.next = c.next
if c.next != nil: c.next.prev = c.prev
c.next = nil
c.prev = nil
c.next = a.freeChunks[s]
a.freeChunks[s] = c
proc isSmallChunk(c: PChunk): bool {.inline.} =
return c.size <= SmallChunkSize-smallChunkOverhead()
#return c.size < SmallChunkSize
proc chunkUnused(c: PChunk): bool {.inline.} =
result = not c.used
proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
var c = c
assert(c.size >= PageSize)
when coalescRight:
var ri = cast[PChunk](cast[TAddress](c) +% c.size)
assert((cast[TAddress](ri) and PageMask) == 0)
if isAccessible(ri) and chunkUnused(ri):
if not isSmallChunk(ri):
ListRemove(a.freeChunksList, cast[PBigChunk](ri))
inc(c.size, ri.size)
excl(a.chunkStarts, pageIndex(ri))
when coalescLeft:
if c.prevSize != 0:
var le = cast[PChunk](cast[TAddress](c) -% c.prevSize)
assert((cast[TAddress](le) and PageMask) == 0)
if isAccessible(le) and chunkUnused(le):
if not isSmallChunk(le):
ListRemove(a.freeChunksList, cast[PBigChunk](le))
inc(le.size, c.size)
excl(a.chunkStarts, pageIndex(c))
c = cast[PBigChunk](le)
proc freeChunk(a: var TAllocator, c: PChunk) =
assert(c.size > 0)
if c.size < PageSize: c.size = PageSize
var le = cast[PChunk](cast[TAddress](p) and not PageMask -% PageSize)
var ri = cast[PChunk](cast[TAddress](p) and not PageMask +%
c.size +% PageSize)
if isStartOfAChunk(ri) and ri.size < 0:
removeChunk(a, ri)
inc(c.size, -ri.size)
if isEndOfAChunk(le):
le = cast[PChunk](cast[TAddress](p) and not PageMask -%
le.chunkStart+PageSize)
if le.size < 0:
removeChunk(a, le)
inc(le.size, c.size)
addChunk(a, le)
return
c.size = -c.size
addChunk(a, c)
if c.size < ChunkOsReturn:
ListAdd(a.freeChunksList, c)
c.used = false
else:
freeOsChunks(a, c, c.size)
proc splitChunk(a: var TAllocator, c: PChunk, size: int) =
var rest = cast[PChunk](cast[TAddress](p) + size)
rest.size = size - c.size # results in negative number, because rest is free
addChunk(a, rest)
# mark pages as accessible:
ChunkTablePut(a, rest, bitAccessible)
proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
var rest = cast[PBigChunk](cast[TAddress](c) +% size)
rest.size = c.size - size
rest.used = false
rest.next = nil # XXX
rest.prev = nil
rest.prevSize = size
c.size = size
incl(a.chunkStarts, pageIndex(rest))
ListAdd(a.freeChunksList, rest)
proc getChunkOfSize(a: var TAllocator, size: int): PChunk =
for i in size..high(a.freeChunks):
result = a.freeChunks[i]
if result != nil:
if i != size: splitChunk(a, result, size)
else: removeChunk(a, result)
result.prev = nil
result.next = nil
break
proc getBigChunk(a: var TAllocator, size: int): PBigChunk =
# use first fit for now:
assert((size and PageMask) == 0)
result = a.freeChunksList
block search:
while result != nil:
assert chunkUnused(result)
if result.size == size:
ListRemove(a.freeChunksList, result)
break search
elif result.size > size:
splitChunk(a, result, size)
ListRemove(a.freeChunksList, result)
break search
result = result.next
if size < InitialMemoryRequest:
result = requestOsChunks(a, InitialMemoryRequest)
splitChunk(a, result, size)
else:
result = requestOsChunks(a, size)
result.prevSize = 0
result.used = true
incl(a.chunkStarts, pageIndex(result))
proc getSmallChunk(a: var TAllocator): PSmallChunk =
var res = getBigChunk(a, PageSize)
assert res.prev == nil
assert res.next == nil
result = cast[PSmallChunk](res)
# -----------------------------------------------------------------------------
proc getChunk(p: pointer): PChunk {.inline.} =
result = cast[PChunk](cast[TAddress](p) and not PageMask)
proc getCellSize(p: pointer): int {.inline.} =
var c = getChunk(p)
result = abs(c.size)
var c = pageAddr(p)
result = c.size
proc alloc(a: var TAllocator, size: int): pointer =
if size <= smallRequest:
# allocate a small block
proc alloc(a: var TAllocator, requestedSize: int): pointer =
var size = roundup(max(requestedSize, sizeof(TFreeCell)), MemAlign)
if size <= SmallChunkSize-smallChunkOverhead():
# allocate a small block: for small chunks, we use only its next pointer
var s = size div MemAlign
var c = a.freeSmallChunks[s]
if c == nil:
c = getChunkOfSize(0)
c = getSmallChunk(a)
c.freeList = nil
assert c.size == PageSize
c.size = size
a.freeSmallChunks[s] = c
c.len = 1
c.used = 1
c.chunkStart = 0
result = addr(c.data[0])
elif c.freeList != nil:
result = c.freeList
assert(c.freeList.zeroField == nil)
c.freeList = c.freeList.next
inc(c.used)
if c.freeList == nil: removeChunk(a, c)
c.acc = size
c.free = SmallChunkSize - smallChunkOverhead() - size
c.next = nil
c.prev = nil
ListAdd(a.freeSmallChunks[s], c)
result = addr(c.data)
else:
assert(c.len*size <= high(c.data))
result = addr(c.data[c.len*size])
inc(c.len)
inc(c.used)
if c.len*size > high(c.data): removeChunk(a, c)
assert c.next != c
assert c.size == size
if c.freeList == nil:
assert(c.acc + smallChunkOverhead() + size <= SmallChunkSize)
result = cast[pointer](cast[TAddress](addr(c.data)) +% c.acc)
inc(c.acc, size)
else:
result = c.freeList
assert(c.freeList.zeroField == nil)
c.freeList = c.freeList.next
dec(c.free, size)
if c.free < size:
ListRemove(a.freeSmallChunks[s], c)
else:
size = roundup(requestedSize+bigChunkOverhead(), PageSize)
# allocate a large block
var c = getChunkOfSize(size shr PageShift)
result = addr(c.data[0])
c.freeList = nil
c.size = size
c.len = 0
c.used = 0
c.chunkStart = 0
var c = getBigChunk(a, size)
assert c.prev == nil
assert c.next == nil
assert c.size == size
result = addr(c.data)
cast[ptr TFreeCell](result).zeroField = cast[ptr TFreeCell](1) # make it != nil
#echo("setting to one: ", $cast[TAddress](addr(cast[ptr TFreeCell](result).zeroField)))
proc contains(list, x: PSmallChunk): bool =
var it = list
while it != nil:
if it == x: return true
it = it.next
proc dealloc(a: var TAllocator, p: pointer) =
var c = getChunk(p)
if c.size <= smallRequest:
# free small block:
var c = pageAddr(p)
if isSmallChunk(c):
# `p` is within a small chunk:
var c = cast[PSmallChunk](c)
var s = c.size
var f = cast[ptr TFreeCell](p)
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
assert(f.zeroField != nil)
f.zeroField = nil
f.next = c.freeList
c.freeList = p
dec(c.used)
if c.used == 0: freeChunk(c)
c.freeList = f
# check if it is not in the freeSmallChunks[s] list:
if c.free < s:
assert c notin a.freeSmallChunks[s div memAlign]
# add it to the freeSmallChunks[s] array:
ListAdd(a.freeSmallChunks[s div memAlign], c)
inc(c.free, s)
else:
inc(c.free, s)
if c.free == SmallChunkSize-smallChunkOverhead():
ListRemove(a.freeSmallChunks[s div memAlign], c)
c.size = SmallChunkSize
freeBigChunk(a, cast[PBigChunk](c))
else:
# free big chunk
freeChunk(c)
freeBigChunk(a, cast[PBigChunk](c))
proc realloc(a: var TAllocator, p: pointer, size: int): pointer =
# could be made faster, but this is unnecessary, the GC does not use it anyway
@ -389,6 +502,34 @@ proc realloc(a: var TAllocator, p: pointer, size: int): pointer =
dealloc(a, p)
proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
var c = getChunk(p)
if c in a.accessibleChunks and c.size > 0:
result = cast[ptr TFreeCell](p).zeroField != nil
if isAccessible(p):
var c = pageAddr(p)
if not chunkUnused(c):
if isSmallChunk(c):
result = (cast[TAddress](p) -% cast[TAddress](c) -%
smallChunkOverhead()) %% c.size == 0 and
cast[ptr TFreeCell](p).zeroField != nil
else:
var c = cast[PBigChunk](c)
result = p == addr(c.data)
when isMainModule:
const iterations = 4000_000
incl(allocator.chunkStarts, 11)
assert 11 in allocator.chunkStarts
excl(allocator.chunkStarts, 11)
assert 11 notin allocator.chunkStarts
var p: array [1..iterations, pointer]
for i in 7..7:
var x = i * 8
for j in 1.. iterations:
p[j] = alloc(allocator, x)
for j in 1..iterations:
assert isAllocatedPtr(allocator, p[j])
echo($i, " used memory: ", $(allocator.currMem))
for j in countdown(iterations, 1):
#echo("j: ", $j)
dealloc(allocator, p[j])
assert(not isAllocatedPtr(allocator, p[j]))
echo($i, " after freeing: ", $(allocator.currMem))

View file

@ -5,7 +5,6 @@
; that is the only convention any C compiler supports.
\python{
# as usual I use my own preprocessor :-)
import os
def c(name):

View file

@ -43,8 +43,12 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
x^ = nil
return
assert(dest != nil)
unsureAsgnRef(cast[ppointer](dest),
newObj(mt, seq.len * mt.base.size + GenericSeqSize))
when defined(boehmGC):
unsureAsgnRef(cast[ppointer](dest),
newObj(seq.len * mt.base.size + GenericSeqSize))
else:
unsureAsgnRef(cast[ppointer](dest),
newObj(mt, seq.len * mt.base.size + GenericSeqSize))
var dst = cast[taddress](cast[ppointer](dest)^)
for i in 0..seq.len-1:
genericAssign(

View file

@ -1,486 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nimrod. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Module cgi</title>
<style type="text/css">
span.DecNumber {color: blue}
span.BinNumber {color: blue}
span.HexNumber {color: blue}
span.OctNumber {color: blue}
span.FloatNumber {color: blue}
span.Identifier {color: black}
span.Keyword {font-weight: bold}
span.StringLit {color: blue}
span.LongStringLit {color: blue}
span.CharLit {color: blue}
span.EscapeSequence {color: black}
span.Operator {color: black}
span.Punctation {color: black}
span.Comment, span.LongComment {font-style:italic; color: green}
span.RegularExpression {color: pink}
span.TagStart {color: yellow}
span.TagEnd {color: yellow}
span.Key {color: blue}
span.Value {color: black}
span.RawData {color: blue}
span.Assembler {color: blue}
span.Preprocessor {color: yellow}
span.Directive {color: yellow}
span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference,
span.Other {color: black}
div.navigation {
float: left;
width: 27%; //25em;
margin: 0; padding: 0; /*
border: 1px dashed gold; */
outline: 3px outset #7F7F7F; //#99ff99; //gold;
background-color: #7F7F7F;
}
div.navigation ul {list-style-type: none;}
div.navigation ul li a, div.navigation ul li a:visited {
font-weight: bold;
color: #FFFFFF; // #CC0000;
text-decoration: none;
}
div.navigation ul li a:hover {
font-weight: bold;
text-decoration: none;
/*outline: 2px outset #7F7F7F;*/
color: gold;
/* background-color: #FFFFFF; // #1A1A1A; // #779977;*/
}
div.content {
margin-left: 27%; // 25em;
padding: 0 1em;
/*border: 1px dashed #1A1A1A;*/
min-width: 16em;
}
dl.item dd, dl.item dd p {
margin-top:3px;
}
dl.item dd pre {
margin-left: 15pt;
border: 0px;
}
dl.item dt, dl.item dt pre {
margin: 20pt 0 0 0;
}
pre, span.tok {
background-color:#F9F9F9;
border:1px dotted #2F6FAB;
color:black;
}
/*
:Author: David Goodger
:Contact: goodger@python.org
:Date: Date: 2006-05-21 22:44:42 +0200 (Sun, 21 May 2006)
:Revision: Revision: 4564
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th { border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first { margin-top: 0 ! important }
.last, .with-subtitle { margin-bottom: 0 ! important }
.hidden { display: none }
a.toc-backref { text-decoration: none ; color: black }
blockquote.epigraph { margin: 2em 5em ; }
dl.docutils dd { margin-bottom: 0.5em }
div.abstract { margin: 2em 5em }
div.abstract p.topic-title { font-weight: bold ; text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ; border: medium outset ; padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title { font-weight: bold ; font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title { color: red ; font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication { margin: 2em 5em ; text-align: center ; font-style: italic }
div.dedication p.topic-title { font-weight: bold ; font-style: normal }
div.figure { margin-left: 2em ; margin-right: 2em }
div.footer, div.header { clear: both; font-size: smaller }
div.line-block { display: block ; margin-top: 1em ; margin-bottom: 1em }
div.line-block div.line-block { margin-top: 0 ; margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar { margin-left: 1em ; border: medium outset ;
padding: 1em ; background-color: #ffffee ; width: 40% ; float: right ;
clear: right }
div.sidebar p.rubric { font-family: sans-serif ; font-size: medium }
div.system-messages { margin: 5em }
div.system-messages h1 { color: red }
div.system-message { border: medium outset ; padding: 1em }
div.system-message p.system-message-title { color: red ; font-weight: bold }
div.topic { margin: 2em;}
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title { text-align: center }
h2.subtitle { text-align: center }
hr.docutils { width: 75% }
img.align-left { clear: left }
img.align-right { clear: right }
ol.simple, ul.simple { margin-bottom: 1em }
ol.arabic { list-style: decimal }
ol.loweralpha { list-style: lower-alpha }
ol.upperalpha { list-style: upper-alpha }
ol.lowerroman { list-style: lower-roman }
ol.upperroman { list-style: upper-roman }
p.attribution { text-align: right ; margin-left: 50% }
p.caption { font-style: italic }
p.credits { font-style: italic ; font-size: smaller }
p.label { white-space: nowrap }
p.rubric { font-weight:bold;font-size:larger;color:maroon;text-align:center}
p.sidebar-title {font-family: sans-serif ;font-weight: bold ;font-size: larger }
p.sidebar-subtitle {font-family: sans-serif ; font-weight: bold }
p.topic-title { font-weight: bold }
pre.address { margin-bottom: 0;margin-top:0;font-family:serif;font-size:100% }
pre.literal-block, pre.doctest-block {margin-left: 2em ;margin-right: 2em }
span.classifier {font-family: sans-serif;font-style: oblique }
span.classifier-delimiter {font-family: sans-serif;font-weight: bold }
span.interpreted {font-family: sans-serif }
span.option {white-space: nowrap }
span.pre {white-space: pre }
span.problematic {color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation { border-left: solid 1px gray; margin-left: 1px }
table.docinfo {margin: 2em 4em }
table.docutils {margin-top: 0.5em;margin-bottom: 0.5em }
table.footnote {border-left: solid 1px black;margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {padding-left: 0.5em;padding-right: 0.5em;
vertical-align: top}
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold;text-align: left;white-space: nowrap;padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {font-size: 100% }
ul.auto-toc { list-style-type: none }
/*a.reference { color: #E00000; font-weight:bold;}
a.reference:hover {color: #E00000;background-color: #ffff00;display: margin;
font-weight:bold;}*/
</style>
<script type="text/javascript">
//<![CDATA[
function toggleElem(id) {
var e = document.getElementById(id);
e.style.display = e.style.display == 'none' ? 'block' : 'none';
}
var gOpen = 'none'
function toggleAll() {
gOpen = gOpen == 'none' ? 'block' : 'none';
var i = 1
while (1) {
var e = document.getElementById("m"+i)
if (!e) break;
e.style.display = gOpen
i++;
}
document.getElementById('toggleButton').value =
gOpen == 'none' ? 'Show Details' : 'Hide Details';
}
//]]>
</script>
</head>
<body>
<div class="document" id="documentId">
<h1 class="title">Module cgi</h1>
<div class="navigation">
<p class="topic-title first">Navigation</p>
<ul class="simple">
<li>
<a class="reference" href="#6" id="56">Types</a>
<ul class="simple">
<li><a class="reference" href="#103">ECgi</a></li>
<li><a class="reference" href="#104">TRequestMethod</a></li>
</ul>
</li>
<li>
<a class="reference" href="#9" id="59">Procs</a>
<ul class="simple">
<li><a class="reference" href="#101">URLencode</a></li>
<li><a class="reference" href="#102">URLdecode</a></li>
<li><a class="reference" href="#105">readData</a></li>
<li><a class="reference" href="#106">validateData</a></li>
<li><a class="reference" href="#107">getContentLength</a></li>
<li><a class="reference" href="#108">getContentType</a></li>
<li><a class="reference" href="#109">getDocumentRoot</a></li>
<li><a class="reference" href="#110">getGatewayInterface</a></li>
<li><a class="reference" href="#111">getHttpAccept</a></li>
<li><a class="reference" href="#112">getHttpAcceptCharset</a></li>
<li><a class="reference" href="#113">getHttpAccept Encoding</a></li>
<li><a class="reference" href="#114">getHttpAccept Language</a></li>
<li><a class="reference" href="#115">getHttpConnection</a></li>
<li><a class="reference" href="#116">getHttpCookie</a></li>
<li><a class="reference" href="#117">getHttpHost</a></li>
<li><a class="reference" href="#118">getHttpReferer</a></li>
<li><a class="reference" href="#119">getHttpUserAgent</a></li>
<li><a class="reference" href="#120">getPathInfo</a></li>
<li><a class="reference" href="#121">getPathTranslated</a></li>
<li><a class="reference" href="#122">getQueryString</a></li>
<li><a class="reference" href="#123">getRemoteAddr</a></li>
<li><a class="reference" href="#124">getRemoteHost</a></li>
<li><a class="reference" href="#125">getRemoteIdent</a></li>
<li><a class="reference" href="#126">getRemotePort</a></li>
<li><a class="reference" href="#127">getRemoteUser</a></li>
<li><a class="reference" href="#128">getRequestMethod</a></li>
<li><a class="reference" href="#129">getRequestURI</a></li>
<li><a class="reference" href="#130">getScriptFilename</a></li>
<li><a class="reference" href="#131">getScriptName</a></li>
<li><a class="reference" href="#132">getServerAddr</a></li>
<li><a class="reference" href="#133">getServerAdmin</a></li>
<li><a class="reference" href="#134">getServerName</a></li>
<li><a class="reference" href="#135">getServerPort</a></li>
<li><a class="reference" href="#136">getServerProtocol</a></li>
<li><a class="reference" href="#137">getServerSignature</a></li>
<li><a class="reference" href="#138">getServerSoftware</a></li>
<li><a class="reference" href="#139">setTestData</a></li>
<li><a class="reference" href="#140">writeContentType</a></li>
</ul>
</li>
</ul>
</div>
<div class="content">
This module implements helper procs for CGI applictions. Example:<pre>
<span class="Keyword">import</span> <span class="Identifier">strtabs</span><span class="Punctation">,</span> <span class="Identifier">cgi</span>
<span class="Comment"># Fill the values when debugging:</span>
<span class="Keyword">when</span> <span class="Identifier">debug</span><span class="Punctation">:</span>
<span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;Klaus&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;123456&quot;</span><span class="Punctation">)</span>
<span class="Comment"># read the data into `myData`</span>
<span class="Keyword">var</span> <span class="Identifier">myData</span> <span class="Operator">=</span> <span class="Identifier">readData</span><span class="Punctation">(</span><span class="Punctation">)</span>
<span class="Comment"># check that the data's variable names are &quot;name&quot; or &quot;passwort&quot;</span>
<span class="Identifier">validateData</span><span class="Punctation">(</span><span class="Identifier">myData</span><span class="Punctation">,</span> <span class="StringLit">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">)</span>
<span class="Comment"># start generating content:</span>
<span class="Identifier">writeContentType</span><span class="Punctation">(</span><span class="Punctation">)</span>
<span class="Comment"># generate content:</span>
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;&lt;!DOCTYPE HTML PUBLIC </span><span class="EscapeSequence">\&quot;</span>-//W3C//DTD HTML 4.01//EN<span class="EscapeSequence">\&quot;</span><span class="EscapeSequence">&gt;\n</span><span class="StringLit">&quot;</span><span class="Punctation">)</span>
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt;&lt;body&gt;</span><span class="EscapeSequence">\n</span><span class="StringLit">&quot;</span><span class="Punctation">)</span>
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;your name: &quot;</span> <span class="Operator">&amp;</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">&quot;name&quot;</span><span class="Punctation">]</span><span class="Punctation">)</span>
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;your password: &quot;</span> <span class="Operator">&amp;</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">&quot;password&quot;</span><span class="Punctation">]</span><span class="Punctation">)</span>
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;&lt;/body&gt;&lt;/html&gt;&quot;</span><span class="Punctation">)</span></pre>
<div class="section" id="6">
<h1><a class="toc-backref" href="#56">Types</a></h1>
<dl class="item">
<dt id="103"><pre><span class="Identifier">ECgi</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">of</span> <span class="Identifier">EIO</span></pre></dt>
<dd>
the exception that is raised, if a CGI error occurs
</dd>
<dt id="104"><pre><span class="Identifier">TRequestMethod</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Comment">## query uses the POST method</span>
<span class="Identifier">methodGet</span> <span class="Comment">## query uses the GET method</span></pre></dt>
<dd>
the used request method
</dd>
</dl></div>
<div class="section" id="9">
<h1><a class="toc-backref" href="#59">Procs</a></h1>
<dl class="item">
<dt id="101"><pre><span class="Keyword">proc</span> <span class="Identifier">URLencode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Encodes a value to be HTTP safe: This means that characters in the set <tt class="docutils literal"><span class="pre">{'A'..'Z', 'a'..'z', '0'..'9', '_'}</span></tt> are carried over to the result, a space is converted to <tt class="docutils literal"><span class="pre">'+'</span></tt> and every other character is encoded as <tt class="docutils literal"><span class="pre">'%xx'</span></tt> where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes its hexadecimal value.
</dd>
<dt id="102"><pre><span class="Keyword">proc</span> <span class="Identifier">URLdecode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Decodes a value from its HTTP representation: This means that a <tt class="docutils literal"><span class="pre">'+'</span></tt> is converted to a space, <tt class="docutils literal"><span class="pre">'%xx'</span></tt> (where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes a hexadecimal value) is converted to the character with ordinal number <tt class="docutils literal"><span class="pre">xx</span></tt>, and and every other character is carried over.
</dd>
<dt id="105"><pre><span class="Keyword">proc</span> <span class="Identifier">readData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">allowedMethods</span><span class="Other">:</span> <span class="Identifier">set</span><span class="Other">[</span><span class="Identifier">TRequestMethod</span><span class="Other">]</span> <span class="Other">=</span> <span class="Other">{</span><span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Identifier">methodGet</span><span class="Other">}</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">PStringTable</span></pre></dt>
<dd>
Read CGI data. If the client does not use a method listed in the <cite>allowedMethods</cite> set, an <cite>ECgi</cite> exception is raised.
</dd>
<dt id="106"><pre><span class="Keyword">proc</span> <span class="Identifier">validateData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">data</span><span class="Other">:</span> <span class="Identifier">PStringTable</span><span class="Other">,</span> <span class="Identifier">validKeys</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
<dd>
validates data; raises <cite>ECgi</cite> if this fails. This checks that each variable name of the CGI <cite>data</cite> occurs in the <cite>validKeys</cite> array.
</dd>
<dt id="107"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentLength</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_LENGTH</span></tt> environment variable
</dd>
<dt id="108"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_TYPE</span></tt> environment variable
</dd>
<dt id="109"><pre><span class="Keyword">proc</span> <span class="Identifier">getDocumentRoot</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">DOCUMENT_ROOT</span></tt> environment variable
</dd>
<dt id="110"><pre><span class="Keyword">proc</span> <span class="Identifier">getGatewayInterface</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">GATEWAY_INTERFACE</span></tt> environment variable
</dd>
<dt id="111"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAccept</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT</span></tt> environment variable
</dd>
<dt id="112"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptCharset</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_CHARSET</span></tt> environment variable
</dd>
<dt id="113"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptEncoding</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_ENCODING</span></tt> environment variable
</dd>
<dt id="114"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptLanguage</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_LANGUAGE</span></tt> environment variable
</dd>
<dt id="115"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpConnection</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_CONNECTION</span></tt> environment variable
</dd>
<dt id="116"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpCookie</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_COOKIE</span></tt> environment variable
</dd>
<dt id="117"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_HOST</span></tt> environment variable
</dd>
<dt id="118"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpReferer</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_REFERER</span></tt> environment variable
</dd>
<dt id="119"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpUserAgent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_USER_AGENT</span></tt> environment variable
</dd>
<dt id="120"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathInfo</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">PATH_INFO</span></tt> environment variable
</dd>
<dt id="121"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathTranslated</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">PATH_TRANSLATED</span></tt> environment variable
</dd>
<dt id="122"><pre><span class="Keyword">proc</span> <span class="Identifier">getQueryString</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">QUERY_STRING</span></tt> environment variable
</dd>
<dt id="123"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_ADDR</span></tt> environment variable
</dd>
<dt id="124"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_HOST</span></tt> environment variable
</dd>
<dt id="125"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteIdent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_IDENT</span></tt> environment variable
</dd>
<dt id="126"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemotePort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_PORT</span></tt> environment variable
</dd>
<dt id="127"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteUser</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_USER</span></tt> environment variable
</dd>
<dt id="128"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestMethod</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_METHOD</span></tt> environment variable
</dd>
<dt id="129"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestURI</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_URI</span></tt> environment variable
</dd>
<dt id="130"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptFilename</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_FILENAME</span></tt> environment variable
</dd>
<dt id="131"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_NAME</span></tt> environment variable
</dd>
<dt id="132"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADDR</span></tt> environment variable
</dd>
<dt id="133"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAdmin</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADMIN</span></tt> environment variable
</dd>
<dt id="134"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt> environment variable
</dd>
<dt id="135"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerPort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PORT</span></tt> environment variable
</dd>
<dt id="136"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerProtocol</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PROTOCOL</span></tt> environment variable
</dd>
<dt id="137"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSignature</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SIGNATURE</span></tt> environment variable
</dd>
<dt id="138"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSoftware</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SOFTWARE</span></tt> environment variable
</dd>
<dt id="139"><pre><span class="Keyword">proc</span> <span class="Identifier">setTestData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">keysvalues</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
<dd>
fills the appropriate environment variables to test your CGI application. This can only simulate the 'GET' 'REQUEST_METHOD'. <cite>keysvalues</cite> should provide embedded (name, value)-pairs. Example:<pre><span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;Hanz&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;12345&quot;</span><span class="Punctation">)</span></pre>
</dd>
<dt id="140"><pre><span class="Keyword">proc</span> <span class="Identifier">writeContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span></pre></dt>
<dd>
call this before starting to send your HTML data to <cite>stdout</cite>. This is just a shorthand for: .. code-block:: Nimrod<blockquote>write(stdout, &quot;Content-type: text/htmlnn&quot;)</blockquote>
</dd>
</dl></div>
</div>
<small>Generated: 2008-12-08 20:29:49 UTC</small>
</div>
</body>
</html>

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -71,20 +71,40 @@ proc URLdecode*(s: string): string =
else: add(result, s[i])
inc(i)
proc addXmlChar(dest: var string, c: Char) {.inline.} =
case c
of '&': add(dest, "&amp;")
of '<': add(dest, "&lt;")
of '>': add(dest, "&gt;")
of '\"': add(dest, "&quot;")
else: add(dest, c)
proc XMLencode*(s: string): string =
## Encodes a value to be XML safe:
## * ``"`` is replaced by ``&quot;``
## * ``<`` is replaced by ``&lt;``
## * ``>`` is replaced by ``&gt;``
## * ``&`` is replaced by ``&amp;``
## * every other character is carried over.
result = ""
for i in 0..len(s)-1: addXmlChar(result, s[i])
type
ECgi* = object of EIO ## the exception that is raised, if a CGI error occurs
TRequestMethod* = enum ## the used request method
methodNone, ## no REQUEST_METHOD environment variable
methodPost, ## query uses the POST method
methodGet ## query uses the GET method
proc cgiError(msg: string) {.noreturn.} =
proc cgiError*(msg: string) {.noreturn.} =
## raises an ECgi exception with message `msg`.
var e: ref ECgi
new(e)
e.msg = msg
raise e
proc readData*(allowedMethods: set[TRequestMethod] =
{methodPost, methodGet}): PStringTable =
{methodNone, methodPost, methodGet}): PStringTable =
## Read CGI data. If the client does not use a method listed in the
## `allowedMethods` set, an `ECgi` exception is raised.
result = newStringTable()
@ -103,7 +123,11 @@ proc readData*(allowedMethods: set[TRequestMethod] =
cgiError("'REQUEST_METHOD' 'GET' is not supported")
# read from the QUERY_STRING environment variable:
enc = getenv("QUERY_STRING")
else: cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
else:
if methodNone in allowedMethods:
return result
else:
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
# decode everything in one pass:
var i = 0
@ -129,7 +153,6 @@ proc readData*(allowedMethods: set[TRequestMethod] =
setLen(value, 0) # reuse memory
while true:
case enc[i]
of '\0': return
of '%':
var x = 0
handleHexChar(enc[i+1], x)
@ -137,12 +160,13 @@ proc readData*(allowedMethods: set[TRequestMethod] =
inc(i, 2)
add(value, chr(x))
of '+': add(value, ' ')
of '=', '&': break
of '&', '\0': break
else: add(value, enc[i])
inc(i)
if enc[i] != '&': cgiError("'&' expected")
inc(i) # skip '='
result[name] = value
if enc[i] == '&': inc(i)
elif enc[i] == '\0': break
else: cgiError("'&' expected")
proc validateData*(data: PStringTable, validKeys: openarray[string]) =
## validates data; raises `ECgi` if this fails. This checks that each variable

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View file

@ -17267,5 +17267,5 @@ proc gtk_file_chooser_set_do_overwrite_confirmation*(chooser: PGtkFileChooser,
proc gtk_nimrod_init*() =
var
cmdLine {.importc: "cmdLine".}: array [0..255, cstring]
cmdCount {.importc: "cmdCount".}: int
cmdCount {.importc: "cmdCount".}: cint
gtk_init(addr(cmdLine), addr(cmdCount))

View file

@ -226,8 +226,8 @@ const
SQL_DRIVER_NOPROMPT* = 0
SQL_DRIVER_COMPLETE* = 1
SQL_DRIVER_PROMPT* = 2
SQL_DRIVER_COMPLETE_REQUIRED* = 3 # whether an attribute is a pointer or not
SQL_IS_POINTER* = (- 4)
SQL_DRIVER_COMPLETE_REQUIRED* = 3
SQL_IS_POINTER* = (- 4) # whether an attribute is a pointer or not
SQL_IS_UINTEGER* = (- 5)
SQL_IS_INTEGER* = (- 6)
SQL_IS_USMALLINT* = (- 7)
@ -737,7 +737,8 @@ proc SQLFreeStmt*(StatementHandle: TSqlHStmt, Option: TSqlUSmallInt): TSqlSmallI
proc SQLColAttribute*(StatementHandle: TSqlHStmt, ColumnNumber: TSqlUSmallInt,
FieldIdentifier: TSqlUSmallInt,
CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt,
StringLength: PSQLSMALLINT, NumericAttribute: TSqlPointer): TSqlSmallInt{.
StringLength: PSQLSMALLINT,
NumericAttribute: TSqlPointer): TSqlSmallInt{.
dynlib: odbclib, importc.}
proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: TSqlHandle,
CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
@ -756,7 +757,8 @@ proc SQLSpecialColumns*(StatementHandle: TSqlHStmt, IdentifierType: TSqlUSmallIn
CatalogName: PSQLCHAR, NameLength1: TSqlSmallInt,
SchemaName: PSQLCHAR, NameLength2: TSqlSmallInt,
TableName: PSQLCHAR, NameLength3: TSqlSmallInt,
Scope: TSqlUSmallInt, Nullable: TSqlUSmallInt): TSqlSmallInt{.
Scope: TSqlUSmallInt,
Nullable: TSqlUSmallInt): TSqlSmallInt{.
dynlib: odbclib, importc.}
proc SQLProcedures*(hstmt: TSqlHStmt, szTableQualifier: PSQLCHAR,
cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
@ -778,6 +780,7 @@ proc SQLStatistics*(hstmt: TSqlHStmt, CatalogName: PSQLCHAR,
NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
NameLength2: TSqlSmallInt, TableName: PSQLCHAR,
NameLength3: TSqlSmallInt, Unique: TSqlUSmallInt,
Reserved: TSqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
Reserved: TSqlUSmallInt): TSqlSmallInt {.
dynlib: odbclib, importc.}
{.pop.}
{.pop.}

View file

@ -1397,34 +1397,34 @@ proc glVertexPointer*(size: TGLint, atype: TGLenum, stride: TGLsizei,
pointer: Pointer){.dynlib: dllname, importc.}
proc glViewport*(x, y: TGLint, width, height: TGLsizei){.dynlib: dllname, importc.}
type
PFNGLARRAYELEMENTEXTPROC* = proc (i: TGLint)
PFNGLDRAWARRAYSEXTPROC* = proc (mode: TGLenum, first: TGLint, count: TGLsizei)
PFNGLVERTEXPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum,
PFN_GLARRAY_ELEMENT_EXTPROC* = proc (i: TGLint)
PFN_GLDRAW_ARRAYS_EXTPROC* = proc (mode: TGLenum, first: TGLint, count: TGLsizei)
PFN_GLVERTEX_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
stride, count: TGLsizei, pointer: Pointer)
PFNGLNORMALPOINTEREXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
PFN_GLNORMAL_POINTER_EXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
pointer: Pointer)
PFNGLCOLORPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum,
PFN_GLCOLOR_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
stride, count: TGLsizei, pointer: Pointer)
PFNGLINDEXPOINTEREXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
PFN_GLINDEX_POINTER_EXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
pointer: Pointer)
PFNGLTEXCOORDPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum,
PFN_GLTEXCOORD_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
stride, count: TGLsizei, pointer: Pointer)
PFNGLEDGEFLAGPOINTEREXTPROC* = proc (stride, count: TGLsizei,
PFN_GLEDGEFLAG_POINTER_EXTPROC* = proc (stride, count: TGLsizei,
pointer: PGLboolean)
PFNGLGETPOINTERVEXTPROC* = proc (pname: TGLenum, params: Pointer)
PFNGLARRAYELEMENTARRAYEXTPROC* = proc (mode: TGLenum, count: TGLsizei,
PFN_GLGET_POINTER_VEXT_PROC* = proc (pname: TGLenum, params: Pointer)
PFN_GLARRAY_ELEMENT_ARRAY_EXTPROC* = proc (mode: TGLenum, count: TGLsizei,
pi: Pointer) # WIN_swap_hint
PFNGLADDSWAPHINTRECTWINPROC* = proc (x, y: TGLint, width, height: TGLsizei)
PFNGLCOLORTABLEEXTPROC* = proc (target, internalFormat: TGLenum,
PFN_GLADDSWAPHINT_RECT_WINPROC* = proc (x, y: TGLint, width, height: TGLsizei)
PFN_GLCOLOR_TABLE_EXTPROC* = proc (target, internalFormat: TGLenum,
width: TGLsizei, format, atype: TGLenum,
data: Pointer)
PFNGLCOLORSUBTABLEEXTPROC* = proc (target: TGLenum, start, count: TGLsizei,
PFN_GLCOLOR_SUBTABLE_EXTPROC* = proc (target: TGLenum, start, count: TGLsizei,
format, atype: TGLenum, data: Pointer)
PFNGLGETCOLORTABLEEXTPROC* = proc (target, format, atype: TGLenum,
PFN_GLGETCOLOR_TABLE_EXTPROC* = proc (target, format, atype: TGLenum,
data: Pointer)
PFNGLGETCOLORTABLEPARAMETERIVEXTPROC* = proc (target, pname: TGLenum,
PFN_GLGETCOLOR_TABLE_PARAMETER_IVEXTPROC* = proc (target, pname: TGLenum,
params: PGLint)
PFNGLGETCOLORTABLEPARAMETERFVEXTPROC* = proc (target, pname: TGLenum,
PFN_GLGETCOLOR_TABLE_PARAMETER_FVEXTPROC* = proc (target, pname: TGLenum,
params: PGLfloat)
{.pop.}

View file

@ -7,18 +7,6 @@
# distribution, for details about the copyright.
#
# This file was created by a complicated procedure which saved me a considerable
# amount of time: the pcre.h header was converted to modpcre.h by hand, so that
# h2pas could handle it. Then I used pas2mor to generate a Nimrod binding.
# Unfortunately, I had to fix some things later on; thus don't do all this
# again! My manual changes will be lost!
# Converted by Pas2mor v1.37
#
# Automatically converted by H2Pas 0.99.16 from modpcre.h
# The following command line parameters were used:
# -D -c -l pcre.lib -T modpcre.h
{.compile: "pcre_all.c" .}
type

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2006 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -35,7 +35,7 @@ const
proc match*(s, pattern: string, matches: var openarray[string],
start: int = 0): bool
## returns ``true`` if ``s`` matches the ``pattern[start..]`` and
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
## the captured substrings in the array ``matches``. If it does not
## match, nothing is written into ``matches`` and ``false`` is
## returned.
@ -113,3 +113,42 @@ proc match(s, pattern: string, start: int = 0): bool =
proc find(s, pattern: string, start: int = 0): bool =
return matchOrFind(s, rawCompile(pattern, PCRE_MULTILINE), start) >= 0'i32
template `=~` *(s, pattern: expr): expr =
## This calls ``match`` with an implicit declared ``matches`` array that
## can be used in the scope of the ``=~`` call:
##
## .. code-block:: nimrod
##
## if line =~ r"\s*(\w+)\s*\=\s*(\w+)":
## # matches a key=value pair:
## echo("Key: ", matches[1])
## echo("Value: ", matches[2])
## elif line =~ r"\s*(\#.*)":
## # matches a comment
## # note that the implicit ``matches`` array is different from the
## # ``matches`` array of the first branch
## echo("comment: ", matches[1])
## else:
## echo("syntax error")
##
var matches: array[0..maxSubPatterns-1, string]
match(s, pattern, matches)
const ## common regular expressions
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier
reNatural* = r"\b\d+\b" ## describes a natural number
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
## describes a floating point number
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|" &
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
## describes a common email address
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
## describes an URL

View file

@ -1,6 +1,6 @@
=======================================================
The Nimrod Runtime Library
Copyright (C) 2004-2007 Andreas Rumpf
Copyright (C) 2004-2009 Andreas Rumpf
=======================================================
This is the file copying.txt, it applies to the Nimrod Run-Time Library

View file

@ -348,9 +348,9 @@ proc dbgStackFrame(s: string, start: int, currFrame: PExtendedFrame) =
proc CommandPrompt() =
# if we return from this routine, user code executes again
var
again: bool = True
again = True
dbgFramePtr = framePtr # for going down and up the stack
dbgDown: int = 0 # how often we did go down
dbgDown = 0 # how often we did go down
while again:
write(stdout, "*** endb| >>")
@ -478,10 +478,10 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
inc(dbgGlobalData.f.len)
proc endb(line: int) {.compilerproc.} =
# This proc is called before any Nimrod code line!
# This proc is called before every Nimrod code line!
# Thus, it must have as few parameters as possible to keep the
# code size small!
# check if we are at an enabled breakpoint or "in the mood"
# Check if we are at an enabled breakpoint or "in the mood"
framePtr.line = line # this is done here for smaller code size!
if dbgLineHook != nil: dbgLineHook()
case dbgState

View file

@ -58,7 +58,10 @@ when defined(posix):
proc nimLoadLibrary(path: string): TLibHandle =
result = dlopen(path, RTLD_NOW)
if result == nil:
raise newException(EInvalidLibrary, "could not load: " & path)
writeToStdErr("could not load: ")
writeToStdErr(path)
writeToStdErr("\n")
#raise newException(EInvalidLibrary, "could not load: " & path)
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
result = dlsym(lib, name)
@ -84,7 +87,10 @@ elif defined(windows) or defined(dos):
proc nimLoadLibrary(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path))
if result == nil:
raise newException(EInvalidLibrary, "could not load: " & path)
writeToStdErr("could not load: ")
writeToStdErr(path)
writeToStdErr("\n")
#raise newException(EInvalidLibrary, "could not load: " & path)
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
result = GetProcAddress(cast[THINSTANCE](lib), name)
@ -118,7 +124,10 @@ elif defined(mac):
NSDestroyObjectFileImage(img)
result = TLibHandle(modul)
if result == nil:
raise newException(EInvalidLibrary, "could not load: " & path)
writeToStdErr("could not load: ")
writeToStdErr(path)
writeToStdErr("\n")
#raise newException(EInvalidLibrary, "could not load: " & path)
proc nimGetProcAddr(lib: TLibHandle, cname: string): TProcAddr =
var

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -15,36 +15,9 @@
# * generational
# Future Improvements:
# * Both dlmalloc and TLSF lack zero-overhead object allocation. Thus, for
# small objects we should use our own allocator.
# * Support for multi-threading. However, locks for the reference counting
# might turn out to be too slow.
# ---------------------------------------------------------------------------
# Interface to TLSF:
const
useTLSF = false # benchmarking showed that *dlmalloc* is faster than *TLSF*
when useTLSF:
{.compile: "tlsf.c".}
proc tlsfUsed: int {.importc: "TLSF_GET_USED_SIZE", noconv.}
proc tlsfMax: int {.importc: "TLSF_GET_MAX_SIZE", noconv.}
proc tlsf_malloc(size: int): pointer {.importc, noconv.}
proc tlsf_free(p: pointer) {.importc, noconv.}
proc tlsf_realloc(p: pointer, size: int): pointer {.importc, noconv.}
else:
# use DL malloc
{.compile: "dlmalloc.c".}
proc tlsfUsed: int {.importc: "dlmalloc_footprint", noconv.}
proc tlsfMax: int {.importc: "dlmalloc_max_footprint", noconv.}
proc tlsf_malloc(size: int): pointer {.importc: "dlmalloc", noconv.}
proc tlsf_free(p: pointer) {.importc: "dlfree", noconv.}
proc tlsf_realloc(p: pointer, size: int): pointer {.
importc: "dlrealloc", noconv.}
# ---------------------------------------------------------------------------
proc getOccupiedMem(): int = return tlsfUsed()
@ -54,38 +27,13 @@ proc getTotalMem(): int = return tlsfMax()
# ---------------------------------------------------------------------------
const
debugGC = false # we wish to debug the GC...
logGC = false
traceGC = false # extensive debugging
reallyDealloc = true # for debugging purposes this can be set to false
cycleGC = true # (de)activate the cycle GC
stressGC = debugGC
# Guess the page size of the system; if it is the
# wrong value, performance may be worse (this is not
# for sure though), but GC still works; must be a power of two!
const
PageShift = if sizeof(pointer) == 4: 12 else: 13
PageSize = 1 shl PageShift # on 32 bit systems 4096
CycleIncrease = 2 # is a multiplicative increase
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
ZctThreshold = 256 # we collect garbage if the ZCT's size
# reaches this threshold
# this seems to be a good value
const
MemAlignment = 8 # BUGFIX: on AMD64, dlmalloc aligns at 8 byte boundary
BitsPerUnit = sizeof(int)*8
# a "unit" is a word, i.e. 4 bytes
# on a 32 bit system; I do not use the term "word" because under 32-bit
# Windows it is sometimes only 16 bits
BitsPerPage = PageSize div MemAlignment
UnitsPerPage = BitsPerPage div BitsPerUnit
# how many units do we need to describe a page:
# on 32 bit systems this is only 16 (!)
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
# NOTE: Most colors are currently unused
rcBlack = 0b000 # cell is colored black; in use or free
@ -101,40 +49,9 @@ type
TWalkOp = enum
waZctDecRef, waPush, waCycleDecRef
TCell {.pure.} = object
refcount: int # the refcount and some flags
typ: PNimType
when debugGC:
filename: cstring
line: int
PCell = ptr TCell
TFinalizer {.compilerproc.} = proc (self: pointer)
# A ref type can have a finalizer that is called before the object's
# storage is freed.
PPointer = ptr pointer
TByteArray = array[0..1000_0000, byte]
PByte = ptr TByteArray
PString = ptr string
PPageDesc = ptr TPageDesc
TBitIndex = range[0..UnitsPerPage-1]
TPageDesc {.final, pure.} = object
next: PPageDesc # all nodes are connected with this pointer
key: TAddress # start address at bit 0
bits: array[TBitIndex, int] # a bit vector
PPageDescArray = ptr array[0..1000_000, PPageDesc]
TCellSet {.final, pure.} = object
counter, max: int
head: PPageDesc
data: PPageDescArray
PCellArray = ptr array[0..100_000_000, PCell]
TCellSeq {.final, pure.} = object
len, cap: int
d: PCellArray
TGcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table
mask: TAddress # mask for fast pointer detection
@ -152,7 +69,6 @@ type
tempStack: TCellSeq # temporary stack for recursion elimination
var
gOutOfMem: ref EOutOfMemory
stackBottom: pointer
gch: TGcHeap
cycleThreshold: int = InitialCycleThreshold
@ -171,12 +87,10 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc.}
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.}
proc raiseOutOfMem() {.noreturn.} =
if gOutOfMem == nil:
writeToStdErr("out of memory; cannot even throw an exception")
quit(1)
gOutOfMem.msg = "out of memory"
raise gOutOfMem
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
if (c.refcount and colorMask) != rcZct:
c.refcount = c.refcount and not colorMask or rcZct
add(s, c)
proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata
@ -198,11 +112,6 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
if result > 0: result = result shr rcShift
else: result = 0
proc gcAlloc(size: int): pointer =
result = tlsf_malloc(size)
if result == nil: raiseOutOfMem()
zeroMem(result, size)
proc GC_disable() = inc(recGcLock)
proc GC_enable() =
if recGcLock > 0: dec(recGcLock)
@ -221,160 +130,10 @@ proc GC_disableMarkAndSweep() =
cycleThreshold = high(cycleThreshold)-1
# set to the max value to suppress the cycle detector
proc nextTry(h, maxHash: int): int {.inline.} =
result = ((5*h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
when BitsPerPage mod BitsPerUnit != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# ------------------- cell set handling ---------------------------------------
proc inOperator(s: TCellSeq, c: PCell): bool {.inline.} =
for i in 0 .. s.len-1:
if s.d[i] == c: return True
return False
proc add(s: var TCellSeq, c: PCell) {.inline.} =
if s.len >= s.cap:
s.cap = s.cap * 3 div 2
var d = cast[PCellArray](tlsf_malloc(s.cap * sizeof(PCell)))
if d == nil: raiseOutOfMem()
copyMem(d, s.d, s.len * sizeof(PCell))
tlsf_free(s.d)
s.d = d
# BUGFIX: realloc failes on AMD64, sigh...
#s.d = cast[PCellArray](tlsf_realloc(s.d, s.cap * sizeof(PCell)))
#if s.d == nil: raiseOutOfMem()
s.d[s.len] = c
inc(s.len)
proc addZCT(s: var TCellSeq, c: PCell) =
if (c.refcount and colorMask) != rcZct:
c.refcount = c.refcount and not colorMask or rcZct
add(s, c)
proc init(s: var TCellSeq, cap: int = 1024) =
s.len = 0
s.cap = cap
s.d = cast[PCellArray](gcAlloc(cap * sizeof(PCell)))
const
InitCellSetSize = 1024 # must be a power of two!
proc CellSetInit(s: var TCellSet) =
s.data = cast[PPageDescArray](gcAlloc(InitCellSetSize * sizeof(PPageDesc)))
s.max = InitCellSetSize-1
s.counter = 0
s.head = nil
proc CellSetDeinit(s: var TCellSet) =
var it = s.head
while it != nil:
var n = it.next
tlsf_free(it)
it = n
s.head = nil # play it safe here
tlsf_free(s.data)
s.data = nil
s.counter = 0
proc CellSetGet(t: TCellSet, key: TAddress): PPageDesc =
var h = cast[int](key) and t.max
while t.data[h] != nil:
if t.data[h].key == key: return t.data[h]
h = nextTry(h, t.max)
return nil
proc CellSetRawInsert(t: TCellSet, data: PPageDescArray,
desc: PPageDesc) =
var h = cast[int](desc.key) and t.max
while data[h] != nil:
assert(data[h] != desc)
h = nextTry(h, t.max)
assert(data[h] == nil)
data[h] = desc
proc CellSetEnlarge(t: var TCellSet) =
var oldMax = t.max
t.max = ((t.max+1)*2)-1
var n = cast[PPageDescArray](gcAlloc((t.max + 1) * sizeof(PPageDesc)))
for i in 0 .. oldmax:
if t.data[i] != nil:
CellSetRawInsert(t, n, t.data[i])
tlsf_free(t.data)
t.data = n
proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
var h = cast[int](key) and t.max
while true:
var x = t.data[h]
if x == nil: break
if x.key == key: return x
h = nextTry(h, t.max)
if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
CellSetEnlarge(t)
inc(t.counter)
h = cast[int](key) and t.max
while t.data[h] != nil: h = nextTry(h, t.max)
assert(t.data[h] == nil)
# the new page descriptor goes into result
result = cast[PPageDesc](gcAlloc(sizeof(TPageDesc)))
result.next = t.head
result.key = key
t.head = result
t.data[h] = result
# ---------- slightly higher level procs --------------------------------------
proc contains(s: TCellSet, cell: PCell): bool =
var u = cast[TAddress](cell)
var t = CellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlignment
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
else:
result = false
proc incl(s: var TCellSet, cell: PCell) =
var u = cast[TAddress](cell)
var t = CellSetPut(s, u shr PageShift)
u = (u %% PageSize) /% MemAlignment
t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or
(1 shl (u %% BitsPerUnit))
proc excl(s: var TCellSet, cell: PCell) =
var u = cast[TAddress](cell)
var t = CellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlignment
t.bits[u /% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and
not (1 shl (u %% BitsPerUnit)))
iterator elements(t: TCellSet): PCell {.inline.} =
# while traversing it is forbidden to add pointers to the tree!
var r = t.head
while r != nil:
var i = 0
while i <= high(r.bits):
var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
# modifying operations are not allowed during traversation
var j = 0
while w != 0: # test all remaining bits for zero
if (w and 1) != 0: # the bit is set!
yield cast[PCell]((r.key shl PageShift) or # +%
(i*%BitsPerUnit+%j) *% MemAlignment)
inc(j)
w = w shr 1
inc(i)
r = r.next
# --------------- end of Cellset routines -------------------------------------
when debugGC:
proc writeCell(msg: CString, c: PCell) =
var kind = -1
@ -450,7 +209,6 @@ proc IsOnStack(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp)
proc doOperation(p: pointer, op: TWalkOp)
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp)
proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
# we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) =
@ -479,13 +237,13 @@ proc decRef(c: PCell) {.inline.} =
if c.refcount <% rcIncrement:
addZCT(gch.zct, c)
elif canBeCycleRoot(c):
possibleRoot(gch, c)
incl(gch.cycleRoots, c)
proc incRef(c: PCell) {.inline.} =
c.refcount = c.refcount +% rcIncrement
if canBeCycleRoot(c):
# OPT: the code generator should special case this
possibleRoot(gch, c)
incl(gch.cycleRoots, c)
proc nimGCref(p: pointer) {.compilerproc, inline.} = incRef(usrToCell(p))
proc nimGCunref(p: pointer) {.compilerproc, inline.} = decRef(usrToCell(p))
@ -534,26 +292,6 @@ proc initGC() =
gch.mask = 0
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
assert(n.kind == nkCase)
var d: int
var a = cast[TAddress](aa)
case n.typ.size
of 1: d = ze(cast[ptr int8](a +% n.offset)^)
of 2: d = ze(cast[ptr int16](a +% n.offset)^)
of 4: d = int(cast[ptr int32](a +% n.offset)^)
else: assert(false)
return d
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
var discr = getDiscriminant(aa, n)
if discr <% n.len:
result = n.sons[discr]
if result == nil: result = n.sons[n.len]
# n.sons[n.len] contains the ``else`` part (but may be nil)
else:
result = n.sons[n.len]
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
var d = cast[TAddress](dest)
case n.kind
@ -749,7 +487,7 @@ proc collectCycles(gch: var TGcHeap) =
CellSetDeinit(gch.cycleRoots)
gch.cycleRoots = newRoots
proc gcMark(p: pointer) = # {.fastcall.} =
proc gcMark(p: pointer) {.noinline.} =
# the addresses are not as objects on the stack, so turn them to objects:
var cell = usrToCell(p)
var c = cast[TAddress](cell)
@ -759,7 +497,7 @@ proc gcMark(p: pointer) = # {.fastcall.} =
incl(gch.stackCells, cell) # yes: mark it
# ----------------- stack management --------------------------------------
# inspired from Smart Eiffel (c)
# inspired from Smart Eiffel
proc stackSize(): int {.noinline.} =
var stackTop: array[0..1, pointer]
@ -885,7 +623,7 @@ else:
# in a platform independant way
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
when true:
when false:
# new version: several C compilers are too smart here
var
max = cast[TAddress](stackBottom)
@ -910,7 +648,7 @@ else:
max = stackBottom
registers: C_JmpBuf # The jmp_buf buffer is in the C stack.
sp: PPointer # Used to traverse the stack and registers assuming
# that `setjmp' will save registers in the C stack.
# that 'setjmp' will save registers in the C stack.
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
sp = cast[ppointer](addr(registers))
while sp <= max:
@ -937,7 +675,7 @@ proc updateZCT() =
dec(L)
d[j] = d[L]
c.refcount = c.refcount and not colorMask
# we have a new cell at position i, so don't increment i
# we have a new cell at position j, so don't increment j
else:
inc(j)
gch.zct.len = L

View file

@ -58,12 +58,12 @@ type
nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally,
nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt,
nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt,
nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkAccessStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType,
nnkBlockType, nnkVm, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkProcTy,
nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkCommentStmt,
nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkBlockType,
nnkVm, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy,
nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy,
nnkPtrTy, nnkVarTy, nnkProcTy, nnkEnumTy,
nnkEnumFieldDef, nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind]
TNimrodTypeKind* = enum
ntyNone, ntyBool, ntyChar, ntyEmpty,
@ -86,12 +86,24 @@ type
#[[[end]]]
type
TNimrodIdent = object of TObject
## represents a Nimrod identifier in the AST
TNimrodNode {.final.} = object # hidden
TNimrodSymbol {.final.} = object # hidden
TNimrodType {.final.} = object # hidden
PNimrodType* {.compilerproc.} = ref TNimrodType
## represents a Nimrod type in the compiler; currently this is not very
## useful as there is no API to deal with Nimrod types.
PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
## *ident*.
PNimrodNode* {.compilerproc.} = ref TNimrodNode
## represents a Nimrod AST node. Macros operate on this type.
expr* = PNimrodNode
stmt* = PNimrodNode
@ -100,23 +112,35 @@ type
# its father. How to do this without back references?
proc `[]`* (n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
## get `n`'s `i`'th child.
proc `[]=`* (n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
## provide access to `n`'s children
## set `n`'s `i`'th child to `child`.
type
TNimrodIdent = object of TObject
proc `!` *(s: string): TNimrodIdent {.magic: "StrToIdent".}
## constructs an identifier from the string `s`
converter StrToIdent*(s: string): TNimrodIdent {.magic: "StrToIdent".}
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
## converts a Nimrod identifier to a string
proc `==`* (a, b: TNimrodIdent): bool {.magic: "EqIdent".}
## compares two Nimrod identifiers
proc len*(n: PNimrodNode): int {.magic: "NLen".}
## returns the number of children of `n`.
## returns the number of children that a node has
proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
proc add*(father: PNimrodNode, child: openArray[PNimrodNode]) {.magic: "NAddMultiple".}
## adds the `child` to the `father` node
proc add*(father: PNimrodNode, children: openArray[PNimrodNode]) {.
magic: "NAddMultiple".}
## adds each `children` to the `father` node
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
## deletes `n` children of `father` starting at index `idx`.
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
## returns the `kind` of the node `n`.
proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
@ -133,43 +157,81 @@ proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".}
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
proc newNimNode*(kind: TNimrodNodeKind,
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
proc error*(msg: string) {.magic: "NError".}
## writes an error message at compile time
proc warning*(msg: string) {.magic: "NWarning".}
## writes a warning message at compile time
proc hint*(msg: string) {.magic: "NHint".}
## writes a hint message at compile time
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
## creates a string literal node from `s`
result = newNimNode(nnkStrLit)
result.strVal = s
proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
## creates a int literal node from `i`
result = newNimNode(nnkIntLit)
result.intVal = i
proc newIntLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
proc newFloatLitNode*(f: biggestFloat): PNimrodNode {.compileTime.} =
## creates a float literal node from `f`
result = newNimNode(nnkFloatLit)
result.floatVal = f
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = i
proc newIdentNode*(i: string): PNimrodNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent)
result.ident = !i
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
## converts the AST `n` to the concrete Nimrod code and wraps that
## in a string literal node
return newStrLitNode(repr(n))
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## macros that check the AST that is passed to them.
if n.kind != k: error("macro expects a node of kind: " & repr(k))
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
## checks that `n` has at least `min` children. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## macros that check its number of arguments.
if n.len < min: error("macro expects a node with " & $min & " children")
proc expectLen*(n: PNimrodNode, len: int) {.compileTime.} =
## checks that `n` has exactly `len` children. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## macros that check its number of arguments.
if n.len != len: error("macro expects a node with " & $len & " children")
proc newCall*(theProc: TNimrodIdent,
args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} =
## produces a new call node. `theProc` is the proc that is called with
## the arguments ``args[0..]``.
result = newNimNode(nnkCall)
result.add(newIdentNode(theProc))
result.add(args)
proc newCall*(theProc: string,
args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} =
## produces a new call node. `theProc` is the proc that is called with
## the arguments ``args[0..]``.
result = newNimNode(nnkCall)
result.add(newIdentNode(theProc))
result.add(args)

View file

@ -51,6 +51,20 @@ proc classify*(x: float): TFloatClass =
# XXX: fcSubnormal is not detected!
proc binom*(n, k: int): int {.noSideEffect.} =
## computes the binomial coefficient
if k <= 0: return 1
if 2*k > n: return binom(n, n-k)
result = n
for i in countup(2, k):
result = (result * (n + 1 - i)) div i
proc fac*(n: int): int {.noSideEffect.} =
## computes the faculty function
result = 1
for i in countup(2, n):
result = result * i
proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
## returns true, if x is a power of two, false otherwise.
## Negative numbers are not a power of two.
@ -74,6 +88,25 @@ proc countBits*(n: int32): int {.noSideEffect.}
include cntbits
proc sum*[T](x: openarray[T]): T {.noSideEffect.} =
## computes the sum of the elements in `x`.
## If `x` is empty, 0 is returned.
for i in items(x): result = result + i
proc mean*(x: openarray[float]): float {.noSideEffect.} =
## computes the mean of the elements in `x`.
## If `x` is empty, NaN is returned.
result = sum(x) / toFloat(len(x))
proc variance*(x: openarray[float]): float {.noSideEffect.} =
## computes the mean of the elements in `x`.
## If `x` is empty, NaN is returned.
result = 0.0
var m = mean(x)
for i in 0 .. high(x):
var diff = x[i] - m
result = result + diff*diff
result = result / toFloat(len(x))
when not defined(ECMAScript):
proc random*(max: int): int

View file

@ -360,7 +360,7 @@ proc findSuitableBlock(t: var TLSF, fl, sl: var int): Pbhdr =
proc extractBlockHdr(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
t.matrix[fl][sl] = b.freePtr.next
if t.matrix[fl][sl] != 0:
if t.matrix[fl][sl] != nil:
t.matrix[fl][sl].freePtr.prev = nil
else:
clear_bit(sl, t.slBitmap[fl])

View file

@ -1,12 +1,24 @@
/*
Nimrod's Runtime Library
(c) Copyright 2008 Andreas Rumpf
(c) Copyright 2009 Andreas Rumpf
See the file "copying.txt", included in this
distribution, for details about the copyright.
*/
/* compiler symbols:
__BORLANDC__
_MSC_VER
__WATCOMC__
__LCC__
__GNUC__
__DMC__
__POCC__
__TINYC__
*/
#ifndef NIMBASE_H
#define NIMBASE_H
@ -239,17 +251,6 @@ static unsigned long nimInf[2]={0xffffffff, 0x7fffffff};
# define INF (*(double*) nimInf)
#endif */
/* compiler symbols:
__BORLANDC__
_MSC_VER
__WATCOMC__
__LCC__
__GNUC__
__DMC__
__POCC__
__TINYC__
*/
/* C99 compiler? */
#if (defined(__STD_VERSION__) && (__STD_VERSION__ >= 199901))
# define HAVE_STDINT_H

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -178,12 +178,14 @@ proc ExistsFile*(filename: string): bool
proc JoinPath*(head, tail: string): string {.noSideEffect.}
## Joins two directory names to one.
##
## For example on Unix::
## For example on Unix:
##
## ..code-block:: nimrod
## JoinPath("usr", "lib")
##
## results in::
## results in:
##
## ..code-block:: nimrod
## "usr/lib"
##
## If head is the empty string, tail is returned.
@ -312,7 +314,7 @@ proc createDir*(dir: string)
##
## The directory may contain several
## subdirectories that do not exist yet. The full path is created. If this
## fails, `EOS` is raised. It does NOT fail if the path already exists
## fails, `EOS` is raised. It does **not** fail if the path already exists
## because for most usages this does not indicate an error.
proc existsDir*(dir: string): bool
@ -650,8 +652,7 @@ proc searchExtPos(s: string): int =
break # do not skip over path
proc SplitFilename(filename: string, name, extension: var string) =
var
extPos = searchExtPos(filename)
var extPos = searchExtPos(filename)
if extPos >= 0:
name = copy(filename, 0, extPos-1)
extension = copy(filename, extPos)
@ -664,14 +665,12 @@ proc normExt(ext: string): string =
else: result = extSep & ext
proc ChangeFileExt(filename, ext: string): string =
var
extPos = searchExtPos(filename)
var extPos = searchExtPos(filename)
if extPos < 0: result = filename & normExt(ext)
else: result = copy(filename, 0, extPos-1) & normExt(ext)
proc AppendFileExt(filename, ext: string): string =
var
extPos = searchExtPos(filename)
var extPos = searchExtPos(filename)
if extPos < 0: result = filename & normExt(ext)
else: result = filename #make a string copy here
@ -850,12 +849,12 @@ proc rawCreateDir(dir: string) =
OSError()
proc createDir(dir: string) =
for i in 0.. dir.len-1:
for i in 1.. dir.len-1:
if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
rawCreateDir(dir)
proc executeShellCommand(command: string): int =
return csystem(command)
result = csystem(command)
var
envComputed: bool = false
@ -892,8 +891,7 @@ else:
proc getEnvVarsC() =
# retrieves the variables of char** env of C's main proc
if not envComputed:
var
i: int = 0
var i = 0
while True:
if gEnv[i] == nil: break
add environment, $gEnv[i]
@ -904,13 +902,13 @@ proc findEnvVar(key: string): int =
getEnvVarsC()
var temp = key & '='
for i in 0..high(environment):
if findSubStr(temp, environment[i]) == 0: return i
if startsWith(environment[i], temp): return i
return -1
proc getEnv(key: string): string =
var i = findEnvVar(key)
if i >= 0:
return copy(environment[i], findSubStr("=", environment[i])+1)
return copy(environment[i], find(environment[i], '=')+1)
else:
var env = cgetenv(key)
if env == nil: return ""
@ -925,7 +923,7 @@ iterator iterOverEnvironment*(): tuple[key, value: string] =
## tuple is the name of the current variable stored, in the second its value.
getEnvVarsC()
for i in 0..high(environment):
var p = findSubStr("=", environment[i])
var p = find(environment[i], '=')
yield (copy(environment[i], 0, p-1), copy(environment[i], p+1))
proc putEnv(key, val: string) =
@ -1140,7 +1138,7 @@ else:
proc GetConfigDir(): string = return getEnv("HOME") & "/.config/"
var
cmdCount {.importc: "cmdCount".}: int
cmdCount {.importc: "cmdCount".}: cint
cmdLine {.importc: "cmdLine".}: cstringArray
proc paramStr(i: int): string =

View file

@ -10,7 +10,7 @@
## The ``parsecfg`` module implements a high performance configuration file
## parser. The configuration file's syntax is similar to the Windows ``.ini``
## format, but much more powerful, as it is not a line based parser. String
## literals, raw string literals and triple quote string literals are supported
## literals, raw string literals and triple quoted string literals are supported
## as in the Nimrod programming language.
## This is an example of how a configuration file may look like:
@ -135,7 +135,6 @@ proc handleDecChars(c: var TCfgParser, xi: var int) =
inc(c.bufpos)
proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
var xi: int
inc(c.bufpos) # skip '\'
case c.buf[c.bufpos]
of 'n', 'N':
@ -173,12 +172,12 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
Inc(c.bufpos)
of 'x', 'X':
inc(c.bufpos)
xi = 0
var xi = 0
handleHexChar(c, xi)
handleHexChar(c, xi)
add(tok.literal, Chr(xi))
of '0'..'9':
xi = 0
var xi = 0
handleDecChars(c, xi)
if (xi <= 255): add(tok.literal, Chr(xi))
else: tok.kind = tkInvalid
@ -191,12 +190,8 @@ proc HandleCRLF(c: var TCfgParser, pos: int): int =
else: result = pos
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
var
pos: int
ch: Char
buf: cstring
pos = c.bufPos + 1 # skip "
buf = c.buf # put `buf` in a register
var pos = c.bufPos + 1 # skip "
var buf = c.buf # put `buf` in a register
tok.kind = tkSymbol
if (buf[pos] == '\"') and (buf[pos + 1] == '\"'):
# long string literal:
@ -211,19 +206,18 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
Inc(pos)
of '\c', '\L':
pos = HandleCRLF(c, pos)
tok.literal = tok.literal & nl
add(tok.literal, nl)
of lexbase.EndOfFile:
tok.kind = tkInvalid
break
else:
add(tok.literal, buf[pos])
Inc(pos)
c.bufpos = pos +
3 # skip the three """
c.bufpos = pos + 3 # skip the three """
else:
# ordinary string literal
while true:
ch = buf[pos]
var ch = buf[pos]
if ch == '\"':
inc(pos) # skip '"'
break
@ -240,11 +234,8 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
c.bufpos = pos
proc getSymbol(c: var TCfgParser, tok: var TToken) =
var
pos: int
buf: cstring
pos = c.bufpos
buf = c.buf
var pos = c.bufpos
var buf = c.buf
while true:
add(tok.literal, buf[pos])
Inc(pos)
@ -253,11 +244,8 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
tok.kind = tkSymbol
proc skip(c: var TCfgParser) =
var
buf: cstring
pos: int
pos = c.bufpos
buf = c.buf
var pos = c.bufpos
var buf = c.buf
while true:
case buf[pos]
of ' ', '\t':

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -73,8 +73,7 @@ proc parseWord(s: string, i: int, w: var string,
inc(result)
proc handleShortOption(p: var TOptParser) =
var i: int
i = p.pos
var i = p.pos
p.kind = cmdShortOption
add(p.key, p.cmd[i])
inc(i)
@ -91,8 +90,7 @@ proc handleShortOption(p: var TOptParser) =
p.pos = i
proc next(p: var TOptParser) =
var i: int
i = p.pos
var i = p.pos
while p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = i
setlen(p.key, 0)

File diff suppressed because it is too large Load diff

View file

@ -104,17 +104,17 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerproc.} =
type
TReprClosure {.final.} = object # we cannot use a global variable here
# as this wouldn't be thread-safe
marked: TCellSet
marked: TCellSeq
recdepth: int # do not recurse endless
indent: int # indentation
proc initReprClosure(cl: var TReprClosure) =
CellSetInit(cl.marked)
Init(cl.marked)
cl.recdepth = -1 # default is to display everything!
cl.indent = 0
proc deinitReprClosure(cl: var TReprClosure) =
CellSetDeinit(cl.marked)
Deinit(cl.marked)
proc reprBreak(result: var string, cl: TReprClosure) =
add result, "\n"
@ -145,7 +145,6 @@ proc reprSequence(result: var string, p: pointer, typ: PNimType,
typ.Base, cl)
add result, "]"
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
cl: var TReprClosure) =
case n.kind
@ -172,11 +171,14 @@ proc reprRecord(result: var string, p: pointer, typ: PNimType,
proc reprRef(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) =
# we know that p is not nil here:
var cell = usrToCell(p)
when defined(boehmGC):
var cell = cast[PCell](p)
else:
var cell = usrToCell(p)
add result, "ref " & reprPointer(p)
if cell notin cl.marked:
# only the address is shown:
incl(cl.marked, cell)
add(cl.marked, cell)
add result, " --> "
reprAux(result, p, typ.base, cl)

View file

@ -100,7 +100,7 @@ proc readLine*(s: PStream): string =
type
PStringStream* = ref TStringStream ## a stream that encapsulates a string
TStringStream* = object of TStream
data: string
data*: string
pos: int
proc ssAtEnd(s: PStringStream): bool =

View file

@ -16,10 +16,10 @@ import
os, hashes, strutils
type
TStringTableMode* = enum # describes the tables operation mode
modeCaseSensitive, # the table is case sensitive
modeCaseInsensitive, # the table is case insensitive
modeStyleInsensitive # the table is style insensitive
TStringTableMode* = enum ## describes the tables operation mode
modeCaseSensitive, ## the table is case sensitive
modeCaseInsensitive, ## the table is case insensitive
modeStyleInsensitive ## the table is style insensitive
TKeyValuePair = tuple[key, val: string]
TKeyValuePairSeq = seq[TKeyValuePair]
TStringTable* = object of TObject
@ -61,14 +61,14 @@ iterator pairs*(t: PStringTable): tuple[key, value: string] =
yield (t.data[h].key, t.data[h].val)
type
TFormatFlag* = enum # flags for the `%` operator
useEnvironment, # use environment variable if the ``$key``
# is not found in the table
useEmpty, # use the empty string as a default, thus it
# won't throw an exception if ``$key`` is not
# in the table
useKey # do not replace ``$key`` if it is not found
# in the table (or in the environment)
TFormatFlag* = enum ## flags for the `%` operator
useEnvironment, ## use environment variable if the ``$key``
## is not found in the table
useEmpty, ## use the empty string as a default, thus it
## won't throw an exception if ``$key`` is not
## in the table
useKey ## do not replace ``$key`` if it is not found
## in the table (or in the environment)
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string
## The `%` operator for string tables.
@ -110,9 +110,7 @@ proc mustRehash(length, counter: int): bool =
result = (length * 2 < counter * 3) or (length - counter < 4)
proc nextTry(h, maxHash: THash): THash =
result = ((5 * h) + 1) and maxHash # For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
result = ((5 * h) + 1) and maxHash
proc RawGet(t: PStringTable, key: string): int =
var h: THash
@ -166,35 +164,30 @@ proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string =
if hasKey(t, key): return t[key]
if useEnvironment in flags: result = os.getEnv(key)
else: result = ""
if (result == ""):
if result.len == 0:
if useKey in flags: result = '$' & key
elif not (useEmpty in flags): raiseFormatException(key)
proc `%`(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string =
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
var
i, j: int
key: string
result = ""
i = strStart
while i <= len(f) + strStart - 1:
var i = 0
while i < len(f):
if f[i] == '$':
case f[i + 1]
case f[i+1]
of '$':
add(result, '$')
inc(i, 2)
of '{':
j = i + 1
while (j <= len(f) + strStart - 1) and (f[j] != '}'): inc(j)
key = copy(f, i + 2, j - 1)
result = result & getValue(t, flags, key)
var j = i + 1
while j < f.len and f[j] != '}': inc(j)
add(result, getValue(t, flags, copy(f, i+2, j-1)))
i = j + 1
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_':
j = i + 1
while (j <= len(f) + strStart - 1) and (f[j] in PatternChars): inc(j)
key = copy(f, i+1, j - 1)
result = result & getValue(t, flags, key)
var j = i + 1
while j < f.len and f[j] in PatternChars: inc(j)
add(result, getValue(t, flags, copy(f, i+1, j-1)))
i = j
else:
add(result, f[i])

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2006 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -9,8 +9,7 @@
## This module contains various string utility routines.
## See the module `regexprs` for regular expression support.
## All the routines here are avaiable for the EMCAScript target
## too!
## All the routines here are avaiable for the EMCAScript target too!
{.deadCodeElim: on.}
@ -33,13 +32,66 @@ type
const
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
## All the characters that count as whitespace.
Letters* = {'A'..'Z', 'a'..'z'}
## the set of letters
Digits* = {'0'..'9'}
## the set of digits
IdentChars* = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
## the set of characters an identifier can consist of
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
## the set of characters an identifier can start with
strStart* = 0 # this is only for bootstraping
# XXX: remove this someday
nl* = "\n" # this is only for bootstraping XXX: remove this somehow
proc strip*(s: string): string {.noSideEffect.}
## Strips leading and trailing whitespace from `s`.
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## The `substitution`:idx: operator performs string substitutions in
## `formatstr` and returns a modified `formatstr`. This is often called
## `string interpolation`:idx:.
##
## This is best explained by an example:
##
## .. code-block:: nimrod
## "$1 eats $2." % ["The cat", "fish"]
##
## Results in:
##
## .. code-block:: nimrod
## "The cat eats fish."
##
## The substitution variables (the thing after the ``$``)
## are enumerated from 1 to 9.
## Substitution variables can also be words (that is
## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
## indices are keys and with odd indices are the corresponding values.
## An example:
##
## .. code-block:: nimrod
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
##
## Results in:
##
## .. code-block:: nimrod
## "The cat eats fish."
##
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
## raised if an ill-formed format string has been passed to the `%` operator.
proc `%` *(formatstr, a: string): string {.noSideEffect.}
## This is the same as ``formatstr % [a]``.
proc addf*(s: var string, formatstr: string, a: openarray[string])
## The same as ``add(s, formatstr % a)``, but more efficient.
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
## Strips whitespace from `s` and returns the resulting string.
## If `leading` is true, leading whitespace is stripped.
## If `trailing` is true, trailing whitespace is stripped.
proc toLower*(s: string): string {.noSideEffect.}
## Converts `s` into lower case. This works only for the letters A-Z.
@ -65,15 +117,36 @@ proc normalize*(s: string): string {.noSideEffect.}
## Normalizes the string `s`. That means to convert it to lower case and
## remove any '_'. This is needed for Nimrod identifiers for example.
proc findSubStr*(sub, s: string, start: int = 0): int {.noSideEffect.}
proc findSubStr*(sub, s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc findSubStr*(sub: char, s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc findChars*(chars: set[char], s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
proc findSubStr*(sub: char, s: string, start: int = 0): int {.noSideEffect.}
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
proc findChars*(chars: set[char], s: string, start: int = 0): int {.noSideEffect.}
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect.}
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
@ -95,15 +168,15 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
## Splits the string `s` into substrings.
##
## Substrings are separated by a substring containing only `seps`.
## The seperator substrings are not returned in `sub`, nor are they part
## of `sub`.
## Examples::
## Examples:
##
## .. code-block:: nimrod
## for word in split(" this is an example "):
## writeln(stdout, word)
##
## Results in::
## Results in:
##
## .. code-block:: nimrod
## "this"
## "is"
## "an"
@ -123,18 +196,54 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
while last < len(s) and s[last] not_in seps: inc(last) # BUGFIX!
yield copy(s, first, last-1)
iterator split*(s: string, sep: char): string =
## Splits the string `s` into substrings.
##
## Substrings are separated by the character `sep`.
## Example:
##
## .. code-block:: nimrod
## for word in split(";;this;is;an;;example;;;", ';'):
## writeln(stdout, word)
##
## Results in:
##
## .. code-block:: nimrod
## ""
## ""
## "this"
## "is"
## "an"
## ""
## "example"
## ""
## ""
## ""
##
var last = 0
assert('\0' != sep)
if len(s) > 0:
# `<=` is correct here for the edge cases!
while last <= len(s):
var first = last
while last < len(s) and s[last] != sep: inc(last)
yield copy(s, first, last-1)
inc(last)
iterator splitLines*(s: string): string =
## Splits the string `s` into its containing lines. Each newline
## combination (CR, LF, CR-LF) is supported. The result strings contain
## no trailing ``\n``.
##
## Example::
## Example:
##
## .. code-block:: nimrod
## for line in lines("\nthis\nis\nan\n\nexample\n"):
## writeln(stdout, line)
##
## Results in::
## Results in:
##
## .. code-block:: nimrod
## ""
## "this"
## "is"
@ -164,6 +273,11 @@ proc splitSeq*(s: string, seps: set[char] = Whitespace): seq[string] {.
noSideEffect.}
## The same as `split`, but is a proc that returns a sequence of substrings.
proc splitSeq*(s: string, sep: char): seq[string] {.noSideEffect.} =
## The same as `split`, but is a proc that returns a sequence of substrings.
result = @[]
for sub in split(s, sep): add(result, sub)
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
## Compares two strings in a case insensitive manner. Returns:
##
@ -207,7 +321,7 @@ proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect.}
## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised.
proc ParseFloat*(s: string): float {.noSideEffect.}
proc ParseFloat*(s: string, start = 0): float {.noSideEffect.}
## Parses a decimal floating point value contained in `s`. If `s` is not
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
@ -217,37 +331,6 @@ proc ParseFloat*(s: string): float {.noSideEffect.}
proc toString*[Ty](x: Ty): string
## This generic proc is the same as the stringify operator `$`.
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## The substitution operator performs string substitutions in `formatstr`
## and returns the modified `formatstr`.
##
## This is best explained by an example::
##
## "$1 eats $2." % ["The cat", "fish"]
##
## Results in::
##
## "The cat eats fish."
##
## The substitution variables (the thing after the ``$``)
## are enumerated from 1 to 9.
## Substitution variables can also be words (that is
## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
## indices are keys and with odd indices are the corresponding values. Again
## an example::
##
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
##
## Results in::
##
## "The cat eats fish."
##
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
## raised if an ill-formed format string has been passed to the `%` operator.
proc `%` *(formatstr, a: string): string {.noSideEffect.}
## This is the same as `formatstr % [a]`.
proc repeatChar*(count: int, c: Char = ' '): string
## Returns a string of length `count` consisting only of
## the character `c`.
@ -260,7 +343,25 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect.}
## Returns true iff ``s`` ends with ``suffix``.
## If ``suffix == ""`` true is returned.
# implementation
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
inline.} =
## A shorthand for:
##
## .. code-block:: nimrod
## if dest.len > startLen: add(dest, sep)
##
## This is often useful for generating some code where the items need to
## be *separated* by `sep`. `sep` is only added if `dest` is longer than
## `startLen`. The following example creates a string describing
## an array of integers:
##
## .. code-block:: nimrod
## var arr = "["
## for x in items([2, 3, 5, 7, 11]):
## addSep(arr, startLen=len("["))
## add(arr, $x)
## add(arr, "]")
if dest.len > startLen: add(dest, sep)
proc allCharsInSet*(s: string, theSet: TCharSet): bool =
## returns true iff each character of `s` is in the set `theSet`.
@ -271,7 +372,7 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
proc quoteIfContainsWhite*(s: string): string =
## returns ``'"' & s & '"'`` if `s` contains a space and does not
## start with a quote, else returns `s`
if findChars({' ', '\t'}, s) >= 0 and s[0] != '"':
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
result = '"' & s & '"'
else:
result = s
@ -307,10 +408,8 @@ proc intToStr(x: int, minchars: int = 1): string =
proc toString[Ty](x: Ty): string = return $x
proc toOctal(c: char): string =
var
val: int
result = newString(3)
val = ord(c)
var val = ord(c)
for i in countdown(2, 0):
result[i] = Chr(val mod 8 + ord('0'))
val = val div 8
@ -326,18 +425,15 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
# security whole ...
return -1
proc `%`(formatstr: string, a: openarray[string]): string =
# the format operator
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
result = ""
proc addf(s: var string, formatstr: string, a: openarray[string]) =
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
var i = 0
while i < len(formatstr):
if formatstr[i] == '$':
case formatstr[i+1] # again we use the fact that strings
# are zero-terminated here
of '$':
add result, '$'
add s, '$'
inc(i, 2)
of '1'..'9':
var j = 0
@ -345,25 +441,29 @@ proc `%`(formatstr: string, a: openarray[string]): string =
while formatstr[i] in {'0'..'9'}:
j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i)
add result, a[j - 1]
add s, a[j - 1]
of '{':
var j = i+1
while formatstr[j] notin {'\0', '}'}: inc(j)
var x = findNormalized(copy(formatstr, i+2, j-1), a)
if x >= 0 and x < high(a): add result, a[x+1]
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j+1
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var j = i+1
while formatstr[j] in PatternChars: inc(j)
var x = findNormalized(copy(formatstr, i+1, j-1), a)
if x >= 0 and x < high(a): add result, a[x+1]
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j
else: raise newException(EInvalidValue, "invalid format string")
else:
add result, formatstr[i]
add s, formatstr[i]
inc(i)
proc `%`(formatstr: string, a: openarray[string]): string =
result = ""
addf(result, formatstr, a)
proc cmpIgnoreCase(a, b: string): int =
# makes usage of the fact that strings are zero-terminated
@ -377,9 +477,8 @@ proc cmpIgnoreCase(a, b: string): int =
# thus we compile without checks here
proc cmpIgnoreStyle(a, b: string): int =
var
i = 0
j = 0
var i = 0
var j = 0
while True:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
@ -400,14 +499,16 @@ proc splitSeq(s: string, seps: set[char]): seq[string] =
# ---------------------------------------------------------------------------
proc strip(s: string): string =
proc strip(s: string, leading = true, trailing = true): string =
const
chars: set[Char] = Whitespace
var
first = 0
last = len(s)-1
while s[first] in chars: inc(first)
while last >= 0 and s[last] in chars: dec(last)
if leading:
while s[first] in chars: inc(first)
if trailing:
while last >= 0 and s[last] in chars: dec(last)
result = copy(s, first, last)
proc toLower(c: Char): Char =
@ -451,7 +552,7 @@ proc preprocessSub(sub: string, a: var TSkipTable) =
for i in 0..0xff: a[chr(i)] = m+1
for i in 0..m-1: a[sub[i]] = m-i
proc findSubStrAux(sub, s: string, start: int, a: TSkipTable): int =
proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
# fast "quick search" algorithm:
var
m = len(sub)
@ -469,7 +570,7 @@ proc findSubStrAux(sub, s: string, start: int, a: TSkipTable): int =
proc findSubStr(sub, s: string, start: int = 0): int =
var a: TSkipTable
preprocessSub(sub, a)
result = findSubStrAux(sub, s, start, a)
result = findSubStrAux(s, sub, start, a)
# slow linear search:
#var
# i, j, M, N: int
@ -492,6 +593,20 @@ proc findSubStr(sub, s: string, start: int = 0): int =
# elif (i >= N):
# return -1
proc find(s, sub: string, start: int = 0): int =
var a: TSkipTable
preprocessSub(sub, a)
result = findSubStrAux(s, sub, start, a)
proc find(s: string, sub: char, start: int = 0): int =
for i in start..len(s)-1:
if sub == s[i]: return i
return -1
proc find(s: string, chars: set[char], start: int = 0): int =
for i in start..s.len-1:
if s[i] in chars: return i
return -1
proc findSubStr(sub: char, s: string, start: int = 0): int =
for i in start..len(s)-1:
@ -504,23 +619,21 @@ proc findChars(chars: set[char], s: string, start: int = 0): int =
return -1
proc contains(s: string, chars: set[char]): bool =
return findChars(chars, s) >= 0
return find(s, chars) >= 0
proc contains(s: string, c: char): bool =
return findSubStr(c, s) >= 0
return find(s, c) >= 0
proc contains(s, sub: string): bool =
return findSubStr(sub, s) >= 0
return find(s, sub) >= 0
proc replaceStr(s, sub, by: string): string =
var
i, j: int
a: TSkipTable
var a: TSkipTable
result = ""
preprocessSub(sub, a)
i = 0
var i = 0
while true:
j = findSubStrAux(sub, s, i, a)
var j = findSubStrAux(s, sub, i, a)
if j < 0: break
add result, copy(s, i, j - 1)
add result, by
@ -583,7 +696,10 @@ proc rawParseInt(s: string, index: var int): BiggestInt =
while s[i] == '_':
inc(i) # underscores are allowed and ignored
result = result * sign
index = i # store index back
if s[i] == '\0':
index = i # store index back
else:
index = -1 # BUGFIX: error!
else:
index = -1
@ -602,17 +718,17 @@ proc parseInt(s: string): int =
result = int(res) # convert to smaller integer type
proc ParseBiggestInt(s: string): biggestInt =
var
index: int = 0
var index = 0
result = rawParseInt(s, index)
if index == -1:
raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseFloat(s: string): float =
proc ParseFloat(s: string, start = 0): float =
var
esign = 1.0
sign = 1.0
exponent, i: int
i = start
exponent: int
flags: int
result = 0.0
if s[i] == '+': inc(i)
@ -677,7 +793,7 @@ proc ParseFloat(s: string): float =
proc toOct*(x: BiggestInt, len: int): string =
## converts `x` into its octal representation. The resulting string is
## always `len` characters long. No leading ``0c`` prefix is generated.
## always `len` characters long. No leading ``0o`` prefix is generated.
var
mask: BiggestInt = 7
shift: BiggestInt = 0
@ -701,7 +817,7 @@ proc toBin*(x: BiggestInt, len: int): string =
shift = shift + 1
mask = mask shl 1
proc escape*(s: string, prefix, suffix = "\""): string =
proc escape*(s: string, prefix = "\"", suffix = "\""): string =
## Escapes a string `s`. This does these operations (at the same time):
## * replaces any ``\`` by ``\\``
## * replaces any ``'`` by ``\'``
@ -723,8 +839,34 @@ proc escape*(s: string, prefix, suffix = "\""): string =
else: add(result, c)
add(result, suffix)
proc validEmailAddress*(s: string): bool =
## returns true if `s` seems to be a valid e-mail address.
## The checking also uses a domain list.
const
chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0
if s[i] notin chars or s[i] == '.': return false
while s[i] in chars:
if s[i] == '.' and s[i+1] == '.': return false
inc(i)
if s[i] != '@': return false
var j = len(s)-1
if s[j] notin letters: return false
while j >= i and s[j] in letters: dec(j)
inc(i) # skip '@'
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if s[i] != '\0': return false
var x = copy(s, j+1)
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
case toLower(x)
of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
"aero", "jobs", "museum": return true
return false
proc editDistance*(a, b: string): int =
## returns the edit distance between `s` and `t`. This uses the Levenshtein
## returns the edit distance between `a` and `b`. This uses the Levenshtein
## distance algorithm with only a linear memory overhead. This implementation
## is highly optimized!
var len1 = a.len

View file

@ -37,16 +37,21 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
proc rawNewString(space: int): NimString {.compilerProc.} =
var s = space
if s < 8: s = 7
result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
(s+1) * sizeof(char)))
#result.len = 0
when defined(boehmGC):
result = cast[NimString](boehmAllocAtomic(
sizeof(TGenericSeq) + (s+1) * sizeof(char)))
result.len = 0
result.data[0] = '\0'
else:
result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
(s+1) * sizeof(char)))
result.space = s
#result.data[0] = '\0'
proc mnewString(len: int): NimString {.exportc.} =
result = rawNewString(len)
result.len = len
#result.data[len] = '\0'
when defined(boehmGC):
result.data[len] = '\0'
proc toNimStr(str: CString, len: int): NimString {.compilerProc.} =
result = rawNewString(len)
@ -224,10 +229,11 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
GenericSeqSize))
elif newLen < result.len:
# we need to decref here, otherwise the GC leaks!
for i in newLen..result.len-1:
forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
GenericSeqSize +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef)
when not defined(boehmGC):
for i in newLen..result.len-1:
forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
GenericSeqSize +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef)
# and set the memory to nil:
zeroMem(cast[pointer](cast[TAddress](result) +% GenericSeqSize +%
(newLen*%elemSize)), (result.len-%newLen) *% elemSize)

View file

@ -14,8 +14,8 @@
## explicitly. Because of this there can not be a user-defined module named
## ``system``.
##
## *"The good thing about reinventing the wheel is that you can get a
## round one."*
## *The good thing about reinventing the wheel is that you can get a
## round one.*
{.push hints: off.}
@ -1044,8 +1044,9 @@ proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
# Fixup some magic symbols here:
{.fixup_system.} # This is an undocumented pragma that can only be used
# once in the system module.
#{.fixup_system.}
# This is an undocumented pragma that can only be used
# once in the system module.
proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
newSeq(result, x.len + y.len)
@ -1099,6 +1100,13 @@ proc find*[T, S](a: T, item: S): int {.inline.} =
inc(result)
result = -1
proc pop*[T](s: var seq[T]): T {.inline.} =
## returns the last item of `s` and decreases ``s.len`` by one. This treats
## `s` as a stack and implements the common *pop* operation.
var L = s.len-1
result = s[L]
setLen(s, L)
# ----------------- FPU ------------------------------------------------------
#proc disableFPUExceptions*()
@ -1401,16 +1409,95 @@ when not defined(EcmaScript) and not defined(NimrodVM):
# as it would recurse endlessly!
include arithm
{.pop.} # stack trace
include dyncalls
const
GenericSeqSize = (2 * sizeof(int))
proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
when not defined(boehmgc) and not defined(nogc):
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
assert(n.kind == nkCase)
var d: int
var a = cast[TAddress](aa)
case n.typ.size
of 1: d = ze(cast[ptr int8](a +% n.offset)^)
of 2: d = ze(cast[ptr int16](a +% n.offset)^)
of 4: d = int(cast[ptr int32](a +% n.offset)^)
else: assert(false)
return d
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
var discr = getDiscriminant(aa, n)
if discr <% n.len:
result = n.sons[discr]
if result == nil: result = n.sons[n.len]
# n.sons[n.len] contains the ``else`` part (but may be nil)
else:
result = n.sons[n.len]
when defined(boehmgc):
const
boehmLib = "/opt/lib/libgc.so"
proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.}
proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.}
proc boehmGCincremental {.
importc: "GC_enable_incremental", dynlib: boehmLib.}
proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.}
proc boehmAlloc(size: int): pointer {.
importc: "GC_malloc", dynlib: boehmLib.}
proc boehmAllocAtomic(size: int): pointer {.
importc: "GC_malloc_atomic", dynlib: boehmLib.}
proc boehmRealloc(p: pointer, size: int): pointer {.
importc: "GC_realloc", dynlib: boehmLib.}
proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
include cellsets
when defined(boehmGC):
proc initGC() = nil
#boehmGCincremental()
proc GC_disable() = boehmGC_disable()
proc GC_enable() = boehmGC_enable()
proc GC_fullCollect() = boehmGCfullCollect()
proc GC_setStrategy(strategy: TGC_Strategy) = nil
proc GC_enableMarkAndSweep() = nil
proc GC_disableMarkAndSweep() = nil
proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
proc growObj(old: pointer, newsize: int): pointer {.inline.} =
result = boehmRealloc(old, newsize)
proc newObj(size: int): pointer {.compilerproc.} =
result = boehmAlloc(size)
proc newSeq(baseSize, len: int): pointer {.compilerproc.} =
# XXX: overflow checks!
result = newObj(len * baseSize + GenericSeqSize)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).space = len
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
dest^ = src
proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
dest^ = src
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} =
dest^ = src
elif not defined(nogc):
include gc
include sysstr
include assign
include dyncalls
include repr
# we have to implement it here after gentostr for the cstrToNimStrDummy proc

View file

@ -1,72 +0,0 @@
# Memory handling for small objects
const
minRequestSize = 2 * sizeof(pointer) # minimal block is 16 bytes
pageSize = 1024 * sizeof(int)
pageBits = pageSize div minRequestSize
pageMask = pageSize-1
bitarraySize = pageBits div (sizeof(int)*8)
dataSize = pageSize - (bitarraySize+6) * sizeof(pointer)
type
TMinRequest {.final.} = object
next, prev: ptr TMinRequest # stores next free bit
TChunk {.pure.} = object # a chunk manages at least a page
size: int # lowest bit signals if it is a small chunk (0) or
# a big chunk (1)
typ: PNimType
next, prev: ptr TChunk
nextOfSameType: ptr TChunk
TSmallChunk = object of TChunk ## manages pageSize bytes for a type and a
## fixed size
free: int ## index of first free bit
bits: array[0..bitarraySize-1, int]
data: array[0..dataSize div minRequestSize - 1, TMinRequest]
PSmallChunk = ptr TSmallChunk
assert(sizeof(TSmallChunk) == pageSize)
proc getNewChunk(size: int, typ: PNimType): PSmallChunk =
result = cast[PSmallChunk](getPages(1))
result.size = PageSize
result.typ = typ
result.next = chunkHead
result.prev = nil
chunkHead.prev = result
chunkHead = result.next
result.nextOfSameType = cast[PSmallChunk](typ.chunk)
typ.chunk = result
result.free = addr(result.data[0])
result.data[0].next = addr(result.data[1])
result.data[0].prev = nil
result.data[high(result.data)].next = nil
result.data[high(result.data)].prev = addr(result.data[high(result.data)-1])
for i in 1..high(result.data)-1:
result.data[i].next = addr(result.data[i+1])
result.data[i].prev = addr(result.data[i-1])
proc newSmallObj(size: int, typ: PNimType): pointer =
var chunk = cast[PSmallChunk](typ.chunk)
if chunk == nil or chunk.free <= 0:
if chunk.free < 0: GC_collect()
chunk = getNewChunk(size, typ)
chunk.nextOfSameType = typ.chunk
typ.chunk = chunk
var idx = chunk.free
setBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
result = cast[pointer](cast[TAddress](addr(chunk.data)) +
minRequestSize * idx)
var res = cast[PMinRequest](result)
chunk.free = res.next
res.next
proc freeObj(obj: pointer) =
var chunk = cast[PChunk](cast[TAddress(obj) and not pageMask)
if size and 1 == 0: # small chunk
var idx = (cast[TAddress](obj) shr pageShift) div minRequestSize
resetBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)

View file

@ -9,6 +9,8 @@
## This module provides support to handle the Unicode UTF-8 encoding.
{.deadCodeElim: on.}
type
TRune* = int ## type that can hold any Unicode character
TRune16* = int16 ## 16 bit Unicode character