Files
genode-world/include/nim/genodeservers.nim
Emery Hemingway 419503db1d Update World Nim libraries
- Prevent GC from freeing active signal handlers.
- New module for implementing Nim servers.
- Stricter typing for compatibility with upcoming language release.

Fix #96
2018-01-29 11:14:00 +01:00

228 lines
6.9 KiB
Nim

#
# \brief Nim utilities for Genode servers
# \author Emery Hemingway
# \date 2017-12-10
#
#
# Copyright (C) 2017 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU Affero General Public License version 3.
#
# TODO: Provide sesion creation and destruction examples.
import parseutils, strutils, strtabs, streams,
parsexml, xmlparser, xmltree
const LabelSep = " ->"
## Pattern used to seperate session label elements.
proc lastLabelElement*(label: TaintedString): string =
## Return the last element in a session label.
let i = label.rFind LabelSep
if unlikely(i == -1):
label
else:
label[i+1+LabelSep.len..label.high]
iterator elements*(label: TaintedString): string =
## Iterate the elements of a session label from left to right.
var
buf = ""
pos = 0
while pos < label.len:
let n = parseuntil(label, buf, LabelSep, pos)
if n == 0: break
yield buf.strip
pos.inc(n + LabelSep.len+1)
proc lookupPolicy*(policies: seq[XmlNode]; label: TaintedString): XmlNode =
## Return a policy matching a given label or return nil.
# TODO: use less GC intensive parsing.
var resultScore: int
for p in policies.items:
if p.tag == "default-policy" and result.isNil:
# this is now the fallthrough policy
result = p
else:
let attrs = p.attrs
if attrs.contains "label":
if attrs["label"] == label:
# return the first policy with a label match
return p
else:
let
prefix = attrs.getOrDefault "prefix"
suffix = attrs.getOrDefault "suffix"
if label.startsWith(prefix) and label.endsWith(suffix):
# match the label against prefix and suffic (empty strings match)
let score = prefix.len*2 + suffix.len
# a prefix match is more significant that a suffix match
if score > resultScore:
# this is now the best match this far
resultScore = score
result = p
proc parseArgString*(args, key: string; default = ""): string =
## Extract a keyed value from session request arguments.
result = default
var off = args.find key
if off != -1:
off.inc key.len
if args[off] == '=' and args[off+1] == '"':
off.inc 2
off.inc parseuntil(args, result, '"', off)
if args[off] != '"':
result = default
proc parseArgInt*(args, key: string; default = -1): BiggestInt =
## Extract an integral argument from session request arguments.
result = default
var off = args.find key
if off != -1:
off.inc key.len
if args[off] == '=':
inc off
off.inc parseBiggestInt(args, result, off)
if off < args.len:
case args[off]
of ',':
discard
of 'K':
result = result shl 10
of 'M':
result = result shl 20
of 'G':
result = result shl 30
of 'P':
result = result shl 40
else:
result = -1
type SessionId* = distinct int
## Session identifier shared between
## parent and child components.
proc `==`*(x, y: SessionId): bool {.borrow.}
type
SessionRequestsParser = object
s: string
xp: XmlParser
args*: string ## Arguments of the session the parser is positioned on.
proc initSessionRequestsParser*(sessionRequestsROM: string): SessionRequestsParser =
## Initialize a parser of the Genode "session_requests" ROM.
result.s = sessionRequestsROM
result.args = ""
proc argString*(srp: var SessionRequestsParser; key: string; default = ""): string =
## Parse an string from the current session arguments.
if srp.args != "":
parseArgString(srp.args, key, default)
else:
default
proc argInt*(srp: var SessionRequestsParser; key: string; default = -1): BiggestInt =
## Parse an integer from the current session arguments.
if srp.args != "":
parseArgInt(srp.args, key, default)
else:
default
proc streamThatCanBeClosed(srp: var SessionRequestsParser): Stream {.inline.} =
# Need to create a new stream because the XML parser closes the stream
# when the XML parser is closed. And the XML parser needs to be closed,
# but that shouldn't mess up the dataspace stream that is stuck to the ROM
# client.
srp.s.newStringStream
proc next(srp: var SessionRequestsParser) {.inline.} =
next srp.xp
proc skipRest(srp: var SessionRequestsParser) =
var depth = 1
while depth > 0:
case srp.xp.kind
of xmlElementStart, xmlElementOpen: inc depth
of xmlElementEnd, xmlElementClose: dec depth
of xmlEof: break
of xmlError: raise newException(ValueError, srp.xp.errorMsg)
else: discard
next srp
iterator create*(srp: var SessionRequestsParser): tuple[id: SessionId, service, label: TaintedString] =
## Iterate over session creation requests.
let str = srp.streamThatCanBeClosed
open srp.xp, str, "session_requests"
next srp
block requestsLoop:
while true:
case srp.xp.kind:
of xmlElementOpen:
if srp.xp.elementName == "create":
next srp
block createLoop:
var result: tuple[id: SessionId, service, label: TaintedString]
srp.args.setLen 0
while srp.xp.kind == xmlAttribute:
case srp.xp.attrKey:
of "id":
result.id = srp.xp.attrValue.parseInt.SessionId
of "service":
result.service = srp.xp.attrValue
of "label":
result.label = srp.xp.attrValue
next srp
while srp.xp.kind != xmlElementClose: # skip until ``>``
next srp
next srp
if srp.xp.kind == xmlElementStart and srp.xp.elementName == "args":
next srp
while srp.xp.kind != xmlElementEnd:
if srp.xp.kind == xmlCharData:
srp.args.add srp.xp.charData
next srp
next srp
skipRest srp
yield result
else:
next srp
of xmlEof:
break requestsLoop
of xmlError:
raise newException(ValueError, srp.xp.errorMsg)
else: next srp # skip other events
close srp.xp
iterator close*(srp: var SessionRequestsParser): SessionId =
## Iterate over session close requests.
let str = srp.streamThatCanBeClosed
open srp.xp, str, "session_requests"
next srp
block requestsLoop:
while true:
case srp.xp.kind:
of xmlElementOpen:
case srp.xp.elementName:
of "close":
var id: SessionId
next srp
while srp.xp.kind == xmlAttribute:
if srp.xp.attrKey == "id":
id = srp.xp.attrValue.parseInt.SessionId
next srp
break
next srp
skipRest srp
yield id
else:
next srp
of xmlEof:
break requestsLoop
of xmlError:
raise newException(ValueError, srp.xp.errorMsg)
else: next srp # skip other events
close srp.xp