native — Native interface functions

This module provides functions which offer a low-level interface for the purpose of marshalling data between Mars and foreign (native) code. These functions provide conversions between the regular Num and foreign CInt types, as well as pointer allocation, manipulation, and dereferencing.

Warning

Mars is, fundamentally, a pure language. This means that no function can modify an existing value or global state, only return a value (and perform input/output, which while technically impure, isn’t considered “impure” in Mars).

The functions in this module break that purity by providing direct access to memory via pointers. They can also be used to crash the program or violate type constraints. They should not be considered part of the language semantics, and are not intended to be used by most code. They are provided to allow compatibility with code written in other languages. Use at your own risk.

Conversion functions

These functions convert back and forth between Num and each of the native types. The conversions from Num raise an error if the value is out of range of the target type. The integral types have separate signed and unsigned conversion functions.

num_cschar(value :: Num) :: CChar

Converts a Num to a signed CChar. It is an error if value is not an integer in the range [-128, 127].

cschar_num(value :: CChar) :: Num

Converts a signed CChar to a Num.

num_cuchar(value :: Num) :: CChar

Converts a Num to an unsigned CChar. It is an error if value is not an integer in the range [0, 255].

cuchar_num(value :: CChar) :: Num

Converts an unsigned CChar to a Num.

num_csshort(value :: Num) :: CShort

Converts a Num to a signed CShort. It is an error if value is not an integer in the range [-32768, 32767].

csshort_num(value :: CShort) :: Num

Converts a signed CShort to a Num.

num_cushort(value :: Num) :: CShort

Converts a Num to an unsigned CShort. It is an error if value is not an integer in the range [0, 65535].

cushort_num(value :: CShort) :: Num

Converts an unsigned CShort to a Num.

num_csint(value :: Num) :: CInt

Converts a Num to a signed CInt. It is an error if value is not an integer that can be represented in sizeof_cint bytes.

csint_num(value :: CInt) :: Num

Converts a signed CInt to a Num.

Warning

If the value’s magnitude is greater than 2^53, this may result in rounding errors. (This is only a possibility if sizeof_cint >= 7.)

num_cuint(value :: Num) :: CInt

Converts a Num to an unsigned CInt. It is an error if value is not a positive integer that can be represented in sizeof_cint bytes.

cuint_num(value :: CInt) :: Num

Converts an unsigned CInt to a Num.

Warning

If the value’s magnitude is greater than 2^53, this may result in rounding errors. (This is only a possibility if sizeof_cint >= 7.)

num_cfloat(value :: Num) :: CFloat

Converts a Num to a CFloat.

Warning

This may result in rounding errors. Furthermore, values too large to be represented in a single-precision float will be rounded to negative or positive infinity.

cfloat_num(value :: CFloat) :: Num

Converts a CFloat to a Num.

num_cdouble(value :: Num) :: CDouble

Converts a Num to a CDouble.

cdouble_num(value :: CDouble) :: Num

Converts a CDouble to a Num.

num_cptr(value :: Num) :: CPtr

Converts a Num to a CPtr. It is an error if value is not a positive integer that can be represented in sizeof_cptr bytes.

Note

This can be used to fabricate a pointer at any address. This is almost never a good idea. Pointers should usually be created using malloc, and the null pointer can be obtained with nullptr. Be aware that Num cannot precisely represent all possible pointer values on a 64-bit system.

cptr_num(value :: CPtr) :: Num

Converts a CPtr to a Num.

Warning

On a 64-bit system, this may result in rounding errors. Therefore, this function should generally be avoided. Never attempt to pass the result of this function back to num_cptr, because the rounding error may alter the pointer’s address. Pointer arithmetic should be performed using cptr_add and cptr_diff instead.

Size constants

These constants get the size, in bytes, of each native type. This is equivalent to the sizeof operator in C. These should be used with malloc to determine how much memory to allocate for a native value or an array of them. These should also be used with cptr_add when indexing into an array.

sizeof_cchar :: Num

The number of bytes of memory used by a CChar. This is always 1. This is equivalent to the C expression sizeof(char).

sizeof_cshort :: Num

The number of bytes of memory used by a CChar. This is 2 on all supported platforms. This is equivalent to the C expression sizeof(short).

sizeof_cint :: Num

The number of bytes of memory used by a CInt. This is typically 4, on both 32-bit and 64-bit machines. This is equivalent to the C expression sizeof(int).

sizeof_cfloat :: Num

The number of bytes of memory used by a CFloat. This is 4 on all supported platforms. This is equivalent to the C expression sizeof(float).

sizeof_cdouble :: Num

The number of bytes of memory used by a CDouble. This is 8 on all supported platforms. This is equivalent to the C expression sizeof(double).

sizeof_cptr :: Num

The number of bytes of memory used by a CPtr. On 32-bit machines, this will be 4. On 64-bit machines, this will be 8. This is equivalent to the C expression sizeof(void*).

Pointer arithmetic functions

These functions directly manipulate pointers, without having to convert back and forth to Num.

cptr_add(pointer :: CPtr, offset :: Num) :: CPtr

Adds offset bytes to pointer and returns the result. The offset may be a negative number. It is an error if offset is not a whole number. If the result is less than 0 or larger than the maximum pointer value, behaviour is undefined (typically wraps).

