第9章:ソフトウェアエンジニアリングの原理 - ライブラリ設計と実世界への応用

9.1 ソフトウェアモジュール性の理論

9.1.1 モジュール性の歴史と哲学

プログラミングの危機(Software Crisis)

1960年代後半、コンピュータの性能向上により、より大規模なソフトウェアが求められるようになった。しかし、従来のプログラミング手法では、大規模システムの開発・保守が困難になる「ソフトウェアの危機」が顕在化した。

1968年のNATO Software Engineering Conferenceで、この問題が正式に提起され、「ソフトウェアエンジニアリング」という分野が誕生した。

David Parnasの情報隠蔽原理

1972年、David Parnasは画期的な論文「On the Criteria To Be Used in Decomposing Systems into Modules」を発表した。この論文で、彼は情報隠蔽(Information Hiding)の原理を提唱した:

Parnas's Principle:
モジュールは変更されやすい設計決定を隠蔽すべきである。
モジュール間のインターフェースは、各モジュールの内部実装について
可能な限り少ない情報を露出すべきである。

Parnasは、従来のフローチャートベースの分解ではなく、「変更の可能性」に基づくモジュール分解を提唱した:

【従来のアプローチ:フローチャート分解】

main() → input() → process() → output()

各モジュールは処理の流れに従って分割
問題:一つの変更が多くのモジュールに影響

【Parnasのアプローチ:情報隠蔽分解】

┌─────────────────────────────────────────┐
│                Application              │
├─────────────────────────────────────────┤
│  ┌─────────┐ ┌─────────┐ ┌─────────┐    │
│  │ UI      │ │ Business│ │ Data    │    │
│  │ Module  │ │ Logic   │ │ Access  │    │
│  └────┬────┘ └────┬────┘ └────┬────┘    │
│       │           │           │         │
│       ▼           ▼           ▼         │
│  ┌─────────────────────────────────┐    │
│  │     Stable Interfaces Only      │    │
│  └─────────────────────────────────┘    │
└─────────────────────────────────────────┘

各モジュールは変更されやすい決定を隠蔽
インターフェースは安定した抽象のみを公開

モジュール性の評価尺度

1. 結合度(Coupling)

モジュール間の依存度を測る尺度。低いほど良い。

結合度の階層(弱い → 強い):

1. データ結合(Data Coupling)
   - モジュール間でプリミティブデータのみを受け渡し
   - 最も望ましい形態
   例:function(int x, int y)

2. スタンプ結合(Stamp Coupling)
   - モジュール間で複合データ構造を受け渡し
   - 使用しないフィールドも含まれる
   例:function(struct entire_record rec)

3. 制御結合(Control Coupling)
   - モジュールが別モジュールの処理を制御するフラグを渡す
   例:function(data, bool special_mode_flag)

4. 共通結合(Common Coupling)
   - グローバル変数を共有
   例:複数のモジュールがglobal_configを参照

5. 内容結合(Content Coupling)
   - モジュールが別モジュールの内部データを直接参照
   - 最も避けるべき形態
   例:直接他モジュールの内部変数を変更

2. 凝集度(Cohesion)

モジュール内の要素がどれだけ関連しているかを測る尺度。高いほど良い。

凝集度の階層(弱い → 強い):

1. 偶発的凝集(Coincidental)
   - 要素間に意味のある関連がない
   - 最悪の形態
   例:misc.c(雑多な関数の寄せ集め)

2. 論理的凝集(Logical)
   - 論理的に類似した機能をまとめる
   例:input.c(すべての入力処理)

3. 時間的凝集(Temporal)
   - 同時に実行される処理をまとめる
   例:initialization.c(すべての初期化)

4. 手続き的凝集(Procedural)
   - 特定の順序で実行される処理をまとめる
   例:process_transaction.c

5. 通信的凝集(Communicational)
   - 同じデータを操作する処理をまとめる
   例:customer_data.c(顧客データの全操作)

6. 逐次的凝集(Sequential)
   - 出力が次の入力となる処理の連鎖
   例:pipeline_stage.c

7. 機能的凝集(Functional)
   - 単一の明確な機能を実行
   - 最も望ましい形態
   例:calculate_checksum.c

9.1.2 ライブラリ設計の原理

APIデザインの原則

1. 最小驚きの原則(Principle of Least Surprise)

// 驚きの少ないAPI設計

// Good: 標準的な命名と動作
size_t ft_strlen(const char *s);  // 文字列の長さを返す

// Bad: 予期しない副作用
size_t strlen_and_print(const char *s);  // 長さを返すが、なぜか表示もする

// Good: 一貫した戻り値規約
// 成功: 0, 失敗: -1 (errno設定)
int ft_function(args);

// Bad: 不一致な戻り値
// ある関数は成功で0、別の関数は成功で1

2. 直交性の原則(Orthogonality)

機能が独立しており、自由に組み合わせ可能であること:

// 直交的なAPI設計

// 各関数は単一の責務
void *ft_memcpy(void *dst, const void *src, size_t n);
void *ft_memset(void *b, int c, size_t len);
void *ft_memmove(void *dst, const void *src, size_t n);

// 組み合わせて使用可能
void example(void)
{
    char buf[100];

    ft_memset(buf, 0, sizeof(buf));      // 初期化
    ft_memcpy(buf, "Hello", 5);          // コピー
    ft_memmove(buf + 2, buf, 3);         // オーバーラップ移動
}

// 非直交的な設計(避けるべき)
// ft_memcpy_and_convert() - コピーと変換を同時に行う
// ft_memset_if_empty() - 条件付きで設定

3. ゼロオーバーヘッドの原則(Zero-overhead Principle)

Bjarne Stroustrupが提唱したC++の設計原則:

「使わない機能にはコストをかけない」
「使う機能については、手書きより効率が悪くならない」

Libftへの適用:

// ゼロオーバーヘッドの例

// デバッグ機能はコンパイル時に除去可能
#ifdef DEBUG
    #define ASSERT(cond) do { if (!(cond)) abort(); } while(0)
#else
    #define ASSERT(cond) ((void)0)
#endif

// インライン化によるオーバーヘッド除去
static inline int ft_isspace(int c)
{
    return (c == ' ' || c == '\t' || c == '\n' ||
            c == '\v' || c == '\f' || c == '\r');
}

// 使わない機能(エラーチェック)を選択可能
#ifdef LIBFT_SAFE
    #define CHECK_NULL(ptr) if (!(ptr)) return NULL
#else
    #define CHECK_NULL(ptr) ((void)0)
#endif

9.1.3 契約による設計(Design by Contract)

Bertrand Meyerが1986年に提唱した設計手法。各関数は呼び出し側と実装側の間の「契約」として定義される。

契約の三要素

/*
 * ft_substr - 部分文字列を抽出する
 *
 * 事前条件(Preconditions)- 呼び出し側の責任:
 *   - s != NULL
 *   - start < ft_strlen(s) の場合、有効な部分文字列を抽出
 *   - len >= 0
 *
 * 事後条件(Postconditions)- 実装側の責任:
 *   - 戻り値 != NULL(メモリ確保成功の場合)
 *   - 戻り値は新しく確保されたメモリ
 *   - 戻り値はnull終端された文字列
 *   - len(戻り値) == min(len, strlen(s) - start)
 *
 * 不変条件(Invariants):
 *   - sは変更されない
 */
char *ft_substr(char const *s, unsigned int start, size_t len)
{
    // 事前条件のチェック(デバッグビルド時)
    REQUIRE(s != NULL);

    // 実装
    ...

    // 事後条件のチェック(デバッグビルド時)
    ENSURE(result != NULL || allocation_failed);
    ENSURE(result == NULL || ft_strlen(result) <= len);

    return result;
}

DbCマクロの実装

// contract.h - 契約による設計のサポート

#ifndef CONTRACT_H
# define CONTRACT_H

# include <stdio.h>
# include <stdlib.h>

# ifdef DEBUG

// 契約違反のハンドラ
static inline void contract_violation(const char *type, const char *cond,
                                       const char *file, int line)
{
    fprintf(stderr, "Contract violation [%s]: %s\n", type, cond);
    fprintf(stderr, "  at %s:%d\n", file, line);
    abort();
}

