hspcmp(exe版)のコードを読む 2-1　～membuf.cpp/.h編～

# 概要  
このシリーズはHSPの`.ax`ファイルの作成をするhspcmpを読んでいくものです。
わかったことをどんどん書き込んでいくスタイルでいきます。
間違いに気づいた方や、加筆すべきポイントを見つけた人はコメントで指摘してください。

あとここに掲載されたコードは
[Hot Soup Processor (HSP) / OpenHSP Copyright (c) 1997-2020, onion software/onitama in collaboration with Sencha, Yume-Yume Yuuka, Y-JINN, chobin, Usuaji, Kenji Yuukoku, puma, tom, sakura, fujidig, zakki, naznyark, Lonely Wolf, Shark++, HyperPageProject, Chokuto, S.Programs, Yuki, K-K, USK, NGND001, yoshis, naka, JET, eller, arue, mjhd_otsuka, tds12 All rights reserved.](https://github.com/onitama/OpenHSP)
から一部改変したものです。
必要に応じて元のコードからインデントを調整したり、コメントや空行を省いています。

あと、これは2023/06/23時点のコードになります。

:::note 
以前別名義で投稿していたものをほぼそのまま再投稿したものです。  
またこれらのコードはOpenHSP 3.6のコードに基づいているため、3.6正式版や最新(3.7β)のものと異なる可能性があることに注意してください。
:::

# membuf.cpp(membuf.h)
hspcmp.exe内で文字列を扱うためのバッファとして使われています。

また、使い方には大きく分けて以下の3つがあります。
- プリプロセスするファイルの格納
- プリプロセスとコンパイル時のメッセージの格納
- 生成する`.ax`ファイルの格納

今回はこの3つの使い方のうち、上二つに関係するメソッドなどを紹介します。

## CMemBufクラス
これがmembuf.cpp/hの本体です。
### フィールド(一部)
- int limit_size
    - [PreparePtrメソッド](#prepareptrメソッド)で拡張される分。値は`0x1000`か`0x4000`か`0x10000`のどれかになる。
- int size
    - `mem_buf`のバッファサイズ。
- int cur
    - `mem_buf`の使用(予約)済みのバッファサイズ。`size`を超えそうになったら[PreparePtrメソッド](#prepareptrメソッド)で`mem_buf`が拡張されます。
- char *mem_buf
    - `CMenBuf`のメインバッファ
- char name[256]
    - 解析されるファイル名

### コンストラクタ
2種類のコンストラクタがありますが、`mem_buf`フィールドのバッファサイズを4KBか16KBか64KBで初期化するように引数を取っているだけで、初期化の処理は[InitMenBufメソッド](#initmembufメソッド)に丸投げしています。
#### 引数(一つ目の方)
なし
#### コードを見てみる(一つ目の方)
```c++:membuf.cpp(299～304行目)
CMemBuf::CMemBuf( void )
{
	InitMemBuf( 0x10000 );
}
```
`mem_buf`フィールドのバッファサイズを64KBで初期化します。
#### 引数(二つ目の方)
- int sz
    - `mem_buf`フィールドを何byte分確保するか。結局4,16,64KBをしきい値にした値になる。最大で64KBになる。

#### コードを見てみる(二つ目の方)
```c++:membuf.cpp(307～312行目)
CMemBuf::CMemBuf( int sz )
{
	InitMemBuf( sz );
}
```
`mem_buf`フィールドのバッファサイズを`sz`byte(4,16,64KBをしきい値にして変更される)で初期化します。

### InitMemBufメソッド
ここでフィールドが初期化されます。
#### 引数
- int sz
    - `mem_buf`フィールドを何byte分確保するか。結局4,16,64KBをしきい値にした値になる。最大で64KBになる。
  
#### コードを見てみる
```c++:membuf.cpp(17～27行目)
void CMemBuf::InitMemBuf( int sz )
{
	size = sz;
	if ( size<0x1000 ) {
		size = 0x1000;
	} else if ( size<0x4000 ) {
		size = 0x4000;
	} else {
		size = 0x10000;
	}
```
ここでは、`size`フィールドに`sz`を代入してから`size`の値によって`size`を4,16,64KBのきりがいい値に変更しています。

```c++:membuf.cpp(28～38行目)
	limit_size = size;
	mem_buf = (char *)malloc( limit_size );
	mem_buf[0] = 0;	name[0] = 0;	cur = 0;
	idxflag = 0;	idxmax = -1;	curidx = 0;	idxbuf = NULL;
}
```
ここでフィールドを初期化しています。それぞれがどの値に初期化されるかはコードを見てください。

### デストラクタ
ここで、`mem_buf`を`free`を使って解放しています。
コードはみません。

### PreparePtrメソッド
`mem_buf`を引数で指定した分だけ使うときに、必要ならば`mem_buf`を`limit_size`byte拡張します。その後、`mem_buf`で使用されていない部分の一番最初へのポインタが返されます。
また、このメソッドの性質上`mem_buf`に文字を書き込む際は必ず呼び出されます。
#### 引数
- int sz
    - `mem_buf`を何byte使いたいか。
   
#### 使われている変数
- int i
    - `mem_buf`を拡張しなければならないときの新しい`mem_buf`のバッファサイズ
- char *p
    - [PreparePtrメソッド](#prepareptrメソッド)の返り値を保存します。 

#### コードを見てみる
```c++:membuf(51～63行目)
char *CMemBuf::PreparePtr( int sz )
{
	int i;
	char *p;
	if ( (cur+sz) < size ) {
		p = mem_buf + cur;
		cur += sz;
		return p;
	}
```
ここでは、`sz`の分だけ使っても構わないとき、`p`に`mem_buf`の最後の文字のポインタを代入し、`cur += sz`して`p`を返しています。

```c++:membuf.cpp(65～66行目)
	i = size;
	while( i<=(cur+sz) ) i+=limit_size;
```
ここからは`sz`文だけ使おうとすると`mem_buf`がオーバーフローすることになるので、`mem_buf`を拡張します。
ここでは、`mem_buf`のサイズがどれほど必要なのかを計算しています。また、切りよくするためか拡張する際は`limit_size`分だけ拡張するようになっています。

```c++:membuf.cpp(67～66行目)
	p = (char *)malloc( i );
	memcpy( p, mem_buf, size );
	free( mem_buf );
	size = i;
	mem_buf = p;
	p = mem_buf + cur;
	cur += sz;
	return p;
}
```
ここでは、拡張後のサイズ(`i`)がわかったので`p`をいったん`i`だけ確保してから`mem_buf`をコピーして、その後`mem_buf`に`P`を代入するといった形になっています。
あとは、`sz`の分だけ使っても構わないときと同じ処理をして終わります。

### Putメソッド
引数で指定したデータを`mem_buf`に書き込みます。
引数の種類によってたくさんあるのでそれぞれについて書きませんが、動作原理としては
1. `char *p`を用意する。
2. 引数のデータサイズを指定した[PreparePtrメソッド](#prepareptrメソッド)を呼び出し、帰ってきたポインタを`p`に代入。
3. `p`に`memcpy`などを使って引数で指定したデータを書き込む。
となります。
どのように実装されているかが気になる人は[OpenHSP/src/hspcmp/membuf.cpp](https://github.com/onitama/OpenHSP/blob/master/src/hspcmp/membuf.cpp#L108)を見てください。

### PutStrメソッド
これもPutメソッドと同じように動作します。
#### 引数
- char *data
    - `mem_buf`に書き込む文字列
   
#### 使われている変数
- char *p
    - [PreparePtrメソッド](#prepareptrメソッド)の返り値を保存します。
    
#### コードを見てみる
```c++:membuf.cpp(156～161行目)
void CMemBuf::PutStr( char *data )
{
	char *p;
	p = PreparePtr( strlen(data) );
	strcpy( p, data );
}
```
まあ前述の[`Put`メソッド](#putメソッド)との違いは、`p`に`data`書き込むときに`strcpy`を使っているぐらいの違いしかありません。

### PutStrBlockメソッド
PutStrメソッドとほぼ同じです。
#### 引数
- char *data
    - `mem_buf`に書き込む文字列

#### 使われている変数
[PutStrメソッド](#putstrメソッド)と同じ
#### コードを見てみる
```c++:membuf.cpp(206～211行目)
void CMemBuf::PutStrBlock( char *data )
{
	char *p;
	p = PreparePtr( strlen(data)+1 );
	strcpy( p, data );
}
```
[PutStrメソッド](#putstrメソッド)との違いは、書き込める文字数を一文字分多くしています。
なぜこんなことをしているのかは不明。([Additional HSP Template & Tools](http://www.onionsoft.net/hsp/v35/doclib/aht.txt)と関係ありそう。)

### PutCRメソッド
`mem_buf`に`\r\n`を追加します。
#### 使われている変数
[PutStrメソッド](#putstrメソッド)と同じ
#### コードを見てみる
```c++:membuf.cpp(214～219行目)
void CMemBuf::PutCR( void )
{
	char *p;
	p = PreparePtr( 2 );
	*p++ = 13; *p++ = 10;
}
```
[PreparePtrメソッド](#prepareptrメソッド)で`mem_buf`に必要があれば2文字分確保してから、`\r`(13)と`\n`(10)を書き込みます。

### PutDataメソッド
受け取ったポインタを`char`のポインタにキャストして、指定文字数分だけ`mem_buf`に代入します。
#### 引数
- void *data
    - `mem_buf`に書き込むデータ
- int sz
    - `mem_buf`に書き込む文字数

#### 使われている変数
[PutStrメソッド](#putstrメソッド)と同じ
#### コードを見てみる
```c++:membuf.cpp(222～227行目)
void CMemBuf::PutData( void *data, int sz )
{
	char *p;
	p = PreparePtr( sz );
	memcpy( p, (char *)data, sz );
}
```
[PreparePtrメソッド](#prepareptrメソッド)で`sz`分だけ必要があれば`mem_buf`を拡張して、`memcpy`で`data`を`sz`だけ`p`に書き込みます。

### PutStrfメソッド
フォーマット指定子を使った文字列を`mem_buf`に書き込みます。
#### 引数
- char *format
    - フォーマット指定子付き文字列
- 可変長引数(`...`)
    - フォーマットをする変数たち

#### 使われている変数
- va_list args
    - フォーマットをする変数たちが格納される？
- int c
- int space
- char *p
- int n

#### コードを見てみる
```c++:membuf.cpp(230～234行目)
#if ( WIN32 || _WIN32 ) && ! __CYGWIN__
# define VSNPRINTF _vsnprintf
#else
# define VSNPRINTF vsnprintf
#endif
```
ここでは`VSNPRINTF`を、Windows(Cygwinではない)なら`_vsnprintf`とし、それ以外(Linux)なら`vsnprintf`としています。

```c++:membuf.cpp(236～240行目)
void CMemBuf::PutStrf( char *format, ... )
{
	va_list args;
	int c = cur;
	int space = size - cur;
```
ここで使う変数を一部定義しています。`c`は`cur`(`mem_buf`のインデックス)を、`space`は`mem_buf`の残りバッファを示します。

```c++:membuf.cpp(241～244行目)
	while(1) {
		char *p = PreparePtr(space - 1);
		cur = c;
		space = size - cur;
```
ここで`p`に`mem_buf`を割り当てて`cur`をもと(`c`)に戻しています。
そして、`space`には`membuf`に書き込める文字数が代入されることになります。

```c++:membuf.cpp(245～248行目)
		int n;
		va_start(args, format);
		n = VSNPRINTF(p, space, format, args);
		va_end(args);
```
ここで、フォーマットに成功すれば`format`にフォーマットされた文字列が代入されて、`n`にその文字数が代入されて、失敗すれば`n`に`-1`が代入されます。

```c++:membuf.cpp(249～259行目)
		if ( 0 <= n && n < space ) {
			cur += n;
			return;
		}
		if ( 0 <= n ) {
			space = n + 1;
		} else {
			space *= 2;
		}
	}
}
```
ここで`n`の値に応じて繰り返したり、処理を終了したりします。

### PutFileメソッド
引数で指定したファイルが存在すれば`mem_buf`にファイルの内容を書き込みます。
ファイルが存在しない場合は`-1`が、ファイルが存在した場合はファイルサイズ(ファイルの文字数)が帰ります。
#### 引数
- char *fname
    - `mem_buf`に書き込みたいファイルのパス。

#### 使われている変数
- char *p
    - [PreparePtrメソッド](#prepareptrメソッド)の返り値を保存します。 
- int length
    - ファイルサイズ(ファイルの文字数)
- FILE *ff
    - ファイルを操作するときに必要な奴(C#でいうところの`Stream`に近い)

#### コードを見てみる
変数の定義箇所は省略します。

```c++:membuf.cpp(262～276行目)
int CMemBuf::PutFile( char *fname )
{
	//変数の定義箇所とコメントは省略
	ff=fopen( fname,"rb" );
	if (ff==NULL) return -1;
	fseek( ff,0,SEEK_END );
	length=(int)ftell( ff );
	fclose(ff);
	if (length < 0) return -1;
```
ここでファイルが存在するかどうかを調べて、存在した場合は`length`にファイルサイズ(ファイルの文字数)が代入され、存在しなければ`-1`で返ります。

```c++:membuf.cpp(277～281行目)
	p = PreparePtr( length+1 );
	ff=fopen( fname,"rb" );
	fread( p, 1, length, ff );
	fclose(ff);
	p[length]=0;
```
ここで、[PreparePtrメソッド](#prepareptrメソッド)で必要であれば`mem_buf`を`length`だけ拡張して、`p`に`mem_buf`のポインタを割り当てて、`fread`で読み取ったファイル内容を`p`(`mem_buf`)に書き込んでます。
そして、終端ヌル文字を代入しています。

```c++:membuf.cpp(283～285行目)
	strcpy( name,fname );
	return length;
}
```
最後に`name`にファイル名を書き込んで、`length`を返します。

# 次は
次回は、membuf.cpp/.hの残りを解説します。
[hspcmp(exe版)のコードを読む 2-2　～membuf.cpp/.h編～](https://qiita.com/aiueo-1234/items/031778a1c79985b1884a)