Review: std.uuid

Jonathan M Davis jmdavisProg at gmx.com
Sat Jun 16 16:57:25 PDT 2012


On Saturday, June 16, 2012 13:27:54 Johannes Pfau wrote:
> Am Wed, 13 Jun 2012 18:57:51 -0700
> So I'd rather like to keep the name consistent with the RFC.

I understand. I don't think that there's a really a good solution. So, if you 
think that keeping it as-is is best, then we can do that.

> _toString is already nothrow, so yes, it's only idups fault ;-)
> Added links to http://d.puremagic.com/issues/show_bug.cgi?id=5700

Please just put it as a comment above the try-catch rather than putting it in 
the ddoc. No one using the function needs to know that there's a workaround 
for nothrow. They just need to know that it's nothrow. It's the Phobos 
maintainers that need to know that there's a workaround. It seems fairly 
common to do something like

//@@@BUG@@@ workaround for bugzilla 5700

so that it's nicely greppable.

> > * parseUUID should be clear on whether it takes both lowercase and
> > uppercase hex digits, and I'd argue that it should definitely accept
> > both. [0-9a-f] implies that it only accepts lowercase hex digits.
> 
> OK

Make sure that you have tests which test UUIDs with both uppercase and 
lowercase letters. I only see tests with lowercase letters. You seem to have 
updated an example with some uppercase letters but not any tests from what I 
can see. Speaking of which, please make sure that you have unittest blocks 
with your examples in them to make sure that they compile. Personally, I 
always put

//Verify Example.
unittest
{
    //example code...
}

after a function with an example and make sure that the example and the code 
in the unittest block match exactly (save for indentation, since any 
indentation in the example in the ddoc ends up in the final ddoc, and we do 
want the indentation in the unittest block).

> > * rndGen returns a Random (which is already aliased to Mt19937), and
> > already initializes it with an unpredictable seed. So, why not just
> > us rndGen in randomUUID? It becomes
> > 
> > return randomUUID(rndGen());
> > 
> > It's much shorter and just as good as far as I can tell.
> 
> Good catch. We'd have to change rndGen to use the new seed function
> though. And the documentation for Random says: "You may want to use it
> if [...] you don't care for the minutiae of the method being used"
> 
> I'm not sure, do we care about the method? We need something which
> generates 'good' random numbers, I don't think a LCG would be
> acceptable. But rndGen could default to something like that on embedded
> systems?
> 
> Then again, I know nothing about random number generators, so someone
> please tell me: Do we need  Mersenne twister for random UUIDS or could
> we use any RNG?

Umm. My point was that as far as I can tell, what you're doing is _identical_ 
to what rndGen is doing.

Yours
-----------
@trusted UUID randomUUID()()
{
    static Mt19937 randomGen;
    static bool seeded = false;
    if(!seeded)
    {
        randomGen.seed(map!((a) => unpredictableSeed)(repeat(0)));
        seeded = true;
    }
    return randomUUID(randomGen);
}
-----------

vs

-----------
alias Mt19937 Random;

@property ref Random rndGen()
{
    static Random result;
    static bool initialized;
    if (!initialized)
    {
        result = Random(unpredictableSeed);
        initialized = true;
    }
    return result;
}
-----------

Really, the only differences are the fact that you're passing the Random into 
randomUUID and returning that rather than returing the Random, and you're 
using a map to do the seed for reasons that completely escape me. In fact, I'm 
not sure how that code even compiles. It creates an infinite range whose every 
value is zero and then maps all of those zeroes to unpredictableSeed. So, 
you've created an infinite range of unpredictableSeed results. But Mt19937's 
seed takes a single uint, not a range. So, I'm baffled as to how that line even 
compiles.

In any case, it looks like they're both using Random, and they're both seeding 
it with an unpredictableSeed. It's just that in noe case, it's using Random's 
constructor to do it, while in the other, seed is being called explicitly 
(thereby _re_seeding the Random).

So, I don't see what you're doing that's any different from just doing

return randomUUID(rndGen());

aside from the fact that you seem to have an impossible line of code for 
seeding your random number generator. And trying to compile that line on my 
computer actually fails (as I'd expect), so I don't know how it's compiling on 
yours.

> I don't think that's a good idea. If the randomGen.front the is e.g.
> ubyte, casting it to uint will produce 3 bytes which are not set to a
> random value. That's not acceptable.
> 
> ------------
> @trusted UUID randomUUID(RNG)(ref RNG randomGen) if(isUniformRNG!(RNG)
> &&
>     isIntegral!(typeof(RNG.front)) && typeof(RNG.front).sizeof <= 16)
> {
>     enum elemSize = typeof(RNG.front).sizeof;
>     UUID u;
>     foreach(size_t i; iota(0, 16, elemSize))
>     {
>         randomGen.popFront();
>         immutable randomValue = randomGen.front;
>         u.data[i .. i + elemSize] = *cast(ubyte[elemSize]*)&randomValue;
>     }
> ------------
> ?

Looks good except for the fact that the typeof(RNG.front).sizeof <= 16 is 
completely unnecessary. isIntgeral guarantees that the type is a built-in 
integral type, the largest of which is 8 bytes (and even if we added cent and 
ucent, it would still be only 16, not greater than 16).

> > * For your functions which can take range types, test with
> > filter!"true" so that you get a range which isn't an array or string.
> 
> Does take!(string, int) return a slice? Should have thought about
> that....

No. It doesn't, because hasSlicing!string is false.

> I can't use filter (and map) though:
> std/algorithm.d(1141): Error: nested structs with constructors are not
> yet supported in CTFE (Bug 6419) std/uuid.d(1273):        called from
> here: filter("00000000-0000-0000-0000-000000000000"d)

filter!"true"("00000000-0000-0000-0000-000000000000") should compile just fine. 
You then pass that to your range-based uuid functions which accept ranges or 
dchar. What does CTFE have to do with it? That would only be an issue if you 
were trying to use filter in CTFE, and even if you have CTFE tests where you'd 
like to do that and can't due to that bug, there are plenty of tests that you 
could run which weren't CTFE. I think that one or both of us is 
misunderstanding something here.

> Thanks for your feedback!

Also, I just noticed that it looks like you don't have any tests which verify 
that empty is false when it's supposed to be. You should probably add a few.

And by the way, before this actually gets merged into Phobos (as I'm guessing 
that it will be, since no way seems to particularly dislike the module thus 
far), you should fix the ddoc to use LREF and XREF when referencing symbols 
elsewhere in the module and elsewhere in std respectively.

- Jonathan M Davis


More information about the Digitalmars-d mailing list