mirror of
https://github.com/carp-lang/Carp.git
synced 2024-09-11 05:25:28 +03:00
* feat: add box templates and box type
This commit adds an implementation of Boxes, memory manged heap
allocated values.
Boxes are implemented as C pointers, with no additional structure but
are treated as structs in Carp. To facilitate this, we need to add them
as a clause to our special type emissions (TypesToC) as they'd otherwise
be emitted like other struct types.
Co-authored-by: Veit Heller <veit@veitheller.de>
* fix: slight memory management fix for Box
Make sure we free the box!
* test: add tests for box (including memory checks)
* Revert "fix: Ignore clang nitpick"
This reverts commit 70ec6d46d4
.
* fix: update example/functor.carp
Now that a builtin type named Box exists, the definitions in this file
cause a conflict. I've renamed the "Box" type in the functor example to
remove the conflict.
* feat: add Box.peek
Box.peek allows users to transform a reference to a box into a a
reference to the box's contained value. The returned reference will have
the same lifetime as the box. This function allows callers to manipulate
the value in a box without re-allocation, for example:
```clojure
(deftype Num [val Int])
(let-do [box (Box.init (Num.init 0))]
(Num.set-val! (Box.peek &box) 1)
@(Num.val (Box.peek &box)))
```
This commit also includes tests for Box.peek.
Co-authored-by: TimDeve <TimDeve@users.noreply.github.com>
* feat: implement blit on ByteOrder
ByteOrder is effectively just an enum that indicates desired endianness.
We can freely copy it around without penalty.
Co-authored-by: Veit Heller <veit@veitheller.de>
Co-authored-by: Erik Svedäng <erik@coherence.io>
Co-authored-by: TimDeve <TimDeve@users.noreply.github.com>
383 lines
16 KiB
Plaintext
383 lines
16 KiB
Plaintext
(system-include "carp_binary.h")
|
|
(load "StdInt.carp")
|
|
|
|
;; Helper functions for making working with Maybe easier
|
|
;; TODO: Replace all of these with a single type-generic
|
|
;; zip-n macro.
|
|
(defmodule Maybe
|
|
(defndynamic zip- [f args names]
|
|
(if (empty? args)
|
|
`(Maybe.Just (~%f %@names))
|
|
(let [n (gensym-with 'zip)]
|
|
`(match %(car args)
|
|
(Maybe.Nothing) (Maybe.Nothing)
|
|
(Maybe.Just %n) %(Maybe.zip- f (cdr args) (cons-last n names))))))
|
|
|
|
(defmacro zip [f :rest args]
|
|
(Maybe.zip- f args '()))
|
|
)
|
|
|
|
;; Temporary fix for issue #698
|
|
;; The underlying issue is deeper, and should probably be fixed.
|
|
(doc ByteOrder
|
|
"The type of byte orders.
|
|
|
|
LittleEndian designates the little endian ordering, and indicates the least
|
|
significant byte appears first in a given byte sequence.
|
|
|
|
BigEndian designates the big endian ordering, and indicates the most
|
|
significant byte occurs first in a given byte sequence.")
|
|
(deftype ByteOrder LittleEndian BigEndian)
|
|
|
|
(defmodule ByteOrder
|
|
(defn blit [x] (the ByteOrder x))
|
|
(implements blit blit)
|
|
)
|
|
|
|
(doc Binary "provides various helper functions to work with bits and bytes.")
|
|
(defmodule Binary
|
|
(register to-int16 (λ [Byte Byte] Uint16))
|
|
(register to-int32 (λ [Byte Byte Byte Byte] Uint32))
|
|
(register to-int64 (λ [Byte Byte Byte Byte Byte Byte Byte Byte] Uint64))
|
|
(register int16-to-byte (λ [(Ref Uint16)] Byte))
|
|
(register int32-to-byte (λ [(Ref Uint32)] Byte))
|
|
(register int64-to-byte (λ [(Ref Uint64)] Byte))
|
|
(register system-endianness-internal (λ [] Int))
|
|
|
|
(defn unwrap-success [x]
|
|
(Result.unwrap-or-zero @x))
|
|
|
|
(defn unwrap-error [x]
|
|
(Result.from-error @x (zero)))
|
|
|
|
(doc byte-converter
|
|
"Returns a function that, when called, attempts to convert an array of bytes using `f` and `order`
|
|
|
|
If the conversion is successful, returns a `Result.Success` containing the converted value.
|
|
|
|
If the conversion fails, returns a `Result.Error` containing the byte array passed as an argument.")
|
|
(defn byte-converter [f order]
|
|
(fn [bs]
|
|
(match (~f order bs)
|
|
(Maybe.Nothing) (Result.Error @bs)
|
|
(Maybe.Just i) (Result.Success i))))
|
|
(doc interpreted
|
|
"Returns the interpreted value from a sequence of byte-converion results")
|
|
(private interpreted)
|
|
(defn interpreted [results]
|
|
(==> results
|
|
(Array.copy-filter &Result.success?)
|
|
(ref)
|
|
(Array.copy-map &unwrap-success)))
|
|
|
|
(doc remaining-bytes
|
|
"Returns the number of uninterpreted bytes from a seuqence of byte-conversion results")
|
|
(private remaining-bytes)
|
|
(defn remaining-bytes [results]
|
|
(==> results
|
|
(Array.copy-filter &Result.error?)
|
|
(ref)
|
|
(Array.copy-map &unwrap-error)
|
|
(ref)
|
|
(Array.copy-map &Array.length)
|
|
(ref)
|
|
(Array.reduce &(fn [x y] (+ x @y)) 0)))
|
|
|
|
(doc system-endianness
|
|
"Returns the endianness of the host system.")
|
|
(sig system-endianness (λ [] ByteOrder))
|
|
(defn system-endianness []
|
|
(if (= (system-endianness-internal) 1)
|
|
(ByteOrder.LittleEndian)
|
|
(ByteOrder.BigEndian)))
|
|
|
|
(doc unsafe-bytes->int16
|
|
"Interprets the first two bytes in a byte sequence as an Uint16 value.
|
|
**This operation is unsafe.**")
|
|
(sig unsafe-bytes->int16 (Fn [ByteOrder (Ref (Array Byte) a)] Uint16))
|
|
(defn unsafe-bytes->int16 [order bs]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(to-int16 @(Array.unsafe-nth bs 0) @(Array.unsafe-nth bs 1))
|
|
(ByteOrder.BigEndian)
|
|
(to-int16 @(Array.unsafe-nth bs 1) @(Array.unsafe-nth bs 0))))
|
|
|
|
(doc bytes->int16
|
|
"Interprets the first two bytes in a byte sequence as an Uint16 value.
|
|
|
|
If the first two bytes are inaccessible, or the given array contains less
|
|
than two bytes, returns Maybe.Nothing.")
|
|
(sig bytes->int16 (Fn [ByteOrder (Ref (Array Byte) a)] (Maybe Uint16)))
|
|
(defn bytes->int16 [order bytes]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(Maybe.zip &to-int16 (Array.nth bytes 0) (Array.nth bytes 1))
|
|
(ByteOrder.BigEndian)
|
|
(Maybe.zip &to-int16 (Array.nth bytes 1) (Array.nth bytes 0))))
|
|
|
|
(doc int16->bytes
|
|
"Converts a Uint16 to a sequence of bytes representing the value using the provided `order`")
|
|
(sig int16->bytes (Fn [ByteOrder Uint16] (Array Byte)))
|
|
(defn int16->bytes [order i]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(Array.copy-map &int16-to-byte &[i (Uint16.bit-shift-right i (Uint16.from-long 8l))])
|
|
(ByteOrder.BigEndian)
|
|
(Array.copy-map &int16-to-byte &[(Uint16.bit-shift-right i (Uint16.from-long 8l)) i])))
|
|
|
|
(doc unsafe-bytes->int16-seq
|
|
"Interprets a sequence of bytes as a sequence of Uint16 values.
|
|
**This operation is unsafe.**")
|
|
(sig unsafe-bytes->int16-seq (Fn [ByteOrder (Ref (Array Byte) a)] (Array Uint16)))
|
|
(defn unsafe-bytes->int16-seq [order bs]
|
|
(let [partitions (Array.partition bs 2)
|
|
f (fn [b] (unsafe-bytes->int16 order b))]
|
|
(Array.copy-map &f &partitions)))
|
|
|
|
(doc bytes->int16-seq
|
|
"Interprets a sequence of bytes as a sequence of Uint16 values.
|
|
|
|
Returns a pair containing interpreted values and the number of bytes that were not interpreted.")
|
|
(sig bytes->int16-seq (Fn [ByteOrder (Ref (Array Byte) a)] (Pair (Array Uint16) Int)))
|
|
(defn bytes->int16-seq [order bs]
|
|
(let [partitions (Array.partition bs 2)
|
|
f (byte-converter &bytes->int16 order)]
|
|
(let [results (Array.copy-map &f &partitions)]
|
|
(Pair.init (interpreted &results) (remaining-bytes &results)))))
|
|
|
|
(doc bytes->int16-seq-exact
|
|
"Attempts to interpret a given byte sequence as an exact sequence of Uint16 values.
|
|
|
|
If successful, returns the interpreted values. If unsuccessful, returns the number of excess bytes.")
|
|
(sig bytes->int16-seq-exact (Fn [ByteOrder (Ref (Array Byte) a)] (Result (Array Uint16) Int)))
|
|
(defn bytes->int16-seq-exact [order bs]
|
|
(let [r (bytes->int16-seq order bs)]
|
|
(if (= 0 @(Pair.b &r))
|
|
(Result.Success @(Pair.a &r))
|
|
(Result.Error @(Pair.b &r)))))
|
|
|
|
(doc int16-seq->bytes
|
|
"Converts an array of Uint16 values into byte sequences.")
|
|
(sig int16-seq->bytes (Fn [ByteOrder (Ref (Array Uint16) a)] (Array (Array Byte))))
|
|
(defn int16-seq->bytes [order is]
|
|
(let [f (fn [i] (int16->bytes order @i))]
|
|
(Array.copy-map &f is)))
|
|
|
|
(doc unsafe-bytes->int32
|
|
"Interprets the first four bytes in a byte sequence as an Uint32 value.
|
|
**This operation is unsafe.**")
|
|
(sig unsafe-bytes->int32 (Fn [ByteOrder (Ref (Array Byte))] Uint32))
|
|
(defn unsafe-bytes->int32 [order bs]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(to-int32 @(Array.unsafe-nth bs 0) @(Array.unsafe-nth bs 1)
|
|
@(Array.unsafe-nth bs 2) @(Array.unsafe-nth bs 3))
|
|
(ByteOrder.BigEndian)
|
|
(to-int32 @(Array.unsafe-nth bs 3) @(Array.unsafe-nth bs 2)
|
|
@(Array.unsafe-nth bs 1) @(Array.unsafe-nth bs 0))))
|
|
|
|
(doc bytes->int32
|
|
"Interprets the first four bytes in a byte sequence as an Uint32 value.
|
|
|
|
If the first four bytes are inaccessible, or the given array contains less
|
|
than four bytes, returns Maybe.Nothing.")
|
|
(sig bytes->int32 (Fn [ByteOrder (Ref (Array Byte))] (Maybe Uint32)))
|
|
(defn bytes->int32 [order bs]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(Maybe.zip &to-int32 (Array.nth bs 0) (Array.nth bs 1)
|
|
(Array.nth bs 2) (Array.nth bs 3))
|
|
(ByteOrder.BigEndian)
|
|
(Maybe.zip &to-int32 (Array.nth bs 3) (Array.nth bs 2)
|
|
(Array.nth bs 1) (Array.nth bs 0))))
|
|
|
|
(doc int32->bytes
|
|
"Converts a Uint32 to a sequence of bytes representing the value using the provided `order`")
|
|
(sig int32->bytes (Fn [ByteOrder Uint32] (Array Byte)))
|
|
(defn int32->bytes [order i]
|
|
(let [shift (fn [lng] (Uint32.bit-shift-right i (Uint32.from-long lng)))]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(Array.copy-map &int32-to-byte
|
|
&[i (shift 8l) (shift 16l) (shift 24l)])
|
|
(ByteOrder.BigEndian)
|
|
(Array.copy-map &int32-to-byte
|
|
&[(shift 24l) (shift 16l) (shift 8l) i]))))
|
|
|
|
(doc unsafe-bytes->int32-seq
|
|
"Interprets a sequence of bytes as a sequence of Uint32 values.
|
|
**This operation is unsafe.**")
|
|
(sig unsafe-bytes->int32-seq (Fn [ByteOrder (Ref (Array Byte) a)] (Array Uint32)))
|
|
(defn unsafe-bytes->int32-seq [order bs]
|
|
(let [partitions (Array.partition bs 4)
|
|
f (fn [b] (unsafe-bytes->int32 order b))]
|
|
(Array.copy-map &f &partitions)))
|
|
|
|
(doc bytes->int32-seq
|
|
"Interprets a sequence of bytes as a sequence of Uint32 values.
|
|
|
|
Returns a pair containing interpreted values and the number of bytes that were not interpreted.")
|
|
(sig bytes->int32-seq (Fn [ByteOrder (Ref (Array Byte) a)] (Pair (Array Uint32) Int)))
|
|
(defn bytes->int32-seq [order bs]
|
|
(let [partitions (Array.partition bs 4)
|
|
f (byte-converter &bytes->int32 order)]
|
|
(let [results (Array.copy-map &f &partitions)]
|
|
(Pair.init (interpreted &results) (remaining-bytes &results)))))
|
|
|
|
(doc bytes->int32-seq-exact
|
|
"Attempts to interpret a given byte sequence as an exact sequence of Uint32 values.
|
|
|
|
If successful, returns the interpreted values. If unsuccessful, returns the number of excess bytes.")
|
|
(sig bytes->int32-seq-exact (Fn [ByteOrder (Ref (Array Byte) a)] (Result (Array Uint32) Int)))
|
|
(defn bytes->int32-seq-exact [order bs]
|
|
(let [r (bytes->int32-seq order bs)]
|
|
(if (= 0 @(Pair.b &r))
|
|
(Result.Success @(Pair.a &r))
|
|
(Result.Error @(Pair.b &r)))))
|
|
|
|
(doc int32-seq->bytes
|
|
"Converts an array of Uint32 values into byte sequences.")
|
|
(sig int32-seq->bytes (Fn [ByteOrder (Ref (Array Uint32) a)] (Array (Array Byte))))
|
|
(defn int32-seq->bytes [order is]
|
|
(let [f (fn [i] (int32->bytes order @i))]
|
|
(Array.copy-map &f is)))
|
|
|
|
(doc unsafe-bytes->int64
|
|
"Interprets the first eight bytes in a byte sequence as an Uint64 value.
|
|
**This operation is unsafe.**")
|
|
(sig unsafe-bytes->int64 (Fn [ByteOrder (Ref (Array Byte) a)] Uint64))
|
|
(defn unsafe-bytes->int64 [order bs]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(to-int64 @(Array.unsafe-nth bs 0) @(Array.unsafe-nth bs 1)
|
|
@(Array.unsafe-nth bs 2) @(Array.unsafe-nth bs 3)
|
|
@(Array.unsafe-nth bs 4) @(Array.unsafe-nth bs 5)
|
|
@(Array.unsafe-nth bs 6) @(Array.unsafe-nth bs 7))
|
|
(ByteOrder.BigEndian)
|
|
(to-int64 @(Array.unsafe-nth bs 7) @(Array.unsafe-nth bs 6)
|
|
@(Array.unsafe-nth bs 5) @(Array.unsafe-nth bs 4)
|
|
@(Array.unsafe-nth bs 3) @(Array.unsafe-nth bs 2)
|
|
@(Array.unsafe-nth bs 1) @(Array.unsafe-nth bs 0))))
|
|
|
|
(doc bytes->int64
|
|
"Interprets the first eight bytes in a byte sequence as an Uint64 value.
|
|
|
|
If the first eight bytes are inaccessible, or the given array contains less
|
|
than eight bytes, returns Maybe.Nothing.")
|
|
(sig bytes->int64 (Fn [ByteOrder (Ref (Array Byte) a)] (Maybe Uint64)))
|
|
(defn bytes->int64 [order bs]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(Maybe.zip &to-int64 (Array.nth bs 0) (Array.nth bs 1)
|
|
(Array.nth bs 2) (Array.nth bs 3)
|
|
(Array.nth bs 4) (Array.nth bs 5)
|
|
(Array.nth bs 6) (Array.nth bs 7))
|
|
(ByteOrder.BigEndian)
|
|
(Maybe.zip &to-int64 (Array.nth bs 7) (Array.nth bs 6)
|
|
(Array.nth bs 5) (Array.nth bs 4)
|
|
(Array.nth bs 3) (Array.nth bs 2)
|
|
(Array.nth bs 1) (Array.nth bs 0))))
|
|
(doc int64->bytes
|
|
"Converts a Uint64 to a sequence of bytes representing the value using the provided `order`")
|
|
(sig int64->bytes (Fn [ByteOrder Uint64] (Array Byte)))
|
|
(defn int64->bytes [order i]
|
|
(let [shift (fn [lng] (Uint64.bit-shift-right i (Uint64.from-long lng)))]
|
|
(match order
|
|
(ByteOrder.LittleEndian)
|
|
(Array.copy-map &int64-to-byte
|
|
&[i (shift 8l) (shift 16l)
|
|
(shift 24l) (shift 32l)
|
|
(shift 40l) (shift 48l) (shift 56l)])
|
|
(ByteOrder.BigEndian)
|
|
(Array.copy-map &int64-to-byte
|
|
&[(shift 56l) (shift 48l)
|
|
(shift 40l) (shift 32l)
|
|
(shift 24l) (shift 16l) (shift 8l) i]))))
|
|
|
|
(doc unsafe-bytes->int64-seq
|
|
"Interprets a sequence of bytes as a sequence of Uint64 values.
|
|
**This operation is unsafe.**")
|
|
(sig unsafe-bytes->int64-seq (Fn [ByteOrder (Ref (Array Byte) a)] (Array Uint64)))
|
|
(defn unsafe-bytes->int64-seq [order bs]
|
|
(let [partitions (Array.partition bs 8)
|
|
f (fn [b] (unsafe-bytes->int64 order b))]
|
|
(Array.copy-map &f &partitions)))
|
|
|
|
(doc bytes->int64-seq
|
|
"Interprets a sequence of bytes as a sequence of Uint64 values.
|
|
|
|
Returns a pair containing interpreted values and the number of bytes that were not interpreted.")
|
|
(sig bytes->int64-seq (Fn [ByteOrder (Ref (Array Byte) a)] (Pair (Array Uint64) Int)))
|
|
(defn bytes->int64-seq [order bs]
|
|
(let [partitions (Array.partition bs 8)
|
|
f (byte-converter &bytes->int64 order)]
|
|
(let [results (Array.copy-map &f &partitions)]
|
|
(Pair.init (interpreted &results) (remaining-bytes &results)))))
|
|
|
|
(doc bytes->int64-seq-exact
|
|
"Attempts to interpret a given byte sequence as an exact sequence of Uint64 values.
|
|
|
|
If successful, returns the interpreted values. If unsuccessful, returns the number of excess bytes.")
|
|
(sig bytes->int64-seq-exact (Fn [ByteOrder (Ref (Array Byte) a)] (Result (Array Uint64) Int)))
|
|
(defn bytes->int64-seq-exact [order bs]
|
|
(let [r (bytes->int64-seq order bs)]
|
|
(if (= 0 @(Pair.b &r))
|
|
(Result.Success @(Pair.a &r))
|
|
(Result.Error @(Pair.b &r)))))
|
|
|
|
(doc int64-seq->bytes
|
|
"Converts an array of Uint64 values into byte sequences.")
|
|
(sig int64-seq->bytes (Fn [ByteOrder (Ref (Array Uint64) a)] (Array (Array Byte))))
|
|
(defn int64-seq->bytes [order is]
|
|
(let [f (fn [i] (int64->bytes order @i))]
|
|
(Array.copy-map &f is)))
|
|
|
|
(defn to-hex-str [b]
|
|
(let [hi (Byte.bit-and b (from-int 0xF0))
|
|
lo (Byte.bit-shift-left b (from-int 4))
|
|
nib-one (case hi
|
|
(from-int 0x00) @"0"
|
|
(from-int 0x10) @"1"
|
|
(from-int 0x20) @"2"
|
|
(from-int 0x30) @"3"
|
|
(from-int 0x40) @"4"
|
|
(from-int 0x50) @"5"
|
|
(from-int 0x60) @"6"
|
|
(from-int 0x70) @"7"
|
|
(from-int 0x80) @"8"
|
|
(from-int 0x90) @"9"
|
|
(from-int 0xA0) @"A"
|
|
(from-int 0xB0) @"B"
|
|
(from-int 0xC0) @"C"
|
|
(from-int 0xD0) @"D"
|
|
(from-int 0xE0) @"E"
|
|
(from-int 0xF0) @"F"
|
|
@"FATAL ERROR IN BIT LAND! ALL IS LOST")
|
|
nib-two (case lo
|
|
(from-int 0x00) @"0"
|
|
(from-int 0x10) @"1"
|
|
(from-int 0x20) @"2"
|
|
(from-int 0x30) @"3"
|
|
(from-int 0x40) @"4"
|
|
(from-int 0x50) @"5"
|
|
(from-int 0x60) @"6"
|
|
(from-int 0x70) @"7"
|
|
(from-int 0x80) @"8"
|
|
(from-int 0x90) @"9"
|
|
(from-int 0xA0) @"A"
|
|
(from-int 0xB0) @"B"
|
|
(from-int 0xC0) @"C"
|
|
(from-int 0xD0) @"D"
|
|
(from-int 0xE0) @"E"
|
|
(from-int 0xF0) @"F"
|
|
@"FATAL ERROR IN BIT LAND! ALL IS LOST")]
|
|
(String.concat &[nib-one nib-two])))
|
|
|
|
(doc bytes->hex-string
|
|
"Converts an array of bytes to a string of its hexadecimal representation")
|
|
(sig bytes->hex-string (Fn [(Ref (Array Byte) q)] String))
|
|
(defn bytes->hex-string [bs]
|
|
(let [f (fn [b] (to-hex-str @b))]
|
|
(String.join " " &(Array.copy-map &f bs))))
|
|
)
|