# 機能設計書 53-Kernel#sprintf

## 概要

本ドキュメントは、mrubyにおける書式指定文字列によるフォーマット出力機能（Kernel#sprintf / Kernel#format / String#%）の設計を記述する。C言語のprintfに類似した書式指定によるフォーマット変換を提供する。

### 本機能の処理概要

Kernel#sprintfは書式指定文字列と引数を受け取り、フォーマットされた文字列を生成して返す。String#%はsprintf呼び出しの簡略記法を提供する。

**業務上の目的・背景**：ログ出力、デバッグメッセージ、ユーザ向けメッセージなど、数値や文字列を特定のフォーマットに整形して出力する場面は非常に多い。特に組み込みシステムでは、センサデータの表示やプロトコルメッセージの構築において書式指定出力が必要となる。

**機能の利用シーン**：数値のゼロ埋め出力、浮動小数点数の桁数指定、文字列の幅揃え出力、16進/8進/2進数への変換出力、名前付き引数によるテンプレート展開など。

**主要な処理内容**：
1. 書式指定文字列のパース（フラグ、幅、精度、変換指定子の解析）
2. 整数フォーマット（d, i, u, o, x, X, b, B）- 各基数への変換
3. 浮動小数点フォーマット（f, e, E, g, G）- mrb_format_floatを使用
4. 文字列フォーマット（s, p）- 文字列出力とinspect出力
5. 文字フォーマット（c）- 単一文字出力
6. 名前付き引数（<name>, {name}）- Hash/名前からの値取得

**関連システム・外部連携**：Stringクラス（String#%メソッド）、Numericクラス（数値変換）と連携する。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | ライブラリ機能であり、画面との直接的関連なし |

## 機能種別

文字列フォーマット処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| format_string | String | Yes | 書式指定文字列 | 有効なフォーマット指定子を含む文字列 |
| args | 可変長引数 | No | フォーマットに埋め込む値 | フォーマット指定子に対応する型 |

### 入力データソース

Rubyプログラムからの直接呼び出し。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 結果文字列 | String | フォーマットされた文字列 |

### 出力先

呼び出し元のRubyプログラムへの戻り値。

## 処理フロー

### 処理シーケンス

```
1. 書式指定文字列の走査開始
   └─ '%'以外の文字はそのまま出力バッファに追加
2. '%'を検出したらフォーマット指定子の解析開始
   └─ フラグ解析: ' ', '#', '+', '-', '0'
   └─ 幅指定: 数字または'*'（引数から取得）
   └─ 精度指定: '.'の後に数字または'*'
   └─ 名前付き引数: '<name>'または'{name}'
3. 変換指定子に基づく値の変換
   └─ 整数型: mrb_uint_to_cstrで基数変換
   └─ 浮動小数点型: fmt_floatで変換
   └─ 文字列型: to_sまたはinspect呼び出し
4. フラグに基づく修飾（ゼロ埋め、左寄せ、符号付き等）
5. 結果文字列の返却
```

### フローチャート

