2021-07-05 15:48:35 +03:00
|
|
|
(doc Pointer "is a data type for representing C pointers.")
|
2019-10-30 10:09:18 +03:00
|
|
|
(defmodule Pointer
|
2020-05-12 22:58:40 +03:00
|
|
|
(doc eq "checks two pointers for equality.")
|
|
|
|
(deftemplate eq (Fn [(Ptr p) (Ptr p)] Bool) "bool $NAME($p *p1, $p *p2)" " $DECL { return p1 == p2; }")
|
2021-05-27 23:04:46 +03:00
|
|
|
(implements = Pointer.eq)
|
|
|
|
(doc eq "checks two pointer references for equality.")
|
|
|
|
(deftemplate ref-eq (Fn [(Ref (Ptr p)) (Ref (Ptr p))] Bool) "bool $NAME($p **p1, $p **p2)" " $DECL { return *p1 == *p2; }")
|
|
|
|
(implements = Pointer.ref-eq)
|
|
|
|
|
|
|
|
(doc address "returns the memory address of a reference. Equivalent to the `&` operator when placed before a variable in C.")
|
|
|
|
(deftemplate address (Fn [(Ref a)] (Ptr a)) "$a * $NAME($a * a)" "$DECL { return a; }")
|
|
|
|
|
2020-05-12 22:58:40 +03:00
|
|
|
(doc to-ref "converts a pointer to a reference type.
|
|
|
|
|
|
|
|
The user will have to ensure themselves that this is a safe operation.")
|
|
|
|
(deftemplate to-ref (Fn [(Ptr p)] (Ref p)) "$p* $NAME($p* p)" " $DECL { return p; }")
|
|
|
|
(doc to-value "converts a pointer to a value.
|
|
|
|
|
|
|
|
The user will have to ensure themselves that this is a safe operation.")
|
2020-05-13 18:15:26 +03:00
|
|
|
(deftemplate to-value (Fn [(Ptr p)] p) "$p $NAME($p* p)" " $DECL { return *p; }")
|
2020-05-12 22:58:40 +03:00
|
|
|
(doc add "adds a long integer value to a pointer.")
|
|
|
|
(deftemplate add (Fn [(Ptr p) Long] (Ptr p)) "$p* $NAME($p* p, Long x)" " $DECL { return p + x; }")
|
|
|
|
(doc sub "subtracts a long integer value from a pointer.")
|
|
|
|
(deftemplate sub (Fn [(Ptr p) Long] (Ptr p)) "$p* $NAME($p* p, Long x)" " $DECL { return p - x; }")
|
|
|
|
(doc width "gets the byte size of a pointer.")
|
|
|
|
(deftemplate width (Fn [(Ptr p)] Long) "Long $NAME($p* p)" " $DECL { return sizeof(*p); }")
|
|
|
|
(doc to-long "converts a pointer to a long integer.")
|
|
|
|
(deftemplate to-long (Fn [(Ptr p)] Long) "Long $NAME($p* p)" " $DECL { return (Long)p; }")
|
|
|
|
(doc from-long "converts a long integer to a pointer.")
|
|
|
|
(deftemplate from-long (Fn [Long] (Ptr p)) "$p* $NAME(Long p)" " $DECL { return ($p*)p; }")
|
|
|
|
|
Pointer: Add utility functions (#1012)
* Pointer: Add utility functions
This commit adds a few more utility functions to `Pointer.carp`.
- Pointer.set-unsafe: Sets the value of a pointer to some arbitrary Carp
value, without type checking. Users need to ensure this operation is
safe.
- Pointer.set: Sets the value of a pointer to a value of type t to a
value that has the same type.
- Pointer.cast: Casts a pointer to a value of type t to a pointer to a
value of type `a`--the argument passed is ignored, it is only used to
determine the type to cast to.
- Pointer.leak: Copies a Carp reference to a new pointer to the same
value. This creates a leak since Carp will not automatically clean up
this memory.
- Pointer.free: Frees a pointer p. Users need to ensure calls to this
function are safe and do not produce errors like a double free.
Intended for use with leak.
Here's an example of some of these functions in action:
```
(defn foo []
(let-do [p (Pointer.leak "leaky")] ;; create a new pointer
(ignore (Pointer.set p @"foo")) ;; set the pointer to "foo"
(println* (Pointer.to-value p)) ;; convert to a Carp val to print
(Pointer.free p) ;; finally, free it.
0))
(foo)
=> Compiled to 'out/Untitled' (executable)
foo
0
```
And the C of interest:
```
int foo() {
int _35;
/* let */ {
static String _6 = "leaky";
String *_6_ref = &_6;
String* _7 = Pointer_leak__String(_6_ref);
String* p = _7;
/* let */ {
static String _15 = "foo";
String *_15_ref = &_15;
String _16 = String_copy(_15_ref);
String* _17 = Pointer_set__String(p, _16);
String* _ = _17;
/* () */
}
String _26 = Pointer_to_MINUS_value__String(p);
String _27 = StringCopy_str(_26);
String* _28 = &_27; // ref
IO_println(_28);
Pointer_free__String(p);
int _34 = 0;
_35 = _34;
String_delete(_27);
}
return _35;
}
```
As mentioned, and as w/ other Pointer functions users need to ensure the
safety of these operations themselves. For example, calling `free` on
`p` twice in the example above produces the expected double free:
```
(defn foo []
(let-do [p (Pointer.leak "leaky")] ;; create a new pointer
(ignore (Pointer.set p @"foo")) ;; set the pointer to "foo"
(println* (Pointer.to-value p)) ;; convert to a Carp val to print
(Pointer.free p) ;; finally, free it.
(Pointer.free p) ;; !Double free!
0))
(foo)
Compiled to 'out/Untitled' (executable)
foo
Untitled(38328,0x10d9a1dc0) malloc: *** error for object 0x7feb86c01790:
pointer being freed was not allocated
Untitled(38328,0x10d9a1dc0) malloc: *** set a breakpoint in
malloc_error_break to debug
[RUNTIME ERROR] '"out/Untitled"' exited with return value -6.
```
Still, these should come in handy in rare cases in which users need to
circumvent the type checker or borrow checker.
diff --git a/core/Pointer.carp b/core/Pointer.carp
index a662c636..4f29d587 100644
--- a/core/Pointer.carp
+++ b/core/Pointer.carp
@@ -20,6 +20,29 @@ The user will have to ensure themselves that this is a safe operation.")
(doc from-long "converts a long integer to a pointer.")
(deftemplate from-long (Fn [Long] (Ptr p)) "$p* $NAME(Long p)" " $DECL { return ($p*)p; }")
+ (doc set-unsafe
+ "Sets the value of a pointer."
+ "The user will have to ensure this operation is safe.")
+ (deftemplate set-unsafe (Fn [(Ptr p) (Ref a b)] (Ptr p)) "$p* $NAME($p* p, void* a)" "$DECL { *p = *($p*)a; return p;}")
+
+ (doc cast
+ "Cast a pointer to type p to a pointer to type a."
+ "The value of the `a` argument is ignored.")
+ (deftemplate cast (Fn [(Ptr p) a] (Ptr a)) "$a* $NAME($p* p, $a a)" "$DECL { *($a*)p = CARP_MALLOC(sizeof($a)); return ($a*)p;}")
+
+ (doc leak
+ "Allocate a new pointer that's a copy of the value of `Ref`"
+ "The Carp borrow checker won't delete this pointer. You will need to delete it manually by calling `Pointer.free`.")
+ (deftemplate leak (Fn [(Ref a b)] (Ptr a)) "$a* $NAME($a* r)" "$DECL { void *leak = CARP_MALLOC(sizeof($a)); memcpy(leak, r, sizeof($a)); return ($a*)leak;}")
+
+ (doc free
+ "Free a pointer."
+ "Users need to manually verify that this operation is safe.")
+ (deftemplate free (Fn [(Ptr p)] Unit) "void $NAME($p* p)" "$DECL {CARP_FREE(p);}")
+
+ (doc set "Sets the value of a pointer.")
+ (deftemplate set (Fn [(Ptr p) p] (Ptr p)) "$p* $NAME($p* p, $p a)" "$DECL { *p = a; return p;}")
+
(defn inc [a] (Pointer.add a 1l))
(implements inc Pointer.inc)
(defn dec [a] (Pointer.sub a 1l))
* Pointer: Change signature of leak to make it more sensible
Instead of `leak` copying a previously allocated value, it now takes
(unmanaged) ownership of a fresh value and allocates. This makes more
sense semantically, as we're just instantiating a new pointer that won't
be managed by Carp and will leak unless freed explicitly.
Thanks to @TimDeve for the suggestion!
* Pointer: Improve apis on set and alloc
- Rename set-unsafe to align w/ naming conventions
Most unsafe functions are prefixed with `unsafe`, not suffixed.
- Rename leak to `unsafe-alloc` to better convey its semantics (leak
also already exists as `Unsafe.leak`.
- Remove `cast` since its use is covered by `Unsafe.coerce`.
Thanks to TimDeve and hellerve for the suggestions!
* Pointer: Make unsafe-set take ownership
* Pointer: Correctly cast in unsafe-alloc; add unsafe-realloc
Here's a short illustration of why we need `realloc` even though we
already have `Pointer.add`:
```
(defn foo []
(let-do [p (Pointer.unsafe-alloc 2)]
(set! p (Pointer.add p (Pointer.width (Pointer.unsafe-alloc @"foo"))))
(ignore (Pointer.unsafe-set p @"foo"))
(println* (Pointer.to-value (the (Ptr String) (Unsafe.coerce p))))
(Pointer.free p)
0))
```
This function seems fine at first glance, but since `add` returns a new
pointer, `p` is reset to the new pointer, the reference to the original
is lost, and `free` is called on a value that was never actually
allocated since `add` does not malloc.
Using unsafe-realloc, we can avoid the additional allocation:
```
(defn foo []
(let-do [p (Pointer.unsafe-alloc 2)]
(Pointer.unsafe-realloc p @"foo")
(ignore (Pointer.unsafe-set p @"foo"))
(println* (Pointer.to-value (the (Ptr String) (Unsafe.coerce p))))
(Pointer.free p)
0))
```
The allocation is what we care about here. One still needs to use
`Unsafe.coerce` since as far as the Carp compiler is concerned, `p` is
still a (Ptr Int) even though the corresponding c has cast it silently
to a `String` in order to reallocate.
* Pointer: Change signature of unsafe-set to align with set!
* Pointer: Change signature of `set` to align with `set!`
* Pointer: Remove unsafe-realloc
* Pointer: Update docs for unsafe-alloc and free
* System: Remove System.free
Pointer.free serves a similar function, and is more restrictive, so
we'll remove System.free. One can use `delete` or cast to a pointer and
free that way.See PR #1012 for further discussion.
2020-11-30 09:06:04 +03:00
|
|
|
(doc unsafe-set
|
|
|
|
"Sets the value of a pointer to a value of any type."
|
|
|
|
"The user will have to ensure this operation is safe.")
|
|
|
|
(deftemplate unsafe-set (Fn [(Ptr p) a] Unit) "void $NAME($p* p, $a a)" "$DECL { *p = ($p)a; }")
|
|
|
|
|
|
|
|
(doc unsafe-alloc
|
|
|
|
"Allocate a new pointer to the value `a`."
|
|
|
|
("This pointer won't be managed by Carp's borrow checker and will cause " false)
|
|
|
|
"memory leaks unless explicitly freed using `Pointer.free`."
|
|
|
|
""
|
|
|
|
("See `Unsafe.leak` if you want to prevent Carp's automatic memory management " false)
|
|
|
|
"from deallocating a value without performing an allocation."
|
|
|
|
""
|
|
|
|
"```"
|
|
|
|
"(let-do [x (Pointer.unsafe-alloc @\"c\")]"
|
|
|
|
" (Pointer.set x @\"carp\")"
|
|
|
|
" (println* (Pointer.to-value x))"
|
|
|
|
" (Pointer.free x))"
|
|
|
|
"```")
|
|
|
|
(deftemplate unsafe-alloc (Fn [a] (Ptr a)) "$a* $NAME($a r)" "$DECL { void *leak = CARP_MALLOC(sizeof($a)); *($a*)leak= r; return ($a*)leak;}")
|
|
|
|
|
|
|
|
(doc free
|
|
|
|
"Free a pointer, deallocating the memory associated with it."
|
|
|
|
("Carp's borrow checker handles deallocation automatically for managed types. " false)
|
|
|
|
("The Ptr type is unmanaged, so pointers allocated with functions " false)
|
|
|
|
"such as `Pointer.unsafe-alloc` need to be deallocated manually using this function."
|
|
|
|
"Users need to manually verify that this operation is safe."
|
|
|
|
""
|
|
|
|
"```"
|
|
|
|
"(let-do [x (Pointer.unsafe-alloc @\"c\")]"
|
|
|
|
" (Pointer.set x @\"carp\")"
|
|
|
|
" (println* (Pointer.to-value x))"
|
|
|
|
" (Pointer.free x))"
|
|
|
|
"```")
|
|
|
|
(deftemplate free (Fn [(Ptr p)] Unit) "void $NAME($p* p)" "$DECL {CARP_FREE(p);}")
|
|
|
|
|
|
|
|
(doc set "Sets the value of a pointer.")
|
|
|
|
(deftemplate set (Fn [(Ptr p) p] Unit) "void $NAME($p* p, $p a)" "$DECL { *p = a; }")
|
|
|
|
|
2019-10-30 10:09:18 +03:00
|
|
|
(defn inc [a] (Pointer.add a 1l))
|
2020-05-10 20:32:22 +03:00
|
|
|
(implements inc Pointer.inc)
|
2019-10-30 10:09:18 +03:00
|
|
|
(defn dec [a] (Pointer.sub a 1l))
|
2020-05-10 20:32:22 +03:00
|
|
|
(implements dec Pointer.dec)
|
2019-10-30 10:09:18 +03:00
|
|
|
)
|