Implement readInto and read(n)

This commit is contained in:
Zahary Karadjov 2020-05-01 21:39:08 +03:00
commit bf930b6f1b
No known key found for this signature in database
GPG key ID: C8936F8A3073D609
12 changed files with 636 additions and 220 deletions

View file

@ -1,16 +1,34 @@
import
stew/ptrops,
inputs, outputs, buffers
inputs, outputs, buffers, multisync
template matchingIntType(T: type int64): type = uint64
template matchingIntType(T: type int32): type = uint32
template matchingIntType(T: type uint64): type = int64
template matchingIntType(T: type uint32): type = int32
# To reduce the produce code bloat, we will compile the integer
# handling functions only for the native type of the platform.
# Smaller int types will be automatically promoted to the native.
# On a 32-bit platforms, we'll also compile support for 64-bit types.
when sizeof(int) == sizeof(int64):
type
CompiledIntTypes = int64
PromotedIntTypes = int8|int16|int32
CompiledUIntTypes = uint64
PromotedUintTypes = uint8|uint16|uint32
else:
type
CompiledIntTypes = int|int64
PromotedIntTypes = int8|int16
CompiledUIntTypes = uint|uint64
PromotedUintTypes = uint8|uint16
# The following code implements writing numbers to a stream without going
# through Nim's `$` operator which will allocate memory.
# It's based on some speed comparisons of different methods presented here:
# http://www.zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html
# TODO Maybe the `writeText` proc shouldn't be instantiated for every integer
# type, but only for the largest "native" one. We can promote the rest with
# a template.
const
digitsTable = block:
var s = ""
@ -21,7 +39,7 @@ const
maxLen = ($BiggestInt.high).len + 4 # null terminator, sign
proc writeText*(s: OutputStream, x: SomeUnsignedInt) =
proc writeText*(s: OutputStream, x: CompiledUIntTypes) =
var
num: array[maxLen, char]
pos = num.len
@ -60,9 +78,8 @@ proc writeText*(s: OutputStream, x: SomeUnsignedInt) =
write s, num.toOpenArray(pos, static(num.len - 1))
proc writeText*(s: OutputStream, x: SomeSignedInt) =
# TODO: Determine this accurately
type MatchingUInt = BiggestUInt
proc writeText*(s: OutputStream, x: CompiledIntTypes) =
type MatchingUInt = matchingIntType typeof(x)
if x < 0:
s.write '-'
@ -90,3 +107,89 @@ proc writeHex*(s: OutputStream, bytes: openarray[byte]) =
proc writeHex*(s: OutputStream, chars: openarray[char]) =
writeHex s, charsToBytes(chars)
const
NewLines* = {'\r', '\n'}
Digits* = {'0'..'9'}
proc readLine*(s: InputStream, keepEol = false): TaintedString =
doAssert readableNow(s)
while s.readable:
let c = s.peek.char
if c in NewLines:
if keepEol:
result.add c
if c == '\r' and s.readable and s.peek.char == '\n':
result.add s.read.char
else:
advance s
if c == '\r' and s.readable and s.peek.char == '\n':
advance s
return
result.add s.read.char
proc readUntil*(s: InputStream,
sep: openarray[char]): Option[TaintedString] =
doAssert readableNow(s)
var res = ""
while s.readable(sep.len):
if s.lookAheadMatch(charsToBytes(sep)):
return some(res)
res.add s.read.char
template nextLine*(sp: InputStream, keepEol = false): Option[TaintedString] =
let s = sp
if s.readable:
some readLine(s, keepEol)
else:
none string
iterator lines*(s: InputStream, keepEol = false): TaintedString =
while s.readable:
yield readLine(s, keepEol)
proc readUnsignedInt*(s: InputStream, T: type[CompiledUIntTypes]): T =
doAssert s.readable and s.peek.char in Digits
template eatDigitAndPeek: char =
advance s
if not s.readable: return
s.peek.char
var c = s.peek.char
result = T(ord(c) - ord('0'))
c = eatDigitAndPeek()
while c.isDigit:
# TODO: How do we handle the possible overflow here?
result = result * 10 + T(ord(c) - ord('0'))
c = eatDigitAndPeek()
template readUnsignedInt*(s: InputStream): uint =
readUnsignedInt(s, uint)
proc readSignedInt*(s: InputStream, T: type[CompiledIntTypes]): Option[T] =
if s.readable:
let maybeSign = s.read.peek
if maybeSign in {'-', '+'}:
if not s.readable(2) or s.peekAt(1).char notin Digits:
return
else:
advance s
elif maybeSign notin Digits:
return
type UIntType = matchingIntType T
let uintVal = readUnsignedInt(s, UIntType)
if maybeSign == '-':
if uintVal > UIntType(max(T)) + 1:
return # Overflow. We've consumed part of the stream though.
# TODO: Should we rewind it to a previous state?
return some cast[T](UIntType(0) - uintVal)
else:
if uintVal > UIntType(max(T)):
return # Overflow. We've consumed part of the stream though.
# TODO: Should we rewind it to a previous state?
return some T(uintVal)