version 0.7.6
This commit is contained in:
parent
439aa2d04d
commit
e792940f52
106 changed files with 8394 additions and 10083 deletions
667
lib/alloc.nim
667
lib/alloc.nim
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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">"name"</span><span class="Punctation">,</span> <span class="StringLit">"Klaus"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">,</span> <span class="StringLit">"123456"</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 "name" or "passwort"</span>
|
||||
<span class="Identifier">validateData</span><span class="Punctation">(</span><span class="Identifier">myData</span><span class="Punctation">,</span> <span class="StringLit">"name"</span><span class="Punctation">,</span> <span class="StringLit">"password"</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">"<!DOCTYPE HTML PUBLIC </span><span class="EscapeSequence">\"</span>-//W3C//DTD HTML 4.01//EN<span class="EscapeSequence">\"</span><span class="EscapeSequence">>\n</span><span class="StringLit">"</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">"<html><head><title>Test</title></head><body></span><span class="EscapeSequence">\n</span><span class="StringLit">"</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">"your name: "</span> <span class="Operator">&</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">"name"</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">"your password: "</span> <span class="Operator">&</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">"password"</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">"</body></html>"</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">"name"</span><span class="Punctation">,</span> <span class="StringLit">"Hanz"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">,</span> <span class="StringLit">"12345"</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, "Content-type: text/htmlnn")</blockquote>
|
||||
|
||||
</dd>
|
||||
|
||||
</dl></div>
|
||||
|
||||
</div>
|
||||
|
||||
<small>Generated: 2008-12-08 20:29:49 UTC</small>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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, "&")
|
||||
of '<': add(dest, "<")
|
||||
of '>': add(dest, ">")
|
||||
of '\"': add(dest, """)
|
||||
else: add(dest, c)
|
||||
|
||||
proc XMLencode*(s: string): string =
|
||||
## Encodes a value to be XML safe:
|
||||
## * ``"`` is replaced by ``"``
|
||||
## * ``<`` is replaced by ``<``
|
||||
## * ``>`` is replaced by ``>``
|
||||
## * ``&`` is replaced by ``&``
|
||||
## * 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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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.}
|
||||
|
|
|
|||
|
|
@ -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.}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
286
lib/gc.nim
286
lib/gc.nim
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
33
lib/math.nim
33
lib/math.nim
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
34
lib/os.nim
34
lib/os.nim
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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
14
lib/repr.nim
14
lib/repr.nim
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
312
lib/strutils.nim
312
lib/strutils.nim
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue