Implement readInto and read(n)
This commit is contained in:
parent
b24300bd3f
commit
bf930b6f1b
12 changed files with 636 additions and 220 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue