MessagePack (msgpack-rust)
シリアライゼーションライブラリ
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エコシステムとの互換性がない
参考ページ
- GitHubリポジトリ: https://github.com/3Hren/msgpack-rust
- ドキュメント: https://docs.rs/rmp
- MessagePack公式サイト: https://msgpack.org/
- Serde統合: https://docs.rs/rmp-serde
書き方の例
基本的な使用方法(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(())
}