Skip to content

နိုင်ငံခြား Function Interfaces များ (FFI)

Rust မှ C ABI ချည်နှောင်မှုများကို ထုတ်လုပ်ရန်အတွက် iroha_ffi သေတ္တာသည် မက်ကရိုများနှင့် အရည်အသွေးများကို ထောက်ပံ့ပေးသည်။ APIs မှ Iroha အမျိုးအစားများသည် FFI နယ်နိမိတ်ကို ဖြတ်ကျော်ရန် လိုအပ်ပါက အသုံးပြုသည်။ ဥပမာအားဖြင့် SDK ချည်နှိုင်းမှုများ သို့မဟုတ် အိမ်ရှင်ပေါင်းစပ်ခြင်းတို့ဖြင့်။

ဘာကြောင့်လဲ FFI

လုပ်ဆောင်ချက်တစ်ခုဟာ တော်တော်လေး စိတ်ကူးယဉ်တဲ့ အဖွဲ့အစည်းတစ်ခုဖြစ်ပြီး ဘာသာစကားအများစုက လုပ်ဆောင်မှုတစ်ခု ဘာလုပ်သင့်တယ်ဆိုတာ သဘောတူပေမဲ့ လုပ်ဆောင်ချက်တွေကို ကိုယ်စားပြုပုံက အရမ်းခြားနားပါတယ်။ ထို့အပြင် Rust ကဲ့သို့သော ဘာသာစကားတစ်ချို့တွင် function ကိုခေါ်ဆိုခြင်း၏ အကျိုးဆက်များနှင့် လုပ်ဆောင်ခွင့်ရှိသောအရာများသည်လည်းကွဲပြားခြားနားသည်။ Rust APIs ကို အခြားဘာသာစကား (သို့) မတူညီသော host ပတ်ဝန်းကျင်မှခေါ်ဆိုရန်လိုအပ်ပါက၊ Iroha သည် ကစားကွင်းကို တန်းတူစေရန် နိုင်ငံခြား လုပ်ဆောင်ချက် ကြားခံ (FFI) ကို အသုံးပြုသည်။

ယနေ့အသုံးပြုသော အဓိကစံညွှန်းက C application binary interface ဖြစ်သည်။ ၎င်းသည်ရိုးစင်း၊ အများပြည်သူရရှိနိုင်ပြီး တည်ငြိမ်သည်။ အခြေခံအားဖြင့်သင်ဟာလက်နဲ့အကုန်လုံးကိုလုပ်နိုင်ပေမဲ့ Iroha သည်တည်ရှိနေတဲ့ Rust API မှ FFI ကိုက်ညီတဲ့ လုပ်ဆောင်ချက်များကိုထုတ်လုပ်ရန် iroha_ffi သေတ္တာကိုပေးသည်။

iroha_ffi သေတ္တာက ဘယ်လိုပဲဖြစ်ဖြစ် သင်ဖန်တီးဖို့လိုတဲ့ ကုဒ်ကို ထုတ်ပေးရုံပါ။ လိုအပ်တဲ့ ဘိုလီးယားပြားကို ရေးသားဖို့ ကြိုးစားမှုနဲ့ စည်းကမ်းမှု အများကြီးလိုပါတယ်။ FFI နယ်နိမိတ်ပေါ်က လုပ်ဆောင်ချက်ခေါ်ဆိုမှုတိုင်းဟာ unsafe ဖြစ်ပြီး မသတ်မှတ်ထားတဲ့ အပြုအမူကိုဖြစ်စေဖို့ အလားအလာရှိပါတယ်။ ဒါကို ဖြေရှင်းနိုင်ခဲ့တဲ့နည်းလမ်းက ခိုင်မာတဲ့ repr(C) အမျိုးအစားတွေကို အသုံးပြုခြင်းနဲ့ လည်ပတ်ပါတယ်။

INFO

ခြွင်းချက်တစ်ခုတည်းက ညွှန်ပြသူတွေပါ။ အလွတ်စစ်ဆေးမှုနဲ့ သက်တမ်းကို ကမ္ဘာတစ်ဝှမ်းမှာ အကောင်အထည်ဖော်လို့မရတော့ ရေနံညွှန်ပြသူတွေ (အမြဲလိုပဲ) ဟာ ထူးခြားတဲ့ ကိစ္စတွေမှာသာ အသုံးပြုပါတယ်။ Iroha ဒေတာမော်ဒယ်ထဲက အရာဝတ္ထုရဲ့ နမူနာတိုင်းနီးပါးကို ဖုံးအုပ်ပေးတာကြောင့် ဆ raw pointers ကို လုံးဝသုံးစရာ မလိုပါဘူး။

နမူနာ

ဒီမှာ ချည်နှောင်မှုတစ်ခု ဖန်တီးခြင်းရဲ့ နမူနာတစ်ခုပါ။

rust
#[derive(FfiType)]
struct DaysSinceEquinox(u32);

#[ffi_export]
impl DaysSinceEquinox {
    pub fn update_value(&mut self, a: &u8) {
        self.0 = *a as u32;
    }
}

အထက်ပါဥပမာက DaysSinceEquinox ကို ပွင့်လင်းမြင်သာမှုမရှိတဲ့ ညွှန်ပြသူအဖြစ် ကိုယ်စားပြုထားတဲ့ အောက်ပါ ချည်နှောင်မှုကို ဖြစ်ပေါ်စေမှာပါ။

rust
pub extern fn DaysSinceEquinox__update_value(handle: *mut DaysSinceEquinox, a: *const u8) -> FfiReturn {
    // function implementation
}

