[OutputStream] Clean up TODO items unlocked by Nim 1.2; Simplify the public API

This commit is contained in:
Zahary Karadjov 2020-04-09 22:20:56 +03:00
commit 62136f8e79
No known key found for this signature in database
GPG key ID: C8936F8A3073D609
2 changed files with 48 additions and 59 deletions

View file

@ -1,14 +1,15 @@
import import
deques, stew/[ptrops, strings, ranges/ptr_arith] deques, typetraits,
stew/[ptrops, strings, ranges/ptr_arith]
type type
OutputPage = object OutputPage = object
buffer: string buffer: string
startOffset: int startOffset: int
# somehow casting outputDevice to # We inherit from RootObj because in layered streams
# OutputStreamVar requires RootObj # the stream itself is often used as an `outputDevice`
OutputStream* = object of RootObj OutputStreamObj = object of RootObj
cursor*: WriteCursor cursor*: WriteCursor
pages: Deque[OutputPage] pages: Deque[OutputPage]
endPos: int endPos: int
@ -19,31 +20,24 @@ type
maxWriteSize*: int maxWriteSize*: int
minWriteSize*: int minWriteSize*: int
OutputStream* = ref OutputStreamObj
OutputStreamVar = OutputStream
OutputStreamVTable* = object
writePage*: proc (s: OutputStream, page: openarray[byte])
{.nimcall, gcsafe, raises: [IOError, Defect].}
flush*: proc (s: OutputStream)
{.nimcall, gcsafe, raises: [IOError, Defect].}
WriteCursor* = object WriteCursor* = object
head, bufferEnd: ptr byte head, bufferEnd: ptr byte
stream: OutputStreamVar stream: OutputStreamVar
FileOutput = ref object of RootObj
file: File
OutputStreamVar* = ref OutputStream
MemOutputStream* = distinct OutputStreamVar
# Writing to a MemOutputStream produces no side-effects
# Keep this temporary for backward-compatibility
DelayedWriteCursor* = WriteCursor
VarSizeWriteCursor* = distinct WriteCursor VarSizeWriteCursor* = distinct WriteCursor
OutputStreamVTable* = object FileOutput = ref object of RootObj
# TODO - the noSideEffect is temporary here until we switch to Nim 0.20.2 file: File
# where noSideEffects overrides may make it possible to implement
# the MemOutputStream handling.
writePage*: proc (s: OutputStreamVar, page: openarray[byte])
{.noSideEffect, nimcall, gcsafe, raises: [IOError, Defect] .}
flush*: proc (s: OutputStreamVar)
{.noSideEffect, nimcall, gcsafe, raises: [IOError, Defect].}
const const
allocatorMetadata = 0 # TODO: Get this from Nim's allocator. allocatorMetadata = 0 # TODO: Get this from Nim's allocator.
@ -89,14 +83,11 @@ proc initWithSinglePage*(s: OutputStreamVar,
s.cursor.stream = s s.cursor.stream = s
proc init*(T: type OutputStream, proc init*(T: type OutputStream,
pageSize = defaultPageSize): ref OutputStream = pageSize = defaultPageSize): OutputStream =
new result result = OutputStream()
result.initWithSinglePage pageSize, high(int) result.initWithSinglePage pageSize, high(int)
when false: let FileStreamVTable = OutputStreamVTable(
# TODO: revisit this when we switch to Nim 0.20.2 and we have working
# noSideEffect overrides.
let FileStreamVTable = OutputStreamVTable(
writePage: proc (s: OutputStreamVar, data: openarray[byte]) {.nimcall, gcsafe.} = writePage: proc (s: OutputStreamVar, data: openarray[byte]) {.nimcall, gcsafe.} =
var output = FileOutput(s.outputDevice) var output = FileOutput(s.outputDevice)
var written = output.file.writeBuffer(unsafeAddr data[0], data.len) var written = output.file.writeBuffer(unsafeAddr data[0], data.len)
@ -106,19 +97,19 @@ when false:
flush: proc (s: OutputStreamVar) {.nimcall, gcsafe.} = flush: proc (s: OutputStreamVar) {.nimcall, gcsafe.} =
var output = FileOutput(s.outputDevice) var output = FileOutput(s.outputDevice)
flushFile output.file flushFile output.file
) )
proc init*(T: type OutputStream, proc init*(T: type OutputStream,
filename: string, filename: string,
pageSize = defaultPageSize): ref OutputStream = pageSize = defaultPageSize): OutputStream =
new result result = OutputStream(
result.outputDevice = FileOutput(file: open(filename, fmWrite)) outputDevice: FileOutput(file: open(filename, fmWrite)),
result.vtable = unsafeAddr FileStreamVTable vtable: unsafeAddr FileStreamVTable)
result.initWithSinglePage pageSize, high(int) result.initWithSinglePage pageSize, high(int)
proc init*(T: type OutputStream, proc init*(T: type OutputStream,
buffer: pointer, len: int): ref OutputStream = buffer: pointer, len: int): OutputStream =
new result result = OutputStream()
let buffer = cast[ptr byte](buffer) let buffer = cast[ptr byte](buffer)
result.cursor.head = buffer result.cursor.head = buffer
result.cursor.bufferEnd = offset(buffer, len) result.cursor.bufferEnd = offset(buffer, len)
@ -369,9 +360,9 @@ proc append*(c: var WriteCursor, chars: openarray[char]) {.inline.} =
var charsStart = unsafeAddr chars[0] var charsStart = unsafeAddr chars[0]
c.append makeOpenArray(cast[ptr byte](charsStart), chars.len) c.append makeOpenArray(cast[ptr byte](charsStart), chars.len)
template appendMemCopy*(c: var WriteCursor, value: auto) = template appendMemCopy*[T](c: var WriteCursor, value: T) =
bind append bind append
# TODO: add a check that this is a trivial type static: assert supportsCopyMem(T)
let valueAddr = unsafeAddr value let valueAddr = unsafeAddr value
c.append makeOpenArray(cast[ptr byte](valueAddr), sizeof(value)) c.append makeOpenArray(cast[ptr byte](valueAddr), sizeof(value))
@ -396,17 +387,15 @@ proc getOutput*(s: OutputStreamVar, T: type string): string =
result.swap s.pages[0].buffer result.swap s.pages[0].buffer
else: else:
result = newStringOfCap(s.pos) result = newStringOfCap(s.pos)
for page in s.pages: for page in items(s.pages):
result.add page.buffer.toOpenArray(page.startOffset.int, result.add page.buffer.toOpenArray(page.startOffset.int,
page.buffer.len - 1) page.buffer.len - 1)
template getOutput*(s: OutputStreamVar, T: type seq[byte]): seq[byte] = template getOutput*(s: OutputStreamVar, T: type seq[byte]): seq[byte] =
cast[seq[byte]](s.getOutput(string)) cast[seq[byte]](s.getOutput(string))
proc getOutput*(s: OutputStreamVar): seq[byte] = template getOutput*(s: OutputStreamVar): seq[byte] =
# TODO: is the extra copy here optimized away? cast[seq[byte]](s.getOutput(string))
# Turning this proc into a template creates problems at the moment.
s.getOutput(seq[byte])
proc finishPageEarly(s: OutputStreamVar, unwrittenBytes: int) {.inline.} = proc finishPageEarly(s: OutputStreamVar, unwrittenBytes: int) {.inline.} =
s.pages[s.pages.len - 1].buffer.setLen(s.pageSize - unwrittenBytes) s.pages[s.pages.len - 1].buffer.setLen(s.pageSize - unwrittenBytes)

View file

@ -154,11 +154,11 @@ suite "output stream":
test "nim odd behavior": test "nim odd behavior":
# discovered in status-im/nim-snappy#2 # discovered in status-im/nim-snappy#2
proc encode(s: OutputStreamVar) = proc encode(s: OutputStream) =
check true check true
proc abc(s: OutputStreamVar) = proc abc(s: OutputStream) =
OutputStreamVar(s.outputDevice).encode OutputStream(s.outputDevice).encode
check true check true
var os = new OutputStream var os = new OutputStream