Add Haschable and UUID

This commit is contained in:
Christian Treffs 2017-11-05 12:31:50 +01:00
parent 4c48aab814
commit faee5998af
4 changed files with 182 additions and 12 deletions

View File

@ -5,14 +5,11 @@
// Created by Christian Treffs on 08.10.17.
//
public protocol Component: class, UniqueComponentIdentifiable, Activatable {}
public protocol Component: class, TypeIdentifiable, Activatable {}
// MARK: UCI
extension Component {
/// Uniquely identifies the component by its meta type
public static var identifier: ComponentIdentifier { return ComponentIdentifier(Self.self) }
/// Uniquely identifies the component by its meta type
public var identifier: ComponentIdentifier { return Self.identifier }
public var identifier: ComponentIdentifier { return typeObjectIdentifier }
public static var identifier: ComponentIdentifier { return typeObjectIdentifier }
}
// MARK: - activatable protocol

View File

@ -7,12 +7,6 @@
public typealias ComponentIdentifier = ObjectIdentifier
// MARK: Unique Component Identifiable
public protocol UniqueComponentIdentifiable {
static var identifier: ComponentIdentifier { get }
var identifier: ComponentIdentifier { get }
}
extension ComponentIdentifier {
/// Provides XOR hash value from component identifier (aka type) and entity index.

View File

@ -0,0 +1,57 @@
//
// Hashable.swift
// FirebladeECS
//
// Created by Christian Treffs on 04.11.17.
//
/// TypeIdentifiable
/// Identifies an object by it's meta type.
public protocol TypeIdentifiable {
static var typeObjectIdentifier: ObjectIdentifier { get }
var typeObjectIdentifier: ObjectIdentifier { get }
}
/// TypeHashable
/// Identifies an object by it's meta type and conforms to Hashable.
/// This introduces a Self type requirement.
public protocol TypeHashable: TypeIdentifiable, Hashable {
static var hashValue: Int { get }
}
/// InstanceHashable
/// Identifies an object instance by it's object identifier and conforms to Hashable
public protocol InstanceHashable: class, Hashable {
var instanceObjectIdentifier: ObjectIdentifier { get }
}
/// TypeInstanceHashable
/// Identifies an object by instance and meta type and conforms to Hashable
public typealias TypeInstanceHashable = TypeHashable & InstanceHashable
extension InstanceHashable {
public var instanceObjectIdentifier: ObjectIdentifier { return ObjectIdentifier.init(self) }
public static func == (lhs: Self, rhs: Self) -> Bool { return lhs.hashValue == rhs.hashValue }
public var hashValue: Int { return instanceObjectIdentifier.hashValue }
}
extension TypeIdentifiable {
public static var typeObjectIdentifier: ObjectIdentifier { return ObjectIdentifier.init(Self.self) }
public var typeObjectIdentifier: ObjectIdentifier { return Self.typeObjectIdentifier }
}
extension TypeHashable {
public static func == (lhs: Self, rhs: Self) -> Bool { return lhs.hashValue == rhs.hashValue }
public var hashValue: Int { return typeObjectIdentifier.hashValue }
public static var hashValue: Int { return typeObjectIdentifier.hashValue }
}
extension TypeHashable where Self: InstanceHashable {
public static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.typeObjectIdentifier == rhs.typeObjectIdentifier &&
lhs.instanceObjectIdentifier == rhs.instanceObjectIdentifier
}
public var hashValue: Int {
return typeObjectIdentifier.hashValue ^ instanceObjectIdentifier.hashValue // TODO: this might not be best - use hash combine?
}
}

View File

@ -0,0 +1,122 @@
//
// UUID.swift
// FirebladeECSPackageDescription
//
// Created by Christian Treffs on 04.11.17.
//
#if os(macOS) || os(iOS) || os(tvOS)
import Darwin.C.stdlib
#else
// TODO: support linux and other platforms
#endif
public struct UUID {
public static let count: Int = 16 // https://tools.ietf.org/html/rfc4122#section-4.1
private let bytes: ContiguousArray<UInt8>
public init(_ bytes: ContiguousArray<UInt8>) {
assert(bytes.count == UUID.count, "An UUID must have a count of exactly \(UUID.count).")
self.bytes = bytes
}
public init() {
self.init(UUID.generateUUID())
}
public init(_ bytes: [UInt8]) {
self.init(ContiguousArray(bytes))
}
public init?(uuidString: String) {
guard uuidString.count == 2*UUID.count+4 else {
// "An UUID string must have a count of exactly 36."
return nil
}
var uuid: ContiguousArray<UInt8> = ContiguousArray<UInt8>(repeating: 0, count: UUID.count)
let contiguousString: String = uuidString.split(separator: "-").joined()
guard contiguousString.count == 2*UUID.count else {
// An UUID string must have exactly 4 separators
return nil
}
var endIdx: String.Index = contiguousString.startIndex
for i in 0..<UUID.count {
let startIdx: String.Index = endIdx
endIdx = contiguousString.index(endIdx, offsetBy: 2)
let substring: Substring = contiguousString[startIdx..<endIdx] // take 2 characters as one byte
guard let byte: UInt8 = UInt8(substring, radix: UUID.count) else {
return nil
}
uuid[i] = byte
}
self.init(uuid)
}
private static func generateUUID() -> ContiguousArray<UInt8> {
var uuid: ContiguousArray<UInt8> = ContiguousArray<UInt8>(repeating: 0, count: UUID.count)
uuid.withUnsafeMutableBufferPointer { (uuidPtr: inout UnsafeMutableBufferPointer<UInt8>) -> Void in
// TODO: use /dev/urandom
arc4random_buf(uuidPtr.baseAddress, UUID.count) // TODO: linux
}
makeRFC4122compliant(uuid: &uuid)
return uuid
}
private static func makeRFC4122compliant(uuid: inout ContiguousArray<UInt8>) {
uuid[6] = (uuid[6] & 0x0F) | 0x40 // version https://tools.ietf.org/html/rfc4122#section-4.1.3
uuid[8] = (uuid[8] & 0x3f) | 0x80 // variant https://tools.ietf.org/html/rfc4122#section-4.1.1
}
}
extension UUID: Equatable {
public static func == (lhs: UUID, rhs: UUID) -> Bool {
return lhs.bytes == rhs.bytes
}
}
extension UUID: Hashable {
/// One-at-a-Time hash
/// http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
private var oat_hash: Int {
var hash: Int = 0
for i: Int in 0..<UUID.count {
hash = hash &+ numericCast(bytes[i])
hash = hash &+ (hash << 10)
hash ^= (hash >> 6)
}
hash = hash &+ (hash << 3)
hash ^= (hash << 11)
hash = hash &+ (hash << 15)
return hash
}
public var hashValue: Int { return oat_hash }
}
extension UUID: CustomStringConvertible, CustomDebugStringConvertible {
public var uuidString: String {
var out: String = String()
out.reserveCapacity(UUID.count)
let separatorLayout: [Int] = [0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
let separator: String = "-"
var idx: Int = 0
for byte in bytes {
let char = String(byte, radix: UUID.count, uppercase: true)
switch char.count {
case 2:
out.append(char)
default:
out.append("0" + char)
}
if separatorLayout[idx] == 1 {
out.append(separator)
}
idx += 1
}
assert(idx == UUID.count)
assert(out.count == 36)
return out
}
public var description: String { return uuidString }
public var debugDescription: String { return uuidString }
}