「`repeated` プロトコルバッファフィールドの逐次的エンコードとデコード」

2026/04/05 21:46

「`repeated` プロトコルバッファフィールドの逐次的エンコードとデコード」

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

要約

この記事では、プロトコルバッファのワイヤーフォーマットを直接扱うことで、大規模な Perfetto トレースファイルをストリームエンコードおよびデコードする方法について説明しています。これにより、トレース全体をメモリに読み込むことなく、継続的なディスクI/Oが可能になります。

Trace
メッセージは
packet = 1
の繰り返しフィールドであり、各パケットには
TracePacket
が格納されています。バイナリエンコーディングでは、各パケットの先頭にキー バイト
0x0A
(フィールド 1、ワイヤータイプ 2 – 長さ限定)が配置され、その後に埋め込みメッセージが何バイトで構成されるかを示す varint 長さが続きます。プロトコルバッファの基本概念―varint エンコードとキー計算
(field_number << 3) | wire_type
、および四つのワイヤータイプ(0:varint、1:64ビット固定長、2:長さ限定、5:32ビット固定長)―を使用してこれらのキーと長さを解析します。
記事では、Rust のコードスニペット (
encode_varint
append_trace_packet
decode_next_trace_packet
) を示し、パケットデータの前にキー/長さペアを書き込む方法と、ストリームからそれらを読み取る方法を説明しています。また、プロトコルバッファメッセージは自己完結型ではないため、単純にバイト
0x0A
を検索しても十分ではなく、埋め込みデータ内にその値が含まれる可能性があることにも触れています。
このように各パケットを手動でフレーミングすることで、開発者はパケットを増分的に追加・読み取りでき、大きなメモリ構造を避けつつ、ギガバイト規模の Perfetto トレースを効率的に処理できます。

本文

「repeated」フィールドの逐次エンコード・デコード


目次

  • 動機:Perfettoとの連携
  • 課題:順序付きエンコードとデコード
  • Protobuf のワイヤーフォーマット
    • VARINTs
    • 基本的なメッセージ構造
    • ネストされたメッセージ
    • repeated フィールド
  • 全体像の整理
    • 逐次シリアライズ/追加
    • 逐次デシリアライズ
    • 完全なサンプル

本記事は、Google のオープンソース Protobuf スイートに関するユーザー向けの基礎知識を前提としています。簡潔に言うと、Protobuf は

.proto
ファイルでメッセージ群を定義し、コンパイラ(または代替実装)が多くの言語用にエンコーダ・デコーダを生成します。すべて共通のバイナリ「ワイヤ」フォーマットを共有しています。


動機:Perfettoとの連携

Perfetto のトレースビューアは、Protobuf で保存された時間ベースのトレースを可視化します。1 つのトレースは、

TracePacket
メッセージが長い列として並ぶ構造です。各
TracePacket
は、トラックとそのトラック上のイベントを記述します。例:

message TracePacket {
  optional uint64 timestamp = 8;
  oneof data {
    TrackEvent      track_event      = 11;
    TrackDescriptor track_descriptor = 60;
    // …
  }
}

課題:順序付きエンコードとデシリアライズ

CircumSpect でトレースを生成すると、

TracePacket
の数が数百万に達し、ギガバイト級のデータになります。Perfetto はすべてのパケットを単一の
Trace
メッセージにまとめ、repeated フィールドとして保持します:

message Trace {
  repeated TracePacket packet = 1;
}

Protobuf のシリアライズ形式は「ゼロコピー」ではなく、まずメモリ上で構造体を作成し、それからワイヤフォーマットへ変換する必要があります。非常に大きなトレースの場合、これは膨大なメモリを消費します。

理想的には以下のようにしたい:

  1. パケットが生成され次第、ディスクへ書き込む(ストリーミング出力)。
  2. 全体のトレースを読み込まずに個々のパケットを処理する(ストリーミング入力)。

ほとんどの Protobuf ライブラリはこの機能を直接サポートしていません。


Protobuf のワイヤーフォーマット

VARINTs

Protobuf で最も重要なのが可変長整数(「varint」)です。小さい数値は 1 バイト、より大きいものは複数バイトで表現されます。例として

42
(
0x2a
) は 1 バイトの
0x2a
に、
255
(
0xff
) は 2 バイト
0xff 0x01
になります。

基本的なメッセージ構造

メッセージは key‑value ペアの連続です。key はフィールド番号とワイヤタイプをエンコードします:

(key = (field_number << 3) | wire_type)

ワイヤタイプ(wire type)は以下の通りです。

ID
0varint (int32, int64, uint32 等)
164‑bit 固定長(fixed64, double)
2長さ付き(string, bytes, 埋め込みメッセージ、packed repeated)
532‑bit 固定長(fixed32, float)

