# 機能設計書 69-IO/Fileクラス

## 概要

本ドキュメントは、mrubyにおけるIO/Fileクラスの設計を記述する。mruby-io gemとして実装され、ファイル入出力、パイプ通信、標準入出力等のI/O操作を提供する。

### 本機能の処理概要

IO/Fileクラスは、POSIX互換のファイルI/O操作をmrubyから利用可能にする機能を提供する。IOクラスがファイルディスクリプタベースのI/O操作の基底クラスとなり、FileクラスがIOを継承してファイルパスベースの操作を追加する。内部構造体mrb_ioでファイルディスクリプタ、読み書きバッファ、プロセスID等を管理する。

**業務上の目的・背景**：組み込み環境においてもファイル読み書き、設定ファイルの読み込み、ログ出力等のI/O操作が必要である。mruby-ioはこれらの基本的なI/O機能をRubyスタイルのAPIで提供する。

**機能の利用シーン**：ファイルの読み書き、設定ファイルの読み込み、ログファイルへの出力、パイプを使った外部コマンド実行（IO.popen）、標準入出力の操作等で使用される。

**主要な処理内容**：
1. IOオブジェクトの初期化（IO.new / IO.sysopen）
2. 読み込み操作（read, gets, getc, getbyte, sysread, readline, readlines）
3. 書き込み操作（write, puts, print, putc, syswrite, <<）
4. シーク操作（seek, sysseek, pos）
5. ファイル制御（close, close_write, flush, sync）
6. パイプ操作（IO.popen, IO.pipe）
7. I/O多重化（IO.select）
8. Fileクラス固有操作（パスベースのopen、atime/ctime/mtime等）

**関連システム・外部連携**：HAL（Hardware Abstraction Layer）gemに依存（hal-posix-io / hal-win-io）。mruby-socket（IOを継承するSocketクラス）と連携。

**権限による制御**：特になし。ファイルアクセスはOS側のパーミッションに依存。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIベースのため画面なし |

## 機能種別

入出力 / ファイル操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| fd / path | Integer / String | Yes | ファイルディスクリプタまたはファイルパス | 有効なfd or パス |
| mode | String / Integer | No | アクセスモード（デフォルト"r"） | "r","w","a","r+","w+","a+","rb"等 |
| perm | Integer | No | ファイルパーミッション（デフォルト0666） | 整数 |
| length | Integer | No | 読み込みバイト数 | 正の整数 |
| separator | String | No | 行区切り文字 | 文字列 |

### 入力データソース

mrubyスクリプトからの直接呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| IO | IO | I/Oオブジェクト |
| 読み込みデータ | String | 読み込んだ文字列データ |
| 書き込みバイト数 | Integer | 書き込んだバイト数 |
| ファイル位置 | Integer | 現在のファイル位置 |

### 出力先

mruby値スタック上に結果を返却

## 処理フロー

### 処理シーケンス

```
1. IOオブジェクト生成
   └─ io_init() / io_s_sysopen()
       a. io_alloc()でmrb_io構造体を割り当て
       b. io_mode_to_flags()でモード文字列をフラグに変換
       c. open()でファイルディスクリプタを取得
       d. io_fd_cloexec()でCLOEXECを設定
       e. io_init_buf()で読み込みバッファを初期化
2. 読み込み操作
   └─ io_read() / io_gets() / io_getc()
       a. io_get_read_fptr()で読み込み可能なfptrを取得
       b. io_fill_buf()でバッファにデータを充填
       c. バッファからデータを消費して返却
3. 書き込み操作
   └─ io_write() / io_puts() / io_print()
       a. io_get_write_fptr()で書き込み可能なfptrを取得
       b. io_prepare_write()でバッファ状態を調整
       c. fd_write()でファイルディスクリプタに書き込み
4. クローズ
   └─ io_close()
       a. fptr_finalize()でファイルディスクリプタをクローズ
```

