No matching definitions.

tur/forms/unsafe

stdlib/forms/unsafe.tur

the unsafe block, raw pointers, raw memory and dynamic loading.

The compiler implements these itself (src/compiler/elab_unsafe.c), so there is nothing to import. This file is reference only: it declares nothing the compiler reads.

Every builtin here is refused outside an enclosing (unsafe ...) block or a function declared #fx{Unsafe}. None is checked at run time: a bad pointer or offset is undefined behaviour, exactly as in C.

Pointers are ptr<void>, and the pointer builtins read and write whole 8-byte words (int64_t): ptr-deref is *(int64_t *)p, the unchecked array builtins index in words, and ptr-add / ptr-sub offset in bytes. A ptr<void> from raw-malloc is move-typed: passing it to a function consumes it (TUR-E0201 on a second use), while the builtins here borrow it.

form

unsafe

(unsafe body ...)

run code that uses the unsafe builtins.

Evaluates the body forms in order and returns the last one's value. Inside it, the builtins on this page and calls to #fx{Unsafe} functions are allowed. The block discharges the Unsafe effect: a function whose unsafe code is all inside (unsafe ...) has no Unsafe in its row, so a safe wrapper over an unsafe primitive is an ordinary function to its callers. A function annotated #fx{Unsafe} gets the same permission for its whole body, and its callers need a block of their own.

Lints: an empty block warns; a block of more than 20 forms warns (change the limit with --lint-unsafe-max-lines=N); --lint-unsafe also warns on a nested block.

bodythe forms to evaluate

The value of the last body form.

(unsafe (+ 1 2))  ; => 3
builtin

raw-malloc

(raw-malloc [size :int] :ptr<void>)

allocate uninitialized memory.

C's malloc. Release the block with raw-free. The result is move-typed; see the module notes.

sizethe number of bytes

A pointer to the block, or a null pointer when allocation fails.

(let [p (raw-malloc 8)] (ptr-write p 42) (let [v (ptr-deref p)] (raw-free p) v))  ; => 42
builtin

raw-free

(raw-free [p :ptr<void>] :nil)

release memory from raw-malloc or raw-realloc.

C's free. A null pointer is a no-op; freeing anything else twice is undefined behaviour.

pthe block to release
(let [p (raw-malloc 8)] (raw-free p))
builtin

raw-realloc

(raw-realloc [p :ptr<void> size :int] :ptr<void>)

resize a block, keeping its contents.

C's realloc: the block may move, so use the returned pointer and not p.

pthe block to resize (or a null pointer, to allocate)
sizethe new size in bytes

The resized block, or a null pointer when allocation fails (p is then still valid).

(let [p (raw-realloc (raw-malloc 8) 64)] (array-set-unchecked p 7 3) (let [v (array-get-unchecked p 7)] (raw-free p) v))  ; => 3
builtin

raw-memcpy

(raw-memcpy [dest :ptr<void> src :ptr<void> n :int] :nil)

copy bytes between two blocks.

C's memcpy: the blocks must not overlap.

destwhere to copy to
srcwhere to copy from
nthe number of bytes
(let [p (raw-malloc 8) q (raw-malloc 8)] (ptr-write p 1234) (raw-memcpy q p 8) (let [v (ptr-deref q)] (raw-free p) (raw-free q) v))  ; => 1234
builtin

raw-memset

(raw-memset [dest :ptr<void> byte :int n :int] :nil)

fill bytes with one value.

C's memset: the low 8 bits of byte are written to each of the n bytes at dest.

destthe block to fill
bytethe fill value
nthe number of bytes
(let [p (raw-malloc 16)] (raw-memset p 0 16) (let [v (array-get-unchecked p 1)] (raw-free p) v))  ; => 0
builtin

ptr-deref

(ptr-deref [p :ptr<void>] :int)

read the 8-byte word a pointer points at.

*(int64_t *)p. The word is returned as an int, whatever was stored.

pthe address to read

The word at p.

(let [x 41] (ptr-deref (ptr-of x)))  ; => 41
builtin

ptr-write

(ptr-write [p :ptr<void> value :int] :nil)

store an 8-byte word through a pointer.

*(int64_t *)p = value.

pthe address to write
valuethe word to store
(let [p (raw-malloc 8)] (ptr-write p 7) (let [v (ptr-deref p)] (raw-free p) v))  ; => 7
builtin

ptr-add

(ptr-add [p :ptr<void> n :int] :ptr<void>)

advance a pointer by a number of BYTES.

(char *)p + n: the offset is in bytes, not words -- the second word of a block is (ptr-add p 8).

pthe base address
nthe offset in bytes

The address n bytes past p.

(let [p (raw-malloc 16)] (ptr-write (ptr-add p 8) 7) (let [v (array-get-unchecked p 1)] (raw-free p) v))  ; => 7
builtin

ptr-sub

