blob: eccb14e3342ccb2e1f035b15a88031f39656f774 [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
import CoreFoundation
#if os(OSX) || os(iOS)
import Darwin
#elseif os(Linux) || CYGWIN
import Glibc
#endif
internal class NSConcreteValue : NSValue {
struct TypeInfo : Equatable {
let size : Int
let name : String
init?(objCType spec: String) {
var size: Int = 0
var align: Int = 0
var count : Int = 0
var type = _NSSimpleObjCType(spec)
guard type != nil else {
print("NSConcreteValue.TypeInfo: unsupported type encoding spec '\(spec)'")
return nil
}
if type == .StructBegin {
fatalError("NSConcreteValue.TypeInfo: cannot encode structs")
} else if type == .ArrayBegin {
let scanner = Scanner(string: spec)
scanner.scanLocation = 1
guard scanner.scanInt(&count) && count > 0 else {
print("NSConcreteValue.TypeInfo: array count is missing or zero")
return nil
}
guard let elementType = _NSSimpleObjCType(scanner.scanUpToString(String(_NSSimpleObjCType.ArrayEnd))) else {
print("NSConcreteValue.TypeInfo: array type is missing")
return nil
}
guard _NSGetSizeAndAlignment(elementType, &size, &align) else {
print("NSConcreteValue.TypeInfo: unsupported type encoding spec '\(spec)'")
return nil
}
type = elementType
}
guard _NSGetSizeAndAlignment(type!, &size, &align) else {
print("NSConcreteValue.TypeInfo: unsupported type encoding spec '\(spec)'")
return nil
}
self.size = count != 0 ? size * count : size
self.name = spec
}
}
private static var _cachedTypeInfo = Dictionary<String, TypeInfo>()
private static var _cachedTypeInfoLock = NSLock()
private var _typeInfo : TypeInfo
private var _storage : UnsafeMutableRawPointer
required init(bytes value: UnsafeRawPointer, objCType type: UnsafePointer<Int8>) {
let spec = String(cString: type)
var typeInfo : TypeInfo? = nil
NSConcreteValue._cachedTypeInfoLock.synchronized {
typeInfo = NSConcreteValue._cachedTypeInfo[spec]
if typeInfo == nil {
typeInfo = TypeInfo(objCType: spec)
NSConcreteValue._cachedTypeInfo[spec] = typeInfo
}
}
guard typeInfo != nil else {
fatalError("NSConcreteValue.init: failed to initialize from type encoding spec '\(spec)'")
}
self._typeInfo = typeInfo!
self._storage = UnsafeMutableRawPointer.allocate(bytes: self._typeInfo.size, alignedTo: 1)
self._storage.copyBytes(from: value, count: self._typeInfo.size)
}
deinit {
// Cannot deinitialize raw memory.
self._storage.deallocate(bytes: self._size, alignedTo: 1)
}
override func getValue(_ value: UnsafeMutableRawPointer) {
value.copyBytes(from: self._storage, count: self._size)
}
override var objCType : UnsafePointer<Int8> {
return NSString(self._typeInfo.name).utf8String! // XXX leaky
}
override var classForCoder: AnyClass {
return NSValue.self
}
override var description : String {
let boundBytes = self.value.bindMemory(to: UInt8.self, capacity: self._size)
return Data(bytes: boundBytes, count: self._size).description
}
convenience required init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
guard let type = aDecoder.decodeObject() as? NSString else {
return nil
}
let typep = type._swiftObject
// FIXME: This will result in reading garbage memory.
self.init(bytes: [], objCType: typep)
aDecoder.decodeValue(ofObjCType: typep, at: self.value)
}
override func encode(with aCoder: NSCoder) {
guard aCoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
aCoder.encode(String(cString: self.objCType)._bridgeToObjectiveC())
aCoder.encodeValue(ofObjCType: self.objCType, at: self.value)
}
private var _size : Int {
return self._typeInfo.size
}
private var value : UnsafeMutableRawPointer {
return self._storage
}
private func _isEqualToValue(_ other: NSConcreteValue) -> Bool {
if self === other {
return true
}
if self._size != other._size {
return false
}
let bytes1 = self.value
let bytes2 = other.value
if bytes1 == bytes2 {
return true
}
return memcmp(bytes1, bytes2, self._size) == 0
}
override func isEqual(_ value: Any?) -> Bool {
guard let other = value as? NSConcreteValue else { return false }
return self._typeInfo == other._typeInfo && self._isEqualToValue(other)
}
override var hash: Int {
return self._typeInfo.name.hashValue &+
Int(bitPattern: CFHashBytes(self.value.assumingMemoryBound(to: UInt8.self), self._size))
}
}
internal func ==(x : NSConcreteValue.TypeInfo, y : NSConcreteValue.TypeInfo) -> Bool {
return x.name == y.name && x.size == y.size
}