Fix #14 (don't use alloca)

This commit is contained in:
Zahary Karadjov 2021-01-06 21:20:27 +02:00
commit 92205d608b
No known key found for this signature in database
GPG key ID: C8936F8A3073D609
2 changed files with 72 additions and 21 deletions

View file

@ -812,21 +812,22 @@ template readInto*(sp: AsyncInputStream, dst: var openarray[byte]): bool =
let (dstAddr, dstLen) = openArrayToPair(dst) let (dstAddr, dstLen) = openArrayToPair(dst)
readIntoExImpl(s, dstAddr, dstLen, fsAwait, readAsync) == dstLen readIntoExImpl(s, dstAddr, dstLen, fsAwait, readAsync) == dstLen
when defined(windows): template useHeapMem(_: Natural) =
proc alloca(n: int): ptr byte {.importc, header: "<malloc.h>".} var buffer: seq[byte]
else:
proc alloca(n: int): ptr byte {.importc, header: "<alloca.h>".}
template allocHeapMem(tmpSeq: var seq[byte], n, _: Natural): ptr byte = template allocMem(n: Natural): ptr byte =
tmpSeq.setLen(n) buffer.setLen(n)
addr tmpSeq[0] addr buffer[0]
template allocStackMem(tmpSeq: var seq[byte], _, n: Natural): ptr byte = template useStackMem(n: static Natural) =
alloca(n) var buffer: array[n, byte]
template allocMem(_: Natural): ptr byte =
addr buffer[0]
template readNImpl(sp: InputStream, template readNImpl(sp: InputStream,
np: Natural, np: Natural,
allocMem: untyped): openarray[byte] = createAllocMemOp: untyped): openarray[byte] =
let let
s = sp s = sp
n = np n = np
@ -837,28 +838,35 @@ template readNImpl(sp: InputStream,
# be written in this branch-free linear fashion. The `dataCopy` seq # be written in this branch-free linear fashion. The `dataCopy` seq
# may remain empty in the case where we use stack memory or return # may remain empty in the case where we use stack memory or return
# an `openarray` from the existing span. # an `openarray` from the existing span.
var tmpSeq: seq[byte]
var startAddr: ptr byte var startAddr: ptr byte
if n > runway: block:
startAddr = allocMem(tmpSeq, n, np) # This defines the `allocMem` operation used below.
let drained {.used.} = drainBuffersInto(s, startAddr, n) # See `useHeapMem` and `useStackMem` for the possible definitions.
fsAssert drained == n # We are creating a block scope in order to allow multiple usages
else: # of `read` within a single scope (avoiding duplicate definitions
startAddr = s.span.startAddr # of `allocMem`)
bumpPointer s.span, n createAllocMemOp(np)
if n > runway:
startAddr = allocMem(n)
let drained {.used.} = drainBuffersInto(s, startAddr, n)
fsAssert drained == n
else:
startAddr = s.span.startAddr
bumpPointer s.span, n
makeOpenArray(startAddr, n) makeOpenArray(startAddr, n)
template read*(sp: InputStream, np: static Natural): openarray[byte] = template read*(sp: InputStream, np: static Natural): openarray[byte] =
const n = np const n = np
when n < maxStackUsage: when n < maxStackUsage:
readNImpl(sp, n, allocStackMem) readNImpl(sp, n, useStackMem)
else: else:
readNImpl(sp, n, allocHeapMem) readNImpl(sp, n, useHeapMem)
template read*(s: InputStream, n: Natural): openarray[byte] = template read*(s: InputStream, n: Natural): openarray[byte] =
readNImpl(s, n, allocHeapMem) readNImpl(s, n, useHeapMem)
template read*(s: AsyncInputStream, n: Natural): openarray[byte] = template read*(s: AsyncInputStream, n: Natural): openarray[byte] =
read InputStream(s), n read InputStream(s), n

View file

@ -325,3 +325,46 @@ suite "randomized tests":
randomizedCursorsTest(memoryOutput(), VarSize, 100) randomizedCursorsTest(memoryOutput(), VarSize, 100)
randomizedCursorsTest(memoryOutput(pageSize = 10), Mixed, 10) randomizedCursorsTest(memoryOutput(pageSize = 10), Mixed, 10)
test "randomized file roundtrip":
const randomBytesFileName = "random_bytes_file"
var
referenceBytes, restoredBytes: seq[byte]
try:
let output = fileOutput randomBytesFileName
for i in 0 .. 3000:
let bytes = randomBytes(rand(9999) + 1)
referenceBytes.add bytes
output.write bytes
close output
let input = fileInput randomBytesFileName
while input.readable(10000):
let r = 1 + rand(9999)
if r < 5000:
restoredBytes.add input.read(8000)
restoredBytes.add input.read(500)
elif r < 7000:
restoredBytes.add input.read(1)
restoredBytes.add input.read(2)
restoredBytes.add input.read(5)
restoredBytes.add input.read(17)
restoredBytes.add input.read(128)
else:
restoredBytes.add input.read(r)
while input.readable:
restoredBytes.add input.read
close input
doAssert referenceBytes == restoredBytes
finally:
if fileExists(randomBytesFileName):
removeFile randomBytesFileName