mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-21 16:31:39 +00:00
version 0.7.8
This commit is contained in:
698
lib/tlsfnim.nim
Normal file
698
lib/tlsfnim.nim
Normal file
@@ -0,0 +1,698 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2008 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
|
||||
# Memory manager. Based on:
|
||||
# Two Levels Segregate Fit memory allocator (TLSF)
|
||||
# Version 2.4.2
|
||||
#
|
||||
# Written by Miguel Masmano Tello <mimastel@doctor.upv.es>
|
||||
#
|
||||
# Thanks to Ismael Ripoll for his suggestions and reviews
|
||||
#
|
||||
# Copyright (C) 2008, 2007, 2006, 2005, 2004
|
||||
#
|
||||
# This code is released using a dual license strategy: GPL/LGPL
|
||||
# You can choose the licence that better fits your requirements.
|
||||
#
|
||||
# Released under the terms of the GNU General Public License Version 2.0
|
||||
# Released under the terms of the GNU Lesser General Public License Version 2.1
|
||||
|
||||
|
||||
# Some IMPORTANT TLSF parameters
|
||||
const
|
||||
blockAlign = sizeof(pointer) * 2
|
||||
maxFli = 30
|
||||
maxLog2Sli = 5
|
||||
maxSli = 1 shl maxLog2Sli
|
||||
|
||||
fliOffset = 6 # tlsf structure just will manage blocks bigger than 128 bytes
|
||||
smallBlock = 128
|
||||
realFli = MaxFli - fliOffset
|
||||
|
||||
type
|
||||
TFreePtr {.final.} = object
|
||||
prev, next: PBhdr
|
||||
Pbhdr = ptr Tbhdr
|
||||
Tbhdr {.final.} = object
|
||||
prevHdr: Pbhdr # this is just valid if the first bit of size is set
|
||||
size: int # the size is stored in bytes
|
||||
# bit 0 indicates whether the block is used and
|
||||
# bit 1 allows to know whether the previous block is free
|
||||
freePtr: TFreePtr # at this offset bhdr.buffer starts (was a union in the
|
||||
# C version)
|
||||
TAreaInfo {.final.} = object # This structure is embedded at the beginning
|
||||
# of each area, giving us enough information
|
||||
# to cope with a set of areas
|
||||
theEnd: Pbhdr
|
||||
next: PAreaInfo
|
||||
PAreaInfo = ptr TAreaInfo
|
||||
|
||||
TLSF {.final.} = object
|
||||
tlsf_signature: int32 # the TLSF's structure signature
|
||||
usedSize, maxSize: int
|
||||
areaHead: PAreaInfo # A linked list holding all the existing areas
|
||||
|
||||
flBitmap: int32 # the first-level bitmap
|
||||
# This array should have a size of REAL_FLI bits
|
||||
slBitmap: array[0..realFli, int32] # the second-level bitmap
|
||||
matrix: array [0..realFli, array[0..maxSli, PBhdr]]
|
||||
|
||||
const
|
||||
minBlockSize = sizeof(TFreePtr)
|
||||
bhdrOverhead = sizeof(Tbhdr) - minBlockSize
|
||||
tlsfSignature = 0x2A59FA59
|
||||
ptrMask = sizeof(pointer) - 1
|
||||
blockSize = 0xFFFFFFFF - ptrMask
|
||||
memAlign = blockAlign - 1
|
||||
blockState = 0x1
|
||||
prevState = 0x2
|
||||
|
||||
freeBlock = 0x1 # bit 0 of the block size
|
||||
usedBlock = 0x0
|
||||
|
||||
prevFree = 0x2 # bit 1 of the block size
|
||||
prevUsed = 0x0
|
||||
|
||||
defaultAreaSize = 64*1024 # 1024*10
|
||||
pageSize = if defined(cpu32): 4096 else: 4096*2
|
||||
|
||||
|
||||
proc getNextBlock(adr: pointer, r: int): PBhdr {.inline.} =
|
||||
return cast[PBhdr](cast[TAddress](adr) +% r)
|
||||
|
||||
proc roundupSize(r: int): int = return (r +% memAlign) and not memAlign
|
||||
proc rounddownSize(r: int): int = return r and not memAlign
|
||||
proc roundup(x, v: int): int = return (((not x)+%1) and (v-%1)) +% x
|
||||
|
||||
proc addSize(s: PTLSF, b: Pbhdr) =
|
||||
inc(s.usedSize, (b.size and blockSize) + bhdrOverhead)
|
||||
s.maxSize = max(s.maxSize, s.usedSize)
|
||||
|
||||
proc removeSize(s: PTLSF, b: Pbhdr) =
|
||||
dec(s.usedSize, (b.size and blockSize) + bhdrOverhead)
|
||||
|
||||
# ------------ platform specific code -----------------------------------------
|
||||
|
||||
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
|
||||
|
||||
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
|
||||
off: int): pointer {.header: "<sys/mman.h>".}
|
||||
|
||||
proc getNewArea(size: var int): pointer {.inline.} =
|
||||
size = roundup(size, PageSize)
|
||||
result = mmap(0, size, PROT_READ or PROT_WRITE,
|
||||
MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
|
||||
if result == nil or result == cast[pointer](-1):
|
||||
raiseOutOfMem()
|
||||
|
||||
elif defined(windows):
|
||||
const
|
||||
MEM_RESERVE = 0x2000
|
||||
MEM_COMMIT = 0x1000
|
||||
MEM_TOP_DOWN = 0x100000
|
||||
PAGE_READWRITE = 0x04
|
||||
|
||||
proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
|
||||
flProtect: int32): pointer {.
|
||||
header: "<windows.h>", stdcall.}
|
||||
|
||||
proc getNewArea(size: var int): pointer {.inline.} =
|
||||
size = roundup(size, PageSize)
|
||||
result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT or MEM_TOP_DOWN,
|
||||
PAGE_READWRITE)
|
||||
if result == nil: raiseOutOfMem()
|
||||
|
||||
else:
|
||||
{.warning: "Generic code for allocating pages is used".}
|
||||
# generic implementation relying on malloc:
|
||||
proc malloc(size: int): pointer {.nodecl, importc.}
|
||||
|
||||
proc getNewArea(size: var int): pointer {.inline.} =
|
||||
size = roundup(size, PageSize)
|
||||
result = malloc(size)
|
||||
if result == nil: raiseOutOfMem()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# small fixed size allocator:
|
||||
|
||||
# Design: We manage pages. A page is of constant size, but not necessarily
|
||||
# the OS's page size. Pages are managed in a hash table taking advantage of
|
||||
# the fact that the OS is likely to give us pages with contingous numbers.
|
||||
# A page contains either small fixed size objects of the same size or
|
||||
# variable length objects. An object's size is always aligned at 16 byte
|
||||
# boundary. Huge objects are dealt with the TLSF algorithm.
|
||||
# The design supports iterating over any object in a fast way.
|
||||
|
||||
# A bitset contains any page that starts an allocated page. The page may be
|
||||
# empty however. This bitset can be used to quickly determine if a given
|
||||
# page belongs to the GC heap. The design of the memory allocator makes it
|
||||
# simple to return unused pages back to the OS.
|
||||
|
||||
|
||||
# Small bocks
|
||||
# -----------
|
||||
#
|
||||
# If we use a list in the free object's space. Allocation and deallocation are
|
||||
# O(1). Since any object is of the same size, iteration is quite efficient too.
|
||||
# However, pointer detection is easy too: Just check if the type-field is nil.
|
||||
# Deallocation sets it to nil.
|
||||
# Algorithm:
|
||||
|
||||
# i = 0
|
||||
# f = b.f # first free address
|
||||
# while i < max:
|
||||
# if a[i] == f: # not a valid block
|
||||
# f = f.next # next free address
|
||||
# else:
|
||||
# a[i] is a valid object of size s
|
||||
# inc(i)
|
||||
|
||||
# The zero count table is an array. Since we know that the RC is zero, we can
|
||||
# use the bits for an index into this array. Thus multiple ZCT tables are not
|
||||
# difficult to support and insertion and removal is O(1). We use negative
|
||||
# indexes for this. This makes it even fast enough (and necessary!) to do a ZCT
|
||||
# removal if the RC is incremented.
|
||||
#
|
||||
|
||||
# Huge blocks
|
||||
# -----------
|
||||
#
|
||||
# Huge blocks are always rounded up to a multiple of the page size. These are
|
||||
# called *strides*. We also need to keep an index structure
|
||||
# of (stridesize, pagelist).
|
||||
#
|
||||
|
||||
const
|
||||
MemAlign = 8
|
||||
PageShift = if sizeof(int) == 4: 12 else: 13
|
||||
PageSize = 1 shl PageShift
|
||||
type
|
||||
TFreeList {.final.} = object
|
||||
next, prev: ptr TFreeList
|
||||
|
||||
TPageDesc {.final.} = object # the start of a stride always starts with this!
|
||||
size: int # lowest bit is set, if it is a huge block
|
||||
free: ptr TFreeList # only used if it manages multiple cells
|
||||
snext, sprev: ptr TPageDesc # next and prev pagedescs with the same size
|
||||
|
||||
TCellArray {.final.} = object
|
||||
i: int # length
|
||||
d: ptr array [0..1000_000, TCell]
|
||||
|
||||
TPageManager = table[page, ptr TPageDesc]
|
||||
|
||||
TGcHeap {.final.} = object
|
||||
# structure that manages the garbage collected heap
|
||||
zct: TCellArray
|
||||
stackCells: TCellArray
|
||||
smallBlocks: array [PageSize div MemAlign, ptr TPageDesc]
|
||||
freeLists: array [PageSize div MemAlign, ptr TFreeList]
|
||||
pages: TPageManager
|
||||
usedPages: TPageList
|
||||
freePages: TPageList
|
||||
|
||||
# small blocks:
|
||||
proc allocSmall(var h: TGcHeap, size: int): pointer =
|
||||
var s = align(size)
|
||||
var f = h.freeLists[s]
|
||||
if f != nil:
|
||||
f.prev = f.next # remove from list
|
||||
f.next.prev = f.prev
|
||||
return f
|
||||
var p = h.smallBlocks[s]
|
||||
if p == nil or p.free == nil:
|
||||
p = newSmallBlock(s, p)
|
||||
h.smallBlocks[s] = p
|
||||
|
||||
|
||||
|
||||
proc decRef(cell: PCell) {.inline.} =
|
||||
assert(cell in ct.AT)
|
||||
assert(cell.refcount > 0) # this should be the case!
|
||||
assert(seqCheck(cell))
|
||||
dec(cell.refcount)
|
||||
if cell.refcount == 0:
|
||||
# add to zero count table:
|
||||
zct.d[zct.i] = cell
|
||||
cell.recfcount = -zct.i
|
||||
inc(zct.i)
|
||||
|
||||
proc incRef(cell: PCell) {.inline.} =
|
||||
assert(seqCheck(cell))
|
||||
if cell.refcount < 0:
|
||||
# remove from zero count table:
|
||||
zct.d[-cell.refcount] = zct.d[zct.i-1]
|
||||
dec(zct.i)
|
||||
cell.refcount = 1
|
||||
else:
|
||||
inc(cell.refcount)
|
||||
|
||||
proc asgnRef(dest: ppointer, src: pointer) =
|
||||
# the code generator calls this proc!
|
||||
assert(not isOnStack(dest))
|
||||
# BUGFIX: first incRef then decRef!
|
||||
if src != nil: incRef(usrToCell(src))
|
||||
if dest^ != nil: decRef(usrToCell(dest^))
|
||||
dest^ = src
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# helpers
|
||||
|
||||
const
|
||||
table: array[0..255, int8] = [
|
||||
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 6, 6, 6, 6,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7
|
||||
]
|
||||
|
||||
proc ls_bit(i: int32): int {.inline.} =
|
||||
var
|
||||
a: int = 0
|
||||
x: int = i and -i
|
||||
if x <=% 0xffff:
|
||||
if x <=% ff: a = 0
|
||||
else: a = 8
|
||||
elif x <=% 0xffffff: a = 16
|
||||
else: a = 24
|
||||
return table[x shr a] + a
|
||||
|
||||
proc ms_bit(i: int): int {.inline.} =
|
||||
var
|
||||
a = if i <=% 0xffff: (if i <=% 0xff: 0 else: 8) elif
|
||||
i <=% 0xffffff: 16 else: 24
|
||||
return table[i shr a] + a
|
||||
|
||||
proc set_bit[IX](nr: int, adr: var array[IX, int32]) {.inline.} =
|
||||
adr[nr shr 5] = adr[nr shr 5] or (1 shl (nr and 0x1f))
|
||||
|
||||
proc clear_bit[IX](nr: int, adr: var array[IX, int32]) {.inline.} =
|
||||
adr[nr shr 5] = adr[nr shr 5] and not (1 shl (nr and 0x1f))
|
||||
|
||||
proc mappingSearch(r, fl, sl: var int) {.inline.} =
|
||||
if r < smallBlock:
|
||||
fl = 0
|
||||
sl = r div (smallBlock div maxSli)
|
||||
else:
|
||||
var t = (1 shl (ms_bit(r) - maxLog2Sli)) - 1
|
||||
r = r + t
|
||||
fl = ms_bit(r)
|
||||
sl = (r shl (fl - maxLog2Sli)) - maxSli
|
||||
fl = fl - fliOffset
|
||||
r = r and not t
|
||||
|
||||
proc mappingInsert(r: int, fl, sl: var int) {.inline.} =
|
||||
if r < smallBlock:
|
||||
fl = 0
|
||||
sl = r div (smallBlock div maxSli)
|
||||
else:
|
||||
fl = ms_bit(r)
|
||||
sl = (r shr (fl - maxLog2Sli)) - maxSli
|
||||
fl = fl - fliOffset
|
||||
|
||||
proc findSuitableBlock(t: var TLSF, fl, sl: var int): Pbhdr =
|
||||
var tmp = t.slBitmap[fl] and ((not 0) shl sl)
|
||||
if tmp != 0:
|
||||
sl = ls_bit(tmp)
|
||||
result = t.matrix[fl][sl]
|
||||
else:
|
||||
fl = ls_bit(t.flBitmap and (not 0 shl (fl + 1)))
|
||||
if fl > 0: # likely
|
||||
sl = ls_bit(t.slBitmap[fl])
|
||||
result = t.matrix[fl][sl]
|
||||
|
||||
proc extractBlockHdr(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
|
||||
t.matrix[fl][sl] = b.freePtr.next
|
||||
if t.matrix[fl][sl] != nil:
|
||||
t.matrix[fl][sl].freePtr.prev = nil
|
||||
else:
|
||||
clear_bit(sl, t.slBitmap[fl])
|
||||
if t.slBitmap[fl] == 0:
|
||||
clear_bit(fl, t.flBitmap)
|
||||
b.freePtr.prev = nil
|
||||
b.freePtr.next = nil
|
||||
|
||||
proc extractBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
|
||||
if b.freePtr.next != nil:
|
||||
b.freePtr.next.freePtr.prev = b.freePtr.prev
|
||||
if b.freePtr.prev != nil:
|
||||
b.freePtr.prev.freePtr.next = b.freePtr.next
|
||||
if t.matrix[fl][sl] == b:
|
||||
t.matrix[fl][sl] = b.freePtr.next
|
||||
if t.matrix[fl][sl] == nil:
|
||||
clear_bit(sl, t.slBitmap[fl])
|
||||
if t.slBitmap[fl] == 0:
|
||||
clear_bit(fl, t.flBitmap)
|
||||
b.freePtr.prev = nil
|
||||
b.freePtr.next = nil
|
||||
|
||||
proc insertBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
|
||||
b.freePtr.prev = nil
|
||||
b.freePtr.next = t.matrix[fl][sl]
|
||||
if t.matrix[fl][sl] != nil:
|
||||
t.matrix[fl][sl].freePtr.prev = b
|
||||
t.matrix[fl][sl] = b
|
||||
set_bit(sl, t.slBitmap[fl])
|
||||
set_bit(fl, t.flBitmap)
|
||||
|
||||
proc getBuffer(b: Pbhdr): pointer {.inline.} =
|
||||
result = cast[pointer](addr(b.freePtr))
|
||||
|
||||
proc processArea(area: pointer, size: int): Pbhdr =
|
||||
var
|
||||
b, lb, ib: Pbhdr
|
||||
ai: PAreaInfo
|
||||
ib = cast[Pbhdr](area)
|
||||
if sizeof(TAreaInfo) < minBlockSize:
|
||||
ib.size = minBlockSize or usedBlock or prevUsed
|
||||
else
|
||||
ib.size = roundupSize(sizeof(TAreaInfo)) or usedBlock or prevUsed
|
||||
b = getNextBlock(getBuffer(ib), ib.size and blockSize)
|
||||
b.size = rounddownSize(size - 3 * bhdrOverhead - (ib.size and blockSize)) or
|
||||
usedBlock or prevUsed
|
||||
b.freePtr.prev = nil
|
||||
b.freePtr.next = nil
|
||||
lb = getNextBlock(getBuffer(b), b.size and blockSize)
|
||||
lb.prevHdr = b
|
||||
lb.size = 0 or usedBlock or prevFree
|
||||
ai = cast[PAreaInfo](getBuffer(ib))
|
||||
ai.next = nil
|
||||
ai.theEnd = lb
|
||||
return ib
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Begin of the allocator code
|
||||
|
||||
proc initMemoryPool(memPoolSize: int, memPool: pointer): int =
|
||||
var
|
||||
t: PLSF
|
||||
b, ib: Pbhdr
|
||||
|
||||
if memPool == nil or memPoolSize < sizeof(TLSF) + bhdrOverhead * 8:
|
||||
writeToStdErr("initMemoryPool(): memory_pool invalid\n")
|
||||
return -1
|
||||
|
||||
if (cast[TAddress](memPool) and ptrMask) != 0:
|
||||
writeToStdErr("initMemoryPool(): memPool must be aligned to a word\n")
|
||||
return -1
|
||||
t = cast[PLSF](memPool)
|
||||
# Check if already initialised
|
||||
if t.signature == tlsfSignature:
|
||||
b = getNextBlock(memPool, roundupSize(sizeof(TLSF)))
|
||||
return b.size and blockSize
|
||||
zeroMem(memPool, sizeof(TLSF))
|
||||
|
||||
t.signature = tlsfSignature
|
||||
ib = processArea(getNextBlock(memPool, roundupSize(sizeof(TLSF))),
|
||||
rounddownSize(memPoolSize - sizeof(TLSF)))
|
||||
b = getNextBlock(getBuffer(ib), ib.size and blockSize)
|
||||
freeEx(getBuffer(b), t)
|
||||
t.areaHead = cast[PAreaInfo](getBuffer(ib))
|
||||
|
||||
t.used_size = memPoolSize - (b.size and blockSize)
|
||||
t.max_size = t.used_size
|
||||
return b.size and blockSize
|
||||
|
||||
|
||||
proc addNewArea(area: pointer, areaSize: int, t: var TLSF): int =
|
||||
var
|
||||
p, ptrPrev, ai: PAreaInfo
|
||||
ib0, b0, lb0, ib1, b1, lb1, nextB: Pbhdr
|
||||
|
||||
zeroMem(area, areaSize)
|
||||
p = t.areaHead
|
||||
ptrPrev = 0
|
||||
|
||||
ib0 = processArea(area, areaSize)
|
||||
b0 = getNextBlock(getBuffer(ib0), ib0.size and blockSize)
|
||||
lb0 = getNextBlock(getBuffer(b0), b0.size and blockSize)
|
||||
|
||||
# Before inserting the new area, we have to merge this area with the
|
||||
# already existing ones
|
||||
while p != nil:
|
||||
ib1 = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
|
||||
b1 = getNextBlock(getBuffer(ib1), ib1.size and blockSize)
|
||||
lb1 = p.theEnd
|
||||
|
||||
# Merging the new area with the next physically contigous one
|
||||
if cast[TAddress](ib1) == cast[TAddress](lb0) +% bhdrOverhead:
|
||||
if t.areaHead == p:
|
||||
t.areaHead = p.next
|
||||
p = p.next
|
||||
else:
|
||||
ptrPrev.next = p.next
|
||||
p = p.next
|
||||
b0.size = rounddownSize((b0.size and blockSize) +
|
||||
(ib1.size and blockSize) + 2 * bhdrOverhead) or
|
||||
usedBlock or prevUsed
|
||||
b1.prevHdr = b0
|
||||
lb0 = lb1
|
||||
continue
|
||||
|
||||
# Merging the new area with the previous physically contigous one
|
||||
if getBuffer(lb1) == pointer(ib0):
|
||||
if t.areaHead == p:
|
||||
t.areaHead = p.next
|
||||
p = p.next
|
||||
else:
|
||||
ptrPrev.next = p.next
|
||||
p = p.next
|
||||
lb1->size = rounddownSize((b0.size and blockSize) +
|
||||
(ib0.size and blockSize) + 2 * bhdrOverhead) or
|
||||
usedBlock or (lb1.size and prevState)
|
||||
nextB = getNextBlock(getBuffer(lb1), lb1.size and blockSize)
|
||||
nextB.prevHdr = lb1
|
||||
b0 = lb1
|
||||
ib0 = ib1
|
||||
continue
|
||||
ptrPrev = p
|
||||
p = p.next
|
||||
|
||||
# Inserting the area in the list of linked areas
|
||||
ai = cast[PAreaInfo](getBuffer(ib0))
|
||||
ai.next = t.areaHead
|
||||
ai.theEnd = lb0
|
||||
t.areaHead = ai
|
||||
freeEx(getBuffer(b0), memPool)
|
||||
return (b0.size and blockSize)
|
||||
|
||||
proc mallocEx(asize: int, t: var TLSF): pointer =
|
||||
var
|
||||
b, b2, nextB: Pbhdr
|
||||
fl, sl, tmpSize, size: int
|
||||
|
||||
size = if asize < minBlockSize: minBlockSize else: roundupSize(asize)
|
||||
|
||||
# Rounding up the requested size and calculating fl and sl
|
||||
mappingSearch(size, fl, sl)
|
||||
|
||||
# Searching a free block, recall that this function changes the values
|
||||
# of fl and sl, so they are not longer valid when the function fails
|
||||
b = findSuitableBlock(tlsf, fl, sl)
|
||||
if b == nil:
|
||||
# Growing the pool size when needed
|
||||
# size plus enough room for the required headers:
|
||||
var areaSize = max(size + bhdrOverhead * 8, defaultAreaSize)
|
||||
var area = getNewArea(areaSize)
|
||||
addNewArea(area, areaSize, t)
|
||||
# Rounding up the requested size and calculating fl and sl
|
||||
mappingSearch(size, fl, sl)
|
||||
# Searching a free block
|
||||
b = findSuitableBlock(t, fl, sl)
|
||||
if b == nil:
|
||||
raiseOutOfMem()
|
||||
|
||||
extractBlockHdr(b, t, fl, sl)
|
||||
|
||||
#-- found:
|
||||
nextB = getNextBlock(getBuffer(b), b.size and blockSize)
|
||||
# Should the block be split?
|
||||
tmpSize = (b.size and blockSize) - size
|
||||
if tmpSize >= sizeof(Tbhdr):
|
||||
dec(tmpSize, bhdrOverhead)
|
||||
b2 = getNextBlock(getBuffer(b), size)
|
||||
b2.size = tmpSize or freeBlock or prevUsed
|
||||
nextB.prevHdr = b2
|
||||
mappingInsert(tmpSize, fl, sl)
|
||||
insertBlock(b2, t, fl, sl)
|
||||
|
||||
b.size = size or (b.size and prevState)
|
||||
else:
|
||||
nextB.size = nextB.size and not prevFree
|
||||
b.size = b.size and not freeBlock # Now it's used
|
||||
|
||||
addSize(t, b)
|
||||
return getBuffer(b)
|
||||
|
||||
|
||||
proc freeEx(p: pointer, t: var TLSF) =
|
||||
var
|
||||
fl = 0
|
||||
sl = 0
|
||||
b, tmpB: Pbhdr
|
||||
|
||||
assert(p != nil)
|
||||
b = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
|
||||
b.size = b.size or freeBlock
|
||||
|
||||
removeSize(t, b)
|
||||
b.freePtr.prev = nil
|
||||
b.freePtr.next = nil
|
||||
tmpB = getNextBlock(getBuffer(b), b.size and blockSize)
|
||||
if (tmpB.size and freeBlock) != 0:
|
||||
mappingInsert(tmpB.size and blockSize, fl, sl)
|
||||
extractBlock(tmpB, t, fl, sl)
|
||||
inc(b.size, (tmpB.size and blockSize) + bhdrOverhead)
|
||||
if (b.size and prevFree) != 0:
|
||||
tmpB = b.prevHdr
|
||||
mappingInsert(tmpB.size and blockSize, fl, sl)
|
||||
extractBlock(tmpB, t, fl, sl)
|
||||
inc(tmpB.size, (b.size and blockSize) + bhdrOverhead)
|
||||
b = tmpB
|
||||
mappingInsert(b.size and blockSize, fl, sl)
|
||||
insertBlock(b, t, fl, sl)
|
||||
|
||||
tmpB = getNextBlock(getBuffer(b), b.size and blockSize)
|
||||
tmpB.size = tmpB.size or prevFree
|
||||
tmpB.prevHdr = b
|
||||
|
||||
proc reallocEx(p: pointer, newSize: int, t: var TLSF): pointer =
|
||||
var
|
||||
cpsize, fl, sl, tmpSize: int
|
||||
b, tmpB, nextB: Pbhdr
|
||||
|
||||
assert(p != nil)
|
||||
assert(newSize > 0)
|
||||
|
||||
b = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
|
||||
nextB = getNextBlock(getBuffer(b), b.size and blockSize)
|
||||
newSize = if newSize < minBlockSize: minBlockSize else: roundupSize(newSize)
|
||||
tmpSize = b.size and blockSize
|
||||
if newSize <= tmpSize:
|
||||
removeSize(t, b)
|
||||
if (nextB.size and freeBlock) != 0:
|
||||
mappingInsert(nextB.size and blockSize, fl, sl)
|
||||
extractBlock(nextB, t, fl, sl)
|
||||
inc(tmpSize, (nextB.size and blockSize) + bhdrOverhead)
|
||||
nextB = getNextBlock(getBuffer(nextB), nextB.size and blockSize)
|
||||
# We always reenter this free block because tmpSize will
|
||||
# be greater then sizeof(Tbhdr)
|
||||
dec(tmpSize, newSize)
|
||||
if tmpSize >= sizeof(Tbhdr):
|
||||
dec(tmpSize, bhdrOverhead)
|
||||
tmpB = getNextBlock(getBuffer(b), newSize)
|
||||
tmpB.size = tmpSize or freeBlock or prevUsed
|
||||
nextB.prevHdr = tmpB
|
||||
nextB.size = nextB.size or prevFree
|
||||
mappingInsert(tmpSize, fl, sl)
|
||||
insertBlock(tmpB, t, fl, sl)
|
||||
b.size = newSize or (b.size and prevState)
|
||||
addSize(t, b)
|
||||
return getBuffer(b)
|
||||
|
||||
if (nextB.size and freeBlock) != 0:
|
||||
if newSize <= tmpSize + (nextB.size and blockSize):
|
||||
removeSize(t, b)
|
||||
mappingInsert(nextB.size and blockSize, fl, sl)
|
||||
extractBlock(nextB, t, fl, sl)
|
||||
inc(b.size, (nextB.size and blockSize) + bhdrOverhead)
|
||||
nextB = getNextBlock(getBuffer(b), b.size and blockSize)
|
||||
nextB.prevHdr = b
|
||||
nextB.size = nextB.size and not prevFree
|
||||
tmpSize = (b.size and blockSize) - newSize
|
||||
if tmpSize >= sizeof(Tbhdr):
|
||||
dec(tmpSize, bhdrOverhead)
|
||||
tmpB = getNextBlock(getBuffer(b), newSize)
|
||||
tmpB.size = tmpSize or freeBlock or prevUsed
|
||||
nextB.prevHdr = tmpB
|
||||
nextB.size = nextB.size or prevFree
|
||||
mappingInsert(tmpSize, fl, sl)
|
||||
insertBlock(tmpB, t, fl, sl)
|
||||
b.size = newSize or (b.size and prevState)
|
||||
addSize(t, b)
|
||||
return getBuffer(b)
|
||||
|
||||
var ptrAux = mallocEx(newSize, t)
|
||||
cpsize = if (b.size and blockSize) > newSize: newSize else:
|
||||
(b.size and blockSize)
|
||||
copyMem(ptrAux, p, cpsize)
|
||||
freeEx(p, memPool)
|
||||
return ptrAux
|
||||
|
||||
|
||||
proc ansiCrealloc(p: pointer, newSize: int, t: var TLSF): pointer =
|
||||
if p == nil:
|
||||
if newSize > 0:
|
||||
result = mallocEx(newSize, t)
|
||||
else:
|
||||
result = nil
|
||||
elif newSize <= 0:
|
||||
freeEx(p, t)
|
||||
result = nil
|
||||
else:
|
||||
result = reallocEx(p, newSize, t)
|
||||
|
||||
proc InitTLSF(t: var TLSF) =
|
||||
var areaSize = sizeof(TLSF) + BHDR_OVERHEAD * 8 # Just a safety constant
|
||||
areaSize = max(areaSize, DEFAULT_areaSize)
|
||||
var area = getNewArea(areaSize)
|
||||
|
||||
|
||||
initMemoryPool(areaSize, area)
|
||||
|
||||
var
|
||||
t: PLSF
|
||||
b, ib: Pbhdr
|
||||
|
||||
t = cast[PLSF](memPool)
|
||||
|
||||
zeroMem(area, areaSize)
|
||||
|
||||
t.signature = tlsfSignature
|
||||
var ib = processArea(getNextBlock(memPool, roundupSize(sizeof(TLSF))),
|
||||
rounddownSize(memPoolSize - sizeof(TLSF)))
|
||||
var b = getNextBlock(getBuffer(ib), ib.size and blockSize)
|
||||
freeEx(getBuffer(b), t)
|
||||
t.areaHead = cast[PAreaInfo](getBuffer(ib))
|
||||
|
||||
t.used_size = memPoolSize - (b.size and blockSize)
|
||||
t.max_size = t.used_size
|
||||
|
||||
# XXX
|
||||
Reference in New Issue
Block a user