key 自体も varint でエンコードされます。

message Msg { uint64 data = 1; }

{data: 42}
のエンコード:

key   : (1 << 3) | 0 = 0x08   → 0x08
value : 42                   → 0x2a

結果は

0x08 0x2a

ネストされたメッセージ

埋め込みメッセージは wire type 2 を使用します。key の後に varint 長さが付き、続いて子メッセージのエンコードが入ります。

例:

message Parent { Msg child = 1; }

{child: {data: 42}}
のエンコード:

key   : (1 << 3) | 2 = 0x0a   → 0x0a
len   : 2                    → 0x02
child : 0x08 0x2a             → 0x08 0x2a

結果は

0x0a 0x02 0x08 0x2a

repeated フィールド

スカラーの repeated は packed(wire type 2)でエンコードされます。埋め込みメッセージの repeated は単に key‑value ペアを繰り返すだけです。

例:

message MultiParent {
  repeated Msg children = 1;
}

{data:42}
を 2 個持つ場合:

0x0a 0x02 0x08 0x2a   (最初の子)
0x0a 0x02 0x08 0x2a   (次の子)

全体像の整理

Trace
は「packet = 1」という repeated 埋め込みメッセージフィールドです。したがって各
TracePacket
のエンコードは以下のようになります:

  1. 0x0a
    – フィールド 1、wire type 2 の key。
  2. パケット長を varint で表す。
  3. パケット自身の Protobuf エンコード。

この構造により ストリーミング が可能です:パケットをエンコードし、key と長さを書き込み、ファイルへ追記します。同様に、最初に key と長さを解析すれば 1 パケットずつ読み込めます。


逐次シリアライズ/追加

以下は Rust(

prost
)の擬似コードで、既存の Trace バッファにパケットを追記する例です:

/// u64 を Protobuf varint としてエンコード。
fn encode_varint(mut v: u64, buf: &mut Vec<u8>) {
    loop {
        let septet = (v & 0x7F) as u8;
        v >>= 7;
        let ctrl_bit = if v == 0 { 0 } else { 0x80 };
        buf.push(septet | ctrl_bit);
        if v == 0 { return; }
    }
}

/// 既にエンコード済みの Trace バッファへ TracePacket を追加。
fn append_trace_packet(pckg: &TracePacket, buf: &mut Vec<u8>) -> anyhow::Result<()> {
    encode_varint(0x0A, buf);                     // key
    encode_varint(pckg.encoded_len() as u64, buf); // 長さ
    pckg.encode(buf)?;                            // パケットデータ
    Ok(())
}

逐次デシリアライズ

トレースバッファから1パケットを読み取るには、まず key と長さを解析します:

fn consume_byte(buf: &mut &[u8]) -> Option<u8> {
    if buf.is_empty() { return None; }
    let (byte, rest) = buf.split_at(1);
    *buf = rest;
    Some(byte[0])
}

fn decode_varint(buf: &mut &[u8]) -> anyhow::Result<u64> {
    let mut v = 0u64;
    let mut offs = 0usize;
    loop {
        let byte = consume_byte(buf).ok_or_else(|| anyhow!("Unterminated varint"))?;
        let septet = (byte & 0x7F) as u64;
        let ctrl_bit = byte & 0x80;
        v |= septet << offs;
        offs += 7;
        if ctrl_bit == 0 { break; }
        if offs >= 70 { return Err(anyhow!("Varint too long")); }
    }
    Ok(v)
}

fn decode_next_trace_packet(buf: &mut &[u8]) -> anyhow::Result<(TracePacket, usize)> {
    let len_orig = buf.len();

    // key
    let key = decode_varint(buf)?;
    if key != 0x0A { return Err(anyhow!("Incorrect Key Byte")); }

    // 長さ
    let len = decode_varint(buf)?;
    if len == 0 { return Err(anyhow!("Incorrect Len (zero)")); }
    if len > buf.len() as u64 { return Err(anyhow!("Incorrect Len (more than remaining bytes)")); }

    // パケットデータ
    let (packet_buf, rest) = buf.split_at(len as usize);
    *buf = rest;
    let pkt = TracePacket::decode(&mut packet_buf[..])?;
    if !packet_buf.is_empty() { return Err(anyhow!("Incorrect Len (bytes left after decode)")); }

    Ok((pkt, len_orig - buf.len()))
}

完全なサンプル

完全に動作する例(テスト付き)は同梱リポジトリで確認できます。ここでは

TracePacket
オブジェクトをファイルへ逐次書き込みし、メモリ上に全体のトレースを保持せずに 1 パケットずつ読み返す方法を示しています。

同じ日のほかのニュース

一覧に戻る →

2026/04/10 4:48

