Make classRegistry use hashes instead of strings as keys for faster lookup
Up version to 0.7.16
This commit is contained in:
parent
fd5f77e7c2
commit
ceaf66a34c
9 changed files with 101 additions and 23 deletions
|
|
@ -1,4 +1,4 @@
|
|||
version = "0.7.15"
|
||||
version = "0.7.16"
|
||||
author = "Xored Software, Inc."
|
||||
description = "Godot Engine bindings"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -1374,7 +1374,8 @@ type
|
|||
stringNewWithWideString: pointer
|
||||
stringOperatorIndex: pointer
|
||||
stringOperatorIndexConst: pointer
|
||||
stringWideStr: pointer
|
||||
stringWideStr: proc (self: GodotString): ptr cwchar_t
|
||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||
stringOperatorEqual: proc (self, other: GodotString): bool
|
||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||
stringOperatorLess: proc (self, other: GodotString): bool
|
||||
|
|
@ -2811,6 +2812,8 @@ type
|
|||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||
stringNewCopy*: proc (dest: var GodotString, src: GodotString)
|
||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||
stringWideStr*: proc (self: GodotString): ptr cwchar_t
|
||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||
stringOperatorEqual*: proc (self, other: GodotString): bool
|
||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||
stringOperatorLess*: proc (self, other: GodotString): bool
|
||||
|
|
@ -3481,6 +3484,7 @@ proc setGDNativeAPIInternal(apiStruct: pointer, initOptions: ptr GDNativeInitOpt
|
|||
|
||||
stringNew
|
||||
stringNewCopy
|
||||
stringWideStr
|
||||
stringOperatorEqual
|
||||
stringOperatorLess
|
||||
stringOperatorPlus
|
||||
|
|
|
|||
|
|
@ -839,7 +839,7 @@ proc typeNameToModuleName(name: string): string =
|
|||
proc newRegisterClassNode(typ: GodotType): PNode =
|
||||
newCall("registerClass",
|
||||
ident(typ.name),
|
||||
newCStringLit(typ.godotName),
|
||||
newStrLit(typ.godotName),
|
||||
ident("true") # isNative
|
||||
)
|
||||
|
||||
|
|
@ -877,7 +877,7 @@ proc genTypeFile(types: Table[string, GodotType], targetDir: string) =
|
|||
if not typ.shouldExport(types): continue
|
||||
let regNode = newCall("registerClass",
|
||||
ident(typ.name),
|
||||
newCStringLit(typ.godotName),
|
||||
newStrLit(typ.godotName),
|
||||
ident("true") # isNative
|
||||
)
|
||||
godotApiTypesTree.add(regNode)
|
||||
|
|
|
|||
|
|
@ -93,6 +93,11 @@ proc getClassName*(o: ptr GodotObject): string =
|
|||
# There are physics type not known by ClassDB
|
||||
result = result[0..result.len-3]
|
||||
|
||||
proc getClassNameRaw*(o: ptr GodotObject): GodotString =
|
||||
if getClassMethodBind.isNil:
|
||||
getClassMethodBind = getMethod(cstring"Object", cstring"get_class")
|
||||
getClassMethodBind.ptrCall(o, nil, addr result)
|
||||
|
||||
proc getGodotSingleton*(name: cstring): ptr GodotObject {.inline.} =
|
||||
getGDNativeAPI().globalGetSingleton(name)
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ type
|
|||
GodotString* {.byref.} = object
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
cwchar_t* {.importc: "wchar_t", nodecl.} = object
|
||||
|
||||
GodotCharString* {.byref.} = object
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
|
|
@ -305,3 +307,6 @@ type
|
|||
|
||||
GodotClassConstructor* = proc (): ptr GodotObject {.
|
||||
noconv, gcsafe, locks: 0, raises: [], tags: [].}
|
||||
|
||||
template offset*[T](p: ptr T, offset: int): ptr T =
|
||||
cast[ptr T](cast[ByteAddress](p) +% (offset * sizeof(T)))
|
||||
|
|
@ -3,9 +3,6 @@
|
|||
import godotinternaltypes, gdnativeapi
|
||||
import core.godotcoretypes
|
||||
|
||||
proc offset[T](p: ptr T, offset: int): ptr T {.inline.} =
|
||||
cast[ptr T](cast[ByteAddress](p) +% (offset * sizeof(T)))
|
||||
|
||||
template genPoolArrayAPI(ArrayT, initIdent, DataT,
|
||||
newProc, newCopyProc, newWithArrayProc, appendProc,
|
||||
appendArrayProc, insertProc, invertProc, pushBackProc,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ proc `&`*(self, b: GodotString): GodotString {.inline.} =
|
|||
proc deinit*(self: var GodotString) {.inline.} =
|
||||
getGDNativeAPI().stringDestroy(self)
|
||||
|
||||
proc len*(self: GodotString): cint {.inline.} =
|
||||
getGDNativeAPI().stringLength(self)
|
||||
|
||||
proc dataPtr*(self: GodotString): ptr cwchar_t {.inline.} =
|
||||
getGDNativeAPI().stringWideStr(self)
|
||||
|
||||
proc `$`*(self: GodotString): string =
|
||||
## Converts the ``GodotString`` into Nim string
|
||||
var charStr = getGDNativeAPI().stringUtf8(self)
|
||||
|
|
|
|||
|
|
@ -508,7 +508,7 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
|
|||
# Register Godot object
|
||||
let parentName = if obj.parentName.isNil: newStrLitNode("Object")
|
||||
else: newStrLitNode(obj.parentName)
|
||||
let classNameLit = newCStringLit(obj.name)
|
||||
let classNameLit = newStrLitNode(obj.name)
|
||||
let classNameIdent = ident(obj.name)
|
||||
let isRef: bool = if obj.parentName.isNil: false
|
||||
else: obj.parentName in refClasses
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright 2018 Xored Software, Inc.
|
||||
|
||||
import tables, typetraits, macros
|
||||
import tables, typetraits, macros, unicode
|
||||
import gdnativeapi
|
||||
import core.godotcoretypes, core.godotbase
|
||||
import core.vector2, core.rect2,
|
||||
|
|
@ -89,21 +89,71 @@ type
|
|||
## values.
|
||||
## See documentation of ``GodotPropertyHint`` for description of formats.
|
||||
|
||||
FNV1Hash = uint32
|
||||
|
||||
proc isFinalized*(obj: NimGodotObject): bool {.inline.} =
|
||||
obj.isFinalized
|
||||
|
||||
var classRegistry {.threadvar.}: TableRef[cstring, ObjectInfo]
|
||||
var classRegistryStatic* {.compileTime.}: TableRef[cstring, ObjectInfo]
|
||||
var classRegistry {.threadvar.}: TableRef[FNV1Hash, ObjectInfo]
|
||||
var classRegistryStatic* {.compileTime.}: TableRef[FNV1Hash, ObjectInfo]
|
||||
## Compile-time variable used for implementation of several procedures
|
||||
## and macros
|
||||
static:
|
||||
classRegistryStatic = newTable[cstring, ObjectInfo]()
|
||||
classRegistryStatic = newTable[FNV1Hash, ObjectInfo]()
|
||||
|
||||
static:
|
||||
import sets, strutils
|
||||
var nativeClasses {.compileTime.} = newSeq[string]()
|
||||
var refClasses* {.compileTime.} = newSeq[string]()
|
||||
|
||||
template initFNV1Hash(hash: var FNV1Hash) =
|
||||
hash = 0x811c9dc5'u32
|
||||
|
||||
template appendFNV1Hash(hash: var FNV1Hash, val: uint8) =
|
||||
block:
|
||||
let u64hash = hash.uint64
|
||||
hash = (
|
||||
u64hash +
|
||||
(u64hash shl 1'u64) +
|
||||
(u64hash shl 4'u64) +
|
||||
(u64hash shl 7'u64) +
|
||||
(u64hash shl 8'u64) +
|
||||
(u64hash shl 24'u64)).uint32 xor val
|
||||
|
||||
{.push stackTrace:off.}
|
||||
proc lsb(c: ptr cwchar_t): char {.noinit, inline.} =
|
||||
{.emit: [result, " = (char)(", c[]," & 0xFF);"]}
|
||||
{.pop.}
|
||||
|
||||
proc fnv1Hash(godotClassName: GodotString): FNV1Hash =
|
||||
var charsCount = godotClassName.len
|
||||
let charsPtr = godotClassName.dataPtr
|
||||
if charsCount > 2 and
|
||||
charsPtr.offset(charsCount - 2).lsb == 'S' and
|
||||
charsPtr.offset(charsCount - 1).lsb == 'W':
|
||||
charsCount -= 2
|
||||
|
||||
initFNV1Hash(result)
|
||||
for i in 0..<charsCount:
|
||||
let c = charsPtr.offset(i).lsb
|
||||
appendFNV1Hash(result, c.uint8)
|
||||
|
||||
proc fnv1Hash(godotClassName: string): FNV1Hash {.compileTime.} =
|
||||
var charsCount = godotClassName.len
|
||||
if godotClassName.endsWith("SW"):
|
||||
charsCount -= 2
|
||||
|
||||
initFNV1Hash(result)
|
||||
var i = 0
|
||||
for rune in runes(godotClassName):
|
||||
if i >= charsCount:
|
||||
break
|
||||
let firstByte = uint8(rune.uint64 and 0xFF'u64)
|
||||
appendFNV1Hash(result, firstByte)
|
||||
# We don't really need to calculate hash of other bytes,
|
||||
# since we only use hashes of ASCII strings
|
||||
inc i
|
||||
|
||||
proc getClassName*(o: NimGodotObject): string =
|
||||
o.godotObject.getClassName()
|
||||
|
||||
|
|
@ -179,12 +229,12 @@ macro isResource(T: typedesc): bool =
|
|||
result = if inherits(getType(T), "Resource"): ident("true")
|
||||
else: ident("false")
|
||||
|
||||
template registerClass*(T: typedesc; godotClassName: cstring,
|
||||
template registerClass*(T: typedesc; godotClassName: string or cstring,
|
||||
native: bool) =
|
||||
## Registers the specified Godot type.
|
||||
## Used by ``gdobj`` macro and `godotapigen <godotapigen.html>`_.
|
||||
if classRegistry.isNil:
|
||||
classRegistry = newTable[cstring, ObjectInfo]()
|
||||
classRegistry = newTable[FNV1Hash, ObjectInfo]()
|
||||
let constructor = proc(): NimGodotObject =
|
||||
var t: T
|
||||
new(t, nimGodotObjectFinalizer[T])
|
||||
|
|
@ -198,14 +248,22 @@ template registerClass*(T: typedesc; godotClassName: cstring,
|
|||
isNative: native,
|
||||
isRef: isRef
|
||||
)
|
||||
classRegistry[godotClassName] = objInfo
|
||||
classRegistry[fnv1Hash($godotClassName)] = objInfo
|
||||
static:
|
||||
let objInfoStatic = ObjectInfo(
|
||||
baseNativeClass: base,
|
||||
isNative: native,
|
||||
isRef: isRef,
|
||||
)
|
||||
classRegistryStatic[godotClassName] = objInfoStatic
|
||||
let nameHash = fnv1Hash($godotClassName)
|
||||
if not classRegistryStatic.contains(nameHash):
|
||||
classRegistryStatic[nameHash] = objInfoStatic
|
||||
elif not endsWith($godotClassName, "SW"):
|
||||
# For simplicity we assume that all class names must have
|
||||
# different hashes
|
||||
# If this exception is ever raised, I guess, we should
|
||||
# implement a proper collision resolving
|
||||
raise newException(Exception, "Hash collision " & $godotClassName)
|
||||
when isRef:
|
||||
static:
|
||||
refClasses.add(T.name)
|
||||
|
|
@ -214,10 +272,10 @@ template registerClass*(T: typedesc; godotClassName: cstring,
|
|||
nativeClasses.add(T.name)
|
||||
|
||||
proc newNimGodotObject[T: NimGodotObject](
|
||||
godotObject: ptr GodotObject, godotClassName: cstring, noRef: bool): T =
|
||||
godotObject: ptr GodotObject, godotClassName: GodotString, noRef: bool): T =
|
||||
assert(not classRegistry.isNil)
|
||||
assert(not godotObject.isNil)
|
||||
let objInfo = classRegistry.getOrDefault(godotClassName)
|
||||
let objInfo = classRegistry.getOrDefault(fnv1Hash(godotClassName))
|
||||
if objInfo.constructor.isNil:
|
||||
printError("Nim constructor not found for class " & $godotClassName)
|
||||
else:
|
||||
|
|
@ -240,9 +298,11 @@ proc asNimGodotObject*[T: NimGodotObject](
|
|||
# Could be data from other bindings
|
||||
result = nil
|
||||
if result.isNil:
|
||||
var classNameStr = godotObject.getClassNameRaw()
|
||||
result = newNimGodotObject[T](
|
||||
godotObject, cstring(godotObject.getClassName()),
|
||||
godotObject, classNameStr,
|
||||
forceNativeObject or noRef)
|
||||
deinit(classNameStr)
|
||||
|
||||
proc newVariant*(obj: NimGodotObject): Variant {.inline.} =
|
||||
newVariant(obj.godotObject)
|
||||
|
|
@ -363,17 +423,18 @@ proc newOwnObj[T: NimGodotObject](name: cstring): T =
|
|||
|
||||
proc gdnew*[T: NimGodotObject](): T =
|
||||
## Instantiates new object of type ``T``.
|
||||
const godotName = asCString(toGodotName(T))
|
||||
const objInfo = classRegistryStatic[godotName]
|
||||
const godotName = toGodotName(T)
|
||||
const cGodotName = asCString(godotName)
|
||||
const objInfo = classRegistryStatic[fnv1Hash(godotName)]
|
||||
when objInfo.isNative:
|
||||
let godotObject = getClassConstructor(godotName)()
|
||||
let godotObject = getClassConstructor(cGodotName)()
|
||||
new(result, nimGodotObjectFinalizer[T])
|
||||
result.godotObject = godotObject
|
||||
when objInfo.isRef:
|
||||
godotObject.initRef()
|
||||
result.isRef = true
|
||||
else:
|
||||
result = newOwnObj[T](godotName)
|
||||
result = newOwnObj[T](cGodotName)
|
||||
|
||||
proc newCallError*(err: VariantCallError): ref CallError =
|
||||
## Instantiates ``CallError`` from Godot ``err``.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue