when notJSnotNims: proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect, tags: [], locks: 0, raises: [].} ## Overwrites the contents of the memory at `p` with the value 0. ## ## Exactly `size` bytes will be overwritten. Like any procedure ## dealing with raw memory this is **unsafe**. proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign, tags: [], locks: 0, raises: [].} ## Copies the contents from the memory at `source` to the memory ## at `dest`. ## Exactly `size` bytes will be copied. The memory ## regions may not overlap. Like any procedure dealing with raw ## memory this is **unsafe**. proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign, tags: [], locks: 0, raises: [].} ## Copies the contents from the memory at `source` to the memory ## at `dest`. ## ## Exactly `size` bytes will be copied. The memory ## regions may overlap, `moveMem` handles this case appropriately ## and is thus somewhat more safe than `copyMem`. Like any procedure ## dealing with raw memory this is still **unsafe**, though. proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect, tags: [], locks: 0, raises: [].} ## Compares the memory blocks `a` and `b`. `size` bytes will ## be compared. ## ## If the blocks are equal, `true` is returned, `false` ## otherwise. Like any procedure dealing with raw memory this is ## **unsafe**. proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect, tags: [], locks: 0, raises: [].} ## Compares the memory blocks `a` and `b`. `size` bytes will ## be compared. ## ## Returns: ## * a value less than zero, if `a < b` ## * a value greater than zero, if `a > b` ## * zero, if `a == b` ## ## Like any procedure dealing with raw memory this is ## **unsafe**. when hasAlloc and not defined(js): proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} proc alloc0Impl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} proc deallocImpl*(p: pointer) {.noconv, rtl, tags: [], benign, raises: [].} proc reallocImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} proc realloc0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} proc allocSharedImpl*(size: Natural): pointer {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} proc allocShared0Impl*(size: Natural): pointer {.noconv, rtl, benign, raises: [], tags: [].} proc deallocSharedImpl*(p: pointer) {.noconv, rtl, benign, raises: [], tags: [].} proc reallocSharedImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} proc reallocShared0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} # Allocator statistics for memory leak tests {.push stackTrace: off.} type AllocStats* = object allocCount: int deallocCount: int proc `-`*(a, b: AllocStats): AllocStats = result.allocCount = a.allocCount - b.allocCount result.deallocCount = a.deallocCount - b.deallocCount template dumpAllocstats*(code: untyped) = let stats1 = getAllocStats() code let stats2 = getAllocStats() echo $(stats2 - stats1) when defined(nimAllocStats): var stats: AllocStats template incStat(what: untyped) = inc stats.what proc getAllocStats*(): AllocStats = stats else: template incStat(what: untyped) = discard proc getAllocStats*(): AllocStats = discard template alloc*(size: Natural): pointer = ## Allocates a new memory block with at least `size` bytes. ## ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. ## The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## ## The allocated memory belongs to its allocating thread! ## Use `allocShared <#allocShared.t,Natural>`_ to allocate from a shared heap. ## ## See also: ## * `alloc0 <#alloc0.t,Natural>`_ incStat(allocCount) allocImpl(size) proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} = ## Allocates a new memory block with at least `T.sizeof * size` bytes. ## ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. ## The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## ## The allocated memory belongs to its allocating thread! ## Use `createSharedU <#createSharedU,typedesc>`_ to allocate from a shared heap. ## ## See also: ## * `create <#create,typedesc>`_ cast[ptr T](alloc(T.sizeof * size)) template alloc0*(size: Natural): pointer = ## Allocates a new memory block with at least `size` bytes. ## ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. ## The block is initialized with all bytes containing zero, so it is ## somewhat safer than `alloc <#alloc.t,Natural>`_. ## ## The allocated memory belongs to its allocating thread! ## Use `allocShared0 <#allocShared0.t,Natural>`_ to allocate from a shared heap. incStat(allocCount) alloc0Impl(size) proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} = ## Allocates a new memory block with at least `T.sizeof * size` bytes. ## ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. ## The block is initialized with all bytes containing zero, so it is ## somewhat safer than `createU <#createU,typedesc>`_. ## ## The allocated memory belongs to its allocating thread! ## Use `createShared <#createShared,typedesc>`_ to allocate from a shared heap. cast[ptr T](alloc0(sizeof(T) * size)) template realloc*(p: pointer, newSize: Natural): pointer = ## Grows or shrinks a given memory block. ## ## If `p` is **nil** then a new memory block is returned. ## In either way the block has at least `newSize` bytes. ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`. ## In other cases the block has to be freed with ## `dealloc(block) <#dealloc,pointer>`_. ## ## The allocated memory belongs to its allocating thread! ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate ## from a shared heap. reallocImpl(p, newSize) template realloc0*(p: pointer, oldSize, newSize: Natural): pointer = ## Grows or shrinks a given memory block. ## ## If `p` is **nil** then a new memory block is returned. ## In either way the block has at least `newSize` bytes. ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`. ## In other cases the block has to be freed with ## `dealloc(block) <#dealloc,pointer>`_. ## ## The block is initialized with all bytes containing zero, so it is ## somewhat safer then realloc ## ## The allocated memory belongs to its allocating thread! ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate ## from a shared heap. realloc0Impl(p, oldSize, newSize) proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, benign, raises: [].} = ## Grows or shrinks a given memory block. ## ## If `p` is **nil** then a new memory block is returned. ## In either way the block has at least `T.sizeof * newSize` bytes. ## If `newSize == 0` and `p` is not **nil** `resize` calls `dealloc(p)`. ## In other cases the block has to be freed with `free`. ## ## The allocated memory belongs to its allocating thread! ## Use `resizeShared <#resizeShared,ptr.T,Natural>`_ to reallocate ## from a shared heap. cast[ptr T](realloc(p, T.sizeof * newSize)) proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = ## Frees the memory allocated with `alloc`, `alloc0`, ## `realloc`, `create` or `createU`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to ## access freed memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. ## ## The freed memory must belong to its allocating thread! ## Use `deallocShared <#deallocShared,pointer>`_ to deallocate from a shared heap. incStat(deallocCount) deallocImpl(p) template allocShared*(size: Natural): pointer = ## Allocates a new memory block on the shared heap with at ## least `size` bytes. ## ## The block has to be freed with ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_ ## or `deallocShared(block) <#deallocShared,pointer>`_. ## ## The block is not initialized, so reading from it before writing ## to it is undefined behaviour! ## ## See also: ## * `allocShared0 <#allocShared0.t,Natural>`_. incStat(allocCount) allocSharedImpl(size) proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, tags: [], benign, raises: [].} = ## Allocates a new memory block on the shared heap with at ## least `T.sizeof * size` bytes. ## ## The block has to be freed with ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or ## `freeShared(block) <#freeShared,ptr.T>`_. ## ## The block is not initialized, so reading from it before writing ## to it is undefined behaviour! ## ## See also: ## * `createShared <#createShared,typedesc>`_ cast[ptr T](allocShared(T.sizeof * size)) template allocShared0*(size: Natural): pointer = ## Allocates a new memory block on the shared heap with at ## least `size` bytes. ## ## The block has to be freed with ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_ ## or `deallocShared(block) <#deallocShared,pointer>`_. ## ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ## `allocShared <#allocShared.t,Natural>`_. incStat(allocCount) allocShared0Impl(size) proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} = ## Allocates a new memory block on the shared heap with at ## least `T.sizeof * size` bytes. ## ## The block has to be freed with ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or ## `freeShared(block) <#freeShared,ptr.T>`_. ## ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ## `createSharedU <#createSharedU,typedesc>`_. cast[ptr T](allocShared0(T.sizeof * size)) template reallocShared*(p: pointer, newSize: Natural): pointer = ## Grows or shrinks a given memory block on the heap. ## ## If `p` is **nil** then a new memory block is returned. ## In either way the block has at least `newSize` bytes. ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls ## `deallocShared(p)`. ## In other cases the block has to be freed with ## `deallocShared <#deallocShared,pointer>`_. reallocSharedImpl(p, newSize) template reallocShared0*(p: pointer, oldSize, newSize: Natural): pointer = ## Grows or shrinks a given memory block on the heap. ## ## When growing, the new bytes of the block is initialized with all bytes ## containing zero, so it is somewhat safer then reallocShared ## ## If `p` is **nil** then a new memory block is returned. ## In either way the block has at least `newSize` bytes. ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls ## `deallocShared(p)`. ## In other cases the block has to be freed with ## `deallocShared <#deallocShared,pointer>`_. reallocShared0Impl(p, oldSize, newSize) proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline, raises: [].} = ## Grows or shrinks a given memory block on the heap. ## ## If `p` is **nil** then a new memory block is returned. ## In either way the block has at least `T.sizeof * newSize` bytes. ## If `newSize == 0` and `p` is not **nil** `resizeShared` calls ## `freeShared(p)`. ## In other cases the block has to be freed with ## `freeShared <#freeShared,ptr.T>`_. cast[ptr T](reallocShared(p, T.sizeof * newSize)) proc deallocShared*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = ## Frees the memory allocated with `allocShared`, `allocShared0` or ## `reallocShared`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to ## access freed memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. incStat(deallocCount) deallocSharedImpl(p) proc freeShared*[T](p: ptr T) {.inline, benign, raises: [].} = ## Frees the memory allocated with `createShared`, `createSharedU` or ## `resizeShared`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to ## access freed memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. deallocShared(p) include bitmasks template `+!`(p: pointer, s: SomeInteger): pointer = cast[pointer](cast[int](p) +% int(s)) template `-!`(p: pointer, s: SomeInteger): pointer = cast[pointer](cast[int](p) -% int(s)) proc alignedAlloc(size, align: Natural): pointer = if align <= MemAlign: when compileOption("threads"): result = allocShared(size) else: result = alloc(size) else: # allocate (size + align - 1) necessary for alignment, # plus 2 bytes to store offset when compileOption("threads"): let base = allocShared(size + align - 1 + sizeof(uint16)) else: let base = alloc(size + align - 1 + sizeof(uint16)) # memory layout: padding + offset (2 bytes) + user_data # in order to deallocate: read offset at user_data - 2 bytes, # then deallocate user_data - offset let offset = align - (cast[int](base) and (align - 1)) cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset) result = base +! offset proc alignedAlloc0(size, align: Natural): pointer = if align <= MemAlign: when compileOption("threads"): result = allocShared0(size) else: result = alloc0(size) else: # see comments for alignedAlloc when compileOption("threads"): let base = allocShared0(size + align - 1 + sizeof(uint16)) else: let base = alloc0(size + align - 1 + sizeof(uint16)) let offset = align - (cast[int](base) and (align - 1)) cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset) result = base +! offset proc alignedDealloc(p: pointer, align: int) {.compilerproc.} = if align <= MemAlign: when compileOption("threads"): deallocShared(p) else: dealloc(p) else: # read offset at p - 2 bytes, then deallocate (p - offset) pointer let offset = cast[ptr uint16](p -! sizeof(uint16))[] when compileOption("threads"): deallocShared(p -! offset) else: dealloc(p -! offset) proc alignedRealloc(p: pointer, oldSize, newSize, align: Natural): pointer = if align <= MemAlign: when compileOption("threads"): result = reallocShared(p, newSize) else: result = realloc(p, newSize) else: result = alignedAlloc(newSize, align) copyMem(result, p, oldSize) alignedDealloc(p, align) proc alignedRealloc0(p: pointer, oldSize, newSize, align: Natural): pointer = if align <= MemAlign: when compileOption("threads"): result = reallocShared0(p, oldSize, newSize) else: result = realloc0(p, oldSize, newSize) else: result = alignedAlloc(newSize, align) copyMem(result, p, oldSize) zeroMem(result +! oldSize, newSize - oldSize) alignedDealloc(p, align) {.pop.} # GC interface: when hasAlloc: proc getOccupiedMem*(): int {.rtl.} ## Returns the number of bytes that are owned by the process and hold data. proc getFreeMem*(): int {.rtl.} ## Returns the number of bytes that are owned by the process, but do not ## hold any meaningful data. proc getTotalMem*(): int {.rtl.} ## Returns the number of bytes that are owned by the process. when defined(js): # Stubs: proc getOccupiedMem(): int = return -1 proc getFreeMem(): int = return -1 proc getTotalMem(): int = return -1 proc dealloc(p: pointer) = discard proc alloc(size: Natural): pointer = discard proc alloc0(size: Natural): pointer = discard proc realloc(p: pointer, newsize: Natural): pointer = discard proc realloc0(p: pointer, oldsize, newsize: Natural): pointer = discard proc allocShared(size: Natural): pointer = discard proc allocShared0(size: Natural): pointer = discard proc deallocShared(p: pointer) = discard proc reallocShared(p: pointer, newsize: Natural): pointer = discard proc reallocShared0(p: pointer, oldsize, newsize: Natural): pointer = discard when hasAlloc and hasThreadSupport and not defined(useMalloc): proc getOccupiedSharedMem*(): int {.rtl.} ## Returns the number of bytes that are owned by the process ## on the shared heap and hold data. This is only available when ## threads are enabled. proc getFreeSharedMem*(): int {.rtl.} ## Returns the number of bytes that are owned by the ## process on the shared heap, but do not hold any meaningful data. ## This is only available when threads are enabled. proc getTotalSharedMem*(): int {.rtl.} ## Returns the number of bytes on the shared heap that are owned by the ## process. This is only available when threads are enabled.