Merge branch 'develop' into feature/classdojo_fixHashingOverflow

This commit is contained in:
Christian Treffs 2020-07-09 22:35:38 +02:00
commit 3dd3d7fc10
No known key found for this signature in database
GPG Key ID: 49A4B4B460BE3ED4
8 changed files with 74 additions and 38 deletions

View File

@ -8,12 +8,14 @@
public struct EntityIdentifier {
static let invalid = EntityIdentifier(.max)
public typealias Id = Int
/// provides 4294967295 unique identifiers since it's constrained to UInt32 - invalid.
@usableFromInline let id: Int
@usableFromInline let id: Id
@usableFromInline
init(_ uint32: UInt32) {
self.id = Int(uint32)
self.id = Id(uint32)
}
}

View File

@ -17,7 +17,7 @@ public struct Family<R> where R: FamilyRequirementsManaging {
nexus.onFamilyInit(traits: traits)
}
@inlinable public var memberIds: UnorderedSparseSet<EntityIdentifier> {
@inlinable public var memberIds: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id> {
nexus.members(withFamilyTraits: traits)
}
@ -58,7 +58,7 @@ extension Family: LazySequenceProtocol { }
// MARK: - components iterator
extension Family {
public struct ComponentsIterator: IteratorProtocol {
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier>.ElementIterator
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>.ElementIterator
@usableFromInline unowned let nexus: Nexus
public init(family: Family<R>) {
@ -85,7 +85,7 @@ extension Family {
}
public struct EntityIterator: IteratorProtocol {
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier>.ElementIterator
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>.ElementIterator
@usableFromInline unowned let nexus: Nexus
public init(family: Family<R>) {
@ -111,7 +111,7 @@ extension Family {
}
public struct EntityComponentIterator: IteratorProtocol {
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier>.ElementIterator
@usableFromInline var memberIdsIterator: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>.ElementIterator
@usableFromInline unowned let nexus: Nexus
public init(family: Family<R>) {

View File

@ -18,8 +18,8 @@ extension Nexus {
return traits.isMatch(components: componentIds)
}
public func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet<EntityIdentifier> {
familyMembersByTraits[traits] ?? UnorderedSparseSet<EntityIdentifier>()
public func members(withFamilyTraits traits: FamilyTraitSet) -> UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id> {
familyMembersByTraits[traits] ?? UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>()
}
public func isMember(_ entity: Entity, in family: FamilyTraitSet) -> Bool {

View File

@ -12,7 +12,7 @@ extension Nexus {
return
}
familyMembersByTraits[traits] = UnorderedSparseSet<EntityIdentifier>()
familyMembersByTraits[traits] = UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>()
update(familyMembership: traits)
}

View File

@ -8,7 +8,7 @@
public final class Nexus {
/// Main entity storage.
/// Entities are tightly packed by EntityIdentifier.
@usableFromInline final var entityStorage: UnorderedSparseSet<EntityIdentifier>
@usableFromInline final var entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>
/// Entity ids that are currently not used.
@usableFromInline final var freeEntities: [EntityIdentifier]
@ -29,12 +29,12 @@ public final class Nexus {
/// - Key: FamilyTraitSet aka component types that make up one distinct family.
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier>]
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>]
public final weak var delegate: NexusEventDelegate?
public convenience init() {
self.init(entityStorage: UnorderedSparseSet<EntityIdentifier>(),
self.init(entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>(),
componentsByType: [:],
componentsByEntity: [:],
freeEntities: [],
@ -42,11 +42,11 @@ public final class Nexus {
childrenByParentEntity: [:])
}
internal init(entityStorage: UnorderedSparseSet<EntityIdentifier>,
internal init(entityStorage: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>,
componentsByType: [ComponentIdentifier: ManagedContiguousArray<Component>],
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
freeEntities: [EntityIdentifier],
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier>],
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>],
childrenByParentEntity: [EntityIdentifier: Set<EntityIdentifier>]) {
self.entityStorage = entityStorage
self.componentsByType = componentsByType

View File

@ -5,23 +5,36 @@
// Created by Christian Treffs on 30.10.17.
//
public struct UnorderedSparseSet<Element> {
public typealias Index = Int
public typealias Key = Int
/// An (unordered) sparse set.
///
/// - `Element`: the element (instance) to store.
/// - `Key`: the unique, hashable datastructure to use as a key to retrieve
/// an element from the sparse set.
///
/// See <https://github.com/bombela/sparseset/blob/master/src/lib.rs> for a reference implementation.
public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
/// An index into the dense store.
public typealias DenseIndex = Int
/// A sparse store holding indices into the dense mapped to key.
public typealias SparseStore = [Key: DenseIndex]
/// A dense store holding all the entries.
public typealias DenseStore = ContiguousArray<Entry>
public struct Entry {
public let key: Key
public let element: Element
}
@usableFromInline var dense: ContiguousArray<Entry>
@usableFromInline var sparse: [Index: Key]
@usableFromInline var dense: DenseStore
@usableFromInline var sparse: SparseStore
public init() {
self.init(sparse: [:], dense: [])
}
init(sparse: [Index: Key], dense: ContiguousArray<Entry>) {
init(sparse: SparseStore, dense: DenseStore) {
self.sparse = sparse
self.dense = dense
}
@ -120,8 +133,18 @@ public struct UnorderedSparseSet<Element> {
return (denseIndex, entry.element)
}
@inlinable public var first: Element? {
dense.first?.element
}
@inlinable public var last: Element? {
dense.last?.element
}
}
extension UnorderedSparseSet where Key == Int {
@inlinable
public subscript(position: Index) -> Element {
public subscript(position: DenseIndex) -> Element {
get {
get(unsafeAt: position)
}
@ -130,14 +153,6 @@ public struct UnorderedSparseSet<Element> {
insert(newValue, at: position)
}
}
@inlinable public var first: Element? {
dense.first?.element
}
@inlinable public var last: Element? {
dense.last?.element
}
}
// MARK: - Sequence
@ -148,9 +163,9 @@ extension UnorderedSparseSet: Sequence {
// MARK: - UnorderedSparseSetIterator
public struct ElementIterator: IteratorProtocol {
public private(set) var iterator: IndexingIterator<ContiguousArray<UnorderedSparseSet<Element>.Entry>>
public private(set) var iterator: IndexingIterator<ContiguousArray<UnorderedSparseSet<Element, Key>.Entry>>
public init(_ sparseSet: UnorderedSparseSet<Element>) {
public init(_ sparseSet: UnorderedSparseSet<Element, Key>) {
iterator = sparseSet.dense.makeIterator()
}
@ -163,7 +178,7 @@ extension UnorderedSparseSet: Sequence {
// MARK: - Equatable
extension UnorderedSparseSet.Entry: Equatable where Element: Equatable { }
extension UnorderedSparseSet: Equatable where Element: Equatable {
public static func == (lhs: UnorderedSparseSet<Element>, rhs: UnorderedSparseSet<Element>) -> Bool {
public static func == (lhs: UnorderedSparseSet<Element, Key>, rhs: UnorderedSparseSet<Element, Key>) -> Bool {
lhs.dense == rhs.dense && lhs.sparse == rhs.sparse
}
}

View File

@ -9,11 +9,11 @@
import XCTest
class SparseSetTests: XCTestCase {
var set: UnorderedSparseSet<Position>!
var set: UnorderedSparseSet<Position, Int>!
override func setUp() {
super.setUp()
set = UnorderedSparseSet<Position>()
set = UnorderedSparseSet<Position, Int>()
}
override func tearDown() {
@ -387,7 +387,7 @@ class SparseSetTests: XCTestCase {
func testSparseSetDoubleRemove() {
class AClass { }
var set = UnorderedSparseSet<AClass>()
var set = UnorderedSparseSet<AClass, Int>()
let a = AClass()
let b = AClass()
set.insert(a, at: 0)
@ -471,7 +471,7 @@ class SparseSetTests: XCTestCase {
}
func testSparseSetReduce() {
var characters = UnorderedSparseSet<Character>()
var characters = UnorderedSparseSet<Character, Int>()
characters.insert("H", at: 4)
characters.insert("e", at: 13)
@ -497,7 +497,7 @@ class SparseSetTests: XCTestCase {
}
func testSubscript() {
var characters = UnorderedSparseSet<Character>()
var characters = UnorderedSparseSet<Character, Int>()
characters[4] = "H"
characters[13] = "e"
@ -528,7 +528,7 @@ class SparseSetTests: XCTestCase {
}
func testStartEndIndex() {
var set = UnorderedSparseSet<Character>()
var set = UnorderedSparseSet<Character, Int>()
set.insert("C", at: 33)
set.insert("A", at: 11)
@ -538,4 +538,22 @@ class SparseSetTests: XCTestCase {
XCTAssertEqual(mapped, ["C", "A", "B"])
}
func testAlternativeKey() {
var set = UnorderedSparseSet<Character, String>()
set.insert("A", at: "a")
set.insert("C", at: "c")
set.insert("B", at: "b")
let mapped = set.dense.map { $0.element }
XCTAssertEqual(mapped, ["A", "C", "B"])
let keyValues = set.sparse.sorted(by: { $0.value < $1.value }).map { ($0.key, $0.value) }
for (a, b) in zip(keyValues, [("a", 0), ("c", 1), ("b", 2)]) {
XCTAssertEqual(a.0, b.0)
XCTAssertEqual(a.1, b.1)
}
}
}

View File

@ -109,6 +109,7 @@ extension SparseSetTests {
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__SparseSetTests = [
("testAlternativeKey", testAlternativeKey),
("testSparseSetAdd", testSparseSetAdd),
("testSparseSetAddAndReplace", testSparseSetAddAndReplace),
("testSparseSetClear", testSparseSetClear),