RustのCrate調査 (bitflags, byteorder, chrono, encoding_rs, num_cpus)

Rustのコードをコンパイルしていると、直接的には使っていないですが、依存関係のためにダウンロードされてくるcrateをよく見ます。

そういうcrateの中には便利なものもあるだろうと思い、いくつかのcrateの調査をしてみました。

今回は、bitflags、byteorder、chrono、encoding_rs、num_cpusの5つについて調べました。

コンパイラのバージョンは「rustc 1.33.0」です。

bitflags : 定数として使うビットフラグを簡単に構築

bitflagは、Linux環境のファイルパーミッションのような、ビットフラグを定義するのを助けるcrateです。

実際にサンプルコードとして、ファイルパーミッションのようなビットフラグを定義してみました。

crateのバージョンは、「bitflags = "1.0.4"」です。

use bitflags::bitflags;

bitflags! {
    struct FileFlags: u32 {
        const EXEC = 0b00000001;
        const WRITE = 0b00000010;
        const READ = 0b00000100;
        const RWX = Self::READ.bits | Self::WRITE.bits | Self::EXEC.bits;
    }
}

pub fn run() {
    println!("##### bitflags #####");
    let file_flags = FileFlags::READ | FileFlags::WRITE; // OR
    println!("OR: {:?}", file_flags);
    let file_flags = FileFlags::READ & FileFlags::WRITE; // AND
    println!("AND: {:?}", file_flags);
    let file_flags = FileFlags::RWX - FileFlags::WRITE; // Sub
    println!("Sub: {:?}", file_flags);
    let file_flags = FileFlags::RWX - FileFlags::WRITE - FileFlags::WRITE; // Sub2
    println!("Sub2: {:?}", file_flags);
}

出力は次のようになります。

##### bitflags #####
OR: WRITE | READ
AND: (empty)
Sub: EXEC | READ
Sub2: EXEC | READ

引き算の演算子は、きちんと内部でビット演算しているので、2回引いてもおかしなビットにならないようです。

byteorder : バイト列のエンディアンを指定

byteorderはデータを扱う際に、リトルエンディアンかビッグエンディアンかを指定することができます。

crateのバージョンは、「byteorder = "1.3.1"」です。

use byteorder::{ReadBytesExt, BigEndian, LittleEndian};

pub fn run() {
    println!("##### byteorder #####");
    let bytes: Vec<u8> = vec![0, 0, 255, 255];
    println!("Big Endian: {}", (&bytes[0..4]).read_u32::<BigEndian>().unwrap());
    println!("Little Endian: {}", (&bytes[0..4]).read_u32::<LittleEndian>().unwrap());
}

出力は次のようになります。

##### byteorder #####
Big Endian: 65535
Little Endian: 4294901760

ビッグエンディアンは、バイトの上位から読んで、65535=0x0000ffff。

トルエンディアンは、バイトの下位から読んで、16842752=0xffff0000。

今回はデータをu32で読み取る(read_u32)ことをしていますが、他の型(u16、u64など)のための読み込み関数も各種用意されており、それらはbyteorder::ReadBytesExtで定義されています。

また、書き込み用のbyteorder::WriteBytesExtも定義されています。

ネットで検索していたところ、PNG画像のデータをBigEndianで読み取る場面で使用している例がありました。データ構造によっては、エンディアンが指定されているものもあるのかもしれませんね。

chrono : 時間・日付を操作を便利に

chronoは、UTCと現地時間の変換や、文字列から時間データへの変換などの操作が簡単にできます。

crateのバージョンは、「chrono = "0.4.6"」です。

use chrono::prelude::*;

pub fn run() {
    println!("##### chrono #####");
    let utc: DateTime<Utc> = Utc::now();
    println!("UTC: {}", utc);
    println!("Convert from UTC to Local Timezone: {}", utc.with_timezone(&Local));
    println!("Convert from UTC to Unix Timestamp: {}", utc.timestamp());
    println!("");

    let local: DateTime<Local> = Local::now();
    println!("Local: {}", local);
    println!("Convert from Local Timezone to UTC: {}", local.with_timezone(&Utc));
    println!("Convert from Local to Unix Timestamp: {}", local.timestamp());
    println!("");

    let dt = Utc.timestamp(1_500_000_000, 0);
    println!("Convert from Unix Timestamp: {}", dt);
    println!("RFC3339: {}", dt.to_rfc3339());
    println!("");

    if let Ok(dt) = "2014-11-28T12:00:09Z".parse::<DateTime<Utc>>() {
        println!("Convert from String: {}", dt);
    }
}

出力は次のようになります。

##### chrono #####
UTC: 2019-03-30 12:53:56.797502255 UTC
Convert from UTC to Local Timezone: 2019-03-30 21:53:56.797502255 +09:00
Convert from UTC to Unix Timestamp: 1553950436

Local: 2019-03-30 21:53:56.797587758 +09:00
Convert from Local Timezone to UTC: 2019-03-30 12:53:56.797587758 UTC
Convert from Local to Unix Timestamp: 1553950436

Convert from Unix Timestamp: 2017-07-14 02:40:00 UTC
RFC3339: 2017-07-14T02:40:00+00:00

Convert from String: 2014-11-28 12:00:09 UTC

encoding_rs : Webブラウザでも使われているエンコーディング機能を提供

encoding_rsは、エンコーディングのためのcrateです。

GitHubのREADMEによると、レンダリングエンジン「Gecko」でも使われているようです。

crateのバージョンは、「encoding_rs = "0.8.17"」です。

use encoding_rs::*;

pub fn run() {
    println!("##### encoding_rs #####");
    let bytes = b"\x83n\x83\x8D\x81[\x81E\x83\x8F\x81[\x83\x8B\x83h"; // SJIS

    let (cow, encoding_used, had_errors) = SHIFT_JIS.decode(bytes);
    println!("bytes: {:?}", bytes);
    println!("cow: {}", cow);
    println!("encoding: {:?}", encoding_used);
    println!("error: {}", had_errors);
}

出力は次のようになります。

##### encoding_rs #####
bytes: [131, 110, 131, 141, 129, 91, 129, 69, 131, 143, 129, 91, 131, 139, 131, 104]
cow: ハロー・ワールド
encoding: Encoding { Shift_JIS }
error: false

Shift JISの文字列が変換されてプリントできるようになりました。

Webから取得した文字列には、様々なエンコーディングのものもあるので、文字列を変換したいときに重宝しそうです。

num_cpus : CPUの数を取得

num_cpusは、その名の通りCPUの数を取得できるcrateです。

use num_cpus;

pub fn run() {
    println!("##### num_cpus #####");
    println!("num of cpus: {}", num_cpus::get());
    println!("num of physical core: {}", num_cpus::get_physical());
}

出力は次のようになります。

##### num_cpus #####
num of cpus: 4
num of physical core: 2

実際に計算できるCPUのスレッド数と、物理的なCPUのコア数が、取得できます。

Rustでコマンドラインツールを作っている人も多いと思いますが、重い処理の高速化を図るためにマルチスレッドにしている場合は、この値を参考にしてスレッドの数を調節できそうですね。

まとめ

今回は、bitflags、byteorder、chrono、encoding_rs、num_cpusの5つについて調べました。

また、他のcrateについても調べていこうと思います。