(ptr-sub [p :ptr<void> n :int] :ptr<void>)

move a pointer back by a number of BYTES.

(char *)p - n.

pthe base address
nthe offset in bytes

The address n bytes before p.

(let [p (raw-malloc 16)] (ptr-write p 5) (let [v (ptr-deref (ptr-sub (ptr-add p 8) 8))] (raw-free p) v))  ; => 5
builtin

ptr-null?

(ptr-null? [p :ptr<void>] :bool)

is a pointer null?

pthe pointer to test

true for a null pointer.

(let [p (raw-malloc 8)] (let [n (ptr-null? p)] (raw-free p) n))  ; => false
builtin

ptr-of

(ptr-of [x :T] :ptr<void>)

the address of a value.

C's &. The pointer is good only while the value's storage is live -- the address of a local is dangling once its scope ends.

xthe value whose address is wanted

A pointer to x.

(let [x 41] (ptr-deref (ptr-of x)))  ; => 41
builtin

array-get-unchecked

(array-get-unchecked [p :ptr<void> i :int] :int)

read word i of a block, with no bounds check.

((int64_t *)p)[i]: the index counts 8-byte words, not bytes.

pthe block
ithe word index

The word at index i.

(let [p (raw-malloc 24)] (array-set-unchecked p 2 99) (let [v (array-get-unchecked p 2)] (raw-free p) v))  ; => 99
builtin

array-set-unchecked

(array-set-unchecked [p :ptr<void> i :int value :int] :nil)

write word i of a block, with no bounds check.

((int64_t *)p)[i] = value.

pthe block
ithe word index
valuethe word to store
(let [p (raw-malloc 24)] (array-set-unchecked p 0 1) (let [v (ptr-deref p)] (raw-free p) v))  ; => 1
form

unsafe-cast

(unsafe-cast x type)

a C cast to int.

(int64_t)x. The target type is required but not read yet: the result is always an int, converted as C converts (a float truncates, true is 1).

xthe value to convert
typethe target type (currently ignored)

x as an int.

(unsafe-cast true :int)  ; => 1
form

reinterpret

(reinterpret x type)

reinterpret a value's bits as another type.

The same bit copy as transmute: the target type must have the same size as x's, checked at compile time, and the result has the target type. Nothing is converted -- (reinterpret 1.5 :int) is 1.5's IEEE-754 bit pattern, not 1.

xthe value
typethe target type, a primitive kind such as :int

x's bits, read as the target type.

(reinterpret 1.5 :int)  ; => 4609434218613702656
form

transmute

(transmute x type)

reinterpret a value's bits as another type.

The target type must have the same size as x's, checked at compile time ((transmute (:: 1 :int32) :int) is an error), and the result has the target type. The bits are copied, not converted: (transmute 1.5 :int) is 4609434218613702656, and transmute back to :float gives 1.5. float->bits / bits->float (tur/bits) are the same copy outside an unsafe block. For the NUMBER, use unsafe-cast or float->int.

xthe value
typethe target type, a primitive kind such as :float

x's bits, read as the target type.

(transmute 4609434218613702656 :float)  ; => 1.5
form

c-call

(c-call f args ...)

call a C function declared with extern-c.

The function must be in scope from an extern-c declaration. The result is typed int, whatever the declaration says; calling the extern-c name directly keeps its declared types and needs no (unsafe ...).

fthe extern-c function's name
argsits arguments

The C function's result, as an int.

(extern-c labs [x : int] : int)
(unsafe (c-call labs -5))  ; prints 5
builtin

dlopen

(dlopen [path :cstr] :ptr<void>)

load a shared library at run time.

POSIX dlopen(path, RTLD_LAZY). Look symbols up with dlsym and call them through call-ptr; see ffi-guide. Needs the FFI capability under a sandboxed interpreter.

paththe library's file name or path

A library handle, or a null pointer when it cannot be loaded.

(let [h (dlopen "libm.so.6")] (let [ok (not (ptr-null? h))] (dlclose h) ok))  ; => true  ; doctest: needs libm.so.6 (Linux)
builtin

dlsym

(dlsym [h :ptr<void> name :cstr] :ptr<void>)

look a symbol up in a loaded library.

POSIX dlsym.

hthe handle from dlopen
namethe symbol's name

The symbol's address, or a null pointer when the library lacks it.

(let [h (dlopen "libm.so.6")] (let [ok (not (ptr-null? (dlsym h "cos")))] (dlclose h) ok))  ; => true  ; doctest: needs libm.so.6 (Linux)
builtin

dlclose

(dlclose [h :ptr<void>] :int)

release a library handle from dlopen.

POSIX dlclose. Addresses from dlsym on it may be invalid afterwards.

hthe handle to release

0 on success, non-zero on failure.

(dlclose (dlopen "libm.so.6"))  ; => 0  ; doctest: needs libm.so.6 (Linux)