This is a summary of porting the Amiga IFF ILBM converter to the D Programming Language.
This program went through several different languages, each had different strengths and weaknesses:
Mozilla Rust - Fast and fairly memory safe but horrible file handling when it came to loading structs. There were a lot of other things the compiler really should have been able to figure out too such as this nonsense '#[derive(Default,Copy,Clone)]'.
FreePascal - No real negatives, just had to make sure bounds checking was turned on. Dynamic arrays simplfied allocations.
Google Go - Again, no real negatives and I like Go, but the language lacks a lot of minor nicities that the others had (enums, packed structs, immutables).
C (TCC) - Enabling bounds checking with '-b' in TCC helped, the main task was making sure all allocations were freed before the program exited.
I was happy with the Pascal version but thought porting it to D would be interesting. The D version may be a little quicker (not checked), is fairly memory safe and there were more modern programming techniques/methods that could be applied.
This guide does not repeat the items mentioned in the previous Converting Avi2Cvc from C to D guide.
In no particular order:
Replaced dynamic memory allocations such as:
char *frame; ... frame=(unsigned char *)malloc(framesize);
with
ubyte[] frame; ... frame.length=framesize;
The main advantage of doing this is that the compiler/runtime is aware of the allocations. I did not have to track every path in the program to make sure I deallocated the memory before the program exited. Also, thanks to the bounds checking I can be sure I will not be trashing memory and if I do overshoot the bounds, I will be notified!
In fairness, both Pascal's and Rust's dynamic arrays were not terrible...
A minor thing but, from this:
// These could have been a memset()
for(int p=0;p<sizeof(pix);p++){pix[p]=0;}
for(int p=0;p<sizeof(green);p++){green[p]=0;}
for(int p=0;p<sizeof(blue);p++){blue[p]=0;}
to
// Set all values in the arrays to zero
pix[]=0;
green[]=0;
blue[]=0;
Advantages to this include:
These are variables that can be set at runtime but then not changed. They are useful as another defense against accidently changing a value that should not be changed. Good for the compiler too.
// Find out byte width (including padding) needed
immutable uint bmp_row_width=byte_width(bmp_info[0].width);
immutable uint bmp_size=(bmp_row_width*bmp_info[0].height);
Trying to change bmp_size would give an error. Pascal, Go and C do not (to my knowledge) have immutable variables, in Rust everything is immutable by default.
I mentioned the GC profiling before but it is worth mentioning again! To get a GC profile log, compile with -profile=gc and when you run the program you get an easy to read profilegc.log file.
Running the profile enabled version gave this:
bytes allocated, allocations, type, function, file:line 65536 1 ubyte[] D main iff2bmp.d:437 65536 1 ubyte[] D main iff2bmp.d:523 32768 1 ubyte[] D main iff2bmp.d:409 128 1 ubyte[] D main iff2bmp.d:391 16 1 immutable(char)[] D main iff2bmp.d:528 16 1 ubyte[] D main iff2bmp.d:420
All good and as expected. The 'immutable(char)' is the output file name, the others allocations are data chunks and buffers.
Last Updated 20/05/2024
Created 15/02/2020