Created
February 17, 2019 10:31
-
-
Save lvsecoto/dcab1efd3b734499bb77abff14977ff3 to your computer and use it in GitHub Desktop.
计算Google翻译请求中的TK值 参考 https://github.com/guyrotem/google-translate-server/blob/master/scripts/hash/tk-hash.js Swift版本
This file contains 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
import Foundation | |
func hexCharAsNumber(_ char: String) -> Int64 { | |
assert(char.count == 1) | |
let c = Int64((char as NSString).character(at: 0)) | |
if c >= Int(("a" as NSString).character(at: 0)) { | |
return c - 87 | |
} else { | |
return Int64(char) ?? 0 | |
} | |
} | |
func normalizeHash(_ encondindRound2: Int64) -> Int64 { | |
var result = encondindRound2 | |
if (encondindRound2 < 0) { | |
result = (encondindRound2 & 0x7fffffff) + 0x80000000 | |
} | |
return result % 1000000 | |
} | |
func transformQuery(_ query: String) -> [Int64] { | |
var result = [Int64]() | |
var i = 0 | |
while i < query.count { | |
// for (int i = 0 i < query.length() i++) { | |
var c = Int64((query as NSString).character(at: i)) | |
if (c < 128) { | |
result.append(c) // 0{l[6-0]} | |
} else if (c < 2048) { | |
result.append(Int64(c >> 6 | 0xC0)) // 110{l[10-6]} | |
result.append(Int64(c & 0x3F | 0x80)) // 10{l[5-0]} | |
} else if (0xD800 == (c & 0xFC00) && c + 1 < query.count && 0xDC00 == ((query as NSString).character(at: i + 1) & 0xFC00)) { | |
// that's pretty rare... (avoid ovf?) | |
i = i + 1 | |
c = Int64((query as NSString).character(at: i)) | |
let pre = Int64((1 << 16) + ((c & 0x03FF) << 10)) | |
c = pre + (c & 0x03FF) | |
result.append(c >> 18 | 0xF0) // 111100{l[9-8*]} | |
result.append(c >> 12 & 0x3F | 0x80) // 10{l[7*-2]} | |
result.append(c & 0x3F | 0x80) // 10{(l+1)[5-0]} | |
} else { | |
result.append(c >> 12 | 0xE0) // 1110{l[15-12]} | |
result.append(c >> 6 & 0x3F | 0x80) // 10{l[11-6]} | |
result.append(c & 0x3F | 0x80) // 10{l[5-0]} | |
} | |
i = i + 1 | |
} | |
return result | |
} | |
func shiftLeftOrRightThenSumOrXor(_ opArray: [String], _ num: Int64) -> Int64 { | |
var result = num | |
for opString in opArray { | |
let op1 = opString[1] // '+' | '-' ~ SUM | XOR | |
let op2 = opString[0] // '+' | '^' ~ SLL | SRL | |
let xd = opString[2] // [0-9a-f] | |
let shiftAmount = hexCharAsNumber(xd) | |
let mask = (op1 == "+") ? result.unsignedRightShift(count: shiftAmount) : result << shiftAmount | |
result = (op2 == "+") ? ((result + mask) & Int64(0xffffffff)) : (result ^ mask) | |
} | |
return result | |
} | |
fileprivate extension Int64 { | |
func unsignedRightShift(count: Int64) -> Int64 { | |
return Int64(bitPattern: (UInt64(bitPattern: self) >> count)) | |
} | |
} | |
fileprivate extension String { | |
subscript (index: Int) -> String { | |
return String(self[self.index(self.startIndex, offsetBy: index)]) | |
} | |
} | |
// EXAMPLE: | |
// | |
// INPUT: query: 'hola', windowTkk: '409837.2120040981' | |
// OUTPUT: '70528.480109' | |
func calcHash(_ query: String, _ windowTkk: String) -> String { | |
// STEP 1: spread the the query char codes on a byte-array, 1-3 bytes per char | |
let bytesArray = transformQuery(query) | |
// STEP 2: starting with TKK index, add the array from last step one-by-one, and do 2 rounds of shift+add/xor | |
let d = windowTkk.split(separator: ".") | |
let tkkIndex = Int64(d[0]) ?? 0 | |
let tkkKey = Int64(d[1]) ?? 0 | |
var acc = tkkIndex | |
for current in bytesArray { | |
acc += current | |
acc = shiftLeftOrRightThenSumOrXor(["+-a", "^+6"], acc) | |
} | |
let encondingRound1 = acc | |
// STEP 3: apply 3 rounds of shift+add/xor and XOR with they TKK key | |
let encondingRound2 = shiftLeftOrRightThenSumOrXor(["+-3", "^+b", "+-f"], encondingRound1) ^ tkkKey | |
// STEP 4: Normalize to 2s complement & format | |
let normalizedResult = normalizeHash(encondingRound2) | |
return "\(normalizedResult).\(normalizedResult ^ tkkIndex)" | |
} |
This file contains 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
import XCTest | |
class TkCaculateUtilsTest: XCTestCase { | |
override func setUp() { | |
// Put setup code here. This method is called before the invocation of each test method in the class. | |
} | |
override func tearDown() { | |
// Put teardown code here. This method is called after the invocation of each test method in the class. | |
} | |
func testHexCharAsNumber() { | |
XCTAssertEqual(hexCharAsNumber("3"), 3); | |
XCTAssertEqual(hexCharAsNumber("a"), 10); | |
} | |
func testNormalizeHash() { | |
XCTAssertEqual(normalizeHash(8134240), 134240); | |
} | |
func testTransformQuery() { | |
XCTAssertEqual(transformQuery("123ABCabc我"), [49, 50, 51, 65, 66, 67, 97, 98, 99, 230, 136, 145]); | |
} | |
func testShiftLeftOrRightThenSumOrXor() { | |
XCTAssertEqual(shiftLeftOrRightThenSumOrXor(["+-a", "^+9"], 12), 12308); | |
} | |
func testCalcHash() { | |
XCTAssertEqual(calcHash("hola", "409837.2120040981"), "70528.480109"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment