ハッシュテーブルのチェイン法を見るまでの記事でデータ構造のハッシュテーブルを見た。

ハッシュテーブルはPHPの変数や関数の登録の際にも利用されているので、PHPのハッシュテーブルを見る。


ソースコードは今日現在のGitHub - php/php-src: The PHP Interpreterのソースコードとする。

早速、HashTableで検索をしたら、

php-src/Zend/zend_types.h

typedef struct _zend_array HashTable;

となっていた。

_zend_array構造体をHashTableと定義する?


更に読み進めてみると、

struct _zend_array {
	zend_refcounted_h gc;
	union {
		struct {
			ZEND_ENDIAN_LOHI_4(
				zend_uchar    flags,
				zend_uchar    _unused,
				zend_uchar    nIteratorsCount,
				zend_uchar    _unused2)
		} v;
		uint32_t flags;
	} u;
	uint32_t          nTableMask;
	Bucket           *arData;
	uint32_t          nNumUsed;
	uint32_t          nNumOfElements;
	uint32_t          nTableSize;
	uint32_t          nInternalPointer;
	zend_long         nNextFreeElement;
	dtor_func_t       pDestructor;
};

/*
 * HashTable Data Layout
 * =====================
 *
 *                 +=============================+
 *                 | HT_HASH(ht, ht->nTableMask) |
 *                 | ...                         |
 *                 | HT_HASH(ht, -1)             |
 *                 +-----------------------------+
 * ht->arData ---> | Bucket[0]                   |
 *                 | ...                         |
 *                 | Bucket[ht->nTableSize-1]    |
 *                 +=============================+
 */

_zend_array構造体と一緒にハッシュテーブルのデータ構造の説明があった。

nTableMaskは難しい問題があるらしいので今は触れない事にして、もう一つのBucketの方に触れる。


typedef struct _Bucket {
	zval              val;
	zend_ulong        h;                /* hash value (or numeric index)   */
	zend_string      *key;              /* string key or NULL for numerics */
} Bucket;

上記はBucket構造体で、ハッシュテーブルのチェイン法で見たようなnextの値等はない。

Bucketの値から何らかの方法でハッシュ値を計算して、arDataの任意の箇所にBucketを格納するという認識で間違いないだろうか?




typedef struct _Bucket {
	zval              val;
	zend_ulong        h;                /* hash value (or numeric index)   */
	zend_string      *key;              /* string key or NULL for numerics */
} Bucket;

次にBucketの方を見る。

zvalの箇所に関数型のzvalをセットするということになるだろうから、それに該当するものを探してみる。

zvalはzend_valueを含む構造体であったので、

typedef union _zend_value {
	zend_long         lval;				/* long value */
	double            dval;				/* double value */
	zend_refcounted  *counted;
	zend_string      *str;
	zend_array       *arr;
	zend_object      *obj;
	zend_resource    *res;
	zend_reference   *ref;
	zend_ast_ref     *ast;
	zval             *zv;
	void             *ptr;
	zend_class_entry *ce;
	zend_function    *func;
	struct {
		uint32_t w1;
		uint32_t w2;
	} ww;
} zend_value;

zend_valueの方を見ると、zend_valueは共用体であって、どれか一つのメンバを使用することになっていたはず。




zend_functionで検索してみると、

php-src/Zend/zend_compile.c

union _zend_function {
	zend_uchar type;	/* MUST be the first element of this struct! */
	uint32_t   quick_arg_flags;

	struct {
		zend_uchar type;  /* never used */
		zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */
		uint32_t fn_flags;
		zend_string *function_name;
		zend_class_entry *scope;
		zend_function *prototype;
		uint32_t num_args;
		uint32_t required_num_args;
		zend_arg_info *arg_info;  /* index -1 represents the return value info, if any */
		HashTable   *attributes;
	} common;

	zend_op_array op_array;
	zend_internal_function internal_function;
};

共用体であった。

今の知識レベルだと、この共用体の解釈をどうすれば良いのかわからないので、今回はここまでにしておく。


今回の内容を整理すると、関数を登録するためのハッシュテーブルがあって、

HashTable - Bucket - zval - zend_value - zend_functionと関数を登録するだけでもいくつかデータをつなげている事がわかった。


わからない事が多いなと痛感する…