### フローチャート

```mermaid
flowchart TD
    A[IO.new / File.new] --> B[io_alloc]
    B --> C[io_mode_to_flags]
    C --> D[open fd]
    D --> E[io_fd_cloexec]
    E --> F[io_init_buf]
    F --> G[IOオブジェクト]

    G --> H{操作}
    H -->|read| I[io_fill_buf]
    I --> J[バッファからデータ取得]
    H -->|write| K[io_prepare_write]
    K --> L[fd_write]
    H -->|close| M[fptr_finalize]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-69-01 | モード文字列 | "r","w","a"を基本に"b","x","+"を組み合わせ可能 | IO.new, File.new |
| BR-69-02 | CLOEXEC設定 | fd > 2の場合はFD_CLOEXECを自動設定、fd <= 2（標準IO）はクリア | fd取得時 |
| BR-69-03 | バッファサイズ | MRB_IO_BUF_SIZE（4096バイト）の読み込みバッファ | readable IO |
| BR-69-04 | popen制限 | iOSではIO.popenが無効（MRB_NO_IO_POPEN） | プラットフォーム依存 |
| BR-69-05 | HAL依存 | プラットフォーム固有のI/O操作はHAL gemに委譲 | ビルド時 |

### 計算ロジック

mrb_io構造体はfd（読み込み用）、fd2（書き込み用、duplexの場合のみ）の2つのファイルディスクリプタを管理する。読み込み操作ではmrb_io_bufバッファ（4096バイト）を使用し、システムコールの呼び出し回数を最小化する。

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | IOError | 未初期化のストリーム操作 | IOオブジェクトを初期化 |
| - | IOError | クローズ済みストリーム操作 | 新しいIOオブジェクトを開く |
| - | IOError | 読み込み不可のストリームからの読み込み | 読み込みモードで開く |
| - | IOError | 書き込み不可のストリームへの書き込み | 書き込みモードで開く |
| - | EOFError | ファイル末端でのreadchar/readbyte | getc/getbyteを使用（nilを返す） |
| - | ArgumentError | 不正なアクセスモード | 正しいモード文字列を使用 |
| - | Errno::* | OSレベルのI/Oエラー | mrb_sys_fail()でシステムエラーを通知 |

### リトライ仕様

write操作でEINTRが発生した場合は自動リトライ（fd_write内のwhileループ）。

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

該当なし

## パフォーマンス要件

- 読み込みバッファ（4096バイト）によるシステムコール削減
- ROMテーブル（MRB_MT_INIT_ROM）による36メソッドの効率的な初期化
- io_prepare_write()によるread-writeモード切り替え時のバッファ同期

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

- IO.popenは外部コマンド実行を許可するため、信頼できない入力をコマンドとして渡してはならない
- ファイルパスのトラバーサル攻撃はOS側のセキュリティ機構に依存

## 備考

- HAL（Hardware Abstraction Layer）gemが必須（hal-posix-io or hal-win-io）
- FileクラスはRuby側（mrblib/file.rb）で定義されIOを継承
- Enumerableモジュールをインクルード
- MRB_NO_STDIOと競合（ビルド時エラー）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | io.h | `mrbgems/mruby-io/include/mruby/io.h` | mrb_io構造体とバッファ構造体 |

**読解のコツ**:
- **26行目**: MRB_IO_BUF_SIZE = 4096
- **28-32行目**: struct mrb_io_buf - 読み込みバッファ（start, len, mem[4096]）
- **34-46行目**: struct mrb_io - IOの内部構造体（fd, fd2, pid, ビットフィールド、buf）
- **48-66行目**: MRB_O_*定数 - プラットフォーム非依存のファイルオープンフラグ

#### Step 2: 初期化とモード変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | io.c | `mrbgems/mruby-io/src/io.c` | io_alloc()、io_modestr_to_flags()、io_init() |

**主要処理フロー**:
- **76行目**: mrb_io_type データ型定義（"IO", io_free）
- **83-94行目**: io_get_open_fptr() - 有効なIO fptrの取得
- **129-171行目**: io_modestr_to_flags() - モード文字列→フラグ変換
- **242-264行目**: io_fd_cloexec() - CLOEXECの設定
- **287-302行目**: io_alloc() - mrb_io構造体の割り当てと初期化

#### Step 3: 読み書き操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | io.c | `mrbgems/mruby-io/src/io.c` | io_read(), io_write(), io_gets() |

**主要処理フロー**:
- **914-934行目**: fd_write() - ファイルディスクリプタへの書き込み（EINTRリトライ付き）
- **954-976行目**: io_write() - IO#writeのエントリーポイント
- **1020-1052行目**: io_puts() - IO#putsの実装

#### Step 4: gem初期化とROMテーブルを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | io.c | `mrbgems/mruby-io/src/io.c` | io_rom_entriesとmrb_init_io() |

**主要処理フロー**:
- **2200-2237行目**: io_rom_entries - 36メソッドのROMテーブル定義
- **2239-2259行目**: mrb_init_io() - IOクラスの初期化、MRB_TT_CDATAの設定

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

```
IO.new(fd, mode)
    |
    +-- io_init()
            +-- io_alloc()              ... mrb_io構造体割り当て
            +-- io_mode_to_flags()      ... モード変換
            +-- io_fd_cloexec()         ... CLOEXEC設定
            +-- io_init_buf()           ... 読み込みバッファ初期化

