Here's an example from a recent project. I needed to create UUIDs. Looking around, I found RFC 4122, which describes UUID generation, and helpfully provides a sample implementation in C. Unfortunately, that code generates random UUIDs that aren't very random (32 bits of randomness in a 128 bit value).
Now, I could reimplement this code (or something similar) in Haskell, and fix the lack-of-randomness issue. Or I could find another UUID generator, and reimplement that directly. But really, my goal here is to grab random 128-bit UUID values and move on. Getting distracted by a research project isn't part of the plan.
A little googling found that a version of
uuid_generate(3)
is bundled within e2fsprogs
. My target system has e2fsprogs
installed, so that's a plus. And, interestingly enough, 'man uuid_generate
' on my MacBook turns up this manpage:UUID_GENERATE(3) UUID_GENERATE(3)(Turns out that Apple includes this as part of the standard C library. Thanks, Apple!)
NAME
uuid_generate, uuid_generate_random, uuid_generate_time - create a new
unique UUID value
SYNOPSIS
#include <uuid/uuid.h>
void uuid_generate(uuid_t out);
void uuid_generate_random(uuid_t out);
void uuid_generate_time(uuid_t out);
....
AUTHOR
Theodore Y. Ts'o
AVAILABILITY
http://e2fsprogs.sourceforge.net/
A little more lurking, and I see that a
uuid_t
is merely an array of 16 characters. As the man page says, this is an output parameter. (It's odd to see code like this after programming in high level languages for so long, but this is C, and what are you going to do?)Next, I started paging through the GHC User's Guide and the Standard Libraries documentation, and came up with this declaration:
foreign import ccall unsafe "uuid_generate" uuid_generateThis declaration is a direct translation out of the man page.
:: Ptr Word8 -> IO ()
Next, I needed to construct a 16-byte buffer to receive the UUID value from
uuid_generate
, which requires using some functions in the Foreign
library. As soon as uuid_generate
finishes, the buffer needs to be translated into a Haskell string. Note that the return value is a 128-bit integer, and I want to convert that into sequence of printable hex digits. Here's the more convenient interface into the UUID generator:import ForeignThats it! Easy Peasy.
import Numeric (showHex)
uuid :: IO String
uuid = allocaBytes 16 $ \p -> do
uuid_generate p
uuid <- peekArray 16 p
return $ concat $ map toHex uuid
toHex :: Word8 -> String
toHex w = case showHex w "" of
w1:w2:[] -> w1:w2:[]
w2:[] -> '0':w2:[]
_ -> error "showHex returned []"
1 comment:
Nice work! UUIDs are really useful and important library additions, so I think it would be useful to find out how you could get something like this added to a library or put this code in a more visible place.
Perhaps here: http://www.haskell.org/haskellwiki/Category:Tutorials
(I'm not affiliated with the maintainers of www.haskell.org)
Thanks!
Post a Comment