Swift و 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 بعنوان وظيفتنا. أضف Bridging-Header.h ، حيث يتم توصيل MyCFile.h نفسه.



مزيد من النوع يلقي. لنبدأ بواحد بسيط - int هو Int32. ثم المؤشرات. هناك عدة مؤشرات في السرعة. نحن مهتمون بـ 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 (بحيث تكون بايت واحد) من السلسلة المحددة. نقوم بإنشاء مؤشر لبيانات ذات حجم معين. نشير. اتصل ، انظر ما هو ليس لا شيء.



وإذا كان كل شيء يبدو بسيطًا باستخدام المؤشر الذي تم تمريره ، فإن الدقة هي حاليًا من النوع UnsafeMutablePointer؟ في بضع نقرات ، تجاوز StackOverflow ، نجد خاصية Pointee . باستخدام هذه الخاصية ، يمكنك الوصول إلى الذاكرة من خلال هذا المؤشر. نحاول توسيع كلمة "qwerty" باستخدام هذه الخاصية ، وهناك ... Badum-ts ... "121". حسنًا ، لفة الأسطوانة غير ضرورية ، لكن النتيجة ليست ما أود الحصول عليه.



على الرغم من أنك إذا فكرت في الأمر ، فكل شيء منطقي. في Swift ، فإن الدقة (التي أرجعها الدالة C) هي مؤشر إلى مصفوفة من Int8. منذ العصور القديمة ، يشير المؤشر إلى العنصر الأول في المصفوفة. لذلك هكذا. نصعد إلى طاولة ASKII. 121 هو رمز الحرف "y". صدفة؟ لا أعتقد ذلك. تم عد شخصية واحدة.



علاوة على ذلك ، وفقًا لتقليد 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).



نتمكن من الوصول إلى ذاكرة هذا الهيكل. يحتوي الهيكل على حجم البيانات والبيانات.



يمكنك الإشارة إليهم على أنهم حقول هيكل تم إنشاؤه بسرعة (من خلال نقطة).



نجمع من هذا مجموعة من البايتات (البيانات) ، والتي نفك تشفيرها إلى سلسلة. حسنًا ، لا تنس أن تنظف بعد أنفسنا. نقوم بإنشاء وظيفتين:




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


عندما يتم استدعاء هذه الوظائف من Swift ، فإننا نمرر إما المؤشرات المستلمة أو المؤشرات من الهيكل كمعلمات.



freeMemmory(tmp.result)
freeSMemmory(structPointer)


وفويلا - إنها في الحقيبة!



بالطبع ، لا يوجد شيء جديد في هذا النهج ، لكنه يسمح لك بالعمل بنشاط مع وظائف عبر الأنظمة الأساسية وهو مناسب تمامًا.



شكرا لمن قرأها.



رابط إلى المشروع في بوابة - هنا

EOF



All Articles