1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Common Lisp風のLISPを作ってみる(3.文字列バッファ)

Last updated at Posted at 2024-08-19

今回は、JavaやC#でいうStringBuffer, StringBuilderにあたるモジュールを作成します。これはプロンプトから入力された文字を受け取るのに必要となります。buffer.h, buffer.cを見ていきます。
ソースコードはこちら

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関数はHEADERNODEを生成し、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_allocateBUFFERを生成したら必ず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は使い回すことが想定されています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?