summary refs log tree commit diff stats
path: root/lib/system/memalloc.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system/memalloc.nim')
-rw-r--r--lib/system/memalloc.nim212
1 files changed, 153 insertions, 59 deletions
diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim
index 178d199b8..a94d0995c 100644
--- a/lib/system/memalloc.nim
+++ b/lib/system/memalloc.nim
@@ -1,38 +1,51 @@
 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.
+    tags: [], raises: [].}
+    ## Overwrites the contents of the memory at `p` with the value 0.
     ##
-    ## Exactly ``size`` bytes will be overwritten. Like any procedure
+    ## 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
+    tags: [], 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``.
+    tags: [], 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
+    ## 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
+    tags: [], 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: [], 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: [].}
@@ -67,7 +80,7 @@ when hasAlloc and not defined(js):
 
   when defined(nimAllocStats):
     var stats: AllocStats
-    template incStat(what: untyped) = inc stats.what
+    template incStat(what: untyped) = atomicInc stats.what
     proc getAllocStats*(): AllocStats = stats
 
   else:
@@ -75,23 +88,23 @@ when hasAlloc and not defined(js):
     proc getAllocStats*(): AllocStats = discard
 
   template alloc*(size: Natural): pointer =
-    ## Allocates a new memory block with at least ``size`` bytes.
+    ## Allocates a new memory block with at least `size` bytes.
     ##
-    ## The block has to be freed with `realloc(block, 0) <#realloc,pointer,Natural>`_
+    ## 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,Natural>`_ to allocate from a shared heap.
+    ## Use `allocShared <#allocShared.t,Natural>`_ to allocate from a shared heap.
     ##
     ## See also:
-    ## * `alloc0 <#alloc0,Natural>`_
+    ## * `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.
+    ## 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>`_.
@@ -106,20 +119,20 @@ when hasAlloc and not defined(js):
     cast[ptr T](alloc(T.sizeof * size))
 
   template alloc0*(size: Natural): pointer =
-    ## Allocates a new memory block with at least ``size`` bytes.
+    ## Allocates a new memory block with at least `size` bytes.
     ##
-    ## The block has to be freed with `realloc(block, 0) <#realloc,pointer,Natural>`_
+    ## 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,Natural>`_.
+    ## somewhat safer than  `alloc <#alloc.t,Natural>`_.
     ##
     ## The allocated memory belongs to its allocating thread!
-    ## Use `allocShared0 <#allocShared0,Natural>`_ to allocate from a shared heap.
+    ## 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.
+    ## 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>`_.
@@ -134,13 +147,13 @@ when hasAlloc and not defined(js):
     ## 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 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,pointer,Natural>`_ to reallocate
+    ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate
     ## from a shared heap.
     reallocImpl(p, newSize)
 
@@ -148,8 +161,8 @@ when hasAlloc and not defined(js):
     ## 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 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>`_.
     ##
@@ -157,7 +170,7 @@ when hasAlloc and not defined(js):
     ## somewhat safer then realloc
     ##
     ## The allocated memory belongs to its allocating thread!
-    ## Use `reallocShared <#reallocShared,pointer,Natural>`_ to reallocate
+    ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate
     ## from a shared heap.
     realloc0Impl(p, oldSize, newSize)
 
@@ -165,18 +178,18 @@ when hasAlloc and not defined(js):
     ## 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``.
+    ## 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))
 
-  template dealloc*(p: pointer) =
-    ## Frees the memory allocated with ``alloc``, ``alloc0`` or
-    ## ``realloc``.
+  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
@@ -190,24 +203,24 @@ when hasAlloc and not defined(js):
 
   template allocShared*(size: Natural): pointer =
     ## Allocates a new memory block on the shared heap with at
-    ## least ``size`` bytes.
+    ## least `size` bytes.
     ##
     ## The block has to be freed with
-    ## `reallocShared(block, 0) <#reallocShared,pointer,Natural>`_
+    ## `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,Natural>`_.
+    ## * `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.
+    ## least `T.sizeof * size` bytes.
     ##
     ## The block has to be freed with
     ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or
@@ -222,21 +235,21 @@ when hasAlloc and not defined(js):
 
   template allocShared0*(size: Natural): pointer =
     ## Allocates a new memory block on the shared heap with at
-    ## least ``size`` bytes.
+    ## least `size` bytes.
     ##
     ## The block has to be freed with
-    ## `reallocShared(block, 0) <#reallocShared,pointer,Natural>`_
+    ## `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,Natural>`_.
+    ## `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.
+    ## least `T.sizeof * size` bytes.
     ##
     ## The block has to be freed with
     ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or
@@ -251,9 +264,9 @@ when hasAlloc and not defined(js):
     ## 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 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)
@@ -265,9 +278,9 @@ when hasAlloc and not defined(js):
     ## 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 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)
@@ -276,16 +289,16 @@ when hasAlloc and not defined(js):
     ## 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 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``.
+    ## 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
@@ -295,8 +308,8 @@ when hasAlloc and not defined(js):
     deallocSharedImpl(p)
 
   proc freeShared*[T](p: ptr T) {.inline, benign, raises: [].} =
-    ## Frees the memory allocated with ``createShared``, ``createSharedU`` or
-    ## ``resizeShared``.
+    ## 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
@@ -304,6 +317,87 @@ when hasAlloc and not defined(js):
     ## 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: