Common Lisp風のLISPを作ってみる（３．文字列バッファ）

今回は、JavaやC#でいうStringBuffer, StringBuilderにあたるモジュールを作成します。これはプロンプトから入力された文字を受け取るのに必要となります。`buffer.h`, `buffer.c`を見ていきます。
[ソースコードはこちら](https://github.com/haruo-wakakusa/my-lisp2/tree/main/chapter03)
## Cのファイルスコープ
皆さんはCのファイルをどのように分割していますか？Cのファイルスコープに着目して、1つのファイルを1つのモジュールないしクラスとみなしてみると可読性が向上するのではないでしょうか？
　今回からの3回はモジュールを作ることに注力します。文字列バッファ、コンスバッファ、環境の3つはmy-lispで生み出したモジュールであり、それぞれのオブジェクトを生成・操作するものであります。従って、これらのファイルサイズは小さいですが意図的に分割されているものです。
## buffer.h
```buffer.h
/*
 * buffer.h
 */

#ifndef BUFFER_H_
#define BUFFER_H_

typedef void * BUFFER;

BUFFER buffer_allocate(void);
void buffer_free(BUFFER buf);
void buffer_write_char(BUFFER buf, char c);
size_t buffer_get_size(BUFFER buf);
void buffer_copy(BUFFER buf, char *dst);
void buffer_clear(BUFFER buf);

#endif
```
このファイルでは`BUFFER`型が定義されています。しかしそれが何であるのかについては明かされていません。その実体はヘッダーではなくソースファイルに書いてあります。
## buffer.c
```buffer.c
/*
 * buffer.c
 */

#include <stdlib.h>
#include <string.h>
#include "buffer.h"

#define BUFFER_SIZE 256

typedef struct tag_NODE {
    struct tag_NODE *prev;
    struct tag_NODE *next;
    char body[BUFFER_SIZE];
} NODE;

typedef struct tag_HEADER {
    NODE *start;
    NODE *cur;
    int index;
} HEADER;
```
その実体は256バイトの配列を持つ`NODE`型と、`NODE`型にアクセスするための`HEADER`型からなります。`NODE`型は双方向リストを構成するようになっています。実は現在公開されている機能だけだと単方向リストで問題ないことが分かるかと思います（あらかじめアナウンスしておきます）。
```buffer.c
BUFFER buffer_allocate(void) {
    HEADER *h = (HEADER *)malloc(sizeof(HEADER));
    NODE *n = (NODE *)malloc(sizeof(NODE));
    h->start = n;
    h->cur = n;
    h->index = 0;
    n->prev = 0;
    n->next = 0;
    memset(n->body, 0, BUFFER_SIZE);
    return (void *)h;
}
```
`buffer_allocate`関数は`HEADER`と`NODE`を生成し、`HEADER`を戻り値としてユーザーに渡しています。
```buffer.c
void buffer_free(BUFFER buf) {
    HEADER *h = (HEADER *)buf;
    NODE *n = h->start;
    while (n->next) {
        n = n->next;
        free(n->prev);
    }
    free(n);
    free(h);
}
```
`buffer_free`関数は生成した`NODE`を全て解放し、`HEADER`も解放します。ユーザーは`buffer_allocate`で`BUFFER`を生成したら必ず`buffer_free`で解放する必要があります。my-lispでは必ずしもこれが守れていたわけではないのですが...
　`malloc`/`free`の話が出てきたのでここで話しておきます。my-lisp2ではGC(ガーベジコレクタ)を実装しません。これは具体的には`malloc`したメモリを`free`しないということを意味します。この実装が許されるかどうかは、実はOSに依存していて、デスクトップOSならば問題がないようです(あくまで経験則です)。組み込み系の場合は`malloc`したメモリを`free`しないと不具合を生じるようで、その対策は万全に行なっているようです。
　とはいえmy-lisp2はmy-lispよりもエラー処理、終了処理を強化して、文字列バッファ、コンスバッファの解放はしっかり行う心積りです。
```buffer.c
void buffer_write_char(BUFFER buf, char c) {
    HEADER *h = (HEADER *)buf;
    h->cur->body[h->index] = c;
    h->index++;
    if (h->index == BUFFER_SIZE) {
        NODE *n = (NODE *)malloc(sizeof(NODE));
        h->cur->next = n;
        h->index = 0;
        n->prev = h->cur;
        n->next = 0;
        h->cur = n;
        memset(n->body, 0, BUFFER_SIZE);
    }
}
```
`buffer_write_char`関数は`BUFFER`に1文字書き込みます。`NODE`はそのサイズが256バイトと決められているので、必要に応じて`NODE`を追加で生成します。この関数がそもそも文字の書き込みであって文字列の書き込みでない理由は、my-lisp2が最初にリーダーでストリームから1文字ずつ読み込むことに依ります。
```buffer.c
size_t buffer_get_size(BUFFER buf) {
    HEADER *h = (HEADER *)buf;
    NODE *n = h->start;
    size_t res = 0;
    while (n != h->cur) {
        res += BUFFER_SIZE;
        n = n->next;
    }
    res += h->index;
    return res;
}
```
`buffer_get_size`関数は、`BUFFER`に書き込まれた文字数を返します。
```buffer.c
void buffer_copy(BUFFER buf, char *dst) {
    HEADER *h = (HEADER *)buf;
    NODE *n = h->start;
    while (n != h->cur) {
        memcpy(dst, n->body, BUFFER_SIZE);
        dst += BUFFER_SIZE;
        n = n->next;
    }
    memcpy(dst, n->body, h->index);
}
```
`buffer_copy`関数は、`BUFFER`から連続したメモリに文字列を書き出します。ただしこの関数がヌル終端処理を行なっていないことに気をつけてください。`buffer_get_size`関数と組み合わせて使うことで安全にデータのコピーを行うことができます。
```buffer.c
void buffer_clear(BUFFER buf) {
    HEADER *h = (HEADER *)buf;
    NODE *n = h->start;
    while (n) {
        memset(n->body, 0, BUFFER_SIZE);
        n = n->next;
    }
    h->cur = h->start;
    h->index = 0;
}
```
`buffer_clear`関数はメモリの解放を行わずに`BUFFER`を初期化します。`BUFFER`は使い回すことが想定されています。