optimized VStyle representation

This commit is contained in:
Andreas Rumpf 2017-06-24 08:22:31 +02:00
commit 4bfe66edd6
2 changed files with 72 additions and 32 deletions

View file

@ -11,3 +11,12 @@ proc contains*[K, V](d: JDict[K, V], k: K): bool {.importcpp: "#.hasOwnProperty(
proc del*[K, V](d: JDict[K, V], k: K) {.importcpp: "delete #[#]".} proc del*[K, V](d: JDict[K, V], k: K) {.importcpp: "delete #[#]".}
type
JSeq*[T] = ref object
proc `[]`*[T](s: JSeq[T], i: int): T {.importcpp: "#[#]", noSideEffect.}
proc `[]=`*[T](s: JSeq[T], i: int, v: T) {.importcpp: "#[#] = #", noSideEffect.}
proc newJSeq*[T](): JSeq[T] {.importcpp: "[@]".}
proc len*[T](s: JSeq[T]): int {.importcpp: "#.length", noSideEffect.}
proc add*[T](s: JSeq[T]; x: T) {.importcpp: "#.push(#)", noSideEffect.}

View file

@ -1,5 +1,5 @@
import kdom, macros import kdom, macros, jdict
type type
StyleAttr* {.pure.} = enum StyleAttr* {.pure.} = enum
@ -208,62 +208,93 @@ macro buildLookupTables(): untyped =
buildLookupTables() buildLookupTables()
type # I optimized the heck out of this representation since profiling showed
VStyle* = ref object # it's relevant.
attrs: array[StyleAttr, cstring] # even index: key, odd index: value; done this way for memory efficiency:
mask: set[StyleAttr]
proc kout[T](x: T) {.importc: "console.log", varargs.} type
VStyle* = JSeq[cstring]
proc eq*(a, b: VStyle): bool = proc eq*(a, b: VStyle): bool =
if a.isNil: if a.isNil:
if b.isNil: return true if b.isNil: return true
else: return false else: return false
elif b.isNil: return false elif b.isNil: return false
if a.mask != b.mask: return false if a.len != b.len: return false
for x in a.mask: for i in 0..<a.len:
if a.attrs[x] != b.attrs[x]: return false if a[i] != b[i]: return false
return true return true
proc setAttr*(s: VStyle; attr: StyleAttr, value: cstring) = proc kout[T](x: T) {.importc: "console.log", varargs.}
proc setAttr(s: VStyle; a, value: cstring) {.noSideEffect.} =
var i = 0
while i < s.len:
if s[i] == a:
s[i+1] = value
return
elif s[i] > a:
s.add nil
s.add nil
# insertion point here:
for j in countdown(s.len-1, i, 2):
s[j] = s[j-2]
s[j-1] = s[j-3]
s[i] = a
s[i+1] = value
return
inc i, 2
s.add a
s.add value
proc setAttr*(s: VStyle; attr: StyleAttr, value: cstring) {.noSideEffect.} =
assert value != nil, "value must not be nil" assert value != nil, "value must not be nil"
s.attrs[attr] = value setAttr(s, toStyleAttrName[attr], value)
incl(s.mask, attr)
proc getAttr*(s: VStyle; attr: StyleAttr): cstring = proc getAttr*(s: VStyle; attr: StyleAttr): cstring {.noSideEffect.} =
## returns 'nil' if the attribute has not been set. ## returns 'nil' if the attribute has not been set.
result = s.attrs[attr] var i = 0
let a = toStyleAttrName[attr]
while i < s.len:
if s[i] == a:
return s[i+1]
elif s[i] > a:
return nil
inc i, 2
proc style*(pairs: varargs[(StyleAttr, cstring)]): VStyle = proc style*(pairs: varargs[(StyleAttr, cstring)]): VStyle {.noSideEffect.} =
## constructs a VStyle object from a list of (attribute, value)-pairs. ## constructs a VStyle object from a list of (attribute, value)-pairs.
result = VStyle(mask: {}) result = newJSeq[cstring]()
for x in pairs: for x in pairs:
result.setAttr x[0], x[1] result.setAttr x[0], x[1]
proc style*(a: StyleAttr; val: cstring): VStyle = proc style*(a: StyleAttr; val: cstring): VStyle {.noSideEffect.} =
## constructs a VStyle object from a single (attribute, value)-pair. ## constructs a VStyle object from a single (attribute, value)-pair.
result = VStyle(mask: {}) result = newJSeq[cstring]()
result.setAttr a, val result.setAttr a, val
proc setStyle(d: Style; key, val: cstring) {.importcpp: "#[#] = #".} proc setStyle(d: Style; key, val: cstring) {.importcpp: "#[#] = #", noSideEffect.}
proc merge*(a, b: VStyle): VStyle = proc merge*(a, b: VStyle): VStyle {.noSideEffect.} =
## merges two styles. ``b`` takes precedence over ``a``. ## merges two styles. ``b`` takes precedence over ``a``.
result = VStyle(mask: {}) result = newJSeq[cstring]()
for i in low(StyleAttr)..high(StyleAttr): for i in 0..<a.len:
result.attrs[i] = if b.attrs[i].isNil: a.attrs[i] else: b.attrs[i] result.add a[i]
if result.attrs[i] != nil: incl(result.mask, i) for i in countup(0, b.len-1, 2):
setAttr(result, b[i], b[i+1])
proc applyStyle*(n: Node; s: VStyle) = proc applyStyle*(n: Node; s: VStyle) {.noSideEffect.} =
## apply the style to the real DOM node ``n``. ## apply the style to the real DOM node ``n``.
n.style = Style()
for x in s.mask:
n.style.setStyle(toStyleAttrName[x], s.attrs[x])
iterator pairs*(v: VStyle): (cstring, cstring) = #n.style = Style() # optimized, this is a hotspot:
if v != nil: {.emit: "`n`.style = {};".}
for x in v.mask: for i in countup(0, s.len-1, 2):
yield (toStyleAttrName[x], v.attrs[x]) n.style.setStyle(s[i], s[i+1])
iterator pairs*(s: VStyle): (cstring, cstring) {.noSideEffect.} =
if s != nil:
for i in countup(0, s.len-1, 2):
yield (s[i], s[i+1])
import jstrutils import jstrutils