Skip to content

Instantly share code, notes, and snippets.

@JohanEngelen
Created August 7, 2022 21:01
Show Gist options
  • Save JohanEngelen/137a2a8a86a2e1bcdadcc8c7ac81f373 to your computer and use it in GitHub Desktop.
Save JohanEngelen/137a2a8a86a2e1bcdadcc8c7ac81f373 to your computer and use it in GitHub Desktop.
Generates random struct with bitfields in C and D code and tests whether D compiler correctly compiles C bitfields
// Test importC bitfields.
// Generates random bitfield structure type, generates the C code and header file, generates D file that tests reading/writing from bitfields in C-land and D-land.
/+
struct Test {
{<empty>|some __alignment...} {<empty>|signed|unsigned} {int|char|long|size_t|float|...} member1 {<empty>|:<some number 0..bitwidth of the storage type};
... repeat for member2, member3, random number of members
}
void resetMember1_C(Test* t)
{
t->member1 = 0;
}
...repeat for how many members there are
size_t sizeofTest_C()
{
return sizeof(Test);
}
While generating the C file, you also generate a D file:
extern(C)
struct Test {
// ofcourse corresponding to the generated C type!
{<empty>|align(...)} {int|uint|bool|byte|char|long|size_t|float|...} member1 {<empty>|:<some number 0..bitwidth of the storage type};
... repeat for member2, member3, random number of members
}
extern(C) void resetMember1_C(Test* t);
void resetMember1_D(Test* t)
{
t.member1 = 0;
}
...repeat for how many members there are
extern(C) size_t sizeofTest_C();
size_t sizeofTest_D()
{
return Test.sizeof();
}
void main()
{
assert(sizeofTest_C() == sizeofTest_D());
foreach(i; number of members in Test) {
Test s_C;
Test s_D;
memset(&s_C, 0xFF, Test.sizeof);
memset(&s_D, 0xFF, Test.sizeof);
resetMemberi_C(&s_C);
resetMemberi_D(&s_D);
assert(memcmp(&s_C, &s_D, Test.sizeof));
}
}
+/
import std.stdio;
import std.random;
import std.conv;
import std.algorithm;
import std.process;
enum BuiltinType {
BOOL,
CHAR,
UNSIGNED_CHAR,
INT,
UNSIGNED_INT,
LONG,
UNSIGNED_LONG,
}
BuiltinType getRandomType() {
return uniform!BuiltinType();
}
uint getTypeBitWidth(BuiltinType t) {
with (BuiltinType) final switch (t) {
case BOOL: return 1;
case CHAR: return 8;
case UNSIGNED_CHAR: return 8;
case INT: return 32;
case UNSIGNED_INT: return 32;
case LONG: return 64;
case UNSIGNED_LONG: return 64;
}
}
string getCTypeString(BuiltinType t) {
with (BuiltinType) final switch (t) {
case BOOL: return "_Bool";
case CHAR: return "char";
case UNSIGNED_CHAR: return "unsigned char";
case INT: return "int";
case UNSIGNED_INT: return "unsigned";
case LONG: return "long";
case UNSIGNED_LONG: return "unsigned long";
}
}
string getDTypeString(BuiltinType t) {
with (BuiltinType) final switch (t) {
case BOOL: return "bool";
case CHAR: return "char";
case UNSIGNED_CHAR: return "ubyte";
case INT: return "int";
case UNSIGNED_INT: return "uint";
case LONG: return "long";
case UNSIGNED_LONG: return "ulong";
}
}
struct Member {
string name;
BuiltinType type;
bool isBitfield; // is this a bitfield or not
uint bitwidth; // number of bits if it is a bitfield. bitwidth must be less than or equal to the bit width of the specified type.
this(string _name) {
name = _name;
type = getRandomType();
isBitfield = randomBitfieldOrNot();
bitwidth = (uniform(0,10) < 2) ? 0 : (type == BuiltinType.BOOL) ? 1 : uniform(0, getTypeBitWidth(type));
}
}
bool randomBitfieldOrNot() {
return uniform(0,20) < 18; // more chance to generate a bitfield than not
}
struct StructTypeDescription
{
Member[] members;
}
StructTypeDescription generateRandomType() {
StructTypeDescription s;
auto numMembers = uniform(1, 20);
foreach (i; 0..numMembers)
{
s.members ~= Member("member"~to!string(i));
}
return s;
}
void writeCTestCodeFile(StructTypeDescription s, string testname) {
auto f = File(testname ~ "_c.c", "w");
f.writeln("typedef unsigned long size_t;");
f.writeln("typedef struct {");
foreach (member; s.members) {
f.write(" ");
f.write(getCTypeString(member.type));
f.write(" ");
if (! (member.isBitfield && member.bitwidth == 0)) // 0-size bitfield must be anonymous
f.write(member.name);
if (member.isBitfield) {
f.write(" : ");
f.write(to!string(member.bitwidth));
}
f.writeln(";");
}
f.writeln("} Test;");
f.writeln(`size_t sizeofTest_C() { return sizeof(Test); }`);
foreach (member; s.members) {
if (member.isBitfield && member.bitwidth == 0)
continue;
f.writeln(`void reset_`~member.name~`_C(Test* t) { t->`~member.name~` = 0; }`);
}
foreach (member; s.members) with(member) {
if (member.isBitfield && member.bitwidth == 0)
continue;
f.writeln(`void set_`~name~`_C(Test* t) { t->`~name~` = 0; t->`~name~` = ~t->`~name~`; }`);
}
}
void writeDTestCodeFile(StructTypeDescription s, string testname) {
auto f = File(testname ~ ".d", "w");
f.writeln(`import ` ~ testname ~ `_c;`);
f.writeln(`import core.stdc.string;`);
foreach (member; s.members) {
if (member.isBitfield && member.bitwidth == 0)
continue;
f.writeln(`void reset_`~member.name~`_D(Test* t) { t.`~member.name~` = 0; }`);
}
foreach (member; s.members) with(member) {
if (member.isBitfield && member.bitwidth == 0)
continue;
f.writeln(`void set_`~name~`_D(Test* t) { t.`~name~` = 0; t.`~name~` = cast(` ~ getDTypeString(type) ~ `)(~t.`~name~`); }`);
}
f.writeln(`
void print_struct(Test* t) {
import std.stdio;
ubyte* tv = cast(ubyte*) t;
foreach (i; 0..Test.sizeof)
writef!"%x "(tv[i]);
writeln();
}
void main()
{
assert(sizeofTest_C() == Test.sizeof);
Test s_C;
Test s_D;
`);
foreach (i, member; s.members) with(member) {
if (member.isBitfield && member.bitwidth == 0)
continue;
f.writeln(`memset(&s_C, 0xFF, Test.sizeof);`);
f.writeln(`memset(&s_D, 0xFF, Test.sizeof);`);
f.writeln(`reset_`~member.name~`_C(&s_C);`);
f.writeln(`reset_`~member.name~`_D(&s_D);`);
f.writeln(`assert(memcmp(&s_C, &s_D, Test.sizeof) == 0);`);
f.writeln();
}
foreach (i, member; s.members) with(member) {
if (member.isBitfield && member.bitwidth == 0)
continue;
f.writeln(`memset(&s_C, 0, Test.sizeof);`);
f.writeln(`memset(&s_D, 0, Test.sizeof);`);
f.writeln(`set_`~member.name~`_C(&s_C);`);
f.writeln(`set_`~member.name~`_D(&s_D);`);
f.writeln(`assert(memcmp(&s_C, &s_D, Test.sizeof) == 0);`);
f.writeln();
}
f.writeln(`}`);
}
string Dcompiler = "./dmd-2.100.0/osx/bin/dmd";
void writeShellCmdFile(StructTypeDescription s, string testname) {
auto f = File(testname ~ ".sh", "w");
f.writeln(`gcc -c ` ~ testname ~ `_c.c -o ` ~ testname ~ `_c.o`);
f.writeln(Dcompiler ~ " " ~ testname ~ `_c.c -run ` ~ testname ~ `.d`);
}
bool testStruct(StructTypeDescription s, string testname = "bf0") {
write("gen, ");
writeCTestCodeFile(s, testname);
writeDTestCodeFile(s, testname);
writeShellCmdFile(s, testname);
write("gcc, ");
auto gcc_output = execute([`gcc`, `-c`, testname ~ `_c.c`, `-o`, testname ~ `_c.o`]);
if (gcc_output.status != 0) {
writeln("C compilation failed:\n", gcc_output.output);
return false;
}
write("dmd + run");
auto dmd = execute([Dcompiler, testname ~ `_c.c`, `-run`, testname ~ `.d`]);
if (dmd.status != 0) {
writeln("D compilation/execution failed:\n", dmd.output);
return false;
}
writeln(".");
return true;
}
void main() {
bool success;
do {
auto s = generateRandomType();
string testname = "bf0";
success =testStruct(s, testname);
} while (success);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment