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?

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-06-23

概要

このシリーズは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.
から一部改変したものです。
必要に応じて元のコードからインデントを調整したり、コメントや空行を省いています。

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

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

membuf.cpp(membuf.h)

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

また、使い方には大きく分けて以下の3つがあります。

  • プリプロセスするファイルの格納
  • プリプロセスとコンパイル時のメッセージの格納
  • 生成する.axファイルの格納

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

CMemBufクラス

これがmembuf.cpp/hの本体です。

フィールド(一部)

  • int limit_size
  • int size
    • mem_bufのバッファサイズ。
  • int cur
    • mem_bufの使用(予約)済みのバッファサイズ。sizeを超えそうになったらPreparePtrメソッドmem_bufが拡張されます。
  • char *mem_buf
    • CMenBufのメインバッファ
  • char name[256]
    • 解析されるファイル名

コンストラクタ

2種類のコンストラクタがありますが、mem_bufフィールドのバッファサイズを4KBか16KBか64KBで初期化するように引数を取っているだけで、初期化の処理はInitMenBufメソッドに丸投げしています。

引数(一つ目の方)

なし

コードを見てみる(一つ目の方)

membuf.cpp(299~304行目)
CMemBuf::CMemBuf( void )
{
	InitMemBuf( 0x10000 );
}

mem_bufフィールドのバッファサイズを64KBで初期化します。

引数(二つ目の方)

  • int sz
    • mem_bufフィールドを何byte分確保するか。結局4,16,64KBをしきい値にした値になる。最大で64KBになる。

コードを見てみる(二つ目の方)

membuf.cpp(307~312行目)
CMemBuf::CMemBuf( int sz )
{
	InitMemBuf( sz );
}

mem_bufフィールドのバッファサイズをszbyte(4,16,64KBをしきい値にして変更される)で初期化します。

InitMemBufメソッド

ここでフィールドが初期化されます。

引数

  • int sz
    • mem_bufフィールドを何byte分確保するか。結局4,16,64KBをしきい値にした値になる。最大で64KBになる。

コードを見てみる

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のきりがいい値に変更しています。

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_buffreeを使って解放しています。
コードはみません。

PreparePtrメソッド

mem_bufを引数で指定した分だけ使うときに、必要ならばmem_buflimit_sizebyte拡張します。その後、mem_bufで使用されていない部分の一番最初へのポインタが返されます。
また、このメソッドの性質上mem_bufに文字を書き込む際は必ず呼び出されます。

引数

  • int sz
    • mem_bufを何byte使いたいか。

使われている変数

  • int i
    • mem_bufを拡張しなければならないときの新しいmem_bufのバッファサイズ
  • char *p

コードを見てみる

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の分だけ使っても構わないとき、pmem_bufの最後の文字のポインタを代入し、cur += szしてpを返しています。

membuf.cpp(65~66行目)
	i = size;
	while( i<=(cur+sz) ) i+=limit_size;

ここからはsz文だけ使おうとするとmem_bufがオーバーフローすることになるので、mem_bufを拡張します。
ここでは、mem_bufのサイズがどれほど必要なのかを計算しています。また、切りよくするためか拡張する際はlimit_size分だけ拡張するようになっています。

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_bufPを代入するといった形になっています。
あとは、szの分だけ使っても構わないときと同じ処理をして終わります。

Putメソッド

引数で指定したデータをmem_bufに書き込みます。
引数の種類によってたくさんあるのでそれぞれについて書きませんが、動作原理としては

  1. char *pを用意する。
  2. 引数のデータサイズを指定したPreparePtrメソッドを呼び出し、帰ってきたポインタをpに代入。
  3. pmemcpyなどを使って引数で指定したデータを書き込む。
    となります。
    どのように実装されているかが気になる人はOpenHSP/src/hspcmp/membuf.cppを見てください。

PutStrメソッド

これもPutメソッドと同じように動作します。

引数

  • char *data
    • mem_bufに書き込む文字列

使われている変数

コードを見てみる

membuf.cpp(156~161行目)
void CMemBuf::PutStr( char *data )
{
	char *p;
	p = PreparePtr( strlen(data) );
	strcpy( p, data );
}

まあ前述のPutメソッドとの違いは、pdata書き込むときにstrcpyを使っているぐらいの違いしかありません。