// 事前条件
#  define REQUIRE(cond) \
    do { if (!(cond)) contract_violation("REQUIRE", #cond, __FILE__, __LINE__); } while(0)

// 事後条件
#  define ENSURE(cond) \
    do { if (!(cond)) contract_violation("ENSURE", #cond, __FILE__, __LINE__); } while(0)

// 不変条件
#  define INVARIANT(cond) \
    do { if (!(cond)) contract_violation("INVARIANT", #cond, __FILE__, __LINE__); } while(0)

# else

// リリースビルドでは何もしない
#  define REQUIRE(cond)   ((void)0)
#  define ENSURE(cond)    ((void)0)
#  define INVARIANT(cond) ((void)0)

# endif

#endif

---

9.2 ビルドシステムの理論

9.2.1 コンパイルモデルの歴史

分割コンパイルの発明

1950年代、初期のコンピュータプログラムは単一のソースファイルから直接機械語に変換されていた。プログラムが大規模化するにつれ、以下の問題が発生:

  • コンパイル時間の爆発:小さな変更でも全体の再コンパイルが必要
  • メモリ制限:コンパイラが巨大なソースを処理できない
  • チーム開発の困難:複数人が同時に同じファイルを編集できない

この問題を解決するため、「分割コンパイル(Separate Compilation)」が発明された。

翻訳単位(Translation Unit)の概念

【分割コンパイルモデル】

┌─────────────────────────────────────────────────────────────────┐
│                        Source Files                              │
│                                                                  │
│  ┌──────────┐     ┌──────────┐     ┌──────────┐                │
│  │  main.c  │     │ string.c │     │ memory.c │                │
│  └────┬─────┘     └────┬─────┘     └────┬─────┘                │
│       │                │                │                        │
│       ▼                ▼                ▼                        │
│  ┌──────────────────────────────────────────────┐              │
│  │              Preprocessor                      │              │
│  │   #include, #define, #ifdef の処理            │              │
│  └──────────────────────────────────────────────┘              │
│       │                │                │                        │
│       ▼                ▼                ▼                        │
│  ┌──────────┐     ┌──────────┐     ┌──────────┐                │
│  │Translation│     │Translation│     │Translation│                │
│  │ Unit 1   │     │ Unit 2   │     │ Unit 3   │                │
│  └────┬─────┘     └────┬─────┘     └────┬─────┘                │
│       │                │                │                        │
│       ▼                ▼                ▼                        │
│  ┌──────────────────────────────────────────────┐              │
│  │              Compiler                          │              │
│  │   字句解析 → 構文解析 → 意味解析 → 最適化     │              │
│  └──────────────────────────────────────────────┘              │
│       │                │                │                        │
│       ▼                ▼                ▼                        │
│  ┌──────────┐     ┌──────────┐     ┌──────────┐                │
│  │ main.o   │     │ string.o │     │ memory.o │                │
│  │(relocatable)  │(relocatable)   │(relocatable)               │
│  └────┬─────┘     └────┬─────┘     └────┬─────┘                │
│       │                │                │                        │
│       └────────────────┼────────────────┘                        │
│                        │                                         │
│                        ▼                                         │
│              ┌──────────────────┐                               │
│              │      Linker      │                               │
│              │  シンボル解決     │                               │
│              │  アドレス再配置   │                               │
│              └────────┬─────────┘                               │
│                       │                                          │
│                       ▼                                          │
│               ┌──────────────┐                                  │
│               │ Executable   │                                  │
│               │  または       │                                  │
│               │ Library      │                                  │
│               └──────────────┘                                  │
└─────────────────────────────────────────────────────────────────┘

リンクの段階

【シンボル解決のプロセス】

string.c:
    size_t ft_strlen(const char *s)  // 定義
    {
        ...
    }

main.c:
    #include "libft.h"

    int main(void)
    {
        size_t len = ft_strlen("hello");  // 参照
        ...
    }

コンパイル後:

string.o:
    Symbol Table:
    | Name      | Type     | Section | Value    |
    |-----------|----------|---------|----------|
    | ft_strlen | GLOBAL   | .text   | 0x0000   | ← 定義

main.o:
    Symbol Table:
    | Name      | Type     | Section | Value    |
    |-----------|----------|---------|----------|
    | main      | GLOBAL   | .text   | 0x0000   |
    | ft_strlen | UNDEF    | -       | -        | ← 未解決参照

リンク時:
    1. 未解決参照(ft_strlen)を発見
    2. 他のオブジェクトから定義を検索
    3. string.oでft_strlenの定義を発見
    4. 参照を正しいアドレスに解決

9.2.2 依存関係グラフとインクリメンタルビルド

依存関係の有向非巡回グラフ(DAG)

ビルドシステムの核心は、ソースファイル間の依存関係を有向非巡回グラフ(Directed Acyclic Graph)として管理することにある。

【Libftの依存関係グラフ】

                    ┌───────────────┐
                    │   libft.a     │
                    └───────┬───────┘
                            │
            ┌───────────────┼───────────────┐
            │               │               │
            ▼               ▼               ▼
        ┌───────┐       ┌────────┐      ┌────────┐
        │ ft_*.o│       │ ft_*.o │      │ ft_*.o │
        │(ctype)│       │(string)│      │(memory)│
        └───┬───┘       └───┬────┘      └───┬────┘
            │               │               │
            │               │               │
            ▼               ▼               ▼
        ┌───────┐      ┌────────┐      ┌────────┐
        │ ft_*.c│      │ ft_*.c │      │ ft_*.c │
        │(ctype)│      │(string)│      │(memory)│
        └───┬───┘      └────┬───┘      └────┬───┘
            │               │               │
            └───────────────┼───────────────┘
                            │
                            ▼
                    ┌───────────────┐
                    │   libft.h     │
                    └───────────────┘

ルール:
- エッジの方向は依存の方向(A→Bは「AはBに依存」)
- ヘッダファイル変更 → 依存する全.oを再コンパイル
- .cファイル変更 → 対応する.oのみ再コンパイル
- .o変更 → ライブラリを再構築

インクリメンタルビルドの理論

【再ビルド判定アルゴリズム】

function needs_rebuild(target):
    if target does not exist:
        return true

    for each dependency of target:
        if needs_rebuild(dependency):
            return true
        if modification_time(dependency) > modification_time(target):
            return true

    return false

【例:libft.aの再ビルド判定】

libft.a の依存: ft_strlen.o, ft_memcpy.o, ft_strcpy.o, ...

Case 1: ft_strlen.c を編集
    - ft_strlen.c の mtime > ft_strlen.o の mtime
    - ft_strlen.o を再コンパイル
    - ft_strlen.o の mtime > libft.a の mtime
    - libft.a を再構築

Case 2: libft.h を編集
    - libft.h の mtime > 全.o の mtime
    - 全.o を再コンパイル
    - libft.a を再構築

Case 3: 変更なし
    - すべての依存が最新
    - 何もしない

Makefileの理論的基礎

# Makefile は本質的に依存関係グラフの宣言と
# 再構築ルールの定義である

# フォーマット:
# target: dependencies
#     recipe (タブ文字で始まる)

# ターゲット: ライブラリ
# 依存: すべてのオブジェクトファイル
# レシピ: アーカイブコマンド
$(NAME): $(OBJS)
	$(AR) $(ARFLAGS) $@ $^

# ターゲット: オブジェクトファイル (.o)
# 依存: ソースファイル (.c) とヘッダ
# レシピ: コンパイルコマンド
%.o: %.c $(HEADERS)
	$(CC) $(CFLAGS) -c ___CODE_BLOCK_14___lt; -o $@

# パターンルール解説:
# %  - ステムマッチ(任意の文字列)
# ___CODE_BLOCK_14___lt; - 最初の依存ファイル
# $@ - ターゲット名
# $^ - すべての依存ファイル

9.2.3 静的ライブラリと動的ライブラリ

ライブラリの種類と特徴

【静的ライブラリ(.a / .lib)】

コンパイル時:
┌─────────────┐     ┌─────────────┐
│ main.o      │     │ libft.a     │
│             │     │ ┌─────────┐ │
│ call strlen ├────►│ │ft_strlen│ │
│             │     │ └─────────┘ │
│             │     │ ┌─────────┐ │
│ call memcpy ├────►│ │ft_memcpy│ │
│             │     │ └─────────┘ │
└─────────────┘     └─────────────┘
        │                  │
        └────────┬─────────┘
                 │
                 ▼
        ┌─────────────────┐
        │   実行ファイル    │
        │ ┌─────────────┐ │
        │ │  main code  │ │
        │ ├─────────────┤ │
        │ │ ft_strlen   │ │ ← ライブラリコードが
        │ │ (コピー)     │ │   埋め込まれる
        │ ├─────────────┤ │
        │ │ ft_memcpy   │ │
        │ │ (コピー)     │ │
        │ └─────────────┘ │
        └─────────────────┘

利点:
- 実行時に外部依存なし
- 配布が容易
- 実行時オーバーヘッドなし

欠点:
- 実行ファイルサイズが大きい
- ライブラリ更新時は再リンクが必要
- 複数プログラムでメモリ共有不可


【動的ライブラリ(.so / .dll)】

コンパイル時:
┌─────────────┐
│ main.o      │
│             │
│ call strlen ├──────► "ft_strlen" (シンボル参照のみ)
│             │
│ call memcpy ├──────► "ft_memcpy" (シンボル参照のみ)
└─────────────┘
        │
        ▼
┌─────────────────┐
│   実行ファイル    │
│ ┌─────────────┐ │
│ │  main code  │ │
│ ├─────────────┤ │
│ │ 動的リンク   │ │ ← 実行時に解決
│ │ テーブル    │ │
│ └─────────────┘ │
└─────────────────┘

実行時:
┌─────────────────┐     ┌─────────────────┐
│   実行ファイル    │     │   libft.so      │
│                 │     │ (共有メモリ)     │
│ call strlen ────┼────►│ ft_strlen       │
│                 │     │                 │
│ call memcpy ────┼────►│ ft_memcpy       │
└─────────────────┘     └─────────────────┘

利点:
- 実行ファイルサイズが小さい
- ライブラリ更新で再リンク不要
- 複数プログラムでコード共有

欠点:
- 実行時に動的リンカが必要
- わずかな実行時オーバーヘッド
- 依存関係管理が複雑

位置独立コード(PIC: Position Independent Code)

動的ライブラリは任意のアドレスにロードされる可能性があるため、位置独立コードが必要。

【絶対アドレス vs 相対アドレス】

// 絶対アドレス(静的ライブラリ向け)
mov eax, [0x08040000]  // グローバル変数のアドレスが固定

// 位置独立コード(動的ライブラリ向け)
// GOT (Global Offset Table) を使用
call get_pc              // 現在のPC値を取得
add eax, _GLOBAL_OFFSET_TABLE_
mov eax, [eax + var@GOT] // GOT経由でアドレスを解決


【GOTとPLTの仕組み】

┌──────────────────┐
│   Code Section   │
│                  │
│ call printf@PLT  ├───┐
│                  │   │
└──────────────────┘   │
                       │
┌──────────────────┐   │
│   PLT (Procedure │◄──┘
│   Linkage Table) │
│                  │
│ printf@PLT:      │
│   jmp *printf@GOT├───┐
│   push index     │   │
│   jmp resolver   │   │
└──────────────────┘   │
                       │
┌──────────────────┐   │
│   GOT (Global    │◄──┘
│   Offset Table)  │
│                  │
│ printf: 0x...    │ ← 最初は遅延解決スタブ
│ (実アドレス)      │   呼び出し後に実アドレス
└──────────────────┘

---

9.3 メモリアロケータの理論

9.3.1 動的メモリ管理の歴史

メモリ管理の進化

【歴史的発展】

1950年代: 静的メモリ割り当てのみ
          プログラム開始時にすべてのメモリを確保

1960年代: スタックベースの自動管理
          関数呼び出しに伴う自動確保・解放
          LIFO(後入れ先出し)制約

1960年代後半: ヒープによる動的割り当て
              malloc/freeの登場
              任意の順序での確保・解放

1970年代: ガベージコレクション
          LISP、Smalltalkで自動メモリ管理
          参照カウント、マーク&スイープ

1980年代: 領域ベースメモリ管理(Region-based)
          階層的なメモリ領域の概念
          アリーナアロケータの登場

2000年代: スラブアロケータ(Slab Allocator)
          カーネル向け特化型アロケータ
          jemalloc, tcmalloc等の高性能アロケータ

9.3.2 アロケータの設計原理

フラグメンテーション問題

【外部フラグメンテーション】

初期状態:
┌─────────────────────────────────────────┐
│                 Free                    │
└─────────────────────────────────────────┘

A, B, C を順に確保:
┌─────┬─────┬─────┬───────────────────────┐
│  A  │  B  │  C  │        Free           │
└─────┴─────┴─────┴───────────────────────┘

B を解放:
┌─────┬─────┬─────┬───────────────────────┐
│  A  │Free │  C  │        Free           │
└─────┴─────┴─────┴───────────────────────┘

問題: 総フリー容量は十分だが、
      連続した大きな領域がない

【内部フラグメンテーション】

要求: 100バイト
確保: 128バイト(アラインメント要件)
無駄: 28バイト(使用されない)

┌───────────────────────────┐
│ 100 bytes used │ 28 waste │
└───────────────────────────┘

アロケータアルゴリズム

1. First-Fit(最初適合)

リストを先頭から走査し、最初に見つかった
十分なサイズのブロックを使用

Free list: [64] → [128] → [256] → [512]

要求: 100バイト
結果: [128]ブロックを使用

利点: シンプル、高速
欠点: 先頭付近に小さな断片が蓄積

2. Best-Fit(最適適合)

リスト全体を走査し、要求に最も近い
サイズのブロックを使用

Free list: [64] → [128] → [256] → [512]

要求: 100バイト
結果: [128]ブロックを使用(差が最小)

利点: 無駄が少ない
欠点: 検索が遅い、極小断片が発生

3. Segregated Free Lists

サイズクラスごとに別々のフリーリストを管理

mermaid graph LR L16[16 bytes] --> B16_1[■] --> B16_2[■] --> NULL L32[32 bytes] --> B32_1[■] --> NULL L64[64 bytes] --> B64_1[■] --> NULL L128[128 bytes] --> B128_1[■] --> B128_2[■] --> NULL L256[256 bytes] --> NULL L512[512+ bytes] --> B512_1[■] --> NULL

要求: 50バイト → 64バイトクラスから即座に取得
O(1)での割り当てが可能

9.3.3 メモリプールとアリーナ

メモリプールの理論

同一サイズのオブジェクトを大量に確保・解放する場合、専用のメモリプールが効率的。

// メモリプールの理論的構造

/*
 * 固定サイズブロックプール
 *
 * 設計目標:
 * - O(1) 割り当て
 * - O(1) 解放
 * - 外部フラグメンテーションなし
 * - 内部フラグメンテーション最小化
 */

typedef struct s_memory_pool {
    void    *memory;        // プリアロケートされたメモリ領域
    size_t  block_size;     // 各ブロックのサイズ(固定)
    size_t  block_count;    // 総ブロック数
    void    **free_list;    // フリーブロックのスタック
    size_t  free_count;     // 現在のフリーブロック数
} t_memory_pool;

/*
 * 確保操作: O(1)
 *
 * 1. free_listのトップからポインタを取得
 * 2. free_countをデクリメント
 * 3. ポインタを返す
 */

void *pool_alloc(t_memory_pool *pool)
{
    if (pool->free_count == 0)
        return NULL;  // プール枯渇

    return pool->free_list[--pool->free_count];
}

/*
 * 解放操作: O(1)
 *
 * 1. ポインタをfree_listのトップに追加
 * 2. free_countをインクリメント
 */

void pool_free(t_memory_pool *pool, void *ptr)
{
    // 境界チェック(オプション)
    if (ptr < pool->memory ||
        ptr >= (char*)pool->memory + pool->block_size * pool->block_count)
        return;

    pool->free_list[pool->free_count++] = ptr;
}

アリーナアロケータ

一括解放パターンに特化したアロケータ。個別解放をサポートせず、領域全体を一度に解放する。

// アリーナアロケータの理論

/*
 * アリーナ(領域)ベースのメモリ管理
 *
 * 使用パターン:
 * 1. アリーナを作成
 * 2. アリーナから複数のオブジェクトを確保
 * 3. 作業完了後、アリーナ全体を一括解放
 *
 * 典型的な用途:
 * - リクエスト処理(リクエスト終了時に全解放)
 * - パーサー(パース完了時に全解放)
 * - ゲームフレーム(フレーム終了時に全解放)
 */

typedef struct s_arena {
    void    *memory;        // 現在のメモリチャンク
    size_t  size;           // チャンクサイズ
    size_t  used;           // 使用量
    struct s_arena *next;   // 次のチャンク(チェーン)
} t_arena;

typedef struct s_arena_allocator {
    t_arena *first;         // 最初のチャンク
    t_arena *current;       // 現在のチャンク
    size_t  default_size;   // デフォルトチャンクサイズ
} t_arena_allocator;

/*
 * 確保: O(1) 平均(チャンク拡張を除く)
 *
 * 単純なポインタ加算のみ
 * 個別のメタデータ管理不要
 */

void *arena_alloc(t_arena_allocator *arena, size_t size)
{
    // アラインメント調整
    size = (size + 7) & ~7;

    // 現在のチャンクに空きがあるか
    if (!arena->current || arena->current->used + size > arena->current->size) {
        // 新しいチャンクが必要
        t_arena *new_chunk = create_chunk(
            size > arena->default_size ? size : arena->default_size
        );
        if (!new_chunk)
            return NULL;

        // チェーンに追加
        if (arena->current)
            arena->current->next = new_chunk;
        else
            arena->first = new_chunk;
        arena->current = new_chunk;
    }

    // バンプポインタ方式で確保
    void *ptr = (char*)arena->current->memory + arena->current->used;
    arena->current->used += size;

    return ptr;
}

/*
 * 個別解放: サポートしない
 *
 * これは制限ではなく、設計上の選択
 * 個別解放を不要とすることで:
 * - メタデータオーバーヘッドなし
 * - フラグメンテーションなし
 * - 高速な確保
 */

void arena_free(t_arena_allocator *arena, void *ptr)
{
    (void)arena;
    (void)ptr;
    // 何もしない
}

/*
 * 一括解放: O(n) チャンク数
 *
 * すべてのチャンクを一度に解放
 */

void arena_destroy(t_arena_allocator *arena)
{
    t_arena *chunk = arena->first;
    while (chunk) {
        t_arena *next = chunk->next;
        free(chunk->memory);
        free(chunk);
        chunk = next;
    }
    free(arena);
}

/*
 * リセット: O(n) チャンク数
 *
 * メモリを解放せず、使用量をゼロにリセット
 * 再利用パターンで効果的
 */

void arena_reset(t_arena_allocator *arena)
{
    for (t_arena *chunk = arena->first; chunk; chunk = chunk->next) {
        chunk->used = 0;
    }
    arena->current = arena->first;
}

アリーナの実践的実装

// arena.h - 実践的なアリーナアロケータ

#ifndef ARENA_H
# define ARENA_H

# include "libft.h"

# define ARENA_DEFAULT_SIZE  65536  // 64KB
# define ARENA_ALIGNMENT     16

typedef struct s_arena_chunk {
    unsigned char           *memory;
    size_t                  capacity;
    size_t                  used;
    struct s_arena_chunk    *next;
} t_arena_chunk;

typedef struct s_arena {
    t_arena_chunk   *first;
    t_arena_chunk   *current;
    size_t          default_chunk_size;
    size_t          total_allocated;
    size_t          chunk_count;
} t_arena;

// 基本操作
t_arena     *arena_create(size_t default_chunk_size);
void        *arena_alloc(t_arena *arena, size_t size);
void        *arena_calloc(t_arena *arena, size_t count, size_t size);
void        *arena_realloc(t_arena *arena, void *ptr,
                           size_t old_size, size_t new_size);
char        *arena_strdup(t_arena *arena, const char *s);
void        arena_reset(t_arena *arena);
void        arena_destroy(t_arena *arena);

// 診断
typedef struct s_arena_stats {
    size_t  total_capacity;
    size_t  total_used;
    size_t  chunk_count;
    float   efficiency;         // used / capacity
} t_arena_stats;

t_arena_stats   arena_get_stats(t_arena *arena);
void            arena_print_stats(t_arena *arena);

// スコープベースの一時アリーナ
typedef struct s_temp_arena {
    t_arena         *arena;
    t_arena_chunk   *saved_chunk;
    size_t          saved_used;
} t_temp_arena;

t_temp_arena    temp_arena_begin(t_arena *arena);
void            temp_arena_end(t_temp_arena temp);

#endif

// arena.c - 実装

#include "arena.h"

static t_arena_chunk *create_chunk(size_t size)
{
    t_arena_chunk *chunk = malloc(sizeof(t_arena_chunk));
    if (!chunk)
        return NULL;

    // 16バイトアラインメント
    size = (size + ARENA_ALIGNMENT - 1) & ~(ARENA_ALIGNMENT - 1);

    chunk->memory = aligned_alloc(ARENA_ALIGNMENT, size);
    if (!chunk->memory) {
        free(chunk);
        return NULL;
    }

    chunk->capacity = size;
    chunk->used = 0;
    chunk->next = NULL;

    return chunk;
}

t_arena *arena_create(size_t default_chunk_size)
{
    t_arena *arena = malloc(sizeof(t_arena));
    if (!arena)
        return NULL;

    arena->default_chunk_size = default_chunk_size > 0
                                ? default_chunk_size
                                : ARENA_DEFAULT_SIZE;
    arena->first = NULL;
    arena->current = NULL;
    arena->total_allocated = 0;
    arena->chunk_count = 0;

    return arena;
}

void *arena_alloc(t_arena *arena, size_t size)
{
    if (!arena || size == 0)
        return NULL;

    // アラインメント調整
    size = (size + ARENA_ALIGNMENT - 1) & ~(ARENA_ALIGNMENT - 1);

    // 現在のチャンクに十分な空きがあるか
    if (!arena->current ||
        arena->current->used + size > arena->current->capacity) {

        // 必要なサイズを計算(デフォルトより大きい場合は拡張)
        size_t chunk_size = arena->default_chunk_size;
        if (size > chunk_size)
            chunk_size = size * 2;  // 余裕を持たせる

        t_arena_chunk *new_chunk = create_chunk(chunk_size);
        if (!new_chunk)
            return NULL;

        // チェーンに追加
        if (arena->current)
            arena->current->next = new_chunk;
        else
            arena->first = new_chunk;

        arena->current = new_chunk;
        arena->chunk_count++;
    }

    // バンプポインタ方式で確保
    void *ptr = arena->current->memory + arena->current->used;
    arena->current->used += size;
    arena->total_allocated += size;

    return ptr;
}

void *arena_calloc(t_arena *arena, size_t count, size_t size)
{
    size_t total = count * size;
    void *ptr = arena_alloc(arena, total);
    if (ptr)
        ft_memset(ptr, 0, total);
    return ptr;
}

char *arena_strdup(t_arena *arena, const char *s)
{
    if (!s)
        return NULL;

    size_t len = ft_strlen(s) + 1;
    char *copy = arena_alloc(arena, len);
    if (copy)
        ft_memcpy(copy, s, len);

    return copy;
}

// 一時アリーナ: スコープ内での一時的な確保
// スコープ終了時に自動的にロールバック

t_temp_arena temp_arena_begin(t_arena *arena)
{
    t_temp_arena temp;
    temp.arena = arena;
    temp.saved_chunk = arena->current;
    temp.saved_used = arena->current ? arena->current->used : 0;
    return temp;
}

void temp_arena_end(t_temp_arena temp)
{
    // 保存した状態にロールバック
    if (temp.saved_chunk) {
        temp.arena->current = temp.saved_chunk;
        temp.arena->current->used = temp.saved_used;

        // 保存位置以降のチャンクを解放
        t_arena_chunk *chunk = temp.saved_chunk->next;
        temp.saved_chunk->next = NULL;

        while (chunk) {
            t_arena_chunk *next = chunk->next;
            free(chunk->memory);
            free(chunk);
            temp.arena->chunk_count--;
            chunk = next;
        }
    }
}

使用例:パーサーでのアリーナ活用

// JSON パーサーでのアリーナ活用例

typedef struct s_json_value {
    int type;
    union {
        double number;
        char *string;
        struct {
            struct s_json_value **items;
            size_t count;
        } array;
        struct {
            char **keys;
            struct s_json_value **values;
            size_t count;
        } object;
    };
} t_json_value;

// アリーナを使ったパース
t_json_value *json_parse(const char *input)
{
    // パース用のアリーナを作成
    t_arena *arena = arena_create(0);  // デフォルトサイズ
    if (!arena)
        return NULL;

    // パース処理(すべての確保はアリーナから)
    t_json_value *root = parse_value(arena, input);

    // 注意: rootとそのすべての子はarenaに確保されている
    // arena_destroy()を呼ぶと全て無効になる

    return root;  // 呼び出し側がarenaも管理する必要あり
}

// より良い設計: 結果構造体にアリーナを含める
typedef struct s_json_document {
    t_arena *arena;
    t_json_value *root;
} t_json_document;

t_json_document *json_parse_document(const char *input)
{
    t_json_document *doc = malloc(sizeof(t_json_document));
    if (!doc)
        return NULL;

    doc->arena = arena_create(0);
    if (!doc->arena) {
        free(doc);
        return NULL;
    }

    doc->root = parse_value(doc->arena, input);

    return doc;
}

void json_free_document(t_json_document *doc)
{
    if (doc) {
        arena_destroy(doc->arena);  // 全ノードを一括解放
        free(doc);
    }
}

---

9.4 プロトコル工学の基礎

9.4.1 通信プロトコルの理論

OSI参照モデル

国際標準化機構(ISO)が1984年に策定した通信プロトコルの階層モデル:

【OSI 7層モデル】

mermaid block-beta columns 1 L7["Layer 7: Application (HTTP, FTP, SMTP)"] L6["Layer 6: Presentation (TLS, JPEG)"] L5["Layer 5: Session (RPC)"] L4["Layer 4: Transport (TCP, UDP)"] L3["Layer 3: Network (IP)"] L2["Layer 2: Data Link (Ethernet)"] L1["Layer 1: Physical (Cables)"] style L7 fill:#f9f style L6 fill:#f9f style L5 fill:#f9f style L4 fill:#ff9 style L3 fill:#ff9 style L2 fill:#9f9 style L1 fill:#9f9

各層の独立性:
- 上位層は下位層のサービスを利用
- 下位層の実装詳細を知る必要なし
- 同じ層のプロトコルは置き換え可能

HTTPプロトコルの構造

HTTP(Hypertext Transfer Protocol)は、OSI第7層(アプリケーション層)のプロトコル。

【HTTPリクエストの構造】

mermaid packet-beta title HTTP Request 0-31: "Method (GET)" 32-63: "URI (/path/to/resource)" 64-95: "Version (HTTP/1.1)" 96-127: "Headers (Host, User-Agent, ...)" 128-159: "Empty Line (CRLF)" 160-191: "Message Body (Optional)"


【HTTPレスポンスの構造】

mermaid packet-beta title HTTP Response 0-31: "Version (HTTP/1.1)" 32-47: "Code (200)" 48-63: "Reason (OK)" 64-95: "Headers (Content-Type, ...)" 96-127: "Empty Line (CRLF)" 128-159: "Message Body (HTML)"

9.4.2 パーサー設計の原理

有限オートマトン理論

プロトコルパーサーは、有限オートマトン(Finite Automaton)として設計できる。

【HTTPリクエストパーサーのステートマシン】

mermaid stateDiagram-v2 [] --> PARSE_METHOD PARSE_METHOD --> PARSE_URI: Space PARSE_URI --> PARSE_VERSION: Space PARSE_VERSION --> PARSE_HEADER: CRLF state PARSE_HEADER { [] --> CHECK_LINE CHECK_LINE --> HEADER_NAME: Char CHECK_LINE --> CHECK_BODY: CRLF (Empty Line) HEADER_NAME --> HEADER_VALUE: Colon HEADER_VALUE --> CHECK_LINE: CRLF } PARSE_HEADER --> PARSE_BODY: Content-Length > 0 PARSE_HEADER --> COMPLETE: No Body PARSE_BODY --> COMPLETE: Body Length Reached COMPLETE --> [*]

状態遷移ベースのパーサー実装

// http_parser.h - 状態機械ベースのHTTPパーサー

#ifndef HTTP_PARSER_H
# define HTTP_PARSER_H

# include "libft.h"

// パーサー状態
typedef enum e_parser_state {
    PARSE_METHOD,
    PARSE_URI,
    PARSE_VERSION,
    PARSE_HEADER_NAME,
    PARSE_HEADER_VALUE,
    PARSE_BODY,
    PARSE_COMPLETE,
    PARSE_ERROR
} t_parser_state;

// HTTPメソッド
typedef enum e_http_method {
    HTTP_GET,
    HTTP_POST,
    HTTP_PUT,
    HTTP_DELETE,
    HTTP_HEAD,
    HTTP_OPTIONS,
    HTTP_UNKNOWN
} t_http_method;

// HTTPヘッダー
typedef struct s_http_header {
    char    *name;
    char    *value;
    struct s_http_header *next;
} t_http_header;

// HTTPリクエスト
typedef struct s_http_request {
    t_http_method   method;
    char            *uri;
    char            *query_string;
    int             version_major;
    int             version_minor;
    t_http_header   *headers;
    char            *body;
    size_t          body_length;
    size_t          content_length;
} t_http_request;

// パーサーコンテキスト
typedef struct s_http_parser {
    t_parser_state  state;
    t_http_request  *request;

    // 一時バッファ
    char            *buffer;
    size_t          buffer_size;
    size_t          buffer_pos;

    // 現在の解析位置
    char            *current_header_name;

    // エラー情報
    int             error_code;
    const char      *error_message;
} t_http_parser;

// パーサー操作
t_http_parser   *http_parser_create(void);
int             http_parser_feed(t_http_parser *parser,
                                 const char *data, size_t len);
t_http_request  *http_parser_get_request(t_http_parser *parser);
void            http_parser_reset(t_http_parser *parser);
void            http_parser_destroy(t_http_parser *parser);

// ヘルパー関数
const char      *http_method_str(t_http_method method);
t_http_method   http_method_from_str(const char *str);
const char      *http_status_str(int code);

#endif

// http_parser.c

#include "http_parser.h"
#include <ctype.h>

// 文字分類の最適化(ルックアップテーブル)
static const unsigned char token_char[256] = {
    ['A'...'Z'] = 1, ['a'...'z'] = 1,
    ['0'...'9'] = 1,
    ['!'] = 1, ['#'] = 1, ['

---

9.5 データ構造設計の原理

9.5.1 抽象データ型(ADT)の理論

ADTの概念

抽象データ型は、Barbara Liskovが1974年に提唱した概念。データの操作を、実装から分離した抽象として定義する。

【抽象データ型の定義】

ADT = (D, F, A)

D: ドメイン(データの集合)
F: 操作の集合(関数シグネチャ)
A: 公理の集合(操作の性質)


【例: スタックのADT】

Domain: Stack<T>(型Tの要素を持つスタック)

Operations:
- new() → Stack<T>              // 空のスタックを作成
- push(Stack<T>, T) → Stack<T>  // 要素を追加
- pop(Stack<T>) → (Stack<T>, T) // 要素を取り出し
- peek(Stack<T>) → T            // トップを参照
- is_empty(Stack<T>) → bool     // 空かどうか

Axioms:
1. is_empty(new()) = true
2. is_empty(push(s, x)) = false
3. pop(push(s, x)) = (s, x)
4. peek(push(s, x)) = x

実装の自由:
- 配列ベース
- リンクリストベース
- 内部構造は外部から見えない

キー・バリューストアのADT

// kvstore.h - Key-Valueストアの抽象データ型

#ifndef KVSTORE_H
# define KVSTORE_H

# include "libft.h"

/*
 * Key-Value Store ADT
 *
 * Domain: KVStore(文字列キーから任意値へのマッピング)
 *
 * Operations:
 * - create() → KVStore
 * - put(KVStore, string, value) → bool
 * - get(KVStore, string) → value | null
 * - delete(KVStore, string) → bool
 * - contains(KVStore, string) → bool
 * - size(KVStore) → int
 * - destroy(KVStore) → void
 *
 * Axioms:
 * 1. get(put(s, k, v), k) = v
 * 2. get(put(s, k, v), k') = get(s, k')  where k ≠ k'
 * 3. contains(put(s, k, v), k) = true
 * 4. size(put(s, k, v)) = size(s) + 1    if !contains(s, k)
 * 5. size(delete(s, k)) = size(s) - 1    if contains(s, k)
 */

// 不透明な型(実装隠蔽)
typedef struct s_kvstore t_kvstore;

// イテレータ
typedef struct s_kv_iterator t_kv_iterator;

// 基本操作
t_kvstore   *kvstore_create(size_t initial_capacity);
void        kvstore_destroy(t_kvstore *store);

int         kvstore_put(t_kvstore *store, const char *key,
                        const void *value, size_t value_size);
void        *kvstore_get(t_kvstore *store, const char *key, size_t *size);
int         kvstore_delete(t_kvstore *store, const char *key);
int         kvstore_contains(t_kvstore *store, const char *key);
size_t      kvstore_size(t_kvstore *store);

// イテレータ
t_kv_iterator   *kvstore_iterator_create(t_kvstore *store);
int             kvstore_iterator_next(t_kv_iterator *iter,
                                      const char **key,
                                      void **value, size_t *size);
void            kvstore_iterator_destroy(t_kv_iterator *iter);

// 永続化
int         kvstore_save(t_kvstore *store, const char *filename);
t_kvstore   *kvstore_load(const char *filename);

#endif

9.5.2 ハッシュテーブルの理論と実装

ハッシュ関数の理論

良いハッシュ関数は以下の性質を持つ:

  • 決定性:同じ入力には常に同じ出力
  • 均一分布:出力が均等に分布
  • 雪崩効果:入力の小さな変化が出力を大きく変える
  • 効率性:計算が高速

// ハッシュ関数の比較

// 1. 単純な加算ハッシュ(悪い例)
size_t bad_hash(const char *key)
{
    size_t hash = 0;
    while (*key)
        hash += *key++;  // 順序が考慮されない
    return hash;
}
// 問題: "abc"と"cba"が同じハッシュ値

// 2. DJB2ハッシュ(Dan Bernstein)
size_t djb2_hash(const char *key)
{
    size_t hash = 5381;
    int c;

    while ((c = *key++))
        hash = ((hash << 5) + hash) + c;  // hash * 33 + c

    return hash;
}
// 特徴: シンプルで高速、良好な分布

// 3. FNV-1aハッシュ(Fowler-Noll-Vo)
size_t fnv1a_hash(const char *key)
{
    size_t hash = 14695981039346656037ULL;  // FNV offset basis

    while (*key) {
        hash ^= (unsigned char)*key++;
        hash *= 1099511628211ULL;  // FNV prime
    }

    return hash;
}
// 特徴: 衝突が少ない、良好な雪崩効果

// 4. MurmurHash3(Austin Appleby)
// 非暗号学的ハッシュの中で最も優れた性能
// 詳細な実装は複雑なため省略

衝突解決戦略

【チェイン法(Separate Chaining)】

ハッシュテーブル:
┌────┐
│ 0  │ → [A] → [D] → NULL
├────┤
│ 1  │ → NULL
├────┤
│ 2  │ → [B] → NULL
├────┤
│ 3  │ → [C] → [E] → [F] → NULL
├────┤
│ 4  │ → NULL
└────┘

利点:
- シンプルな実装
- 負荷率 > 1 でも動作
- 削除が容易

欠点:
- ポインタのオーバーヘッド
- キャッシュ効率が悪い


【オープンアドレス法(Open Addressing)】

ハッシュテーブル(Linear Probing):
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│  A  │  D  │     │  B  │  C  │  E  │  F  │     │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
  ↑                 ↑     ↑
  hash(A)=0        hash(B)=3
  hash(D)=0 → 1    hash(C)=3 → 4
                   hash(E)=3 → 5
                   hash(F)=3 → 6

利点:
- キャッシュ効率が良い
- メモリオーバーヘッドが少ない

欠点:
- クラスタリング問題
- 削除が複雑(墓石マーカー必要)
- 負荷率に敏感

ハッシュテーブルの実装

// kvstore_impl.c - チェイン法によるKVストア実装

#include "kvstore.h"
#include <pthread.h>

// エントリ構造体
typedef struct s_kv_entry {
    char                *key;
    void                *value;
    size_t              value_size;
    struct s_kv_entry   *next;
} t_kv_entry;

// KVストア構造体
struct s_kvstore {
    t_kv_entry      **buckets;
    size_t          bucket_count;
    size_t          entry_count;
    size_t          threshold;       // リサイズ閾値
    float           load_factor;     // 最大負荷率
    pthread_rwlock_t lock;           // 読み書きロック
};

// イテレータ構造体
struct s_kv_iterator {
    t_kvstore       *store;
    size_t          bucket_index;
    t_kv_entry      *current;
};

// ハッシュ関数
static size_t hash_key(const char *key, size_t bucket_count)
{
    size_t hash = 5381;
    int c;

    while ((c = *key++))
        hash = ((hash << 5) + hash) + c;

    return hash % bucket_count;
}

// 作成
t_kvstore *kvstore_create(size_t initial_capacity)
{
    t_kvstore *store = ft_calloc(1, sizeof(t_kvstore));
    if (!store)
        return NULL;

    store->bucket_count = initial_capacity > 0 ? initial_capacity : 16;
    store->buckets = ft_calloc(store->bucket_count, sizeof(t_kv_entry*));
    if (!store->buckets) {
        free(store);
        return NULL;
    }

    store->load_factor = 0.75f;
    store->threshold = (size_t)(store->bucket_count * store->load_factor);

    pthread_rwlock_init(&store->lock, NULL);

    return store;
}

// リサイズ
static int kvstore_resize(t_kvstore *store)
{
    size_t new_bucket_count = store->bucket_count * 2;
    t_kv_entry **new_buckets = ft_calloc(new_bucket_count, sizeof(t_kv_entry*));
    if (!new_buckets)
        return -1;

    // 全エントリを再ハッシュ
    for (size_t i = 0; i < store->bucket_count; i++) {
        t_kv_entry *entry = store->buckets[i];
        while (entry) {
            t_kv_entry *next = entry->next;

            size_t new_index = hash_key(entry->key, new_bucket_count);
            entry->next = new_buckets[new_index];
            new_buckets[new_index] = entry;

            entry = next;
        }
    }

    free(store->buckets);
    store->buckets = new_buckets;
    store->bucket_count = new_bucket_count;
    store->threshold = (size_t)(store->bucket_count * store->load_factor);

    return 0;
}

// 挿入
int kvstore_put(t_kvstore *store, const char *key,
                const void *value, size_t value_size)
{
    if (!store || !key || !value || value_size == 0)
        return -1;

    pthread_rwlock_wrlock(&store->lock);

    // リサイズチェック
    if (store->entry_count >= store->threshold) {
        if (kvstore_resize(store) < 0) {
            pthread_rwlock_unlock(&store->lock);
            return -1;
        }
    }

    size_t index = hash_key(key, store->bucket_count);

    // 既存エントリの検索
    for (t_kv_entry *e = store->buckets[index]; e; e = e->next) {
        if (ft_strcmp(e->key, key) == 0) {
            // 更新
            void *new_value = malloc(value_size);
            if (!new_value) {
                pthread_rwlock_unlock(&store->lock);
                return -1;
            }
            free(e->value);
            ft_memcpy(new_value, value, value_size);
            e->value = new_value;
            e->value_size = value_size;

            pthread_rwlock_unlock(&store->lock);
            return 0;
        }
    }

    // 新規エントリ
    t_kv_entry *entry = malloc(sizeof(t_kv_entry));
    if (!entry) {
        pthread_rwlock_unlock(&store->lock);
        return -1;
    }

    entry->key = ft_strdup(key);
    entry->value = malloc(value_size);
    if (!entry->key || !entry->value) {
        free(entry->key);
        free(entry->value);
        free(entry);
        pthread_rwlock_unlock(&store->lock);
        return -1;
    }

    ft_memcpy(entry->value, value, value_size);
    entry->value_size = value_size;
    entry->next = store->buckets[index];
    store->buckets[index] = entry;
    store->entry_count++;

    pthread_rwlock_unlock(&store->lock);
    return 0;
}

// 取得
void *kvstore_get(t_kvstore *store, const char *key, size_t *size)
{
    if (!store || !key)
        return NULL;

    pthread_rwlock_rdlock(&store->lock);

    size_t index = hash_key(key, store->bucket_count);

    for (t_kv_entry *e = store->buckets[index]; e; e = e->next) {
        if (ft_strcmp(e->key, key) == 0) {
            void *copy = malloc(e->value_size);
            if (copy) {
                ft_memcpy(copy, e->value, e->value_size);
                if (size)
                    *size = e->value_size;
            }
            pthread_rwlock_unlock(&store->lock);
            return copy;
        }
    }

    pthread_rwlock_unlock(&store->lock);
    return NULL;
}

// 削除
int kvstore_delete(t_kvstore *store, const char *key)
{
    if (!store || !key)
        return -1;

    pthread_rwlock_wrlock(&store->lock);

    size_t index = hash_key(key, store->bucket_count);
    t_kv_entry **pp = &store->buckets[index];

    while (*pp) {
        if (ft_strcmp((*pp)->key, key) == 0) {
            t_kv_entry *to_delete = *pp;
            *pp = to_delete->next;

            free(to_delete->key);
            free(to_delete->value);
            free(to_delete);
            store->entry_count--;

            pthread_rwlock_unlock(&store->lock);
            return 0;
        }
        pp = &(*pp)->next;
    }

    pthread_rwlock_unlock(&store->lock);
    return -1;  // キーが見つからない
}

// 永続化
int kvstore_save(t_kvstore *store, const char *filename)
{
    FILE *fp = fopen(filename, "wb");
    if (!fp)
        return -1;

    pthread_rwlock_rdlock(&store->lock);

    // ヘッダー: マジックナンバー + バージョン + エントリ数
    uint32_t magic = 0x4B565354;  // "KVST"
    uint32_t version = 1;
    uint64_t count = store->entry_count;

    fwrite(&magic, sizeof(magic), 1, fp);
    fwrite(&version, sizeof(version), 1, fp);
    fwrite(&count, sizeof(count), 1, fp);

    // エントリを書き込み
    for (size_t i = 0; i < store->bucket_count; i++) {
        for (t_kv_entry *e = store->buckets[i]; e; e = e->next) {
            uint32_t key_len = ft_strlen(e->key);
            uint64_t value_size = e->value_size;

            fwrite(&key_len, sizeof(key_len), 1, fp);
            fwrite(e->key, key_len, 1, fp);
            fwrite(&value_size, sizeof(value_size), 1, fp);
            fwrite(e->value, e->value_size, 1, fp);
        }
    }

    pthread_rwlock_unlock(&store->lock);
    fclose(fp);

    return 0;
}

// 読み込み
t_kvstore *kvstore_load(const char *filename)
{
    FILE *fp = fopen(filename, "rb");
    if (!fp)
        return NULL;

    // ヘッダーの読み込みと検証
    uint32_t magic, version;
    uint64_t count;

    if (fread(&magic, sizeof(magic), 1, fp) != 1 ||
        fread(&version, sizeof(version), 1, fp) != 1 ||
        fread(&count, sizeof(count), 1, fp) != 1) {
        fclose(fp);
        return NULL;
    }

    if (magic != 0x4B565354 || version != 1) {
        fclose(fp);
        return NULL;
    }

    // ストアを作成
    t_kvstore *store = kvstore_create(count * 2);  // 余裕を持たせる
    if (!store) {
        fclose(fp);
        return NULL;
    }

    // エントリを読み込み
    for (uint64_t i = 0; i < count; i++) {
        uint32_t key_len;
        uint64_t value_size;

        if (fread(&key_len, sizeof(key_len), 1, fp) != 1)
            break;

        char *key = malloc(key_len + 1);
        if (!key || fread(key, key_len, 1, fp) != 1) {
            free(key);
            break;
        }
        key[key_len] = '\0';

        if (fread(&value_size, sizeof(value_size), 1, fp) != 1) {
            free(key);
            break;
        }

        void *value = malloc(value_size);
        if (!value || fread(value, value_size, 1, fp) != 1) {
            free(key);
            free(value);
            break;
        }

        kvstore_put(store, key, value, value_size);

        free(key);
        free(value);
    }

    fclose(fp);
    return store;
}

---

9.6 イベント駆動アーキテクチャ

9.6.1 I/Oモデルの進化

同期 vs 非同期I/O

【ブロッキング(同期)I/O】

スレッド1:
┌─────────────────────────────────────────────────────────┐
│  処理  │  read()でブロック      │  データ処理  │  処理  │
└─────────────────────────────────────────────────────────┘
                    ↓
              この間何もできない

問題:
- スレッドが待機状態で無駄
- 多数の接続にはスレッドプールが必要
- スレッド数 = 同時接続数(スケールしない)


【ノンブロッキング(非同期)I/O】

スレッド1:
┌─────────────────────────────────────────────────────────┐
│ 処理 │fd1確認│fd2確認│fd3確認│fd1処理│fd2処理│fd3処理 │
└─────────────────────────────────────────────────────────┘
          ↓       ↓       ↓
       準備なし  準備OK  準備なし
       (スキップ)      (スキップ)

利点:
- 1スレッドで多数の接続を処理可能
- リソース効率が高い
- C10K問題の解決策

I/O多重化の進化

【select() - 1983年 BSD 4.2】

制限:
- 最大1024ファイルディスクリプタ
- O(n)でファイルディスクリプタをスキャン
- 呼び出しごとにfd_setをコピー

【poll() - 1986年 System V】

改善:
- ファイルディスクリプタ数の制限なし
- より効率的なAPI

残る問題:
- 依然O(n)スキャン

【epoll() - 2002年 Linux 2.5.44】

革新:
- O(1)でイベント取得
- Edge-triggeredモード
- ファイルディスクリプタをカーネル側で管理

【kqueue() - 2000年 FreeBSD 4.1】

特徴:
- epollと同様の性能
- より汎用的(ファイル変更、シグナル等も監視可能)

9.6.2 Reactorパターン

Douglas Schmidtが1995年に体系化したデザインパターン。

【Reactorパターンの構造】

mermaid graph TD subgraph Reactor Demuxer[Event Demuxer (epoll/kqueue)] Dispatcher[Event Dispatcher] Registry[Handler Registry] Demuxer -->|Events| Dispatcher Dispatcher -->|Callback| Registry Registry -->|fd1| AcceptHandler Registry -->|fd2| ReadHandler Registry -->|fd3| WriteHandler end subgraph Handlers AcceptHandler ReadHandler WriteHandler TimerHandler end Dispatcher --> Handlers

Reactorパターンの実装

// reactor.h - Reactorパターンの実装

#ifndef REACTOR_H
# define REACTOR_H

# include "libft.h"
# ifdef __linux__
#  include <sys/epoll.h>
# else
#  include <sys/event.h>
# endif

// イベントタイプ
typedef enum e_event_type {
    EVENT_READ  = 1 << 0,
    EVENT_WRITE = 1 << 1,
    EVENT_ERROR = 1 << 2,
    EVENT_CLOSE = 1 << 3
} t_event_type;

// 前方宣言
typedef struct s_reactor t_reactor;

// イベントハンドラ
typedef void (*t_event_handler)(t_reactor *reactor, int fd,
                                 t_event_type events, void *user_data);

// ハンドラ登録情報
typedef struct s_handler_info {
    int             fd;
    t_event_type    events;
    t_event_handler handler;
    void            *user_data;
} t_handler_info;

// Reactor構造体
struct s_reactor {
    #ifdef __linux__
    int             epoll_fd;
    #else
    int             kqueue_fd;
    #endif

    t_handler_info  **handlers;
    size_t          handler_count;
    size_t          handler_capacity;
    int             running;
};

// Reactor操作
t_reactor   *reactor_create(void);
void        reactor_destroy(t_reactor *reactor);

int         reactor_register(t_reactor *reactor, int fd,
                            t_event_type events,
                            t_event_handler handler,
                            void *user_data);
int         reactor_modify(t_reactor *reactor, int fd,
                          t_event_type new_events);
int         reactor_unregister(t_reactor *reactor, int fd);

int         reactor_run(t_reactor *reactor);
void        reactor_stop(t_reactor *reactor);

#endif

// reactor.c - Linux (epoll) 実装

#include "reactor.h"
#include <unistd.h>
#include <errno.h>

#ifdef __linux__

t_reactor *reactor_create(void)
{
    t_reactor *reactor = ft_calloc(1, sizeof(t_reactor));
    if (!reactor)
        return NULL;

    reactor->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (reactor->epoll_fd < 0) {
        free(reactor);
        return NULL;
    }

    reactor->handler_capacity = 64;
    reactor->handlers = ft_calloc(reactor->handler_capacity,
                                  sizeof(t_handler_info*));
    if (!reactor->handlers) {
        close(reactor->epoll_fd);
        free(reactor);
        return NULL;
    }

    return reactor;
}

static t_handler_info *find_handler(t_reactor *reactor, int fd)
{
    for (size_t i = 0; i < reactor->handler_count; i++) {
        if (reactor->handlers[i] && reactor->handlers[i]->fd == fd)
            return reactor->handlers[i];
    }
    return NULL;
}

static uint32_t events_to_epoll(t_event_type events)
{
    uint32_t epoll_events = 0;
    if (events & EVENT_READ)
        epoll_events |= EPOLLIN;
    if (events & EVENT_WRITE)
        epoll_events |= EPOLLOUT;
    return epoll_events | EPOLLET;  // Edge-triggered
}

static t_event_type epoll_to_events(uint32_t epoll_events)
{
    t_event_type events = 0;
    if (epoll_events & EPOLLIN)
        events |= EVENT_READ;
    if (epoll_events & EPOLLOUT)
        events |= EVENT_WRITE;
    if (epoll_events & EPOLLERR)
        events |= EVENT_ERROR;
    if (epoll_events & EPOLLHUP)
        events |= EVENT_CLOSE;
    return events;
}

int reactor_register(t_reactor *reactor, int fd,
                    t_event_type events,
                    t_event_handler handler,
                    void *user_data)
{
    // 既存チェック
    if (find_handler(reactor, fd))
        return -1;

    // 容量拡張
    if (reactor->handler_count >= reactor->handler_capacity) {
        size_t new_cap = reactor->handler_capacity * 2;
        t_handler_info **new_handlers = realloc(reactor->handlers,
                                                new_cap * sizeof(t_handler_info*));
        if (!new_handlers)
            return -1;
        reactor->handlers = new_handlers;
        reactor->handler_capacity = new_cap;
    }

    // ハンドラ情報作成
    t_handler_info *info = malloc(sizeof(t_handler_info));
    if (!info)
        return -1;

    info->fd = fd;
    info->events = events;
    info->handler = handler;
    info->user_data = user_data;

    // epollに登録
    struct epoll_event ev;
    ev.events = events_to_epoll(events);
    ev.data.ptr = info;

    if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        free(info);
        return -1;
    }

    reactor->handlers[reactor->handler_count++] = info;
    return 0;
}

int reactor_unregister(t_reactor *reactor, int fd)
{
    for (size_t i = 0; i < reactor->handler_count; i++) {
        if (reactor->handlers[i] && reactor->handlers[i]->fd == fd) {
            epoll_ctl(reactor->epoll_fd, EPOLL_CTL_DEL, fd, NULL);

            free(reactor->handlers[i]);
            reactor->handlers[i] = reactor->handlers[--reactor->handler_count];
            reactor->handlers[reactor->handler_count] = NULL;

            return 0;
        }
    }
    return -1;
}

int reactor_run(t_reactor *reactor)
{
    struct epoll_event events[256];
    reactor->running = 1;

    while (reactor->running) {
        int n = epoll_wait(reactor->epoll_fd, events, 256, 1000);

        if (n < 0) {
            if (errno == EINTR)
                continue;
            return -1;
        }

        for (int i = 0; i < n; i++) {
            t_handler_info *info = events[i].data.ptr;
            t_event_type ev_type = epoll_to_events(events[i].events);

            if (info->handler) {
                info->handler(reactor, info->fd, ev_type, info->user_data);
            }
        }
    }

    return 0;
}

void reactor_stop(t_reactor *reactor)
{
    reactor->running = 0;
}

void reactor_destroy(t_reactor *reactor)
{
    if (!reactor)
        return;

    for (size_t i = 0; i < reactor->handler_count; i++) {
        free(reactor->handlers[i]);
    }
    free(reactor->handlers);
    close(reactor->epoll_fd);
    free(reactor);
}

#endif

エコーサーバーの実装例

// echo_server.c - Reactorパターンを使ったエコーサーバー

#include "reactor.h"
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct s_connection {
    int     fd;
    char    buffer[4096];
    size_t  len;
} t_connection;

static void set_nonblocking(int fd)
{
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

// 読み込みイベントハンドラ
static void on_read(t_reactor *reactor, int fd, t_event_type events, void *user_data)
{
    t_connection *conn = user_data;
    (void)events;

    // 読み込み
    ssize_t n = read(fd, conn->buffer + conn->len,
                     sizeof(conn->buffer) - conn->len - 1);

    if (n <= 0) {
        // 接続終了またはエラー
        reactor_unregister(reactor, fd);
        close(fd);
        free(conn);
        return;
    }

    conn->len += n;
    conn->buffer[conn->len] = '\0';

    // エコーバック
    write(fd, conn->buffer, conn->len);
    conn->len = 0;
}

// 新規接続ハンドラ
static void on_accept(t_reactor *reactor, int fd, t_event_type events, void *user_data)
{
    (void)events;
    (void)user_data;

    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);

    while (1) {
        int client_fd = accept(fd, (struct sockaddr*)&client_addr, &addr_len);
        if (client_fd < 0)
            break;

        set_nonblocking(client_fd);

        t_connection *conn = ft_calloc(1, sizeof(t_connection));
        if (!conn) {
            close(client_fd);
            continue;
        }
        conn->fd = client_fd;

        reactor_register(reactor, client_fd, EVENT_READ, on_read, conn);
    }
}

int main(void)
{
    // サーバーソケット作成
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0)
        return 1;

    int reuse = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    set_nonblocking(server_fd);

    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(8080),
        .sin_addr.s_addr = INADDR_ANY
    };

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
        return 1;

    if (listen(server_fd, SOMAXCONN) < 0)
        return 1;

    // Reactor作成・実行
    t_reactor *reactor = reactor_create();
    if (!reactor)
        return 1;

    reactor_register(reactor, server_fd, EVENT_READ, on_accept, NULL);

    reactor_run(reactor);

    reactor_destroy(reactor);
    close(server_fd);

    return 0;
}

---

9.7 Libftの統合パターン

9.7.1 実践的な統合例

コマンドラインツールへの統合

// cli_tool.c - Libftを使ったCLIツールの例

#include "libft.h"
#include "arena.h"

typedef struct s_options {
    int     verbose;
    int     force;
    char    *input_file;
    char    *output_file;
    char    **extra_args;
    int     extra_count;
} t_options;

// 引数解析
t_options *parse_args(t_arena *arena, int argc, char **argv)
{
    t_options *opts = arena_calloc(arena, 1, sizeof(t_options));
    if (!opts)
        return NULL;

    int i = 1;
    while (i < argc) {
        if (ft_strcmp(argv[i], "-v") == 0 ||
            ft_strcmp(argv[i], "--verbose") == 0) {
            opts->verbose = 1;
        }
        else if (ft_strcmp(argv[i], "-f") == 0 ||
                 ft_strcmp(argv[i], "--force") == 0) {
            opts->force = 1;
        }
        else if (ft_strcmp(argv[i], "-i") == 0 && i + 1 < argc) {
            opts->input_file = arena_strdup(arena, argv[++i]);
        }
        else if (ft_strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
            opts->output_file = arena_strdup(arena, argv[++i]);
        }
        else if (argv[i][0] != '-') {
            // 追加引数
            opts->extra_count++;
        }
        i++;
    }

    // 追加引数を収集
    if (opts->extra_count > 0) {
        opts->extra_args = arena_calloc(arena, opts->extra_count, sizeof(char*));
        int j = 0;
        for (i = 1; i < argc && j < opts->extra_count; i++) {
            if (argv[i][0] != '-' &&
                (i < 2 || (ft_strcmp(argv[i-1], "-i") != 0 &&
                           ft_strcmp(argv[i-1], "-o") != 0))) {
                opts->extra_args[j++] = arena_strdup(arena, argv[i]);
            }
        }
    }

    return opts;
}

int main(int argc, char **argv)
{
    // 全体のメモリ管理にアリーナを使用
    t_arena *arena = arena_create(0);
    if (!arena)
        return 1;

    // 引数解析
    t_options *opts = parse_args(arena, argc, argv);
    if (!opts) {
        arena_destroy(arena);
        return 1;
    }

    if (opts->verbose) {
        ft_putstr_fd("Input: ", 1);
        ft_putendl_fd(opts->input_file ? opts->input_file : "(stdin)", 1);
        ft_putstr_fd("Output: ", 1);
        ft_putendl_fd(opts->output_file ? opts->output_file : "(stdout)", 1);
    }

    // 処理...

    // 終了時に一括解放
    arena_destroy(arena);
    return 0;
}

9.7.2 プロジェクト統合のMakefile

# Makefile - 実践的なプロジェクト統合

# プロジェクト情報
NAME = myproject
VERSION = 1.0.0

# ディレクトリ構成
SRC_DIR = src
INC_DIR = include
OBJ_DIR = obj
LIB_DIR = lib
BIN_DIR = bin

# Libftの統合
LIBFT_DIR = libft
LIBFT = $(LIBFT_DIR)/libft.a
LIBFT_INC = $(LIBFT_DIR)/include

# コンパイラ設定
CC = cc
CFLAGS = -Wall -Wextra -Werror -std=c99
CFLAGS += -I$(INC_DIR) -I$(LIBFT_INC)

# ビルドモード
ifeq ($(DEBUG), 1)
    CFLAGS += -g3 -O0 -DDEBUG
    CFLAGS += -fsanitize=address,undefined
    LDFLAGS += -fsanitize=address,undefined
else
    CFLAGS += -O3 -DNDEBUG
endif

# ソースファイル
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)

# デフォルトターゲット
all: $(BIN_DIR)/$(NAME)

# Libftのビルド
$(LIBFT):
	@$(MAKE) -C $(LIBFT_DIR)

# メインバイナリ
$(BIN_DIR)/$(NAME): $(OBJS) $(LIBFT)
	@mkdir -p $(BIN_DIR)
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) -L$(LIBFT_DIR) -lft

# オブジェクトファイル
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
	@mkdir -p $(OBJ_DIR)
	$(CC) $(CFLAGS) -c ___CODE_BLOCK_47___lt; -o $@

# クリーン
clean:
	rm -rf $(OBJ_DIR)
	@$(MAKE) -C $(LIBFT_DIR) clean

fclean: clean
	rm -rf $(BIN_DIR)
	@$(MAKE) -C $(LIBFT_DIR) fclean

re: fclean all

# テスト
test: all
	@echo "Running tests..."
	@./$(BIN_DIR)/$(NAME) --test

.PHONY: all clean fclean re test

---

まとめ

この章では、ソフトウェアエンジニアリングの原理と、Libftを実世界のアプリケーションに統合する方法を学びました。

学んだ理論的基盤:

  • モジュール性の理論
- Parnasの情報隠蔽原理 - 結合度と凝集度の評価 - 契約による設計(Design by Contract)

  • ビルドシステムの原理
- 分割コンパイルモデル - 依存関係グラフとDAG - 静的・動的リンクの理論

  • メモリアロケータ設計
- フラグメンテーション問題 - アロケータアルゴリズム - メモリプールとアリーナアロケータ

  • プロトコル工学
- OSI参照モデル - 状態機械ベースのパーサー設計 - HTTPプロトコルの構造

  • データ構造設計
- 抽象データ型(ADT) - ハッシュテーブルの理論と実装

  • イベント駆動アーキテクチャ
- I/Oモデルの進化 - Reactorパターン

これらの原理を理解することで、Libftの関数群を効果的に活用し、堅牢でスケーラブルなアプリケーションを構築できます。

] = 1, ['%'] = 1, ['&'] = 1, ['\''] = 1, ['*'] = 1, ['+'] = 1, ['-'] = 1, ['.'] = 1, ['^'] = 1, ['_'] = 1, ['`'] = 1, ['|'] = 1, ['~'] = 1, }; static inline int is_token_char(unsigned char c) { return token_char[c]; } t_http_parser *http_parser_create(void) { t_http_parser *parser = ft_calloc(1, sizeof(t_http_parser)); if (!parser) return NULL; parser->request = ft_calloc(1, sizeof(t_http_request)); if (!parser->request) { free(parser); return NULL; } parser->buffer_size = 4096; parser->buffer = malloc(parser->buffer_size); if (!parser->buffer) { free(parser->request); free(parser); return NULL; } parser->state = PARSE_METHOD; return parser; } // メソッド解析 static int parse_method(t_http_parser *parser, const char **pp, const char *end) { const char *p = *pp; const char *start = p; // トークン文字を読み進める while (p < end && is_token_char(*p)) p++; if (p == start) { parser->error_message = "Empty method"; return -1; } if (p >= end) return 0; // もっとデータが必要 if (*p != ' ') { parser->error_message = "Expected space after method"; return -1; } // メソッドを設定 size_t len = p - start; if (len == 3 && ft_memcmp(start, "GET", 3) == 0) parser->request->method = HTTP_GET; else if (len == 4 && ft_memcmp(start, "POST", 4) == 0) parser->request->method = HTTP_POST; else if (len == 3 && ft_memcmp(start, "PUT", 3) == 0) parser->request->method = HTTP_PUT; else if (len == 6 && ft_memcmp(start, "DELETE", 6) == 0) parser->request->method = HTTP_DELETE; else if (len == 4 && ft_memcmp(start, "HEAD", 4) == 0) parser->request->method = HTTP_HEAD; else if (len == 7 && ft_memcmp(start, "OPTIONS", 7) == 0) parser->request->method = HTTP_OPTIONS; else parser->request->method = HTTP_UNKNOWN; parser->state = PARSE_URI; *pp = p + 1; // 空白をスキップ return 1; } // URI解析 static int parse_uri(t_http_parser *parser, const char **pp, const char *end) { const char *p = *pp; const char *start = p; const char *query = NULL; // URIの終端(空白)まで読み進める while (p < end && *p != ' ' && *p != '\r') { if (*p == '?' && !query) query = p; p++; } if (p >= end) return 0; // もっとデータが必要 if (*p != ' ') { parser->error_message = "Expected space after URI"; return -1; } // URIを設定 size_t uri_len = query ? (size_t)(query - start) : (size_t)(p - start); parser->request->uri = malloc(uri_len + 1); if (!parser->request->uri) return -1; ft_memcpy(parser->request->uri, start, uri_len); parser->request->uri[uri_len] = '\0'; // クエリ文字列を設定 if (query) { size_t query_len = p - query - 1; parser->request->query_string = malloc(query_len + 1); if (parser->request->query_string) { ft_memcpy(parser->request->query_string, query + 1, query_len); parser->request->query_string[query_len] = '\0'; } } parser->state = PARSE_VERSION; *pp = p + 1; return 1; } // バージョン解析 static int parse_version(t_http_parser *parser, const char **pp, const char *end) { const char *p = *pp; // "HTTP/X.X\r\n" を期待 if (end - p < 10) return 0; // もっとデータが必要 if (ft_memcmp(p, "HTTP/", 5) != 0) { parser->error_message = "Invalid HTTP version"; return -1; } p += 5; if (!isdigit(*p)) { parser->error_message = "Invalid major version"; return -1; } parser->request->version_major = *p++ - '0'; if (*p++ != '.') { parser->error_message = "Expected dot in version"; return -1; } if (!isdigit(*p)) { parser->error_message = "Invalid minor version"; return -1; } parser->request->version_minor = *p++ - '0'; if (*p++ != '\r' || *p++ != '\n') { parser->error_message = "Expected CRLF after version"; return -1; } parser->state = PARSE_HEADER_NAME; *pp = p; return 1; } // ヘッダー名解析 static int parse_header_name(t_http_parser *parser, const char **pp, const char *end) { const char *p = *pp; // 空行チェック(ヘッダー終了) if (end - p >= 2 && *p == '\r' && *(p + 1) == '\n') { // Content-Lengthがあればボディを読む for (t_http_header *h = parser->request->headers; h; h = h->next) { if (ft_strcmp(h->name, "Content-Length") == 0) { parser->request->content_length = ft_atoi(h->value); break; } } if (parser->request->content_length > 0) { parser->state = PARSE_BODY; } else { parser->state = PARSE_COMPLETE; } *pp = p + 2; return 1; } // ヘッダー名を読む const char *start = p; while (p < end && is_token_char(*p)) p++; if (p >= end) return 0; if (*p != ':') { parser->error_message = "Expected colon after header name"; return -1; } // ヘッダー名を保存 size_t len = p - start; parser->current_header_name = malloc(len + 1); if (!parser->current_header_name) return -1; ft_memcpy(parser->current_header_name, start, len); parser->current_header_name[len] = '\0'; parser->state = PARSE_HEADER_VALUE; *pp = p + 1; return 1; } // ヘッダー値解析 static int parse_header_value(t_http_parser *parser, const char **pp, const char *end) { const char *p = *pp; // 先頭の空白をスキップ while (p < end && (*p == ' ' || *p == '\t')) p++; const char *start = p; // CRLFまで読む while (p < end && *p != '\r') p++; if (p >= end) return 0; if (p + 1 >= end) return 0; if (*(p + 1) != '\n') { parser->error_message = "Expected LF after CR"; return -1; } // ヘッダー値を保存 size_t len = p - start; char *value = malloc(len + 1); if (!value) return -1; ft_memcpy(value, start, len); value[len] = '\0'; // ヘッダーをリストに追加 t_http_header *header = malloc(sizeof(t_http_header)); if (!header) { free(value); return -1; } header->name = parser->current_header_name; header->value = value; header->next = parser->request->headers; parser->request->headers = header; parser->current_header_name = NULL; parser->state = PARSE_HEADER_NAME; *pp = p + 2; return 1; } // ボディ解析 static int parse_body(t_http_parser *parser, const char **pp, const char *end) { size_t available = end - *pp; size_t needed = parser->request->content_length - parser->request->body_length; size_t to_copy = available < needed ? available : needed; if (!parser->request->body) { parser->request->body = malloc(parser->request->content_length + 1); if (!parser->request->body) return -1; } ft_memcpy(parser->request->body + parser->request->body_length, *pp, to_copy); parser->request->body_length += to_copy; *pp += to_copy; if (parser->request->body_length >= parser->request->content_length) { parser->request->body[parser->request->body_length] = '\0'; parser->state = PARSE_COMPLETE; } return 1; } // メイン解析ループ int http_parser_feed(t_http_parser *parser, const char *data, size_t len) { const char *p = data; const char *end = data + len; int result; while (p < end && parser->state != PARSE_COMPLETE && parser->state != PARSE_ERROR) { switch (parser->state) { case PARSE_METHOD: result = parse_method(parser, &p, end); break; case PARSE_URI: result = parse_uri(parser, &p, end); break; case PARSE_VERSION: result = parse_version(parser, &p, end); break; case PARSE_HEADER_NAME: result = parse_header_name(parser, &p, end); break; case PARSE_HEADER_VALUE: result = parse_header_value(parser, &p, end); break; case PARSE_BODY: result = parse_body(parser, &p, end); break; default: result = -1; break; } if (result < 0) { parser->state = PARSE_ERROR; return -1; } if (result == 0) break; // もっとデータが必要 } return parser->state == PARSE_COMPLETE ? 1 : 0; }

---

9.5 データ構造設計の原理

9.5.1 抽象データ型(ADT)の理論

ADTの概念

抽象データ型は、Barbara Liskovが1974年に提唱した概念。データの操作を、実装から分離した抽象として定義する。

___CODE_BLOCK_35___

キー・バリューストアのADT

___CODE_BLOCK_36___

9.5.2 ハッシュテーブルの理論と実装

ハッシュ関数の理論

良いハッシュ関数は以下の性質を持つ:

  • 決定性:同じ入力には常に同じ出力
  • 均一分布:出力が均等に分布
  • 雪崩効果:入力の小さな変化が出力を大きく変える
  • 効率性:計算が高速

___CODE_BLOCK_37___

衝突解決戦略

___CODE_BLOCK_38___

ハッシュテーブルの実装

___CODE_BLOCK_39___

---

9.6 イベント駆動アーキテクチャ

9.6.1 I/Oモデルの進化

同期 vs 非同期I/O

___CODE_BLOCK_40___

I/O多重化の進化

___CODE_BLOCK_41___

9.6.2 Reactorパターン

Douglas Schmidtが1995年に体系化したデザインパターン。

___CODE_BLOCK_42___mermaid graph TD subgraph Reactor Demuxer[Event Demuxer (epoll/kqueue)] Dispatcher[Event Dispatcher] Registry[Handler Registry] Demuxer -->|Events| Dispatcher Dispatcher -->|Callback| Registry Registry -->|fd1| AcceptHandler Registry -->|fd2| ReadHandler Registry -->|fd3| WriteHandler end subgraph Handlers AcceptHandler ReadHandler WriteHandler TimerHandler end Dispatcher --> Handlers ___CODE_BLOCK_43___

Reactorパターンの実装

___CODE_BLOCK_44___

エコーサーバーの実装例

___CODE_BLOCK_45___

---

9.7 Libftの統合パターン

9.7.1 実践的な統合例

コマンドラインツールへの統合

___CODE_BLOCK_46___

9.7.2 プロジェクト統合のMakefile

___CODE_BLOCK_47___

---

まとめ

この章では、ソフトウェアエンジニアリングの原理と、Libftを実世界のアプリケーションに統合する方法を学びました。

学んだ理論的基盤:

  • モジュール性の理論
- Parnasの情報隠蔽原理 - 結合度と凝集度の評価 - 契約による設計(Design by Contract)

  • ビルドシステムの原理
- 分割コンパイルモデル - 依存関係グラフとDAG - 静的・動的リンクの理論

  • メモリアロケータ設計
- フラグメンテーション問題 - アロケータアルゴリズム - メモリプールとアリーナアロケータ

  • プロトコル工学
- OSI参照モデル - 状態機械ベースのパーサー設計 - HTTPプロトコルの構造

  • データ構造設計
- 抽象データ型(ADT) - ハッシュテーブルの理論と実装

  • イベント駆動アーキテクチャ
- I/Oモデルの進化 - Reactorパターン

これらの原理を理解することで、Libftの関数群を効果的に活用し、堅牢でスケーラブルなアプリケーションを構築できます。