Suatu ketika saya diberi tugas untuk iOS - klien VPN dengan kriptografi tertentu.
Perusahaan kami secara tradisional memiliki kriptografinya sendiri, ada implementasi siap pakai di C.
Pada artikel ini saya akan memberi tahu Anda bagaimana saya berhasil berteman antara C dan Swift.
Untuk kejelasan, sebagai contoh, kami akan menulis fungsi sederhana untuk mengonversi string di C dan memanggilnya dari Swift.
Kesulitan utama dalam tugas-tugas tersebut adalah melewatkan parameter dan mengembalikan nilai. Mari kita bicarakan tentang mereka. Mari kita punya fungsi:
uint8_t* flipString(uint8_t* str, int strlen){
uint8_t* result = malloc(strlen);
int i;
int j=0;
for(i = strlen-1; i>=0; --i){
result[j] = str[i];
j++;
}
return result;
}
Fungsi ini membawa pointer ke array byte untuk dibalik dan panjang string. Kami mengembalikan pointer ke array byte yang dihasilkan. Ingat malloc. Jalankan melalui, tuliskan, kembali.
Kami membuat MyCFile.h dengan judul untuk fungsi kami. Tambahkan Bridging-Header.h, di mana MyCFile.h yang sama ini terhubung.
Jenis gips lebih lanjut. Mari kita mulai dengan yang sederhana - int adalah Int32. Lalu petunjuknya. Ada beberapa petunjuk cepat. Kami tertarik dengan UnsafeMutablePointer.
let str = "qwerty"
var array: [UInt8] = Array(str.utf8)
let stringPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: array.count)
stringPointer.initialize(from: &array, count: array.count)
guard let res = flipString(stringPointer, Int32(array.count)) else {return}
Buat array UInt8 (jadi satu byte) dari string yang diberikan. Kami membuat penunjuk ke data dengan ukuran tertentu. Kami menunjukkan. Sebut, lihat apa yang tidak nihil.
Dan jika semuanya tampak sederhana dengan penunjuk yang diteruskan, maka res saat ini adalah Jenis UnsafeMutablePointer? Dalam beberapa klik, melewati StackOverflow, kami menemukan properti pointee . Dengan menggunakan properti ini, Anda dapat mengakses memori dengan penunjuk ini. Kami mencoba memperluas kata "qwerty" dengan menggunakan properti ini, dan di sana ... Badum-ts ... "121". Oke, drum roll tidak berguna, tapi hasilnya tidak seperti yang saya inginkan.
Padahal, kalau dipikir-pikir, semuanya masuk akal. Di Swift, res kami (yang dikembalikan oleh fungsi C) adalah penunjuk ke array Int8. Sejak zaman kuno, penunjuk menunjuk ke elemen pertama dari array. Jadi begitu. Kami naik ke tabel ASKII. 121 adalah kode untuk huruf 'y'. Kebetulan? Saya kira tidak. Satu karakter dihitung.
Lebih jauh, menurut tradisi Sish lama, Anda dapat menelusuri array, menggeser penunjuk, dan mendapatkan byte berikut:
let p = res+1
print(p.pointee)
Jadi kami mendapatkan 116, yang merupakan kode 't'.
Secara teoritis, Anda dapat melanjutkan seperti ini jika Anda mengetahui ukuran memori yang dialokasikan. Dan memori ini dialokasikan di dalam kode C.
Dalam kasus kami, tidak ada masalah, tetapi dalam program yang sedikit lebih serius Anda harus mengutak-atik. Itulah yang saya lakukan.
Solusi datang kepada saya dalam bentuk struktur C tua yang baik.
Rencananya adalah sebagai berikut: buat struktur, salin string dan ukuran terbalik ke bidang yang sesuai, kembalikan penunjuk ke struktur ini.
struct FlipedStringStructure {
void *result;
int resultSize;
};
Mari tulis ulang fungsinya seperti ini:
struct FlipedStringStructure* flipStringToStruct(uint8_t* str, int strlen){
uint8_t* result = malloc(strlen);
int i;
int j=0;
for(i = strlen-1; i>=0; --i){
result[j] = str[i];
j++;
}
struct FlipedStringStructure* structure;
structure = malloc(sizeof(struct FlipedStringStructure));
structure->resultSize=j;
structure->result = malloc(j);
memcpy(structure->result,result,j);
free(result);
return structure;
}
Kami mencatat bahwa kami mengalokasikan memori untuk struktur dan string.
Nah - masih menulis ulang tantangannya. Kami mengikuti tangan kami.
func flip(str:String)->String?{
var array: [UInt8] = Array(str.utf8)
let stringPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: array.count)
stringPointer.initialize(from: &array, count: array.count)
let structPointer = flipStringToStruct(stringPointer, Int32(array.count))
guard structPointer != nil else{return nil}
let tmp = structPointer!.pointee
let res = Data(bytes: tmp.result, count: Int(tmp.resultSize))
let resStr = String(decoding: res, as: UTF8.self)
freeMemmory(tmp.result)
freeSMemmory(structPointer)
return resStr
}
Kami masih menggunakan properti pointee, tetapi sekarang kami mendapatkan anggota pertama dari tipe struktur yang kami buat dalam kode C. Keindahan dari idenya adalah kita bisa merujuk ke tipe data yang dideklarasikan di bagian C kode tanpa casting yang tidak perlu. Bagian pertama sudah dibongkar. Selanjutnya dalam langkah: Dapatkan penunjuk ke struktur yang diisi dalam C (structPointer).
Kami mendapatkan akses ke memori struktur ini. Struktur tersebut memiliki data dan ukuran data.
Anda dapat menyebutnya sebagai bidang struktur yang dibuat dengan cepat (melalui titik).
Kami mengumpulkan dari ini sebuah array byte (Data), yang kami decode menjadi String. Nah, jangan lupa bersih-bersih setelah diri kita sendiri. Kami membuat 2 fungsi:
void freeMemmory(void* s){
free(s);
}
void freeSMemmory(struct FlipedStringStructure* s){
free(s);
}
Saat fungsi ini dipanggil dari Swift, kami meneruskan baik pointer yang diterima atau pointer dari struktur ke mereka sebagai parameter.
freeMemmory(tmp.result)
freeSMemmory(structPointer)
Dan voila - ada di dalam tas!
Tentu saja, tidak ada yang baru dalam pendekatan ini, tetapi ini memungkinkan Anda untuk bekerja secara aktif dengan fungsi lintas platform dan cukup nyaman.
Terima kasih untuk yang membacanya.
Tautkan ke proyek di git - di sini
EOF