{#- ↑ Doesn't apply here, this is the template! +#}
{%- let ctx = self.template +%}
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
{%- for (header, defines) in self.headers +%}
{%- for define in defines +%}
#define {{ define }}
{%- endfor +%}
#include <{{ header }}>
{%- for define in defines +%}
#undef {{ define }}
{%- endfor +%}
{%- endfor +%}
#if defined(__cplusplus)
#define CTEST_ALIGNOF(T) alignof(T)
#define CTEST_EXTERN extern "C"
#else
#define CTEST_ALIGNOF(T) _Alignof(T)
#define CTEST_EXTERN
#endif
typedef void (*ctest_void_func)(void);
/* Query a pointer to string constants.
*
* Define a function that returns a pointer to the value of the constant to test.
* This will later be called on the Rust side via FFI.
*/
{%- for const_cstr in ctx.const_cstr_tests +%}
static char *ctest_const_{{ const_cstr.id }}_val_static = {{ const_cstr.c_val }};
CTEST_EXTERN char *ctest_const_cstr__{{ const_cstr.id }}(void) {
return ctest_const_{{ const_cstr.id }}_val_static;
}
{%- endfor +%}
/* Query a pointer to non-string constants.
*
* Define a function that returns a pointer to the value of the constant to test.
* This will later be called on the Rust side via FFI.
*/
{%- for constant in ctx.const_tests +%}
static {{ constant.c_ty }} ctest_const_{{ constant.id }}_val_static = {{ constant.c_val }};
CTEST_EXTERN {{ constant.c_ty }} *ctest_const__{{ constant.id }}(void) {
return &ctest_const_{{ constant.id }}_val_static;
}
{%- endfor +%}
/* Query the size and alignment of all types */
{%- for item in ctx.size_align_tests +%}
CTEST_EXTERN uint64_t ctest_size_of__{{ item.id }}(void) { return sizeof({{ item.c_ty }}); }
CTEST_EXTERN uint64_t ctest_align_of__{{ item.id }}(void) { return CTEST_ALIGNOF({{ item.c_ty }}); }
{%- endfor +%}
/* Query the signedness of a type.
*
* Return `1` if the type is signed, otherwise return `0`.
* Casting -1 to the aliased type if signed evaluates to `-1 < 0`, if unsigned to `MAX_VALUE < 0`
*/
{%- for alias in ctx.signededness_tests +%}
CTEST_EXTERN uint32_t ctest_signededness_of__{{ alias.id }}(void) {
{{ alias.c_ty }} all_ones = ({{ alias.c_ty }}) -1;
return all_ones < 0;
}
{%- endfor +%}
/* Query the offsets of fields and their sizes. */
{%- for item in ctx.field_size_offset_tests +%}
CTEST_EXTERN uint64_t ctest_offset_of__{{ item.id }}__{{ item.field.ident() }}(void) {
return offsetof({{ item.c_ty }}, {{ item.c_field }});
}
CTEST_EXTERN uint64_t ctest_size_of__{{ item.id }}__{{ item.field.ident() }}(void) {
return sizeof((({{ item.c_ty }}){}).{{ item.c_field }});
}
{%- endfor +%}
/* Query a pointer to a field given a pointer to its struct */
{%- for item in ctx.field_ptr_tests +%}
{#
// This field can have a normal data type, or it could be a function pointer or an array, which
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
#}
typedef {{ item.volatile_keyword }}{{ item.field_return_type }};
CTEST_EXTERN ctest_field_ty__{{ item.id }}__{{ item.field.ident() }}
ctest_field_ptr__{{ item.id }}__{{ item.field.ident() }}({{ item.c_ty }} *b) {
return &b->{{ item.c_field }};
}
{%- endfor +%}
#ifdef _MSC_VER
// Disable signed/unsigned conversion warnings on MSVC.
// These trigger even if the conversion is explicit.
#pragma warning(disable:4365)
#endif
#ifdef __GNUC__
// GCC emits a warning with `-Wextra` if we return a typedef to a type marked `volatile`.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#endif
/* Write a nonrepeating bitpattern to a data type
*
* Tests whether the struct/union/alias `x` when passed by value to C and back to Rust
* remains unchanged.
* It checks if the size is the same as well as if the padding bytes are all in the correct place.
*/
{%- for item in ctx.roundtrip_tests +%}
CTEST_EXTERN {{ item.c_ty }} ctest_roundtrip__{{ item.id }}(
{{ item.c_ty }} value,
const uint8_t is_padding_byte[sizeof({{ item.c_ty }})],
uint8_t value_bytes[sizeof({{ item.c_ty }})]
) {
int size = (int)sizeof({{ item.c_ty }});
{#
// Mark `p` as volatile so that the C compiler does not optimize away the pattern we create.
// Otherwise the Rust side would not be able to see it.
#}
volatile uint8_t* p = (volatile uint8_t*)&value;
int i = 0;
for (i = 0; i < size; ++i) {
{#
// We skip padding bytes in both Rust and C because writing to it is undefined.
// Instead we just make sure the the placement of the padding bytes remains the same.
#}
if (is_padding_byte[i]) { continue; }
value_bytes[i] = p[i];
{#
// After we check that the pattern remained unchanged from Rust to C, we invert the pattern
// and send it back to Rust to make sure that it remains unchanged from C to Rust.
#}
uint8_t d = (uint8_t)(255) - (uint8_t)(i % 256);
d = d == 0 ? 42: d;
p[i] = d;
}
return value;
}
{%- endfor +%}
#ifdef __GNUC__
// Pop allow for `-Wignored-qualifiers`
#pragma GCC diagnostic pop
#endif
#ifdef _MSC_VER
// Pop allow for 4365
#pragma warning(default:4365)
#endif
#ifdef _MSC_VER
// Disable function pointer type conversion warnings on MSVC.
// The conversion may fail only if we call that function, however we only check its address.
#pragma warning(disable:4191)
#endif
/* Query a function's pointer
{%- for item in ctx.foreign_fn_tests +%}
CTEST_EXTERN ctest_void_func ctest_foreign_fn__{{ item.id }}(void) {
return (ctest_void_func){{ item.c_val }};
}
{%- endfor +%}
#ifdef _MSC_VER
#pragma warning(default:4191)
#endif
{%- for static_ in ctx.foreign_static_tests +%}
CTEST_EXTERN void *ctest_static__{{ static_.id }}(void) {
{#
#}
return (void *)&{{ static_.c_val }};
}
{%- endfor +%}