ptrops: add
This commit is contained in:
parent
5f1dc751ca
commit
5ada369741
|
@ -24,6 +24,7 @@ respective folders
|
|||
- `bitops2` - an updated version of `bitops.nim`, filling in gaps in original code
|
||||
- `byteutils` - utilities that make working with the Nim `byte` type convenient
|
||||
- `endians2` - utilities for converting to and from little / big endian integers
|
||||
- `ptrops` - pointer arithmetic utilities
|
||||
- `ranges` - utility functions for working with parts and blobs of memory
|
||||
- `shims` - backports of nim `devel` code to the stable version that Status is using
|
||||
|
||||
|
@ -81,7 +82,7 @@ Typically, you will import either a top-level library or drill down into its
|
|||
submodules:
|
||||
```nim
|
||||
import stew/bitops2
|
||||
import stew/ranges/ptr_arith
|
||||
import stew/ranges/bitranges
|
||||
```
|
||||
|
||||
:warning: No API/ABI stability - pick a commit and stick with it :warning:
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# stew
|
||||
# Copyright 2018-2019 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
# Pointer operations and helpers - generally, dereferencing pointers that have
|
||||
# been offset is unsafe and many of the operations herein have undefined
|
||||
# and platform-specific behavior in corner cases.
|
||||
|
||||
# Due to poor codegen and subsequent lack of inlining, many of these operations
|
||||
# are templates even where they could be func.
|
||||
|
||||
# ByteAddress in std lib is signed - this leads to issues with overflow checking
|
||||
# when address is on boundary.
|
||||
type
|
||||
MemAddress* = distinct uint
|
||||
|
||||
template toMemAddress*(p: pointer): MemAddress = cast[MemAddress](p)
|
||||
template toPointer*(p: MemAddress): pointer = cast[pointer](p)
|
||||
template toPtr*(p: MemAddress, T: type): ptr T = cast[ptr T](p)
|
||||
|
||||
template offset*(p: MemAddress, bytes: int): MemAddress =
|
||||
## Offset a memory address by a number of bytes. Behavior is undefined on
|
||||
## overflow.
|
||||
# Actual behavior is wrapping, but this may be revised in the future to enable
|
||||
# better optimizations
|
||||
{.checks: off.}
|
||||
mixin offset
|
||||
MemAddress(uint(p) + cast[uint](bytes))
|
||||
|
||||
template offset*(p: pointer, bytes: int): pointer =
|
||||
## Offset a memory address by a number of bytes. Behavior is undefined on
|
||||
## overflow.
|
||||
# Actual behavior is wrapping, but this may be revised in the future to enable
|
||||
# better optimizations
|
||||
mixin offset
|
||||
p.toMemAddress().offset(bytes).toPointer()
|
||||
|
||||
template offset*[T](p: ptr T, count: int): ptr T =
|
||||
## Offset a pointer to T by count elements. Behavior is undefined on
|
||||
## overflow.
|
||||
# Actual behavior is wrapping, but this may be revised in the future to enable
|
||||
# better optimizations.
|
||||
# We turn off checking here - too large counts is UB
|
||||
{.checks: off.}
|
||||
mixin offset
|
||||
let bytes = count * sizeof(T)
|
||||
p.toMemAddress().offset(bytes).toPtr(type p[])
|
||||
|
||||
template distance*(a, b: MemAddress): int =
|
||||
cast[int](cast[uint](b) - cast[uint](a))
|
||||
|
||||
template distance*(a, b: pointer): int =
|
||||
# Number of bytes between a and b - undefined behavior when difference exceeds
|
||||
# what can be represented in an int
|
||||
a.toMemAddress().distance(b.toMemAddress())
|
||||
|
||||
template distance*[T](a, b: ptr T): int =
|
||||
# Number of elements between a and b - undefined behavior when difference
|
||||
# exceeds what can be represented in an int
|
||||
{.checks: off.}
|
||||
a.toMemAddress().distance(b.toMemAddress()) div sizeof(T)
|
||||
|
||||
proc `<`*(a, b: MemAddress): bool =
|
||||
cast[uint](a) < cast[uint](b)
|
|
@ -1,3 +1,6 @@
|
|||
import ../ptrops
|
||||
export ptrops
|
||||
|
||||
proc baseAddr*[T](x: openarray[T]): pointer = cast[pointer](x)
|
||||
|
||||
# Please note that we use templates here on purpose.
|
||||
|
@ -5,17 +8,11 @@ proc baseAddr*[T](x: openarray[T]): pointer = cast[pointer](x)
|
|||
# out that the use of forced inlining with templates still creates a
|
||||
# significant difference in the release builds of nim-faststreams
|
||||
|
||||
template shift*(p: pointer, delta: int): pointer =
|
||||
cast[pointer](cast[int](p) + delta)
|
||||
template shift*(p: pointer, delta: int): pointer {.deprecated: "use ptrops".} =
|
||||
p.offset(delta)
|
||||
|
||||
template distance*(a, b: pointer): int =
|
||||
cast[int](b) - cast[int](a)
|
||||
|
||||
template shift*[T](p: ptr T, delta: int): ptr T =
|
||||
cast[ptr T](shift(cast[pointer](p), delta * sizeof(T)))
|
||||
|
||||
proc `<`*(a, b: pointer): bool =
|
||||
cast[uint](a) < cast[uint](b)
|
||||
template shift*[T](p: ptr T, delta: int): ptr T {.deprecated: "use ptrops".} =
|
||||
p.offset(delta)
|
||||
|
||||
when (NimMajor,NimMinor,NimPatch) >= (0,19,9):
|
||||
template makeOpenArray*[T](p: ptr T, len: int): auto =
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# stew
|
||||
# Copyright 2018 Status Research & Development GmbH
|
||||
# Copyright 2018-2019 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
@ -10,7 +10,8 @@
|
|||
import
|
||||
ranges/all,
|
||||
test_bitops2,
|
||||
test_bitseqs,
|
||||
test_byteutils,
|
||||
test_endians2,
|
||||
test_bitseqs
|
||||
|
||||
test_ptrops,
|
||||
test_varints
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# stew
|
||||
# Copyright 2018-2019 Status Research & Development GmbH
|
||||
# Licensed under either of
|
||||
#
|
||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import unittest
|
||||
|
||||
import ../stew/ptrops
|
||||
|
||||
var ints = [2, 3, 4]
|
||||
|
||||
suite "ptrops":
|
||||
test "offset pointer":
|
||||
let
|
||||
p0: pointer = addr ints[0]
|
||||
p1: pointer = addr ints[1]
|
||||
check:
|
||||
p0.offset(sizeof(int)) == p1
|
||||
p1.offset(-sizeof(int)) == p0
|
||||
|
||||
test "offset ptr":
|
||||
let
|
||||
p0 = addr ints[0]
|
||||
p1 = addr ints[1]
|
||||
check:
|
||||
p0.offset(0)[] == ints[0]
|
||||
p0.offset(1)[] == ints[1]
|
||||
p1.offset(-1)[] == ints[0]
|
||||
p0.offset(1) == p1
|
||||
p1.offset(-1) == p0
|
||||
|
||||
test "offset max pointer (no overflows!)":
|
||||
check:
|
||||
cast[pointer](int.high()).offset(3) ==
|
||||
cast[pointer](cast[uint](int.high) + 3)
|
||||
cast[ptr uint16](int.high()).offset(3) ==
|
||||
cast[pointer](cast[uint](int.high) + 6)
|
||||
|
||||
test "distance pointer":
|
||||
let
|
||||
p0: pointer = addr ints[0]
|
||||
p1: pointer = addr ints[2]
|
||||
check:
|
||||
p0.distance(p0) == 0
|
||||
p0.distance(p1) == sizeof(int) * 2
|
||||
p1.distance(p0) == -sizeof(int) * 2
|
||||
|
||||
test "distance ptr uint16":
|
||||
let
|
||||
p0 = addr ints[0]
|
||||
p1 = addr ints[2]
|
||||
check:
|
||||
p0.distance(p0) == 0
|
||||
p0.distance(p1) == 2
|
||||
p1.distance(p0) == -2
|
Loading…
Reference in New Issue