スウィフトとC:前後

こんにちは!



iOSのタスクが割り当てられると、特定の暗号化を備えたVPNクライアントになります。



私たちの会社は伝統的に独自の暗号化を持っており、Cには既製の実装があります。



この記事では、CとSwiftの間でどうやって友達を作ることができたかをお話します。



わかりやすくするために、例として、Cで文字列を変換し、Swiftから呼び出すための簡単な関数を記述します。



このようなタスクの主な問題は、パラメーターの受け渡しと戻り値です。それらについて話しましょう。関数を持ってみましょう:



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;
}


この関数は、反転するバイトの配列と文字列の長さへのポインタを取ります。結果のバイト配列へのポインタを返します。 mallocを覚えておいてください。実行し、書き留め、戻ります。



関数のタイトルを使用してMyCFile.hを作成します。これと同じMyCFile.hが接続されているBridging-Header.hを追加します。



さらなるタイプキャスト。簡単なものから始めましょう-intはInt32です。次に、ポインタ。 swiftにはいくつかの指針があります。 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}


指定された文字列からUInt8配列(つまり1バイト)を作成します。特定のサイズのデータ​​へのポインタを作成します。示します。電話して、何がゼロでないか見てください。



そして、渡されたポインタですべてが単純に見える場合、resは現在UnsafeMutablePointer?タイプです。 StackOverflowをバイパスして数回クリックするだけで、pointeeプロパティが見つかります。このプロパティを使用すると、このポインタでメモリにアクセスできます。このプロパティを使用して「qwerty」という単語を拡張しようとすると、... Badum-ts ...「121」になります。さて、ドラムロールは不要ですが、結果は私が望むものではありません。



しかし、あなたがそれについて考えるならば、すべてが論理的です。 Swiftでは、res(C関数が返した)はInt8の配列へのポインターです。古くから、ポインタは配列の最初の要素を指しています。そそそ。 ASKIIテーブルに登ります。 121は文字「y」のコードです。一致?そうは思いません。 1文字がカウントされました。



さらに、古いSishの伝統によれば、ポインターをシフトして配列を調べ、次のバイトを取得できます。



let p = res+1
print(p.pointee)


したがって、116を取得します。これはコード「t」です。



理論的には、割り当てられたメモリのサイズがわかっていれば、このように続けることができます。そして、このメモリはCコード内に割り当てられます。



私たちの場合、問題はありませんが、もう少し深刻なプログラムでは、いじくり回す必要があります。それが私がしたことです。



解決策は古き良きC構造の形で私に来ました。



計画は次のとおりです。構造を作成し、反転した文字列とサイズを対応するフィールドにコピーして、この構造へのポインターを返します。



struct FlipedStringStructure {
    void *result;
    int resultSize;
};


次のように関数を書き直してみましょう。



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;
}


構造と文字列の両方にメモリを割り当てることに注意してください。



さて、課題を書き直すことは残っています。私たちは自分の手に従います。




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
}


引き続きpointeeプロパティを使用しますが、Cコードで作成した構造の最初のタイプ要素を取得します。このアイデアの優れている点は、コードのC部分で宣言されているデータ型を、不要なキャストなしで参照できることです。最初の部分はすでに分解されています。さらにステップ:Cで入力された構造へのポインターを取得します(structPointer)。



この構造のメモリにアクセスできます。構造にはデータとデータサイズがあります。



それらは、(ドットを介して)迅速に作成された構造のフィールドとして参照できます。



これからバイトの配列(データ)を収集し、それを文字列にデコードします。さて、私たち自身の後に片付けることを忘れないでください。2つの関数を作成します。




void freeMemmory(void* s){
    free(s);
}
void freeSMemmory(struct FlipedStringStructure* s){
    free(s);
}


これらの関数がSwiftから呼び出されると、受信したポインターまたは構造からのポインターのいずれかがパラメーターとしてそれらに渡されます。



freeMemmory(tmp.result)
freeSMemmory(structPointer)


そして出来上がり-それはバッグに入っています!



もちろん、このアプローチに新しいことは何もありませんが、クロスプラットフォーム機能を積極的に操作することができ、非常に便利です。



それを読んだ人たちに感謝します。



gitのプロジェクトへのリンク-ここでは

EOF



All Articles