2015年10月10日 星期六

Rust 的 C 語言界面:struct 和 enum

對於打算使用新語言的人來說,能否容易地使用 C 語言大量的函式庫是選擇語言的重要指標。

使用 C 的函式庫,簡單分兩個層次:
  1. 呼叫 C 的函式,以及提供 callback
  2. 使用 C 的資料結構。
過往的經驗告訴我,麻煩在第二點。不同語言的資料結構採用不同的記憶體 layout,因此很難直接使用他種語言的資料結構。於是常要先撰寫一堆存取資料結構的小函式,再透過這些函式來使用資料結構。

這假日我好好研究了這個問題,驚奇發現 Rust 是僅次於 C++,我用過最容易和 C 界接的語言。以下以我最近處理的一個 struct 裡面有 struct,還有 enum,更有陣列的資料結構的簡化版,說明如何做出有同樣 layout 的 Rust 資料結構。

#define EC_MAX_STRING_LENGTH 64
#define EC_MAX_PORTS 4

typedef enum {
    EC_PORT_NOT_IMPLEMENTED, 
    EC_PORT_NOT_CONFIGURED, 
    EC_PORT_EBUS, 
    EC_PORT_MII 
} ec_slave_port_desc_t;

typedef struct {
    uint8_t link_up; 
    uint8_t loop_closed; 
    uint8_t signal_detected; 
} ec_slave_port_link_t;

typedef struct {
    uint16_t position; 
    uint32_t vendor_id; 
    uint32_t product_code; 
    struct {
        ec_slave_port_desc_t desc; 
        ec_slave_port_link_t link; 
        uint32_t receive_time; 
        uint16_t next_slave; 
    } ports[EC_MAX_PORTS]; 
    uint8_t error_flag; 
    char name[EC_MAX_STRING_LENGTH]; 
} ec_slave_info_t;

以下使用 Rust 做出在記憶體 layout 一模一樣的資料結構:

// 首先要使用 libc 中提供的 C 的型別。
// int  對應 c_int
// uint 對應 c_uint
// char 對應 u8
// int16_t, uint16_t 等直接對應 int16_t 及 uint16_t。

extern crate libc;
use libc::{c_uint, c_int, int16_t, uint8_t, uint16_t, uint32_t};

const EC_MAX_PORTS: usize = 4;
const EC_MAX_STRING_LENGTH: usize = 64;

// 在 enum 前加上 #[repr(C)],告知 Rust 採用 C 的 layout。

#[repr(C)]
pub enum ec_slave_port_desc_t {
    EC_PORT_NOT_IMPLEMENTED = 0,
    EC_PORT_NOT_CONFIGURED,
    EC_PORT_EBUS,
    EC_PORT_MII,
}

#[repr(C)]
pub struct ec_slave_port_link_t {
    link_up: uint8_t,
    loop_closed: uint8_t,
    signal_detected: uint8_t,
}

// 對於 C 結構中的陣列,就使用 Rust 的陣列取代。
#[repr(C)]
pub struct ec_slave_info_t {
    pub position: uint16_t,
    pub vendor_id: uint32_t,
    pub product_code: uint32_t,
    ports: [ec_slave_port_t; EC_MAX_PORTS],
    al_state: uint8_t,
    error_flag: uint8_t,
    name: [u8;EC_MAX_STRING_LENGTH],
}

我產生上列程式的組合語言列表,確定它和 C 語言的記憶體配置一模一樣。 比較麻煩的是給預設值初始化時,這部份以後有機會再說明。

2015年1月31日 星期六

BotBone: Reactive image with D3.js

假日中為我們的 BotBone 計畫設計一款互動式界面。我先以手機拍照,以 Inkscape 將照片內嵌到 SVG 檔中。再以 D3.js 加上互動元件。

初步成果如下圖:
這工作耗費我兩天的時間,接下來就要讀取 BotBone 的 I/O,並依據實體 I/O 的狀態,動態地改變圖片上的互動元件。

未來希望能將這類工作簡化。最好一小時就能完成。

2015年1月5日 星期一

Debian 下的 PostgreSQL

開始建公司網頁。決定採用 Yahoo 的 Isomorphic React 作為前後端的架構。資料庫則使用剛出來的 PostgreSQL 9.4。

我使用 Debian 7,閱讀 /usr/share/doc/postgresql-9.4/README.Debian.gz 讓我很快能建立一個 role 及一個 database。但是建立出來的 role 沒有自己的 password。因此還是乖乖地閱讀 PostgreSQL 文件,並上網查資料。以下是簡單的報告。

首先先依 README.Debian 建立 role 和 database:
$sudo -u postgres bash
$createuser -DRS joe
$createdb -O joe joework
當執行 psql 時,
$psql -U joe -W joework
因為沒有 joe 的 password,所以無法進入。 因為 createuser 時未給 password。
重新來過:
$dropdb joework
$dropuser joe
$createuser -DPRS joe
$createdb -O joe joework
當執行 psql 時,
$psql -U joe -W joework
psql: FATAL:  Peer authentication failed for user "joe"
修改 /etc/postgresql/9.4/main/pg_hba.conf 中的 Authentication method:
# "local" is for Unix domain socket connections only
#local   all             all                                     peer
local   all             all                                     md5
重新啟動 postgresql,因為我未使用 systemd,因此執行:
$sudo service postgresql restart
再次執行 psql,
$psql -U joe -W joework
成功。