This is the preferred way to perform pointer arithmetic, such as when indexing into arrays. This is equivalent to the C expression (char*) pointer + offset.

Note

This is different from pointer arithmetic in C, which offsets the pointer by offset multiplied by the size of the pointed-to type. In order to index into a native array in Mars, the index must be manually multiplied by the size of the type. For example, if p points to an array of floats, element i is addressed as cptr_add(p, i * sizeof_cfloat).

cptr_diff(x :: CPtr, y :: CPtr) :: Num

Subtracts the pointer y from the pointer x, returning the difference between them in bytes. This is equivalent to the C expression (char*) x - (char*) y.

Warning

On a 64-bit system, this may result in rounding errors if x and y are sufficiently far apart. Therefore, this function should only be used when x and y are known to be pointers to the same contiguously allocated block.

Manual memory management functions

malloc(size :: Num) :: CPtr

As with the malloc function in the C standard library, allocates size bytes of memory and returns a pointer to that memory. The memory will be uninitialised.

A runtime error is triggered if the program is out of memory when malloc is called; this function never returns nullptr.

Memory allocated by malloc cannot be used by regular Mars code, but it can be manipulated by other functions in the native module, as well as being passed to functions written in other languages (which is why it is provided).

There is no corresponding free function. As Mars currently uses the Boehm garbage collector, memory allocated with malloc should automatically be freed when it is no longer in use.

Pointer dereferencing functions

These functions read or write values pointed to by a pointer. There is a separate pair of functions for dereferencing pointers to each native type. In all of these functions, behaviour is undefined if pointer is not a valid memory address.

ccharptr_ref(pointer :: CPtr) :: CChar

Read a CChar value from the memory location pointer. This is equivalent to the C expression *((char*) pointer).

ccharptr_set(pointer :: CPtr, value :: CChar) :: CPtr

Write a CChar value to the memory location pointer. This is equivalent to the C expression *((char*) pointer) = value, pointer.

cshortptr_ref(pointer :: CPtr) :: CShort

Read a CShort value from the memory location pointer. This is equivalent to the C expression *((short*) pointer).

cshortptr_set(pointer :: CPtr, value :: CShort) :: CPtr

Write a CShort value to the memory location pointer. This is equivalent to the C expression *((short*) pointer) = value, pointer.

cintptr_ref(pointer :: CPtr) :: CInt

Read a CInt value from the memory location pointer. This is equivalent to the C expression *((int*) pointer).

cintptr_set(pointer :: CPtr, value :: CInt) :: CPtr

Write a CInt value to the memory location pointer. This is equivalent to the C expression *((int*) pointer) = value, pointer.

cfloatptr_ref(pointer :: CPtr) :: CFloat

Read a CFloat value from the memory location pointer. This is equivalent to the C expression *((float*) pointer).

cfloatptr_set(pointer :: CPtr, value :: CFloat) :: CPtr

Write a CFloat value to the memory location pointer. This is equivalent to the C expression *((float*) pointer) = value, pointer.

cdoubleptr_ref(pointer :: CPtr) :: CDouble

Read a CDouble value from the memory location pointer. This is equivalent to the C expression *((double*) pointer).

cdoubleptr_set(pointer :: CPtr, value :: CDouble) :: CPtr

Write a CDouble value to the memory location pointer. This is equivalent to the C expression *((double*) pointer) = value, pointer.

cptrptr_ref(pointer :: CPtr) :: CPtr

Read a CPtr value from the memory location pointer. This is equivalent to the C expression *((void**) pointer).

cptrptr_set(pointer :: CPtr, value :: CPtr) :: CPtr

Write a CPtr value to the memory location pointer. This is equivalent to the C expression *((void**) pointer) = value, pointer.

Utility constants and functions

These functions and constants are written in pure Mars, and their use is optional. However, they are helpful in dealing with converting data between Mars and C.

nullptr :: CPtr

The CPtr value 0 (num_cptr(0)). This is equivalent to the C expression NULL.

nullbyte :: CChar

The CChar value 0 (num_cuchar('\0')). This is equivalent to the C expression '\0'.

string_cptr(string :: Array(Num)) :: CPtr

Converts a Mars string into a C string (a pointer to a contiguous array of bytes, terminated with the byte value 0). This allocates sufficient memory for the C string using malloc, and copies each value into a single byte of the buffer, also writing the 0 byte at the end. Returns a pointer to the allocated C string.

It is an error if any element of string is non-integral or outside the range [0, 255].

This returns a new pointer value every time it is called, even if given the same input.

Warning

If any element of the string is 0, it will be copied into the buffer without error, which will cause any code which expects a null-terminated string to consider the first 0 byte to be the end of the string. This can potentially cause security vulnerabilities.

cptr_string(cstr :: CPtr) :: Array(Num)

Converts a C string (a pointer to a contiguous array of bytes, terminated with the byte value 0) into a Mars string. The bytes in the array cstr are read one after another, and copied into a Mars array until a 0 byte is detected. The 0 byte is not copied into the Mars array.

Behaviour is undefined if cstr is not a valid pointer, or if it does not contain a 0 byte anywhere in its allocated length. cptr_string will keep reading bytes until it detects a 0 byte.