Last active
May 19, 2017 03:00
-
-
Save RoyalIcing/342416bd4ddadb65cf69deb4afe3ac7a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
import Foundation | |
struct ExampleStruct : Codable { | |
var title: String | |
var something: Int | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(title, forKey: .title) | |
try container.encode(something, forKey: .something) | |
} | |
} | |
class ExampleSuperclass : Codable { | |
var title: String | |
private enum CodingKeys : CodingKey { | |
case title | |
} | |
init(title: String) { | |
self.title = title | |
} | |
required init(from decoder: Decoder) throws { | |
fatalError("Unimplemented") | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(title, forKey: .title) | |
} | |
} | |
class ExampleSubclass : ExampleSuperclass { | |
var something: Int | |
private enum CodingKeys : CodingKey { | |
case something | |
} | |
init(title: String, something: Int) { | |
self.something = something | |
super.init(title: title) | |
} | |
required init(from decoder: Decoder) throws { | |
fatalError("Unimplemented") | |
} | |
override func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(something, forKey: .something) | |
try super.encode(to: container.superEncoder()) | |
} | |
} | |
private class EqualityKeyedEncodingBase { | |
var hashValue: Int = 0 | |
var equal = true | |
} | |
fileprivate struct EqualityKeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol { | |
typealias Key = K | |
var codingPath: [CodingKey?] = [] | |
private var base: EqualityKeyedEncodingBase | |
fileprivate init(base: EqualityKeyedEncodingBase, codingPath: [CodingKey?]) { | |
self.base = base | |
self.codingPath = codingPath | |
} | |
private mutating func with<T>(pushedKey key: CodingKey?, _ work: () throws -> T) rethrows -> T { | |
if let key = key { | |
base.hashValue ^= key.stringValue.hashValue | |
} | |
self.codingPath.append(key) | |
let ret: T = try work() | |
self.codingPath.removeLast() | |
return ret | |
} | |
private mutating func add<Value : Hashable>(pushedKey key: CodingKey?, value: Value?) { | |
if let key = key { | |
base.hashValue ^= key.stringValue.hashValue | |
} | |
if let value = value { | |
base.hashValue ^= value.hashValue | |
} | |
} | |
mutating func encode<T : Encodable>(_ value: T?, forKey key: Key) throws { | |
with(pushedKey: key) {} | |
// TODO: no way to derive hash of `value`? | |
} | |
mutating func encode(_ value: Bool?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: Int?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: Int8?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: Int16?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: Int32?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: Int64?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: UInt?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: UInt8?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: UInt16?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: UInt32?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: UInt64?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: Float?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: Double?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func encode(_ value: String?, forKey key: Key) throws { | |
add(pushedKey: key, value: value) | |
} | |
mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer<NestedKey> { | |
return self.with(pushedKey: key) { | |
let container = EqualityKeyedEncodingContainer<NestedKey>(base: self.base, codingPath: self.codingPath) | |
return KeyedEncodingContainer(container) | |
} | |
} | |
mutating func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer { | |
return self.with(pushedKey: key) { | |
return EqualityUnkeyedEncodingContainer(base: self.base, codingPath: self.codingPath) | |
} | |
} | |
mutating func superEncoder() -> Encoder { | |
return EqualityEncoder(base: base, codingPath: self.codingPath) | |
} | |
mutating func superEncoder(forKey key: K) -> Encoder { | |
return self.with(pushedKey: key) { | |
return EqualityEncoder(base: base, codingPath: self.codingPath) | |
} | |
} | |
} | |
fileprivate struct EqualityUnkeyedEncodingContainer : UnkeyedEncodingContainer { | |
var codingPath: [CodingKey?] = [] | |
private var base: EqualityKeyedEncodingBase | |
fileprivate init(base: EqualityKeyedEncodingBase, codingPath: [CodingKey?]) { | |
self.base = base | |
self.codingPath = codingPath | |
} | |
private mutating func with<T>(pushedKey key: CodingKey?, _ work: () throws -> T) rethrows -> T { | |
if let key = key { | |
base.hashValue ^= key.stringValue.hashValue | |
} | |
self.codingPath.append(key) | |
let ret: T = try work() | |
self.codingPath.removeLast() | |
return ret | |
} | |
private mutating func add<Value : Hashable>(value: Value?) { | |
if let value = value { | |
base.hashValue ^= value.hashValue | |
} | |
} | |
mutating func encode<T>(_ value: T?) throws where T : Encodable { | |
// TODO: not sure what is possible to derive here | |
} | |
mutating func encode(_ value: Bool?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: Int?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: Int8?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: Int16?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: Int32?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: Int64?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: UInt?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: UInt8?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: UInt16?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: UInt32?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: UInt64?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: Float?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: Double?) throws { | |
add(value: value) | |
} | |
mutating func encode(_ value: String?) throws { | |
add(value: value) | |
} | |
mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> { | |
return self.with(pushedKey: nil) { | |
let container = EqualityKeyedEncodingContainer<NestedKey>(base: self.base, codingPath: self.codingPath) | |
return KeyedEncodingContainer(container) | |
} | |
} | |
mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { | |
return self.with(pushedKey: nil) { | |
let container = EqualityUnkeyedEncodingContainer(base: self.base, codingPath: self.codingPath) | |
return container | |
} | |
} | |
mutating func superEncoder() -> Encoder { | |
return EqualityEncoder(base: base, codingPath: self.codingPath) | |
} | |
} | |
class EqualityEncoder : Encoder { | |
var userInfo: [CodingUserInfoKey : Any] = [:] | |
private var base: EqualityKeyedEncodingBase | |
private(set) var codingPath: [CodingKey?] | |
private(set) var containers: [Any] = [] | |
fileprivate init(base: EqualityKeyedEncodingBase, codingPath: [CodingKey?]) { | |
self.base = base | |
self.codingPath = codingPath | |
} | |
init() { | |
self.base = EqualityKeyedEncodingBase() | |
self.codingPath = [] | |
} | |
var hashValue: Int { | |
return base.hashValue | |
} | |
func add<Value : Hashable>(value: Value) { | |
base.hashValue ^= value.hashValue | |
} | |
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> { | |
let container = EqualityKeyedEncodingContainer<Key>(base: base, codingPath: codingPath) | |
containers.append(container) | |
return KeyedEncodingContainer(container) | |
} | |
func unkeyedContainer() -> UnkeyedEncodingContainer { | |
//assertCanRequestNewContainer() | |
let container = EqualityUnkeyedEncodingContainer(base: base, codingPath: codingPath) | |
containers.append(container) | |
return container | |
} | |
func singleValueContainer() -> SingleValueEncodingContainer { | |
//assertCanRequestNewContainer() | |
return self | |
} | |
} | |
extension EqualityEncoder : SingleValueEncodingContainer { | |
func encode(_ value: Bool) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: Int) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: Int8) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: Int16) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: Int32) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: Int64) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: UInt) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: UInt8) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: UInt16) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: UInt32) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: UInt64) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: String) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: Float) throws { | |
self.add(value: value) | |
} | |
func encode(_ value: Double) throws { | |
self.add(value: value) | |
} | |
} | |
extension Hashable where Self : Encodable { | |
var hashValue: Int { | |
let encoder = EqualityEncoder() | |
try? self.encode(to: encoder) | |
return encoder.hashValue | |
} | |
} | |
extension Array where Element : Encodable { | |
// TODO: Make Array conform to Hashable | |
var hashValue: Int { | |
let encoder = EqualityEncoder() | |
try? self.encode(to: encoder) | |
return encoder.hashValue | |
} | |
} | |
// TODO: provide == implementation | |
extension ExampleStruct : Hashable { | |
static func == (lhs: ExampleStruct, rhs: ExampleStruct) -> Bool { | |
return lhs.title == rhs.title && lhs.something == rhs.something | |
} | |
} | |
extension ExampleSuperclass : Hashable { | |
static func == (lhs: ExampleSuperclass, rhs: ExampleSuperclass) -> Bool { | |
return lhs.title == rhs.title | |
} | |
} | |
extension ExampleSubclass { | |
static func == (lhs: ExampleSubclass, rhs: ExampleSubclass) -> Bool { | |
return lhs.title == rhs.title && lhs.something == rhs.something | |
} | |
} | |
// Struct | |
ExampleStruct.init(title: "Test", something: 7).hashValue | |
// Class | |
ExampleSuperclass.init(title: "Test").hashValue | |
// Subclass | |
ExampleSubclass.init(title: "Test", something: 7).hashValue | |
[ | |
ExampleStruct.init(title: "Test", something: 7), | |
ExampleStruct.init(title: "Test 2", something: 10) | |
].hashValue | |
// The struct and class have the same keys, so should have same hash | |
ExampleStruct.init(title: "Test", something: 7).hashValue == ExampleSubclass.init(title: "Test", something: 7).hashValue |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment