Skip to content

Instantly share code, notes, and snippets.

@RoyalIcing
Last active May 19, 2017 03:00
Show Gist options
  • Save RoyalIcing/342416bd4ddadb65cf69deb4afe3ac7a to your computer and use it in GitHub Desktop.
Save RoyalIcing/342416bd4ddadb65cf69deb4afe3ac7a to your computer and use it in GitHub Desktop.
//: 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