summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md6
-rw-r--r--lib/system.nim2
-rw-r--r--lib/system/memalloc.nim107
-rw-r--r--tests/stdlib/tmemory.nim15
4 files changed, 81 insertions, 49 deletions
diff --git a/changelog.md b/changelog.md
index 6b12891c5..035ba46b8 100644
--- a/changelog.md
+++ b/changelog.md
@@ -36,6 +36,8 @@
 
 - `nodejs` backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`.
 
+- Added `cmpMem` to `system`.
+
 - `doAssertRaises` now correctly handles foreign exceptions.
 
 - Added `asyncdispatch.activeDescriptors` that returns the number of currently
@@ -57,7 +59,7 @@
 
 - Added `decodeQuery` to `std/uri`.
 - `strscans.scanf` now supports parsing single characters.
-- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`. 
+- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`.
 
 - Added `setutils.toSet` that can take any iterable and convert it to a built-in set,
   if the iterable yields a built-in settable type.
@@ -71,7 +73,7 @@
   and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements
   shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes
   its argument, `addMoved`, is also supplied.
-  
+
 - Added `sequtils` import to `prelude`.
 
 - Added `euclDiv` and `euclMod` to `math`.
diff --git a/lib/system.nim b/lib/system.nim
index fb008dc45..b6f7d655a 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2182,6 +2182,8 @@ when notJSnotNims:
       memTrackerOp("moveMem", dest, size)
   proc equalMem(a, b: pointer, size: Natural): bool =
     nimCmpMem(a, b, size) == 0
+  proc cmpMem(a, b: pointer, size: Natural): int =
+    nimCmpMem(a, b, size)
 
 when not defined(js):
   proc cmp(x, y: string): int =
diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim
index f5e8b2363..ffb5190ca 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.
+    ## 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
+    ## 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``.
+    ## 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
+    ## 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: [].}
@@ -75,7 +88,7 @@ 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.t,pointer,Natural>`_
     ## or `dealloc(block) <#dealloc,pointer>`_.
@@ -91,7 +104,7 @@ when hasAlloc and not defined(js):
     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,7 +119,7 @@ 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.t,pointer,Natural>`_
     ## or `dealloc(block) <#dealloc,pointer>`_.
@@ -119,7 +132,7 @@ when hasAlloc and not defined(js):
     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,8 +147,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>`_.
     ##
@@ -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>`_.
     ##
@@ -165,9 +178,9 @@ 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
@@ -175,8 +188,8 @@ when hasAlloc and not defined(js):
     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`` or
-    ## ``realloc``.
+    ## Frees the memory allocated with `alloc`, `alloc0` or
+    ## `realloc`.
     ##
     ## **This procedure is dangerous!**
     ## If one forgets to free the memory a leak occurs; if one tries to
@@ -190,7 +203,7 @@ 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.t,pointer,Natural>`_
@@ -207,7 +220,7 @@ when hasAlloc and not defined(js):
   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,7 +235,7 @@ 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.t,pointer,Natural>`_
@@ -236,7 +249,7 @@ when hasAlloc and not defined(js):
 
   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,7 +317,7 @@ when hasAlloc and not defined(js):
     ## or other memory may be corrupted.
     deallocShared(p)
 
-  include bitmasks  
+  include bitmasks
 
   template `+!`(p: pointer, s: SomeInteger): pointer =
     cast[pointer](cast[int](p) +% int(s))
@@ -318,7 +331,7 @@ when hasAlloc and not defined(js):
         result = allocShared(size)
       else:
         result = alloc(size)
-    else: 
+    else:
       # allocate (size + align - 1) necessary for alignment,
       # plus 2 bytes to store offset
       when compileOption("threads"):
@@ -326,7 +339,7 @@ when hasAlloc and not defined(js):
       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, 
+      # 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)
@@ -338,7 +351,7 @@ when hasAlloc and not defined(js):
         result = allocShared0(size)
       else:
         result = alloc0(size)
-    else: 
+    else:
       # see comments for alignedAlloc
       when compileOption("threads"):
         let base = allocShared0(size + align - 1 + sizeof(uint16))
@@ -348,13 +361,13 @@ when hasAlloc and not defined(js):
       cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset)
       result = base +! offset
 
-  proc alignedDealloc(p: pointer, align: int) {.compilerproc.} = 
+  proc alignedDealloc(p: pointer, align: int) {.compilerproc.} =
     if align <= MemAlign:
       when compileOption("threads"):
         deallocShared(p)
       else:
         dealloc(p)
-    else:      
+    else:
       # read offset at p - 2 bytes, then deallocate (p - offset) pointer
       let offset = cast[ptr uint16](p -! sizeof(uint16))[]
       when compileOption("threads"):
diff --git a/tests/stdlib/tmemory.nim b/tests/stdlib/tmemory.nim
new file mode 100644
index 000000000..25b5d526a
--- /dev/null
+++ b/tests/stdlib/tmemory.nim
@@ -0,0 +1,15 @@
+
+block: # cmpMem
+  type
+    SomeHash = array[15, byte]
+
+  var
+    a: SomeHash
+    b: SomeHash
+
+  a[^1] = byte(1)
+  let c = a
+
+  doAssert cmpMem(a.addr, b.addr, sizeof(SomeHash)) > 0
+  doAssert cmpMem(b.addr, a.addr, sizeof(SomeHash)) < 0
+  doAssert cmpMem(a.addr, c.unsafeAddr, sizeof(SomeHash)) == 0