MessagePack (msgpack-rust)

シリアライゼーションRustMessagePackバイナリフォーマット高速

シリアライゼーションライブラリ

MessagePack (msgpack-rust)

概要

msgpack-rustは、Rustでの効率的なMessagePackシリアライゼーションを提供する純粋なRust実装です。JSONライクなデータ構造をより高速でコンパクトなバイナリフォーマットで処理し、ゼロコピーデコーディングとno-std環境をサポートしています。安全性を重視した設計により、TDDとCIによる継続的な品質保証を実現しています。

詳細

MessagePackは、JSONより小さく高速なバイナリシリアライゼーションフォーマットです。msgpack-rustは、このフォーマットの高品質なRust実装を提供し、低レベルAPIから高レベルAPIまで、さまざまなユースケースに対応しています。

主な特徴:

  • ゼロコピーデコーディング: ヒープアロケーションなしでのデコーディング
  • 安全な実装: 純粋なRustコードによる安全なパース処理
  • no-std対応: 組み込み環境でも使用可能
  • 多階層API: 低レベルから高レベルまで柔軟なAPI設計
  • Serde統合: #[derive(Serialize, Deserialize)]による簡単な使用

技術的詳細:

  • 低レベルAPI: バイナリフォーマットの直接制御とヒープアロケーション回避
  • 高レベルAPI: 標準ライブラリとコンパイラリフレクションを活用
  • TDD開発: テスト駆動開発とCI/CDによる品質保証
  • エラーハンドリング: 到達不可能なvariantを含まないエラーシステム

パフォーマンス特性:

  • JSONより高速なエンコーディング/デコーディング
  • 小さなバイナリサイズ
  • 自己記述的で拡張可能なフォーマット
  • 64bit数値とバイナリデータの完全サポート

メリット・デメリット

メリット

  • JSONより高速でコンパクトなシリアライゼーション
  • ゼロコピーデコーディングによる高いパフォーマンス
  • 純粋なRustコードによる安全性保証
  • no-std環境での使用が可能
  • 豊富なAPI選択肢(低レベル〜高レベル)
  • 継続的な品質保証とメンテナンス

デメリット

  • JSONと比べて人間が読みにくい
  • デバッグ時のデータ確認が困難
  • バイナリフォーマットのため、テキストツールで編集不可
  • 既存のJSONエコシステムとの互換性がない

参考ページ

書き方の例

基本的な使用方法(Serde統合)

use serde::{Serialize, Deserialize};
use rmp_serde::{Serializer, Deserializer};

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Person {
    name: String,
    age: u32,
    email: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let person = Person {
        name: "Alice".to_string(),
        age: 30,
        email: "[email protected]".to_string(),
    };
    
    // MessagePackにシリアライズ
    let mut buffer = Vec::new();
    person.serialize(&mut Serializer::new(&mut buffer))?;
    
    // MessagePackからデシリアライズ
    let mut deserializer = Deserializer::new(&buffer[..]);
    let deserialized = Person::deserialize(&mut deserializer)?;
    
    assert_eq!(person, deserialized);
    println!("Serialized {} bytes", buffer.len());
    println!("Deserialized: {:?}", deserialized);
    
    Ok(())
}

低レベルAPIの使用

use rmp::{encode, decode};
use std::io::Cursor;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut buffer = Vec::new();
    
    // 数値の書き込み
    encode::write_u32(&mut buffer, 42)?;
    encode::write_str(&mut buffer, "Hello, MessagePack!")?;
    encode::write_f64(&mut buffer, 3.14159)?;
    
    // 配列の書き込み
    encode::write_array_len(&mut buffer, 3)?;
    encode::write_u8(&mut buffer, 1)?;
    encode::write_u8(&mut buffer, 2)?;
    encode::write_u8(&mut buffer, 3)?;
    
    // データの読み込み
    let mut cursor = Cursor::new(&buffer);
    let number = decode::read_u32(&mut cursor)?;
    let text = decode::read_str(&mut cursor, &mut [0u8; 32])?;
    let float = decode::read_f64(&mut cursor)?;
    
    let array_len = decode::read_array_len(&mut cursor)?;
    let mut array = Vec::new();
    for _ in 0..array_len {
        array.push(decode::read_u8(&mut cursor)?);
    }
    
    println!("Number: {}", number);
    println!("Text: {}", text);
    println!("Float: {}", float);
    println!("Array: {:?}", array);
    
    Ok(())
}