```mermaid
flowchart TD
    A[sprintf呼び出し] --> B[書式文字列を1文字ずつ走査]
    B --> C{現在の文字は%?}
    C -->|No| D[そのまま出力バッファに追加]
    C -->|Yes| E[get_fmt_specで指定子解析]
    E --> F{指定子の種別}
    F -->|FMT_INTEGER| G[整数変換]
    F -->|FMT_FLOAT| H[浮動小数点変換]
    F -->|FMT_STRING| I[文字列変換]
    F -->|FMT_CHAR| J[文字変換]
    F -->|FMT_LITERAL| K[リテラル出力]
    F -->|FMT_NAMED| L[名前付き引数]
    G --> M[幅/パディング/符号の修飾]
    H --> M
    I --> M
    J --> M
    L --> M
    M --> B
    D --> B
    K --> B
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-01 | フラグの優先順位 | `-`フラグは`0`フラグに優先する（左寄せの場合ゼロ埋めは無効） | フラグ競合時 |
| BR-53-02 | 精度のデフォルト | 浮動小数点の精度が未指定の場合はデフォルト6桁 | %f, %e, %g |
| BR-53-03 | #フラグの動作 | 8進: 先頭に0追加、16進: 先頭に0x/0X追加、2進: 先頭に0b/0B追加 | #フラグ指定時 |
| BR-53-04 | 名前付き引数 | `%<name>s`または`%{name}`形式でHash引数から値を取得 | Hash引数使用時 |

### 計算ロジック

- BIT_DIGITS(N)マクロ: ビット数Nから10進桁数を算出（(N*146)/485 + 1）
- mrb_uint_to_cstr: 整数を指定基数の文字列に変換

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | フォーマット引数の数が不足 | 必要な引数を全て提供 |
| - | ArgumentError | 不正なフォーマット指定子 | 有効な指定子を使用 |
| - | TypeError | 型が不一致の引数 | 適切な型の値を渡す |

### リトライ仕様

リトライ不要。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- get_fmt_spec()はswitch文による直接判定で、ルックアップテーブルと同等の速度
- fmt_spec_t構造体により、型・基数・サブタイプを一度に取得

## セキュリティ考慮事項

- フォーマット文字列インジェクション攻撃に注意。ユーザ入力をフォーマット文字列として使用しないこと

## 備考

- String#%メソッドはmrblib/string.rbでRuby側から定義され、内部でsprintfを呼び出す
- BigInt対応: mrb_bint_2comp関数で2の補数表現をサポート

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | fmt_spec_t構造体: フォーマット指定子情報（102-106行目） |
| 1-2 | sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | FMT_*定数: フォーマット種別の分類（89-99行目） |
| 1-3 | sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | F*フラグ: FNONE, FSHARP, FMINUS, FPLUS, FZERO, FSPACE等（78-87行目） |

**読解のコツ**: get_fmt_spec()関数（110-174行目）がフォーマット文字から型・基数・修飾情報を返す中心的なディスパッチ関数である。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | string.rb | `mrbgems/mruby-sprintf/mrblib/string.rb` | String#%メソッドの定義（17-23行目） |

**主要処理フロー**:
- **18-21行目**: 引数がArrayならsprintf(self, *args)、それ以外ならsprintf(self, args)

#### Step 3: 整数変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | mrb_uint_to_cstr(): 整数を基数変換して文字列化（48-76行目） |
| 3-2 | sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | remove_sign_bits(): 負数の表現から余分なビットを除去（23-46行目） |

#### Step 4: 浮動小数点変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | fmt_float(): 浮動小数点のフォーマット（178-200行目） |

**主要処理フロー**:
- **190行目**: mrb_format_float()を呼び出して実際の変換を実行
- **194-198行目**: ゼロパディングとinf/nan特殊処理

### プログラム呼び出し階層図

```
Kernel#sprintf (mrb_f_sprintf)
    |
    +-- 書式文字列パース
    |    +-- get_fmt_spec() (フォーマット指定子判定)
    |
    +-- FMT_INTEGER
    |    +-- mrb_uint_to_cstr() (基数変換)
    |    +-- remove_sign_bits() (負数処理)
    |
    +-- FMT_FLOAT
    |    +-- fmt_float() -> mrb_format_float()
    |
    +-- FMT_STRING
    |    +-- mrb_obj_as_string() / mrb_inspect()
    |
    +-- FMT_NAMED
         +-- mrb_hash_fetch() (Hash引数検索)

String#% (mrblib/string.rb)
    |
    +-- sprintf(self, *args) / sprintf(self, args)
```

### データフロー図

```
[入力]                        [処理]                      [出力]

format_string  ──────> パース + 変換処理  ──────────> String
args (可変長)  ──────>   |
                         +-- 整数: mrb_uint_to_cstr
                         +-- 浮動小数点: fmt_float
                         +-- 文字列: to_s / inspect
                         +-- 幅/精度/フラグ修飾
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sprintf.c | `mrbgems/mruby-sprintf/src/sprintf.c` | ソース | sprintf/formatのC実装 |
| string.rb | `mrbgems/mruby-sprintf/mrblib/string.rb` | ソース | String#%メソッドのRuby実装 |
| sprintf.rb | `mrbgems/mruby-sprintf/test/sprintf.rb` | テスト | sprintfの動作テスト |
| mrbgem.rake | `mrbgems/mruby-sprintf/mrbgem.rake` | 設定 | gemメタデータ定義 |