IO#read(length)
    |
    +-- io_read()
            +-- io_get_read_fptr()      ... 読み込み可能チェック
            +-- io_fill_buf()           ... バッファ充填
            +-- io_buf_shift()          ... バッファ消費

IO#write(string)
    |
    +-- io_write()
            +-- io_get_write_fptr()     ... 書き込み可能チェック
            +-- io_prepare_write()      ... バッファ同期
            +-- fd_write()              ... システムコール write()

IO.popen(cmd, mode)
    |
    +-- io_s_popen()
            +-- parse_popen_args()      ... 引数パース
            +-- mrb_hal_io_pipe()       ... パイプ作成（HAL）
            +-- mrb_hal_io_spawn_process() ... プロセス起動（HAL）
```

### データフロー図

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

fd + mode            ──▶ io_init()              ──▶ IO (mrb_io構造体)
                            +-- mode→flags変換

IO + length          ──▶ io_read()              ──▶ String (読み込みデータ)
                            +-- io_fill_buf()
                            +-- read() syscall

IO + String          ──▶ io_write()             ──▶ Integer (書き込みバイト数)
                            +-- io_prepare_write()
                            +-- write() syscall

cmd + mode           ──▶ io_s_popen()           ──▶ IO (パイプ付き)
                            +-- pipe() + fork/spawn
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| io.c | `mrbgems/mruby-io/src/io.c` | ソース | IOクラスC実装（2260行） |
| io.h | `mrbgems/mruby-io/include/mruby/io.h` | ヘッダ | mrb_io構造体定義（77行） |
| io.rb | `mrbgems/mruby-io/mrblib/io.rb` | Ruby実装 | IOクラスRuby側メソッド |
| file.rb | `mrbgems/mruby-io/mrblib/file.rb` | Ruby実装 | FileクラスRuby側実装 |
| file_constants.rb | `mrbgems/mruby-io/mrblib/file_constants.rb` | Ruby実装 | ファイル定数定義 |
| kernel.rb | `mrbgems/mruby-io/mrblib/kernel.rb` | Ruby実装 | Kernel#open等 |
| mrbgem.rake | `mrbgems/mruby-io/mrbgem.rake` | 設定 | gem仕様定義・HAL依存 |
| io_hal.h | `mrbgems/mruby-io/src/io_hal.h` | ヘッダ | HALインタフェース定義 |