複雑なデータ構造の処理

use serde::{Serialize, Deserialize};
use rmp_serde::{encode, decode};
use std::collections::HashMap;

#[derive(Serialize, Deserialize, Debug)]
struct Config {
    server: ServerConfig,
    database: DatabaseConfig,
    features: HashMap<String, bool>,
}

#[derive(Serialize, Deserialize, Debug)]
struct ServerConfig {
    host: String,
    port: u16,
    max_connections: u32,
    timeout: f64,
}

#[derive(Serialize, Deserialize, Debug)]
struct DatabaseConfig {
    url: String,
    pool_size: u32,
    auto_migrate: bool,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut features = HashMap::new();
    features.insert("logging".to_string(), true);
    features.insert("metrics".to_string(), true);
    features.insert("debug".to_string(), false);
    
    let config = Config {
        server: ServerConfig {
            host: "0.0.0.0".to_string(),
            port: 8080,
            max_connections: 1000,
            timeout: 30.0,
        },
        database: DatabaseConfig {
            url: "postgres://localhost/myapp".to_string(),
            pool_size: 20,
            auto_migrate: true,
        },
        features,
    };
    
    // MessagePackにシリアライズ
    let serialized = encode::to_vec(&config)?;
    println!("Serialized {} bytes", serialized.len());
    
    // MessagePackからデシリアライズ
    let deserialized: Config = decode::from_slice(&serialized)?;
    println!("Config: {:?}", deserialized);
    
    Ok(())
}

ストリーム処理とゼロコピー

use rmp::{encode, decode};
use std::io::Cursor;

#[derive(Debug)]
struct SensorData<'a> {
    id: u32,
    name: &'a str,
    values: Vec<f64>,
}

fn serialize_sensor_stream(sensors: &[SensorData]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let mut buffer = Vec::new();
    
    // センサー数を書き込み
    encode::write_array_len(&mut buffer, sensors.len() as u32)?;
    
    for sensor in sensors {
        // 各センサーを配列として書き込み
        encode::write_array_len(&mut buffer, 3)?;
        encode::write_u32(&mut buffer, sensor.id)?;
        encode::write_str(&mut buffer, sensor.name)?;
        
        // 値の配列を書き込み
        encode::write_array_len(&mut buffer, sensor.values.len() as u32)?;
        for &value in &sensor.values {
            encode::write_f64(&mut buffer, value)?;
        }
    }
    
    Ok(buffer)
}

fn deserialize_sensor_stream(data: &[u8]) -> Result<Vec<SensorData>, Box<dyn std::error::Error>> {
    let mut cursor = Cursor::new(data);
    let mut sensors = Vec::new();
    
    let sensor_count = decode::read_array_len(&mut cursor)?;
    
    for _ in 0..sensor_count {
        let _field_count = decode::read_array_len(&mut cursor)?;
        let id = decode::read_u32(&mut cursor)?;
        
        // ゼロコピーで文字列を読み取り
        let mut name_buf = [0u8; 64];
        let name = decode::read_str(&mut cursor, &mut name_buf)?;
        
        let value_count = decode::read_array_len(&mut cursor)?;
        let mut values = Vec::with_capacity(value_count as usize);
        
        for _ in 0..value_count {
            values.push(decode::read_f64(&mut cursor)?);
        }
        
        sensors.push(SensorData { id, name, values });
    }
    
    Ok(sensors)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let sensors = vec![
        SensorData {
            id: 1,
            name: "Temperature",
            values: vec![23.5, 24.1, 22.9, 25.2],
        },
        SensorData {
            id: 2,
            name: "Humidity",
            values: vec![45.2, 47.8, 44.5, 46.1],
        },
    ];
    
    // シリアライズ
    let serialized = serialize_sensor_stream(&sensors)?;
    println!("Serialized {} bytes", serialized.len());
    
    // デシリアライズ
    let deserialized = deserialize_sensor_stream(&serialized)?;
    println!("Deserialized {} sensors", deserialized.len());
    
    for sensor in deserialized {
        println!("Sensor {}: {} = {:?}", sensor.id, sensor.name, sensor.values);
    }
    
    Ok(())
}

