Thread overview
Please take a look
Dec 20, 2007
bearophile
Dec 21, 2007
Lukas Pinkowski
Dec 22, 2007
bearophile
Dec 22, 2007
Lukas Pinkowski
December 20, 2007
I have implemented a struct, with methods, able to manage 24-bit unsigned integers. They are slower than an uint, so they may be useful only in quite uncommon situations when you must save RAM.
In the attach you can find a stripped down version where I have removed unittests, speed tests, most Ddoc comments, etc. Now I know some D, but surely there are *lot* of things I know little about still (about memory management, operator overloading, casting, the best way to use the CPU, etc). If you have a bit of free time can you please take a look at that code in attach and tell me if you like it, if you can see some things that can be done in a better way in D, in a faster way, more D-onic, if you can solve some of the little problems you can find in the comments, etc.

Bye and thank you,
bearophile


December 21, 2007
bearophile wrote:

> I have implemented a struct, with methods, able to manage 24-bit unsigned integers. They are slower than an uint, so they may be useful only in quite uncommon situations when you must save RAM. In the attach you can find a stripped down version where I have removed unittests, speed tests, most Ddoc comments, etc. Now I know some D, but surely there are *lot* of things I know little about still (about memory management, operator overloading, casting, the best way to use the CPU, etc). If you have a bit of free time can you please take a look at that code in attach and tell me if you like it, if you can see some things that can be done in a better way in D, in a faster way, more D-onic, if you can solve some of the little problems you can find in the comments, etc.
> 
> Bye and thank you,
> bearophile


Taking a look at it, I see, you've done the following:

uint opCast() {
        //return (up << 16) | low; // worse?
        return *(cast(uint*)(cast(void*)this));
    }

You really should uncomment the first line and remove the second, since uint* is a pointer to a 4-byte type, while your struct holds 3 bytes. Thus dereferencing the uint* will lead to a 4th byte with an arbitrary value and finally to errors.
December 22, 2007
Lukas Pinkowski:
>Taking a look at it, I see, you've done the following:
> 
> uint opCast() {
>     //return (up << 16) | low; // worse?
>     return *(cast(uint*)(cast(void*)this));
> }
>
> You really should uncomment the first line and remove the second, since uint* is a pointer to a 4-byte type, while your struct holds 3 bytes. Thus dereferencing the uint* will lead to a 4th byte with an arbitrary value and finally to errors.

I belive you; code similar to that ( (up << 16) | low ) was the first thing I have written.

The funny thing is that (before posting the code on this newsgroup) I have written almost 200 lines of testing code along the code for those 24 bit unsigned integers, and they have failed catching the bug you talk about :-]

This other piece of testing code too fails spotting the problem:

import std.random, u24;
int randInt(int max) {
  int k, n;
  n = max + 1;
  k = cast(int)(n * (rand / (uint.max + 1.0)));
  return (k == n) ? k - 1 : k;
}
void main() {
  Uint24[200_000] arr;
  assert(arr.sizeof == 200_000*3);

  rand_seed(10, 0);
  foreach(ref u; arr)
    u = randInt(Uint24.max);

  rand_seed(10, 0);
  foreach(i, u; arr)
    assert(u == randInt(Uint24.max));
}

Eventually I'll probably put back code like that ( (up << 16) | low ) because I must write safe code (despite being slower) but so far I have failed finding testcases that make that double casting fail :-) Can you show me an example where if shows the bug?

Bye and thank you,
bearophile
December 22, 2007
bearophile wrote:
> Can you show me an example where if shows the bug?


int main(char[][] args)
{
   Uint24[4] array;
   array[0] = 0xffffff;
   array[1] = 0x000000;
   array[2] = 0xeeeeee;
   array[3] = 0xffffff;

   uint a = array[0].val;
   uint b = array[1].val;
   uint c = array[2].val;

   Stdout.formatln( "{:x}", a );
   Stdout.formatln( "{:x}", b );
   Stdout.formatln( "{:x}", c );

   assert( a == 0x00ffffff );
   assert( b == 0x00000000 );
   assert( c == 0x00ffffff );

   return 0;
}

Executing gives:

ffffff
ee000000
ffeeeeee
tango.core.Exception.AssertException@bug24(62): Assertion failure


As you see, taking the value of one array element will return the Uint24
value + the first byte of the next array element (shifted 24bits to the
left). (the first is correct because 0xffffff + (0x000000 << 24) is
0x00ffffff)