FFI ချုပ်ဆိုမှုမျိုးဆက်

iroha_ffi သေတ္တာကို FFI မှတစ်ဆင့်ခေါ်ဆိုနိုင်သော လုပ်ဆောင်ချက်များကို ထုတ်လုပ်ရန် အသုံးပြုသည်။ Rust ပုံစံများနှင့် နည်းစနစ်များအားပေးလျှင် ချိတ်ဆက်မှု နယ်နိမိတ်ကို ဖြတ်ကျော်ရန်အတွက် လိုအပ်သည့် unsafe ကုဒ်ကို ထုတ်ပေးပါသည်။

A ကို Rust Type ကို ခိုင်မာတဲ့ repr(C) Type က cross လုပ်နိုင်တယ် FFI နယ်စပ် FfiType::into_ffi. ဒါကလည်း ဆန့်ကျင်ဘက်ပါ။ FFI ReprC Type ကို convert လုပ်လိုက်တယ် Rust အမျိုးအစား via FfiType::try_from_ffi.

WARNING

ဆန့်ကျင်ဘက် ပြောင်းလဲမှုဟာ မှားယွင်းနိုင်ပြီး မသတ်မှတ်တဲ့ အပြုအမူကို ဖြစ်စေနိုင်တာကို သတိထားပါ။ ထင်ရှားဆုံး အမှားတွေကို ရှောင်ရှားဖို့ အကောင်းဆုံးကြိုးပမ်းနိုင်ပေမဲ့ သင်ဟာ သင့်ရဲ့ အဆုံးမှာ ပရိုဂရမ်ရဲ့ မှန်ကန်မှုကို သေချာအောင်လုပ်ဖို့လိုတယ်။

ReprC, FfiType နှင့် FfiConvert တို့သည် ချည်နှောင်မှုထုတ်လုပ်မှုကို ဖြစ်ပေါ်စေသော အဓိကလက္ခဏာဖြစ်သည်။

လက္ခဏာများသရုပ်ဖော်ချက်
ReprCဒီလက္ခဏာက C ABI ကို လိုက်နာတဲ့ ခိုင်မာတဲ့ အမျိုးအစားကို ကိုယ်စားပြုပါတယ်။ ဒီအမျိုးအစားကို FFI နယ်နိမိတ်တွေကြားမှာ ဘေးကင်းစွာ မျှဝေနိုင်တာပါ။
FfiTypeဤလက္ခဏာသည် Rust အမျိုးအစားတစ်ခုအတွက် ကိုက်ညီသော ReprC အမျိုးအစားကိုသတ်မှတ်သည်။ သတ်မှတ်ထားသော ReprC အမျိုးအစားကိုထုတ်လုပ်သည့် FFI လုပ်ဆောင်ချက်၏ API တွင် Rust အမျိုးအစားအစားအစား အစားထိုး၍ အသုံးပြုသည်။
FfiConvertဒီလက္ခဏာက into_ffi နှင့် try_from_ffi အမျိုးအစားနှစ်ခုကို သတ်မှတ်ထားပြီး Rust အမျိုးအစားကို ReprC အမျိုးအစားသို့ (သို့မဟုတ်) မှ ပြောင်းဖို့ အသုံးပြုတယ်။

FFI အပေါ် ပိုင်ဆိုင်မှု လွှဲပြောင်းခြင်းမရှိကြောင်း သတိပြုပါ၊ မရှင်းလင်းတဲ့ ညွှန်ပြသူအမျိုးအစားတွေမှလွဲရင် အခြားပိုင်ဆိုင်မှုကို ဆောင်ရွက်တဲ့ အမျိုးအစားအားလုံး၊ ဥပမာ Vec<T> ကို ခလုန်းလုပ်ထားတာပါ။

နာမည် Mangling

FFI အရာဝတ္ထုများအတွက် generated names များတွင် double underscores ကို အသုံးပြုမှုကို သတိပြုပါ-

  • StructName structure တွင် သတ်မှတ်ထားသော inherent_fn နည်းအတွက် FFI အမည်သည် StructName__inherent_fn ဖြစ်ရမည်။

  • StructName struct ထဲက TraitName လက္ခဏာမှ MethodName နည်းအတွက် FFI နာမည်ကို StructNameTraitNameMethodName လို့ခေါ်ပါတယ်။

  • StructName structure တွင် field_name field ကို သတ်မှတ်ရန်အတွက် FFI function နာမည်ကို StructName__set_field_name ဟု သတ်မှတ်ရမည်။

  • StructName structure ထဲက field_name ကွင်းကိုရဖို့ FFI function နာမည်က StructName__field_name ဖြစ်လိမ့်မယ်။

  • StructName structure ထဲမှာ ပြောင်းလဲနိုင်တဲ့ field_name ကွင်းကိုရဖို့ FFI function နာမည်က StrucuName__field_name_mut ဖြစ်လိမ့်မယ်။

  • လွတ်လပ်စွာ ရပ်တည်နေသော module_name::fn_name အတွက် FFI နာမည်ကို module_name::__fn_name ဟု သတ်မှတ်ထားသည်။

  • FFI (အောက်ပါ Clone ကိုကြည့်ပါ) တွင် အကောင်အထည်ဖော်မှုကို မျှဝေခွင့်ပြုသည့် ယေဘုယျမဟုတ်သော လက္ခဏာများအတွက် FFI အမည်သည် module_name::__clone ဖြစ်သည်။

    rust
    impl Clone for Type1 {
        fn clone(&self) -> Self;
    }
    impl Clone for Type2 {
        fn clone(&self) -> Self;
    }