エラーハンドリングとカスタムタイプ

use serde::{Serialize, Deserialize};
use rmp_serde::{encode, decode};
use std::fmt;

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: u64,
    name: String,
    email: String,
    role: Role,
}

#[derive(Serialize, Deserialize, Debug)]
enum Role {
    Admin,
    User,
    Guest,
}

impl fmt::Display for Role {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Role::Admin => write!(f, "Admin"),
            Role::User => write!(f, "User"),
            Role::Guest => write!(f, "Guest"),
        }
    }
}

fn serialize_user(user: &User) -> Result<Vec<u8>, rmp_serde::encode::Error> {
    encode::to_vec(user)
}

fn deserialize_user(data: &[u8]) -> Result<User, rmp_serde::decode::Error> {
    decode::from_slice(data)
}

fn validate_user(user: &User) -> Result<(), String> {
    if user.name.is_empty() {
        return Err("Name cannot be empty".to_string());
    }
    
    if !user.email.contains('@') {
        return Err("Invalid email format".to_string());
    }
    
    Ok(())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let user = User {
        id: 1,
        name: "Alice".to_string(),
        email: "[email protected]".to_string(),
        role: Role::Admin,
    };
    
    // バリデーション
    validate_user(&user)?;
    
    // シリアライズ
    let serialized = serialize_user(&user)?;
    println!("Serialized {} bytes", serialized.len());
    
    // デシリアライズ
    let deserialized = deserialize_user(&serialized)?;
    println!("User: {} ({}) - Role: {}", 
             deserialized.name, deserialized.email, deserialized.role);
    
    Ok(())
}

パフォーマンスベンチマーク

use serde::{Serialize, Deserialize};
use rmp_serde::{encode, decode};
use std::time::Instant;

#[derive(Serialize, Deserialize, Debug, Clone)]
struct BenchmarkData {
    id: u64,
    values: Vec<f64>,
    metadata: String,
}

fn benchmark_msgpack(data: &BenchmarkData, iterations: usize) -> Result<(), Box<dyn std::error::Error>> {
    let start = Instant::now();
    
    for _ in 0..iterations {
        // シリアライズ
        let serialized = encode::to_vec(data)?;
        
        // デシリアライズ
        let _: BenchmarkData = decode::from_slice(&serialized)?;
    }
    
    let duration = start.elapsed();
    println!("MessagePack: {} iterations in {:?}", iterations, duration);
    println!("Average: {:?} per iteration", duration / iterations as u32);
    
    Ok(())
}

fn compare_sizes(data: &BenchmarkData) -> Result<(), Box<dyn std::error::Error>> {
    // MessagePackサイズ
    let msgpack_data = encode::to_vec(data)?;
    
    // JSONサイズ
    let json_data = serde_json::to_vec(data)?;
    
    println!("MessagePack size: {} bytes", msgpack_data.len());
    println!("JSON size: {} bytes", json_data.len());
    println!("Size reduction: {:.1}%", 
             (1.0 - msgpack_data.len() as f64 / json_data.len() as f64) * 100.0);
    
    Ok(())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data = BenchmarkData {
        id: 12345,
        values: (0..1000).map(|i| i as f64 * 0.1).collect(),
        metadata: "benchmark_data".repeat(10),
    };
    
    benchmark_msgpack(&data, 1000)?;
    compare_sizes(&data)?;
    
    Ok(())
}