Is there any way to know how Swift calculates hashValue? - swift

I recently found that the code below ends up with hash collision.
FYI, I'm using XCode 9.4.1 (9F2000), which uses Swift 4.1.2
import Foundation
let lhs = "あいうえおあいう21あいうえ"
let rhs = "あいうえおあいう22あいうえ"
let percentEncodedLhs = lhs.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
let percentEncodedRhs = rhs.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
let lhsHashValue = lhs.hashValue
let rhsHashValue = rhs.hashValue
let lhsPercentHashValue = percentEncodedLhs.hashValue
let rhsPercentHashValue = percentEncodedRhs.hashValue
print(lhsHashValue == rhsHashValue)
print(lhsPercentHashValue == rhsPercentHashValue)
/*
Output:
false
true
*/
I know that hash collision can happen in some circumstance, but I couldn't find how Swift calculates hashValue for String.
For example, Java calculates hashCode of String like:
https://docs.oracle.com/javase/6/docs/api/java/lang/String.html#hashCode()
Is there any official explanation or maybe some assumptions?

but I couldn't find how Swift calculates hashValue for String
Why couldn’t you? Swift is open source. If you are interested, read the source.
https://github.com/apple/swift/blob/111499d2bfc58dc12fcb9cd1ce1dda7978c995b7/stdlib/public/core/StringHashable.swift

Related

List all window names in Swift

