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."
|
author = "Xored Software, Inc."
|
||||||
description = "Godot Engine bindings"
|
description = "Godot Engine bindings"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
||||||
|
|
@ -1374,7 +1374,8 @@ type
|
||||||
stringNewWithWideString: pointer
|
stringNewWithWideString: pointer
|
||||||
stringOperatorIndex: pointer
|
stringOperatorIndex: pointer
|
||||||
stringOperatorIndexConst: pointer
|
stringOperatorIndexConst: pointer
|
||||||
stringWideStr: pointer
|
stringWideStr: proc (self: GodotString): ptr cwchar_t
|
||||||
|
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||||
stringOperatorEqual: proc (self, other: GodotString): bool
|
stringOperatorEqual: proc (self, other: GodotString): bool
|
||||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||||
stringOperatorLess: proc (self, other: GodotString): bool
|
stringOperatorLess: proc (self, other: GodotString): bool
|
||||||
|
|
@ -2811,6 +2812,8 @@ type
|
||||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||||
stringNewCopy*: proc (dest: var GodotString, src: GodotString)
|
stringNewCopy*: proc (dest: var GodotString, src: GodotString)
|
||||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
{.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
|
stringOperatorEqual*: proc (self, other: GodotString): bool
|
||||||
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
{.noconv, raises: [], gcsafe, tags: [], locks: 0.}
|
||||||
stringOperatorLess*: proc (self, other: GodotString): bool
|
stringOperatorLess*: proc (self, other: GodotString): bool
|
||||||
|
|
@ -3481,6 +3484,7 @@ proc setGDNativeAPIInternal(apiStruct: pointer, initOptions: ptr GDNativeInitOpt
|
||||||
|
|
||||||
stringNew
|
stringNew
|
||||||
stringNewCopy
|
stringNewCopy
|
||||||
|
stringWideStr
|
||||||
stringOperatorEqual
|
stringOperatorEqual
|
||||||
stringOperatorLess
|
stringOperatorLess
|
||||||
stringOperatorPlus
|
stringOperatorPlus
|
||||||
|
|
|
||||||
|
|
@ -839,7 +839,7 @@ proc typeNameToModuleName(name: string): string =
|
||||||
proc newRegisterClassNode(typ: GodotType): PNode =
|
proc newRegisterClassNode(typ: GodotType): PNode =
|
||||||
newCall("registerClass",
|
newCall("registerClass",
|
||||||
ident(typ.name),
|
ident(typ.name),
|
||||||
newCStringLit(typ.godotName),
|
newStrLit(typ.godotName),
|
||||||
ident("true") # isNative
|
ident("true") # isNative
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -877,7 +877,7 @@ proc genTypeFile(types: Table[string, GodotType], targetDir: string) =
|
||||||
if not typ.shouldExport(types): continue
|
if not typ.shouldExport(types): continue
|
||||||
let regNode = newCall("registerClass",
|
let regNode = newCall("registerClass",
|
||||||
ident(typ.name),
|
ident(typ.name),
|
||||||
newCStringLit(typ.godotName),
|
newStrLit(typ.godotName),
|
||||||
ident("true") # isNative
|
ident("true") # isNative
|
||||||
)
|
)
|
||||||
godotApiTypesTree.add(regNode)
|
godotApiTypesTree.add(regNode)
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,11 @@ proc getClassName*(o: ptr GodotObject): string =
|
||||||
# There are physics type not known by ClassDB
|
# There are physics type not known by ClassDB
|
||||||
result = result[0..result.len-3]
|
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.} =
|
proc getGodotSingleton*(name: cstring): ptr GodotObject {.inline.} =
|
||||||
getGDNativeAPI().globalGetSingleton(name)
|
getGDNativeAPI().globalGetSingleton(name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ type
|
||||||
GodotString* {.byref.} = object
|
GodotString* {.byref.} = object
|
||||||
data: array[sizeof(int), byte]
|
data: array[sizeof(int), byte]
|
||||||
|
|
||||||
|
cwchar_t* {.importc: "wchar_t", nodecl.} = object
|
||||||
|
|
||||||
GodotCharString* {.byref.} = object
|
GodotCharString* {.byref.} = object
|
||||||
data: array[sizeof(int), byte]
|
data: array[sizeof(int), byte]
|
||||||
|
|
||||||
|
|
@ -305,3 +307,6 @@ type
|
||||||
|
|
||||||
GodotClassConstructor* = proc (): ptr GodotObject {.
|
GodotClassConstructor* = proc (): ptr GodotObject {.
|
||||||
noconv, gcsafe, locks: 0, raises: [], tags: [].}
|
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 godotinternaltypes, gdnativeapi
|
||||||
import core.godotcoretypes
|
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,
|
template genPoolArrayAPI(ArrayT, initIdent, DataT,
|
||||||
newProc, newCopyProc, newWithArrayProc, appendProc,
|
newProc, newCopyProc, newWithArrayProc, appendProc,
|
||||||
appendArrayProc, insertProc, invertProc, pushBackProc,
|
appendArrayProc, insertProc, invertProc, pushBackProc,
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,12 @@ proc `&`*(self, b: GodotString): GodotString {.inline.} =
|
||||||
proc deinit*(self: var GodotString) {.inline.} =
|
proc deinit*(self: var GodotString) {.inline.} =
|
||||||
getGDNativeAPI().stringDestroy(self)
|
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 =
|
proc `$`*(self: GodotString): string =
|
||||||
## Converts the ``GodotString`` into Nim string
|
## Converts the ``GodotString`` into Nim string
|
||||||
var charStr = getGDNativeAPI().stringUtf8(self)
|
var charStr = getGDNativeAPI().stringUtf8(self)
|
||||||
|
|
|
||||||
|
|
@ -508,7 +508,7 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
|
||||||
# Register Godot object
|
# Register Godot object
|
||||||
let parentName = if obj.parentName.isNil: newStrLitNode("Object")
|
let parentName = if obj.parentName.isNil: newStrLitNode("Object")
|
||||||
else: newStrLitNode(obj.parentName)
|
else: newStrLitNode(obj.parentName)
|
||||||
let classNameLit = newCStringLit(obj.name)
|
let classNameLit = newStrLitNode(obj.name)
|
||||||
let classNameIdent = ident(obj.name)
|
let classNameIdent = ident(obj.name)
|
||||||
let isRef: bool = if obj.parentName.isNil: false
|
let isRef: bool = if obj.parentName.isNil: false
|
||||||
else: obj.parentName in refClasses
|
else: obj.parentName in refClasses
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright 2018 Xored Software, Inc.
|
# Copyright 2018 Xored Software, Inc.
|
||||||
|
|
||||||
import tables, typetraits, macros
|
import tables, typetraits, macros, unicode
|
||||||
import gdnativeapi
|
import gdnativeapi
|
||||||
import core.godotcoretypes, core.godotbase
|
import core.godotcoretypes, core.godotbase
|
||||||
import core.vector2, core.rect2,
|
import core.vector2, core.rect2,
|
||||||
|
|
@ -89,21 +89,71 @@ type
|
||||||
## values.
|
## values.
|
||||||
## See documentation of ``GodotPropertyHint`` for description of formats.
|
## See documentation of ``GodotPropertyHint`` for description of formats.
|
||||||
|
|
||||||
|
FNV1Hash = uint32
|
||||||
|
|
||||||
proc isFinalized*(obj: NimGodotObject): bool {.inline.} =
|
proc isFinalized*(obj: NimGodotObject): bool {.inline.} =
|
||||||
obj.isFinalized
|
obj.isFinalized
|
||||||
|
|
||||||
var classRegistry {.threadvar.}: TableRef[cstring, ObjectInfo]
|
var classRegistry {.threadvar.}: TableRef[FNV1Hash, ObjectInfo]
|
||||||
var classRegistryStatic* {.compileTime.}: TableRef[cstring, ObjectInfo]
|
var classRegistryStatic* {.compileTime.}: TableRef[FNV1Hash, ObjectInfo]
|
||||||
## Compile-time variable used for implementation of several procedures
|
## Compile-time variable used for implementation of several procedures
|
||||||
## and macros
|
## and macros
|
||||||
static:
|
static:
|
||||||
classRegistryStatic = newTable[cstring, ObjectInfo]()
|
classRegistryStatic = newTable[FNV1Hash, ObjectInfo]()
|
||||||
|
|
||||||
static:
|
static:
|
||||||
import sets, strutils
|
import sets, strutils
|
||||||
var nativeClasses {.compileTime.} = newSeq[string]()
|
var nativeClasses {.compileTime.} = newSeq[string]()
|
||||||
var refClasses* {.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 =
|
proc getClassName*(o: NimGodotObject): string =
|
||||||
o.godotObject.getClassName()
|
o.godotObject.getClassName()
|
||||||
|
|
||||||
|
|
@ -179,12 +229,12 @@ macro isResource(T: typedesc): bool =
|
||||||
result = if inherits(getType(T), "Resource"): ident("true")
|
result = if inherits(getType(T), "Resource"): ident("true")
|
||||||
else: ident("false")
|
else: ident("false")
|
||||||
|
|
||||||
template registerClass*(T: typedesc; godotClassName: cstring,
|
template registerClass*(T: typedesc; godotClassName: string or cstring,
|
||||||
native: bool) =
|
native: bool) =
|
||||||
## Registers the specified Godot type.
|
## Registers the specified Godot type.
|
||||||
## Used by ``gdobj`` macro and `godotapigen <godotapigen.html>`_.
|
## Used by ``gdobj`` macro and `godotapigen <godotapigen.html>`_.
|
||||||
if classRegistry.isNil:
|
if classRegistry.isNil:
|
||||||
classRegistry = newTable[cstring, ObjectInfo]()
|
classRegistry = newTable[FNV1Hash, ObjectInfo]()
|
||||||
let constructor = proc(): NimGodotObject =
|
let constructor = proc(): NimGodotObject =
|
||||||
var t: T
|
var t: T
|
||||||
new(t, nimGodotObjectFinalizer[T])
|
new(t, nimGodotObjectFinalizer[T])
|
||||||
|
|
@ -198,14 +248,22 @@ template registerClass*(T: typedesc; godotClassName: cstring,
|
||||||
isNative: native,
|
isNative: native,
|
||||||
isRef: isRef
|
isRef: isRef
|
||||||
)
|
)
|
||||||
classRegistry[godotClassName] = objInfo
|
classRegistry[fnv1Hash($godotClassName)] = objInfo
|
||||||
static:
|
static:
|
||||||
let objInfoStatic = ObjectInfo(
|
let objInfoStatic = ObjectInfo(
|
||||||
baseNativeClass: base,
|
baseNativeClass: base,
|
||||||
isNative: native,
|
isNative: native,
|
||||||
isRef: isRef,
|
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:
|
when isRef:
|
||||||
static:
|
static:
|
||||||
refClasses.add(T.name)
|
refClasses.add(T.name)
|
||||||
|
|
@ -214,10 +272,10 @@ template registerClass*(T: typedesc; godotClassName: cstring,
|
||||||
nativeClasses.add(T.name)
|
nativeClasses.add(T.name)
|
||||||
|
|
||||||
proc newNimGodotObject[T: NimGodotObject](
|
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 classRegistry.isNil)
|
||||||
assert(not godotObject.isNil)
|
assert(not godotObject.isNil)
|
||||||
let objInfo = classRegistry.getOrDefault(godotClassName)
|
let objInfo = classRegistry.getOrDefault(fnv1Hash(godotClassName))
|
||||||
if objInfo.constructor.isNil:
|
if objInfo.constructor.isNil:
|
||||||
printError("Nim constructor not found for class " & $godotClassName)
|
printError("Nim constructor not found for class " & $godotClassName)
|
||||||
else:
|
else:
|
||||||
|
|
@ -240,9 +298,11 @@ proc asNimGodotObject*[T: NimGodotObject](
|
||||||
# Could be data from other bindings
|
# Could be data from other bindings
|
||||||
result = nil
|
result = nil
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
|
var classNameStr = godotObject.getClassNameRaw()
|
||||||
result = newNimGodotObject[T](
|
result = newNimGodotObject[T](
|
||||||
godotObject, cstring(godotObject.getClassName()),
|
godotObject, classNameStr,
|
||||||
forceNativeObject or noRef)
|
forceNativeObject or noRef)
|
||||||
|
deinit(classNameStr)
|
||||||
|
|
||||||
proc newVariant*(obj: NimGodotObject): Variant {.inline.} =
|
proc newVariant*(obj: NimGodotObject): Variant {.inline.} =
|
||||||
newVariant(obj.godotObject)
|
newVariant(obj.godotObject)
|
||||||
|
|
@ -363,17 +423,18 @@ proc newOwnObj[T: NimGodotObject](name: cstring): T =
|
||||||
|
|
||||||
proc gdnew*[T: NimGodotObject](): T =
|
proc gdnew*[T: NimGodotObject](): T =
|
||||||
## Instantiates new object of type ``T``.
|
## Instantiates new object of type ``T``.
|
||||||
const godotName = asCString(toGodotName(T))
|
const godotName = toGodotName(T)
|
||||||
const objInfo = classRegistryStatic[godotName]
|
const cGodotName = asCString(godotName)
|
||||||
|
const objInfo = classRegistryStatic[fnv1Hash(godotName)]
|
||||||
when objInfo.isNative:
|
when objInfo.isNative:
|
||||||
let godotObject = getClassConstructor(godotName)()
|
let godotObject = getClassConstructor(cGodotName)()
|
||||||
new(result, nimGodotObjectFinalizer[T])
|
new(result, nimGodotObjectFinalizer[T])
|
||||||
result.godotObject = godotObject
|
result.godotObject = godotObject
|
||||||
when objInfo.isRef:
|
when objInfo.isRef:
|
||||||
godotObject.initRef()
|
godotObject.initRef()
|
||||||
result.isRef = true
|
result.isRef = true
|
||||||
else:
|
else:
|
||||||
result = newOwnObj[T](godotName)
|
result = newOwnObj[T](cGodotName)
|
||||||
|
|
||||||
proc newCallError*(err: VariantCallError): ref CallError =
|
proc newCallError*(err: VariantCallError): ref CallError =
|
||||||
## Instantiates ``CallError`` from Godot ``err``.
|
## Instantiates ``CallError`` from Godot ``err``.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue