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

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

今回は[hspcmp(exe版)の最新のコードを読む 2-1　～membuf.cpp/.h編～](https://qiita.com/aiueo-1234/items/322666956088547ad0be)の続きになります。

あとここに掲載されたコードは
[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 idxflag
    - `idxbuf`が[InitIndexBufメソッド](#initindexbufメソッド)でメモリを確保されているかどうか。確保されていたら`1`、確保されていなければ`0`が代入されます。
- int *idxbuf
    - 配列的に使われています。
- int idxmax
    - `idxbuf`が何Byte分確保されているか。
- int curidx
    - `idxbuf`の最終インデックス番号。いまどれだけ`idxbuf`が使われているのかをも示します。

### AddIndexBufferメソッド
2種類のコンストラクタがありますが、[`idxbuf`](#フィールド一部)を256Byteか指定バイト分で初期化するように引数を取っているだけで、初期化の処理は[InitIndexBufメソッド](#initindexbufメソッド)に丸投げしています。
#### 引数(一つ目の方)
なし(void)
#### コードを見てみる(一つ目の方)
```c++:membuf.cpp(328～331行目)
void CMemBuf::AddIndexBuffer( void )
{
	InitIndexBuf( 256 );
}
```
[`idxbuf`](#フィールド一部)を256Byteで初期化することになります。
#### 引数(二つ目の方)
- int sz
    - [`idxbuf`](#フィールド一部)を何Byte確保するか。[コンストラクタ](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF)の時とは違って、指定した分だけそのまま確保されます。

#### コードを見てみる
```c++:membuf.cpp(334～337行目)
void CMemBuf::AddIndexBuffer( int sz )
{
	InitIndexBuf( sz );
}
```
[`idxbuf`](#フィールド一部)を`sz`Byteで初期化することになります。

### InitIndexBufメソッド
#### 引数
- int sz
    - [`idxbuf`](#フィールド一部)を何byte分確保するか。

#### コードを見てみる
```c++:membuf.cpp(41～48行目)
void CMemBuf::InitIndexBuf( int sz )
{
	//	Indexバッファ初期化
	idxflag = 1;
	idxmax = sz;
	curidx = 0;
	idxbuf = (int *)malloc( sizeof(int)*sz );
}
```
ここでフィールドを初期化しています。それぞれがどの値に初期化されるかはコードを見てください。

### デストラクタ
[`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)と[`idxbuf`](#フィールド一部)とを`free`を使って解放しています。
([前回のやつ](https://qiita.com/aiueo-1234/items/322666956088547ad0be)で一部紹介しています。)
#### コードを見てみる
```c++:membuf.cpp(315～325行目)
CMemBuf::~CMemBuf( void )
{
	if ( mem_buf != NULL ) {
		free( mem_buf );
		mem_buf = NULL;
	}
	if ( idxbuf != NULL ) {
		free( idxbuf );
		idxbuf = NULL;
	}
}
```
`membuf`は[InitMemBufメソッド](https://qiita.com/aiueo-1234/items/322666956088547ad0be#initmembuf%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89)で、[`idxbuf`](#フィールド一部)は[InitIndexBufメソッド](#initindexbufメソッド)で`malloc`を用いてメモリを確保しており`free`でメモリを解放する必要があるので、ここでそれをやっていますね。

### GetSizeメソッド
[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値を返します。
### GetBufferメソッド
[`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値を返します。
### GetBufferSizeメソッド
[`size`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値を返します。
### GetIndexBufferメソッド
[`idxbuf`](#フィールド一部)の値を返します。
### GetFileNameメソッド
[`name`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値を返します。

### GetIndexメソッド
指定したインデックスの値を返します。指定したインデックスがなければ0が返されます。
#### 引数
- int idx
    - 指定するインデックス番号

#### コードを見てみる
```c++:membuf.cpp(365～370行目)
int CMemBuf::GetIndex( int idx )
{
	if ( idxflag==0 ) return 0;
	if ((idx < 0) || ( idx>=curidx)) return 0;
	return idxbuf[idx];
}
```
ここでは、[`idxbuf`](#フィールド一部)が初期化されているかどうかを判断して指定するインデックスが本当に存在するのかを調べて、なければ`0`が返され、あればそのインデックスの値(`idxbuf[idx]`)が返されます。

### GetIndexBufferSizeメソッド
今[`idxbuf`](#フィールド一部)が何Byte使われているかを示します。そもそも[`idxbuf`](#フィールド一部)がメモリ確保されていなければ`-1`が返されます。
#### 引数
なし(void)
#### コードを見てみる
```c++:membuf.cpp(373～377行目)
int CMemBuf::GetIndexBufferSize( void )
{
	if ( idxflag==0 ) return -1;
	return curidx;
}
```
[`idxflag`](#フィールド一部)が`0`かどうかで[`idxbuf`](#フィールド一部)が確保されているかを判断して、確保されていれば[`curidx`](#フィールド一部)の値を返します。
なぜここで返す値が[`curidx`](#フィールド一部)でよいのかというと、[`curidx`](#フィールド一部)は[`idxflag`](#フィールド一部)のポインタの代わりのようなものであり、その値を調べることで現在[`idxflag`](#フィールド一部)が何Byte使われているのかがわかるからです。

### SearchIndexValueメソッド
指定した値が登録されているかどうかを調べ、登録されていれば[`idxbuf`](#フィールド一部)の何番目に登録されているかが、そうでなければ`-1`が返されます。
#### 引数
- int val
    - 調べる値

#### 使われている変数
- int i
    - `for`の添字。この値が返されます。

#### コードを見てみる
```c++:membuf.cpp(380～388行目)
int CMemBuf::SearchIndexValue( int val )
{
	int i;
	if ( idxflag==0 ) return -1;
	for(i=0;i< curidx;i++) {
		if ( idxbuf[i] == val ) return i;
	}
	return -1;
}
```
初めにそもそも[`idxbuf`](#フィールド一部)が初期化(メモリの確保)がされているかを確認し、確保されていなければ`-1`が返されます。
次に`for`で全探索して値がヒットすればその時の`i`の値が、ヒットしなければ`-1`が返されます。

### SearchIndexedDataメソッド
[`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の末尾と[`idexbuf`](#フィールド一部)の一つを合わせたものと指定した文字が指定したByte分が一致するかどうかを調べます。
一致しなければ`-1`が、一致すればその時合わせた[`idexbuf`](#フィールド一部)の値が返されます。
#### 引数
- char* data
    - 調べる文字列
- int size
    - 指定するByte数

#### 使われている変数
- int i
    - `for`で使う添字
- int j
    - この値が返される。
- int sz
    - `size`か、`data`の文字数(ヌル文字含む)

#### コードを見てみる
```c++:membuf.cpp(391～398行目)
int CMemBuf::SearchIndexedData(char* data, int size)
{
	int i;	int j;
	int sz = size;
	if (idxflag == 0) return -1;
	if (sz < 0) sz = (int)strlen(data)+1;
	if (sz == 0) return -1;
```
ここまでで、[`idxbuf`](#フィールド一部)の初期化(メモリの確保)がされているかを確認して、変数の定義や`sz`の設定をしています。

```c++:membuf.cpp(400～406行目)
	j = -1;
	for (i = 0; i < curidx; i++) {
		char* p = mem_buf + idxbuf[i];
		if (memcmp( p, data, sz )==0) j = idxbuf[i];
	}
	return j;
}
```
ここで[`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)のポインタと`idexbuf[i]`とを合わせたものと`data`が、初めから`sz`Byte分一致するかどうかを調べます。
一致すれば`idexbuf[i]`を、一致しなければ`-1`を返します。

### SetIndexメソッド
指定したインデックスに指定した値を代入します。
[`idxbuf`](#フィールド一部)の最後に登録したいときは、[RegistIndexメソッド](#registindexメソッド)を使います。
#### 引数
- int idx
    - 値を登録したい[`idxbuf`](#フィールド一部)の番号。
- int val
    - 登録したい値。

#### コードを見てみる
```c++:membuf.cpp(358～362行目)
void CMemBuf::SetIndex( int idx, int val )
{
	if ( idxflag==0 ) return;
	idxbuf[idx] = val;
}
```
初めにそもそも[`idxbuf`](#フィールド一部)が初期化(メモリの確保)がされているかを確認して値(`val`)を`idxbuf[idx]`に登録しています。

### RegistIndexメソッド
引数で指定した値を[`idxbuf`](#フィールド一部)の末尾に追加します。
#### 引数
- int val
    - [`idxbuf`](#フィールド一部)に追加する値。

#### 使われている変数
- int *p
    - これを操作して値を代入する。

#### コードを見てみる
```c++:membuf.cpp(78～91行目)
void CMemBuf::RegistIndex( int val )
{
	int *p;
	if ( idxflag==0 ) return;
	idxbuf[ curidx++ ]= val;
	if ( curidx >= idxmax ) {
		idxmax+=256;
		p = (int *)malloc( sizeof(int)*idxmax );
		memcpy( p, idxbuf, sizeof(int)*curidx );
		free( idxbuf );
		idxbuf = p;
	}
}
```
初めにそもそも[`idxbuf`](#フィールド一部)が初期化(メモリの確保)がされているかを確認して値(`val`)を`idxbuf[curidx++]`に登録しています。
そのあと、次に書き込むときに[`curidx`](#フィールド一部)が[`idxbuf`](#フィールド一部)のメモリ確保分を超えてしまうかどうかチェックして、超えていたら256Byte追加で[`idxbuf`](#フィールド一部)を確保します。

### Indexメソッド
引数が[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の[RegistIndexメソッド](#registindexメソッド)を呼び出します。
つまり、[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値を追加します。

### IndexExclusiveメソッド
現在の[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値が登録されているかどうかを確認して、登録されていなければその値を[`idxbuf`](#フィールド一部)の末尾に追加します。
#### 引数
なし(void)
#### コードを見てみる
```c++:membuf.cpp(100～105行目)
void CMemBuf::IndexExclusive(void)
{
	if (SearchIndexValue(cur) < 0) {
		RegistIndex(cur);
	}
}
```
[SearchIndexValueメソッド](#searchindexvalueメソッド)で[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値が登録されているかどうかを確認して、登録されていなければその値を[RegistIndexメソッド](#registindexメソッド)で登録してます。

### ReduceSizeメソッド
新しい[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値を指定します。
#### 引数
- int new_cur
    - 新しい[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の値

#### コードを見てみる
```c++:membuf.cpp(288～292行目)
void CMemBuf::ReduceSize( int new_cur )
{
	assert( new_cur >= 0 && new_cur <= cur );
	cur = new_cur;
}
```
単に[`cur`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)に`new_cur`を代入するだけです。

### PutStrDQメソッド
ダブルクオート内のデータを[`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)に書き込みます。
執筆時点ではこのメソッドはhspcmp.exe内で使用されていません。
##### 引数
- char *data
    - [`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)に書き込むダブルクオート内の文字列。

### SaveFileメソッド
[`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)の内容を指定したファイルに書き出します。
書き出しに成功すればファイルサイズが、失敗すれば`-1`が返されます。
#### 引数
- char *fname
    - セーブするときのファイル名

#### 使われている変数
- FILE *fp
    - ファイルを操作するときに必要な奴。(C#でいうところの`Stream`に近い)
- int flen
    - 書き込んだファイルのファイルサイズ。

#### コードを見てみる

```c++:membuf.cpp(409～417行目)
int CMemBuf::SaveFile( char *fname )
{
	FILE *fp;
	int flen;
	fp=fopen(fname,"wb");
	if (fp==NULL) return -1;
```
ここまででファイルを開いて`fp`を設定しています。
もしファイルが見つからなかったときは`-1`を返すようになっています。

```c++:membuf.cpp(418～422行目)
	flen = fwrite( mem_buf, 1, cur, fp );
	fclose(fp);
	strcpy( name,fname );
	return flen;
}
```
ここでファイル`fp`に[`mem_buf`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)を書き込んで、ファイル操作を終了しています。
そのあと[`name`](https://qiita.com/aiueo-1234/items/322666956088547ad0be#%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E4%B8%80%E9%83%A8)に`fname`をコピーして書き込んだファイルの(実質的に)ファイルサイズを返しています。

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