I’m learning Swift. How do I fix the following code to list the window names?
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
if let window = CFArrayGetValueAtIndex(windows, i) {
print(CFDictionaryGetValue(window, kCGWindowName))
}
}
The error:
main.swift:6:32: error: cannot convert value of type 'UnsafeRawPointer' to expected argument type 'CFDictionary?'
print(CFDictionaryGetValue(window, kCGWindowName))
^~~~~~
as! CFDictionary
It becomes easier if you avoid using the Core Foundation types and methods, and bridge the values to native Swift types as early as possible.
Here, CGWindowListCopyWindowInfo() returns an optional CFArray of CFDictionaries, and that can be bridged to the corresponding Swift type [[String : Any]]. Then you can access its values with the usual Swift methods (array enumeration and dictionary subscripting):
if let windowInfo = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as? [[ String : Any]] {
for windowDict in windowInfo {
if let windowName = windowDict[kCGWindowName as String] as? String {
print(windowName)
}
}
}
You can use unsafeBitCast(_:to:) to convert the opaque raw pointer to a CFDictionary. Note that you'll also need to convert the second parameter, to a raw pointer:
CFDictionaryGetValue(unsafeBitCast(window, to: CFDictionary.self), unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self))
unsafeBitCast(_:to:) tells the compiler to treat that variable as another type, however it's not very safe (thus the unsafe prefix), recommending to read the documentation for more details, especially the following note:
Warning
Calling this function breaks the guarantees of the Swift type system; use with extreme care.
In your particular case there should not be any problems using the function, since you're working with the appropriate types, as declared in the documentation of the Foundation functions you're calling.
Complete, workable code could look something like this:
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
let windowDict = unsafeBitCast(CFArrayGetValueAtIndex(windows, i), to: CFDictionary.self)
let rawWindowNameKey = unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self)
let rawWindowName = CFDictionaryGetValue(windowDict, rawWindowNameKey)
let windowName = unsafeBitCast(rawWindowName, to: CFString?.self) as String?
print(windowName ?? "")
}
Update
You can bring the CoreFoundation array sooner to the Swift world by casting right from the start:
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID) as? [[AnyHashable: Any]]
windows?.forEach { window in
print(window[kCGWindowName])
}
The code is much readable, however it might pose performance problems, as the cast to [[AnyHashable: Any]]` can be expensive for large array consisting of large dictionaries.

Calling getsectiondata from Swift

This question and answer describe how to read data from a Mach-O section with Objective-C on modern OS X/macOS versions: Crash reading bytes from getsectbyname
The described answer works. I'm trying to implement the same thing with Swift. I can't make it work.
I have the following in "Other linker flags": -Wl,-sectcreate,__LOCALIZATIONS,__base,en.lproj/Localizable.strings,-segprot,__LOCALIZATIONS,r,r.
This Swift code gets me the a pointer to the embedded data, until I try to run the code outside Xcode and ASLR breaks it:
var size: UInt = 0
let _localizationSection = getsectdata(
"__LOCALIZATIONS",
"__base",
&size)
To get around the ASLR problem, according to the above question and answer, and based on my own testing, I should be using getsectiondata instead. It works great in Objective-C, but I'm having no luck in Swift. The following is the only thing I've managed to get past the compiler, but it returns nil:
var size: UInt = 0
var header = _mh_execute_header
let localizationSection = getsectiondata(
&header,
"__LOCALIZATIONS",
"__base",
&size)
Is taking a copy of _mh_execute_header the problem and is there any way to avoid it? I need an UnsafePointer<mach_header_64>, but using &_mh_execute_header as the first parameter to getsectiondata causes a compilation error.
I'm using Swift 3.0, and running my code on macOS 10.12.
The difference between the linked-to Objective-C code
void *ptr = getsectiondata(&_mh_execute_header, ...);
and your Swift translation
var header = _mh_execute_header
let localizationSection = getsectiondata(&header, ...)
is that the latter passes the address of a copy of the global
_mh_execute_header variable to the function, and apparently that
is not accepted. If you modify the Objective-C code to
struct mach_header_64 header = _mh_execute_header;
void *ptr = getsectiondata(&header, ...);
then it fails as well (and actually crashed in my test).
Now the problem is that _mh_execute_header is exposed to Swift
as a constant:
public let _mh_execute_header: mach_header_64
and one cannot take the address of a constant in Swift. One possible
workaround is to define
#import <mach-o/ldsyms.h>
static const struct mach_header_64 *mhExecHeaderPtr = &_mh_execute_header;
in the bridging header file, and then use it as
let localizationSection = getsectiondata(mhExecHeaderPtr, ...)
in Swift.
Another option is to lookup the symbol via dlopen/dlsym
import MachO
if let handle = dlopen(nil, RTLD_LAZY) {
defer { dlclose(handle) }
if let ptr = dlsym(handle, MH_EXECUTE_SYM) {
let mhExecHeaderPtr = ptr.assumingMemoryBound(to: mach_header_64.self)
var size: UInt = 0
let localizationSection = getsectiondata(
mhExecHeaderPtr,
"__LOCALIZATIONS",
"__base",
&size)
// ...
}
}

Xcode 8.0 and Swift 3.0 conversion: Looking for explanation for a particular conversion error

I am a little confused about a conversion error.
I migrated my project form Swift 2.3 to Swift 3.0
func updateCelsiusLabel() {
if let value = celsiusValue {
//This was the original code (that worked but is) failing after migration
//due to: Argument labels do not match any available overloads
celsiusLabel.text = numberFormatter.string(from: NSNumber(value))
//This is my code trying to fix this issue and the project is now compiling
//and everything is fine
celsiusLabel.text = numberFormatter.string(from: value as NSNumber)
}
else { celsiusLabel.text = "???"
}
}
At first I thought that in Swift 3.0 the cast Type(value) was now forbidden, but I checked and I get absolutely no compiler warning. Can somebody tell me what the problem with NSNumber(value) is?
As far as I understand value as NSNumber and NSNumber(value) should be the same thing.
In Swift 3, NSNumber(value) won't work. Let's say that your value is an Int. In that case, you'd need NSNUmber(value: yourIntValue). In Swift 3, you must have the name of the first (and in this case the only) parameter in the function call. So, your usage of
value as NSNumber
works, but
NSNumber(value: yourNumberValue)
works too.
First of all I have taken some assumption here, I have assumed that -
numberFormatter = NSNumberFormatter() // Now it has been renamed to NumberFormatter
celsiusLabel.text I am taking text as optional string just for example you can use label.text for same.
After the above assumption please see below code which will work in Swift 3 -
var celsiusValue:Double?
var numberFormatter = NumberFormatter()
var text:String?
func updateCelsiusLabel() {
if let value = celsiusValue {
//This was the original code (that worked but is) failing after migration due to: Argument labels do not match any available overloads
text = numberFormatter.string(from: NSNumber(value: value))!
}
else {
text = "???"
}
}
Hope it help feel free to leave comment in case you have any doubt.

Using IBM Swift Sandbox and rangeOfCharacterFromSet

I am trying swift on IBM's new Swift online Sandbox.
The following script is not running on that sandbox: http://swiftlang.ng.bluemix.net/
import Foundation
func palindromTest(s: String) -> Bool{
let lower = s.lowercaseString
let letters = NSCharacterSet.letterCharacterSet()
let onlyLetters = lower.characters.filter({String($0).rangeOfCharacterFromSet(letters) != nil})
let reverseLetters = Array(onlyLetters).reverse()
return String(onlyLetters) == String(reverseLetters)
}
palindromTest("abc")
The sandbox prints the following error message:
/swift-execution/code-tmp.swift:7:48: error: value of type 'String' has no member 'rangeOfCharacterFromSet'
let onlyLetters = lower.characters.filter({String($0).rangeOfCharacterFromSet(letters) != nil})
Did I forget to import something? Hope you can help me.
Thanks.
rangeOfCharacterFromSet is one of the many String methods
which are actually handled by NSString.
From this commit
to NSString.swift, it seems that support for rangeOfCharacterFromSet
was added only recently to the (non-Apple) Foundation library,
so you cannot use it until the IBM Swift Sandbox is updated to use
a new Swift version.
FWIW, this works now in the Sandbox swiftlang.ng.bluemix.net/#/repl/aae5d1caf4e0a6232ff428c3a0160e6e98cba6ed913ce9176f2baee46d30cb1c
Lots of things have changed since these answers were given. I've updated Pat's Sandbox answer for Swift 3.1.1. Below is the code and a summary of the API changes.
Several methods in the standard library and Foundation have since been renamed:
Array: .reverse() → .reversed()
String: .rangeOfCharacterFromSet() → .rangeOfCharacter(from:)
(NS)CharacterSet: .lettersCharacterSet() → .letters
I also made a slight change, perhaps just from personal preference. Rather than use String.rangeOfCharacter(from:) I chose to use CharacterSet.contains(), which required I use String.unicodeScalars instead of .characters. Not sure about the performance tradeoffs in general, but I liked how cleaned things up.
import Foundation
func palindromTest(_ word: String) -> Bool{
let letters = CharacterSet.letters
let onlyLetters = word.lowercased().unicodeScalars.filter {
letters.contains($0)
}.map { Character($0) }
let reverseLetters = onlyLetters.reversed()
print("\(String(onlyLetters)) == \(String(reverseLetters))")
return String(onlyLetters) == String(reverseLetters)
}
print(palindromTest("abc"))
// false
print(palindromTest("dud"))
// true

Swift 2 #convention use error

I have a problem with Swift 2, I'm not able to convert this part of my code:
let intTran = UnsafeMutablePointer<Int>(bitPattern: -1)
let tranPointer = COpaquePointer(intTran)
let transient = CFunctionPointer<((UnsafeMutablePointer<()>) -> Void)>(tranPointer)
I receive this error:
'CFunctionPointer' is unavailable: use a function type '#convention(c) (T) -> U'
I am trying to change it for this way:
let intTran = UnsafeMutablePointer<Int>(bitPattern: -1)
let tranPointer = COpaquePointer(intTran)
typealias transient2 = #convention(c) (UnsafeMutablePointer<()>) -> Void
But I am not able to use transient2 and I am not sure why.
Than you
I have been playing around with your code inside a Swift playground and noticed that this piece of code (your substitution to the original one) fails due to the bitPattern when it's set to -1 (notice that I've been getting a runtime exception - not a compiler error). When I replaced it with something appropriate (e.g. with 1, as bitPattern must represent a valid address inside you virtual address space and, as implied, must be a non-negative integer) it worked out. I managed to make it up and running with the following code:
import Foundation
let intTran = UnsafeMutablePointer<Int>(bitPattern: 1)
let tranPointer = COpaquePointer(intTran)
typealias transient2 = #convention(c) (UnsafeMutablePointer<()>) -> Void
let abc: transient2? = nil
Good luck, hope this solves your problem.