import Foundation /// Old→new token-id remap for the EN+ZH pruned models. /// /// The `*-en_zh-*` Core ML models were built on a sliced word-embedding matrix /// (only ASCII/CJK tokens + specials kept). The app must therefore translate the /// full-vocabulary XLM-R ids produced by the tokenizer into the pruned id space /// before feeding the model; dropped tokens (`-1`) become ``. /// /// Special ids 0/1/2/3 (``/``/``/``) map to themselves, so the /// same table can be applied uniformly to every id in a window, including the /// padding and sentinel tokens. struct VocabRemap { private let table: [Int32] // table[oldId] = newId, or -1 if dropped let unkNewId: Int32 /// Layout (little-endian): magic "WTPR", u32 count, then `count` × i32. init(url: URL) throws { let data = try Data(contentsOf: url, options: .mappedIfSafe) var table = [Int32]() try data.withUnsafeBytes { (raw: UnsafeRawBufferPointer) in guard raw.count >= 8 else { throw KitError.corruptResource("en_zh_remap.bin too small") } let magic = raw.loadUnaligned(fromByteOffset: 0, as: UInt32.self) guard magic == 0x52505457 else { throw KitError.corruptResource("bad remap magic") } // "WTPR" let count = Int(UInt32(littleEndian: raw.loadUnaligned(fromByteOffset: 4, as: UInt32.self))) guard raw.count >= 8 + count * 4 else { throw KitError.corruptResource("remap truncated") } table = [Int32](repeating: 0, count: count) for k in 0.. old id is 3; its mapped value is the pruned unk id. self.unkNewId = (3 < table.count && table[3] >= 0) ? table[3] : 3 } @inline(__always) func map(_ oldId: Int32) -> Int32 { guard oldId >= 0 && Int(oldId) < table.count else { return unkNewId } let v = table[Int(oldId)] return v >= 0 ? v : unkNewId } }