macOSでのネイティブ即時空間切り替え (Note: The phrase “Instant Space Switching” refers to the feature that allows users to switch between virtual desktops or spaces instantly. In Japanese, it is often rendered as 「即時スペース切り替え」.)

## Japanese Translation: ## Summary 著者は、macOS が即時の Space 変更をサポートしない理由を説明し、Apple の唯一の組み込み回避策である「Reduce motion」が単に問題をフェードアニメーションに置き換えるだけでなく、ブラウザの prefers‑reduced‑motion 設定もトリガーすることを指摘しています。他の解決策には欠点があり、yabai は即時切替を可能にしますが System Integrity Protection (SIP) を無効化し、タイル方式を学ぶ必要があります。FlashSpace と AeroSpace はネイティブではなく不要な複雑さを追加します。BetterTouchTool は「Move Left/Right Space (Without Animation)」機能を提供しますが、有料ライセンスのみで利用可能です。 著者は **InstantSpaceSwitcher**(GitHub: jurplel/InstantSpaceSwitcher)というメニューバーアプリを発見しました。このアプリは SIP を無効にせずに即時の Space 変更を実現します。高速トラックパッドスワイプをシミュレートし、コマンドラインインターフェース(`ISSCli`)を通じてユーザーが直接特定のスペース番号へジャンプできるようにします。 **インストール手順:** ``` git clone https://github.com/jurplel/InstantSpaceSwitcher cd InstantSpaceSwitcher ./build.sh ``` ビルド後、`.build/release/ISSCli --help` を実行して `[left|right|index <n>]` などのコマンドを確認してください。リポジトリは GitHub スターが1つしかありません。著者は役立った場合にスターを付けるよう読者に奨励しています。 別プロジェクト **instantspaces** は yabai のスイッチャーを分離しようとしますが、著者の macOS Tahoe では機能しませんでした。読者は新しい macOS リリースで InstantSpaceSwitcher または instantspaces が成功したかどうか報告することが推奨されています。

2026/04/10 5:12

チャルクテリー – 視覚的類似性を探るUnicodeエクスプローラ

## Japanese Translation: **改訂要約** Charcuterie は Unicode 文字セットのウェブベースのビジュアルエクスプローラーで、ユーザーがグリフを閲覧し、関連するシンボル、スクリプト、および形状を発見できるツールです。各グリフは描画され、ベクトル空間に埋め込まれ、類似度計算を用いて視覚的に似ている文字をグループ化します―すべてブラウザ内で完結します。このプロジェクトは積極的に開発中です。ユーザーからのフィードバックを歓迎し、継続的な改善を支援するための寄付も奨励しています。© 2026 David Aerne.

2026/04/10 5:10

Gemini の SynthID 検出機構のリバース エンジニアリング

## 日本語訳: > **概要:** > 本プロジェクトは、Google の Gemini が生成するすべての画像に埋め込む Spectral SynthID ウォーターマークを検出・除去する高忠実度手法を示しています。ウォーターマークを逆解析した結果、解像度依存性(キャリア周波数が画像サイズとともに変化)であること、および Gemini モデルごとに固定の位相テンプレートが存在し、クロスイメージ位相共鳴率が 99.5 % 超、黒/白検証時には |cos(phase_diff)| が 0.90 を超えることが判明しました。 > 研究者は、解像度ごとにキャリアプロファイルを保存するマルチレゾリューション SpectralCodebook(V3)を構築し、バイパスアルゴリズムではチャネル重み G=1.0、R=0.85、B=0.70 を用いて 3 回のパス(攻撃的 → 中程度 → 穏やか)で位相整合性付きの加重減算を行います。これによりキャリアエネルギーが約 75.8 % 減少し、トップ5 キャリア位相共鳴率が 91.4 % 降低され、1536×2816 画像で PSNR >43 dB、SSIM ≈0.997 の視覚品質を保持します。これは以前の V1(JPEG Q50、37 dB)や V2(27–37 dB)の試みと比べて著しい改善です。 > リポジトリにはコードブック構築用スクリプト (`synthid_bypass.py build-codebook`)、V3 バイパス実行、ウォーターマーク検出用 (`robust_extractor.py detect`) が含まれています。貢献者は、Nano Banana Pro を使用して様々な解像度で純黒(#000000)と純白(#FFFFFF)の画像を生成し、コードブックをさらに充実させることが推奨されています。 > すべてのツールは学術研究専用に公開されており、SynthID は依然として Google DeepMind の独自技術です。 この改訂版は主要ポイントを網羅し、根拠のない推測を排除し、読者向けに明確で簡潔な概要を提示しています。

「`repeated` プロトコルバッファフィールドの逐次的エンコードとデコード」 | そっか~ニュース