rust-pinyin icon indicating copy to clipboard operation
rust-pinyin copied to clipboard

cffi draft api

Open hanabi1224 opened this issue 5 years ago • 4 comments

https://github.com/mozillazg/rust-pinyin/issues/26

hanabi1224 avatar May 11 '19 03:05 hanabi1224

Coverage Status

Coverage decreased (-8.6%) to 72.35% when pulling d2a977c3be644c361fde037195554ee5285eadce on hanabi1224:cffi into 1c75807006bfc45a03d42a5084001593a3be7ae5 on mozillazg:develop.

coveralls avatar May 11 '19 04:05 coveralls

👍

mozillazg avatar May 11 '19 13:05 mozillazg

include/pinyin.h

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

struct PinYinList {
    size_t len;
    char** ptr;
};

struct PinYinListGroup {
    size_t len;
    struct PinYinList* ptr;
};

// 普通风格,不带声调(默认风格)。如: `pin yin`
uint8_t NORMAL_STYLE       = 0;
// 声调风格1,拼音声调在韵母第一个字母上。如: `pīn yīn`
uint8_t TONE_STYLE         = 1;
// 声调风格2,即拼音声调在各个拼音之后,用数字 [0-4] 进行表示。如: `pi1n yi1n`
uint8_t TONE2_STYLE        = 2;
// 声母风格,只返回各个拼音的声母部分。如: 中国 的拼音 `zh g`
uint8_t INITIALS_STYLE     = 3;
// 首字母风格,只返回拼音的首字母部分。如: `p y`
uint8_t FIRST_LETTER_STYLE = 4;
/// 韵母风格1,只返回各个拼音的韵母部分,不带声调。如: `ong uo`
uint8_t FINALS_STYLE       = 5;
// 韵母风格2,带声调,声调在韵母第一个字母上。如: `ōng uó`
uint8_t FINALS_TONE_STYLE  = 6;
// 韵母风格2,带声调,声调在各个拼音之后,用数字 [0-4] 进行表示。如: `o1ng uo2`
uint8_t FINALS_TONE2_STYLE = 7;


void pinyin_free(struct PinYinListGroup list) {
    for (size_t i=0; i<list.len; i++) {
        struct PinYinList pinyin_list = *(list.ptr + i);
        for (size_t idx=0; idx<pinyin_list.len; idx++) {
            char* ptr = *(pinyin_list.ptr + idx);
            free(ptr);
        }
        free(pinyin_list.ptr);
    }
    free(list.ptr);
}

struct PinYinListGroup pinyin_query(unsigned char* input, uint8_t style, bool heteronym);

src/cffi.rs

use crate::{ Args, Style, pinyin, };

use std::ffi::{ CStr, CString, };


#[derive(Debug)]
#[repr(C)]
pub struct PinYinList {
    pub len: libc::size_t,
    pub ptr: *mut *mut libc::c_char,
}

#[derive(Debug)]
#[repr(C)]
pub struct PinYinListGroup {
    pub len: libc::size_t,
    pub ptr: *mut *mut PinYinList,
}

impl PinYinListGroup {
    pub fn empty() -> PinYinListGroup {
        let len = 0;
        let ptr = std::ptr::null_mut();

        PinYinListGroup { len, ptr, }
    }
    
    pub fn as_ptr(&mut self) -> *const PinYinListGroup {
        self
    }

    pub fn as_mut_ptr(&mut self) -> *mut PinYinListGroup {
        self
    }
}

#[no_mangle]
pub extern "C" fn pinyin_query(input: *const libc::c_char,
                                style: Style,
                                heteronym: bool) -> PinYinListGroup {
    let args = Args { style: style, heteronym: heteronym, };
    
    unsafe { CStr::from_ptr(input) }.to_str()
        .ok()
        .map(|s| {
            let mut result = 
                pinyin(s, &args)
                    .iter()
                    .map(|elems| {
                        let len = elems.len();
                        let mut items = elems.iter()
                                    .map(|elem| CString::new(elem.as_str()).unwrap().into_raw() )
                                    .collect::<Vec<*mut libc::c_char>>();
                        let ptr = items.as_mut_ptr();
                        
                        std::mem::forget(items);

                        PinYinList { len, ptr, }
                    })
                    .collect::<Vec<PinYinList>>();
            let len = result.len();
            let ptr = result.as_mut_ptr();

            std::mem::forget(result);

            PinYinListGroup { len, ptr: ptr as _, }
        })
        .unwrap_or(PinYinListGroup::empty())
}

example/cquery.c

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

#include "pinyin.h"

// 
// cargo build
// cc main.c -L ./target/debug/ -l pinyin -o main
// ./main

void query() {
    unsigned char* input = (unsigned char*)"扒手扒手扒手扒手扒手扒";
    uint8_t style = NORMAL_STYLE;
    bool heteronym = true;

    struct PinYinListGroup list = pinyin_query(input, style, heteronym);

    printf("Input: %s\n", input);
    printf("Params: { style: %d, heteronym: %s }\n", style, heteronym ? "true" : "false");
    printf("Output:\n");
    for (size_t i=0; i<list.len; i++) {
        struct PinYinList pinyin_list = *(list.ptr + i);

        printf("    ");
        for (size_t idx=0; idx<pinyin_list.len; idx++) {
            char* ptr = *(pinyin_list.ptr + idx);
            printf("%s, ", ptr);
        }
        printf("\n");
    }

    pinyin_free(list);
}

int main(int argc, char const *argv[]) {
    for (int i = 0; i < 10; ++i) {
        query();
    }

    printf("DONE.\n");

    return 0;
}

仅供参考 :)

LuoZijun avatar May 12 '19 08:05 LuoZijun

@hanabi1224

我提个建议,就是 C 函数前面最好能加上一个统一的命名空间(如 pinyin_xxx) :)

LuoZijun avatar May 20 '19 09:05 LuoZijun