<div>I have some code that reads (infrequently) small amounts of data from /dev/urandom, and because this is pretty infrequent, I simply open the handle and close it every time I need some random bytes. </div><div><br>
</div><div>The problem is that I recently discovered that, thanks to buffering within GHC, I was actually reading 8096 bytes when I only need 16 bytes, and thus wasting entropy. Moreover calling hSetBuffering handle NoBuffering did not change this behavior. </div>
<div><br></div><div>I'm not sure if this behavior is a bug or a feature, but in any case it's unacceptable for dealing with /dev/urandom. Probably the simplest way to fix this is to write a little C helper function that will read from /dev/urandom for me, so that I have precise control over the system calls involved. But I'm curious if GHC can manage this use case correctly; I've just started digging into the <a href="http://GHC.IO">GHC.IO</a> code myself.</div>
<div><br></div><div>Best,</div><div>Leon</div><div><br></div><div><pre style="font-size:13px;margin-bottom:0px;margin-top:0px"><span style="color:rgb(51,51,51)">{-#</span> <span style="color:rgb(79,67,113)">LANGUAGE</span> <span style="color:rgb(79,67,113)">BangPatterns</span><span>,</span> <span style="color:rgb(79,67,113)">ViewPatterns</span> <span>#-</span><span>}</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(79,67,113)">Control</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">Applicative</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(79,67,113)">Data</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">Bits</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(79,67,113)">Data</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">Word</span><span>(</span><span style="color:rgb(79,67,113)">Word64</span><span>)</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(57,116,96)">qualified</span> <span style="color:rgb(79,67,113)">Data</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">ByteString</span> <span style="color:rgb(57,116,96)">as</span> <span style="color:rgb(79,67,113)">S</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(57,116,96)">qualified</span> <span style="color:rgb(79,67,113)">Data</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">ByteString</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">Lazy</span> <span style="color:rgb(57,116,96)">as</span> <span style="color:rgb(79,67,113)">L</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(79,67,113)">Data</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">ByteString</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">Internal</span> <span>(</span><span style="color:rgb(51,51,51)">c2w</span><span>)</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(57,116,96)">qualified</span> <span style="color:rgb(79,67,113)">System</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">IO</span> <span style="color:rgb(57,116,96)">as</span> <span style="color:rgb(79,67,113)">IO</span>
<span style="color:rgb(57,116,96)">import</span> <span style="color:rgb(57,116,96)">qualified</span> <span style="color:rgb(79,67,113)">Data</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">Binary</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">Get</span> <span style="color:rgb(57,116,96)">as</span> <span style="color:rgb(79,67,113)">Get</span>
<span>showHex</span> <span>::</span> <span style="color:rgb(79,67,113)">Word64</span> <span>-></span> <span style="color:rgb(79,67,113)">S</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">ByteString</span>
<span>showHex</span> <span style="color:rgb(51,51,51)">n</span> <span>=</span> <span style="color:rgb(51,51,51)">s</span>
<span style="color:rgb(57,116,96)">where</span>
<span>(</span><span style="color:rgb(51,51,51)">!</span><span style="color:rgb(51,51,51)">s</span><span>,</span><span style="color:rgb(57,116,96)">_</span><span>)</span> <span>=</span> <span style="color:rgb(79,67,113)">S</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">unfoldrN</span> <span style="color:rgb(79,67,113)">16</span> <span style="color:rgb(51,51,51)">f</span> <span style="color:rgb(51,51,51)">n</span>
<span style="color:rgb(51,51,51)">f</span> <span style="color:rgb(51,51,51)">n</span> <span>=</span> <span style="color:rgb(79,67,113)">Just</span> <span>(</span><span style="color:rgb(51,51,51)">char</span> <span>(</span><span style="color:rgb(51,51,51)">n</span> <span style="color:rgb(51,51,51)">`shiftR`</span> <span style="color:rgb(79,67,113)">60</span><span>)</span><span>,</span> <span style="color:rgb(51,51,51)">n</span> <span style="color:rgb(51,51,51)">`shiftL`</span> <span style="color:rgb(79,67,113)">4</span><span>)</span>
<span style="color:rgb(51,51,51)">char</span> <span>(</span><span style="color:rgb(51,51,51)">fromIntegral</span> <span>-></span> <span style="color:rgb(51,51,51)">i</span><span>)</span>
<span>|</span> <span style="color:rgb(51,51,51)">i</span> <span style="color:rgb(51,51,51)"><</span> <span style="color:rgb(79,67,113)">10</span> <span>=</span> <span>(</span><span style="color:rgb(51,51,51)">c2w</span> <span>'0'</span> <span style="color:rgb(85,85,85)">-</span> <span style="color:rgb(79,67,113)">0</span><span>)</span> <span style="color:rgb(51,51,51)">+</span> <span style="color:rgb(51,51,51)">i</span>
<span>|</span> <span style="color:rgb(51,51,51)">otherwise</span> <span>=</span> <span>(</span><span style="color:rgb(51,51,51)">c2w</span> <span>'a'</span> <span style="color:rgb(85,85,85)">-</span> <span style="color:rgb(79,67,113)">10</span><span>)</span> <span style="color:rgb(51,51,51)">+</span> <span style="color:rgb(51,51,51)">i</span>
<span>twoRandomWord64s</span> <span>::</span> <span style="color:rgb(79,67,113)">IO</span> <span>(</span><span style="color:rgb(79,67,113)">Word64</span><span>,</span><span style="color:rgb(79,67,113)">Word64</span><span>)</span>
<span>twoRandomWord64s</span> <span>=</span> <span style="color:rgb(79,67,113)">IO</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">withBinaryFile</span> <span style="color:rgb(54,99,84)">"/dev/urandom"</span> <span style="color:rgb(79,67,113)">IO</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">ReadMode</span> <span style="color:rgb(51,51,51)">$</span> <span>\</span><span style="color:rgb(51,51,51)">handle</span> <span>-></span> <span style="color:rgb(57,116,96)">do</span>
<span style="color:rgb(79,67,113)">IO</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">hSetBuffering</span> <span style="color:rgb(51,51,51)">handle</span> <span style="color:rgb(79,67,113)">IO</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(79,67,113)">NoBuffering</span>
<span style="color:rgb(79,67,113)">Get</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">runGet</span> <span>(</span><span style="color:rgb(79,67,113)">(,)</span> <span style="color:rgb(51,51,51)"><$></span> <span style="color:rgb(79,67,113)">Get</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">getWord64host</span> <span style="color:rgb(51,51,51)"><*></span> <span style="color:rgb(79,67,113)">Get</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">getWord64host</span><span>)</span> <span style="color:rgb(51,51,51)"><$></span> <span style="color:rgb(79,67,113)">L</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">hGet</span> <span style="color:rgb(51,51,51)">handle</span> <span style="color:rgb(79,67,113)">16</span>
<span>main</span> <span>=</span> <span style="color:rgb(57,116,96)">do</span>
<span>(</span><span style="color:rgb(51,51,51)">x</span><span>,</span><span style="color:rgb(51,51,51)">y</span><span>)</span> <span><-</span> <span style="color:rgb(51,51,51)">twoRandomWord64s</span>
<span style="color:rgb(79,67,113)">S</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">hPutStrLn</span> <span style="color:rgb(79,67,113)">IO</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">stdout</span> <span>(</span><span style="color:rgb(79,67,113)">S</span><span style="color:rgb(51,51,51)">.</span><span style="color:rgb(51,51,51)">append</span> <span>(</span><span style="color:rgb(51,51,51)">showHex</span> <span style="color:rgb(51,51,51)">x</span><span>)</span> <span>(</span><span style="color:rgb(51,51,51)">showHex</span> <span style="color:rgb(51,51,51)">y</span><span>)</span><span>)</span>
<span style="color:rgb(85,85,85)">{- Relevant part of strace:
open("/dev/urandom", O_RDONLY|O_NOCTTY|O_NONBLOCK) = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7ffff367e528) = -1 EINVAL (Invalid argument)
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7ffff367e528) = -1 EINVAL (Invalid argument)
read(3, "N\304\4\367/\26c\"\3218\237f\214yKg~i\310\r\262\"\224H\340y\n\376V?\265\344"..., 8096) = 8096
close(3) = 0
-}</span></pre></div>