PutStrBlockメソッド

PutStrメソッドとほぼ同じです。

引数

  • char *data
    • mem_bufに書き込む文字列

使われている変数

PutStrメソッドと同じ

コードを見てみる

membuf.cpp(206~211行目)
void CMemBuf::PutStrBlock( char *data )
{
	char *p;
	p = PreparePtr( strlen(data)+1 );
	strcpy( p, data );
}

PutStrメソッドとの違いは、書き込める文字数を一文字分多くしています。
なぜこんなことをしているのかは不明。(Additional HSP Template & Toolsと関係ありそう。)

PutCRメソッド

mem_buf\r\nを追加します。

使われている変数

PutStrメソッドと同じ

コードを見てみる

membuf.cpp(214~219行目)
void CMemBuf::PutCR( void )
{
	char *p;
	p = PreparePtr( 2 );
	*p++ = 13; *p++ = 10;
}

PreparePtrメソッドmem_bufに必要があれば2文字分確保してから、\r(13)と\n(10)を書き込みます。

PutDataメソッド

受け取ったポインタをcharのポインタにキャストして、指定文字数分だけmem_bufに代入します。

引数

  • void *data
    • mem_bufに書き込むデータ
  • int sz
    • mem_bufに書き込む文字数

使われている変数

PutStrメソッドと同じ

コードを見てみる

membuf.cpp(222~227行目)
void CMemBuf::PutData( void *data, int sz )
{
	char *p;
	p = PreparePtr( sz );
	memcpy( p, (char *)data, sz );
}

PreparePtrメソッドsz分だけ必要があればmem_bufを拡張して、memcpydataszだけpに書き込みます。

PutStrfメソッド

フォーマット指定子を使った文字列をmem_bufに書き込みます。

引数

  • char *format
    • フォーマット指定子付き文字列
  • 可変長引数(...)
    • フォーマットをする変数たち

使われている変数

  • va_list args
    • フォーマットをする変数たちが格納される?
  • int c
  • int space
  • char *p
  • int n

コードを見てみる

membuf.cpp(230~234行目)
#if ( WIN32 || _WIN32 ) && ! __CYGWIN__
# define VSNPRINTF _vsnprintf
#else
# define VSNPRINTF vsnprintf
#endif

ここではVSNPRINTFを、Windows(Cygwinではない)なら_vsnprintfとし、それ以外(Linux)ならvsnprintfとしています。

membuf.cpp(236~240行目)
void CMemBuf::PutStrf( char *format, ... )
{
	va_list args;
	int c = cur;
	int space = size - cur;

ここで使う変数を一部定義しています。ccur(mem_bufのインデックス)を、spacemem_bufの残りバッファを示します。

membuf.cpp(241~244行目)
	while(1) {
		char *p = PreparePtr(space - 1);
		cur = c;
		space = size - cur;

ここでpmem_bufを割り当ててcurをもと(c)に戻しています。
そして、spaceにはmembufに書き込める文字数が代入されることになります。

membuf.cpp(245~248行目)
		int n;
		va_start(args, format);
		n = VSNPRINTF(p, space, format, args);
		va_end(args);

ここで、フォーマットに成功すればformatにフォーマットされた文字列が代入されて、nにその文字数が代入されて、失敗すればn-1が代入されます。

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
  • int length
    • ファイルサイズ(ファイルの文字数)
  • FILE *ff
    • ファイルを操作するときに必要な奴(C#でいうところのStreamに近い)

コードを見てみる

変数の定義箇所は省略します。

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で返ります。

membuf.cpp(277~281行目)
	p = PreparePtr( length+1 );
	ff=fopen( fname,"rb" );
	fread( p, 1, length, ff );
	fclose(ff);
	p[length]=0;

ここで、PreparePtrメソッドで必要であればmem_buflengthだけ拡張して、pmem_bufのポインタを割り当てて、freadで読み取ったファイル内容をp(mem_buf)に書き込んでます。
そして、終端ヌル文字を代入しています。

membuf.cpp(283~285行目)
	strcpy( name,fname );
	return length;
}

最後にnameにファイル名を書き込んで、lengthを返します。

次は

次回は、membuf.cpp/.hの残りを解説します。
hspcmp(exe版)のコードを読む 2-2 ~membuf.cpp/.h編~

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?