PHP extension development-array usage and HashTable introduction. 1 array This section describes php arrays. in php, arrays are implemented using HashTable. This section describes HashTable in detail, and then how to use the HastTable1.1 1 array.
This section describes php arrays. in php, arrays are implemented using HashTable. This section describes HashTable in detail, and then describes how to use HastTable.
1.1 Variable-length struct
The so-called variable length struct is actually a special usage of the C language struct, and there is no novelty. Let's take a look at a general definition method of the variable length struct.
Typedef struct bucket {
Int n;
Char key [30];
Char value [1];
} Bucket;
We have defined a struct Bucket. we want to use this struct to store the student's profile. The key is used to store the student's name, and the value is used to store the student's profile. You may be curious. our value declares how much information can be stored in 1. 1 char?
In fact, for variable-length struct, we cannot directly define variables in use, for example, Bucket bucket. if you use them like this, the value certainly cannot store much information. For a variable-length struct, we need to declare a pointer to the variable-length struct before allocating the function space through the malloc function. what is the length of the space we need, we can use malloc. The general usage is as follows:
Bucket * pBucket;
PBucket = malloc (sizeof (Bucket) + n * sizeof (char ));
N indicates the length of the value. If this is used, will the string pointed to by value become longer soon!
1.2 Introduction to Hashtable
Let's take a look at the definition of HashTable.
Struct _ hashtable;
Typedef struct bucket {
Ulong h; // used when the element is a digital index
Uint nKeyLength; // when a string index is used, this variable indicates the index length, and the index (string) is stored in the last element aKey.
Void * pData; // used to point to the stored data. if the stored data is a pointer, pDataPtr points to the data, and pData points to pDataPtr.
Void * pDataPtr;
Struct bucket * pListNext; // previous element
Struct bucket * pListLast; // The next element
Struct bucket * pNext; // pointer to the next bucket
Struct bucket * pLast; // pointer to the previous bucket
Char arKey [1]; // it must be placed at the end to implement a variable-length struct.
} Bucket;
Typedef struct _ hashtable {
Uint nTableSize; // The size of the hash table
Uint nTableMask; // The value is equal to nTableSize-1.
Uint nNumOfElements; // records the number of records saved in the current HashTable
Ulong nNextFreeElement; // point to the next idle Bucket
Bucket * pInternalPointer; // This variable is used for array inversion.
Bucket * pListHead; // The header pointing to the Bucket
Bucket * pListTail; // point to the end of the Bucket
Bucket ** arBuckets;
Dtor_func_t pDestructor; // Function pointer, which is automatically called during addition, deletion, modification, and query of arrays for some cleanup operations
Zend_bool persistent; // whether it is persistent
Unsigned char nApplyCount;
Zend_bool bApplyProtection; // Works with nApplyCount to prevent infinite recursion of array traversal
# If ZEND_DEBUG
Int inconsistent;
# Endif
} HashTable;
I hope you can take a good look at the above definition. I will not understand some things when I come out. it is better to see the code clearly. PHP arrays are actually two-way linked lists with header nodes. HashTable is the header, and the Bucket stores the specific node information.
1.3 HashTable internal function analysis
1.3.1 macro HASH_PROTECT_RECURSION
# DefineHASH_PROTECT_RECURSION (ht )\
If (ht)-> bApplyProtection ){\
If (ht)-> nApplyCount ++> = 3 ){\
Zend_error (E_ERROR, "Nestinglevel too deep-recursive dependency? ");\
}\
}
This macro is mainly used to prevent circular references.
1.3.2 macro ZEND_HASH_IF_FULL_DO_RESIZE
# DefineZEND_HASH_IF_FULL_DO_RESIZE (ht )\
If (ht)-> nNumOfElements> (ht)-> nTableSize ){\
Zend_hash_do_resize (ht );\
}
This macro is used to check whether the number of elements in HashTable is greater than the total size of HashTable. if the number is greater than the size of HashTable, we will re-allocate the space. Let's take a look at zend_hash_do_resize.
Static int zend_hash_do_resize (HashTable * ht)
{
Bucket ** t;
IS_CONSISTENT (ht );
If (ht-> nTableSize <1)> 0) {/* Let's double the table size */
T = (Bucket **) perealloc_recoverable (ht-> arBuckets,
(Ht-> nTableSize <1) * sizeof (Bucket *), ht-> persistent );
If (t ){
HANDLE_BLOCK_INTERRUPTIONS ();
Ht-> arBuckets = t;
Ht-> nTableSize = (ht-> nTableSize <1 );
Ht-> nTableMask = ht-> nTableSize-1;
Zend_hash_rehash (ht );
HANDLE_UNBLOCK_INTERRUPTIONS ();
Return SUCCESS;
}
Return FAILURE;
}
Return SUCCESS;
}
From the code above, we can see that when HashTable allocates space, the newly allocated space is twice the original space.
1.3.3 function _ zend_hash_init
This function is used to initialize HashTable. let's take a look at the code:
ZEND_API int _ zend_hash_init (HashTable * ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
{
Uint I = 3; // The HashTable size defaults to no power 3 of 2
Bucket ** tmp;
SET_INCONSISTENT (HT_ OK );
If (nSize> = 0x80000000 ){
Ht-> nTableSize = 0x80000000;
} Else {
While (1U <I) <nSize ){
I ++;
}
Ht-> nTableSize = 1 <I;
}
Ht-> nTableMask = ht-> nTableSize-1;
Ht-> pDestructor = pDestructor;
Ht-> arBuckets = NULL;
Ht-> pListHead = NULL;
Ht-> pListTail = NULL;
Ht-> nNumOfElements = 0;
Ht-> nNextFreeElement = 0;
Ht-> pInternalPointer = NULL;
Ht-> persistent = persistent;
Ht-> nApplyCount = 0;
Ht-> bApplyProtection = 1;
/* Uses ecalloc () so that Bucket * = NULL */
If (persistent ){
Tmp = (Bucket **) calloc (ht-> nTableSize, sizeof (Bucket *));
If (! Tmp ){
Return FAILURE;
}
Ht-> arBuckets = tmp;
} Else {
Tmp = (Bucket **) ecalloc_rel (ht-> nTableSize, sizeof (Bucket *));
If (tmp ){
Ht-> arBuckets = tmp;
}
}
Return SUCCESS;
}
We can see that the HashTable size is initialized to the npower of 2. In addition, we can see two memory modes: calloc and ecalloc_rel, the memory allocation methods are described in detail. if you are interested, you can check them by yourself.
1.3.4 function _ zend_hash_add_or_update
This function adds or modifies element information to HashTable.
ZEND_API int _ zend_hash_add_or_update (HashTable * ht, const char * arKey, uint nKeyLength, void * pData, uint nDataSize, void ** pDest, int flag ZEND_FILE_LINE_DC)
{
Ulong h;
Uint nIndex;
Bucket * p;
IS_CONSISTENT (ht );
If (nKeyLength <= 0 ){
# If ZEND_DEBUG
ZEND_PUTS ("zend_hash_update: Can't put inempty key \ n ");
# Endif
Return FAILURE;
}
H = zend_inline_hash_func (arKey, nKeyLength );
NIndex = h & ht-> nTableMask;
P = ht-> arBuckets [nIndex];
While (p! = NULL ){
If (p-> h = h) & (p-> nKeyLength = nKeyLength )){
If (! Memcmp (p-> arKey, arKey, nKeyLength )){
If (flag & HASH_ADD ){
Return FAILURE;
}
HANDLE_BLOCK_INTERRUPTIONS ();
# If ZEND_DEBUG
If (p-> pData = pData ){
ZEND_PUTS ("Fatal error in zend_hash_update: p-> pData = pData \ n ");
HANDLE_UNBLOCK_INTERRUPTIONS ();
Return FAILURE;
}
# Endif
If (ht-> pDestructor ){
Ht-> pDestructor (p-> pData );
}
UPDATE_DATA (ht, p, pData, nDataSize );
If (pDest ){
* PDest = p-> pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS ();
Return SUCCESS;
}
}
P = p-> pNext;
}
P = (Bucket *) pemalloc (sizeof (Bucket)-1 + nKeyLength, ht-> persistent );
If (! P ){
Return FAILURE;
}
Memcpy (p-> arKey, arKey, nKeyLength );
P-> nKeyLength = nKeyLength;
INIT_DATA (ht, p, pData, nDataSize );
P-> h = h;
CONNECT_TO_BUCKET_DLLIST (p, ht-> arBuckets [nIndex]);
If (pDest ){
* PDest = p-> pData;
}
HANDLE_BLOCK_INTERRUPTIONS ();
CONNECT_TO_GLOBAL_DLLIST (p, ht );
Ht-> arBuckets [nIndex] = p;
HANDLE_UNBLOCK_INTERRUPTIONS ();
Ht-> nNumOfElements ++;
ZEND_HASH_IF_FULL_DO_RESIZE (ht);/* If the Hash table is full, resize it */
Return SUCCESS;
}
1.3.5 macro CONNECT_TO_BUCKET_DLLIST
# Define CONNECT_TO_BUCKET_DLLIST (element, list_head )\
(Element)-> pNext = (list_head );\
(Element)-> pLast = NULL ;\
If (element)-> pNext ){\
(Element)-> pNext-> pLast = (element );\
}
This macro adds the bucket to the bucket linked list.
1.3.6 Other functions or macro definitions
We will just briefly introduce HashTable. if you want to learn more about HashTable, we suggest you look at the php source code. the HashTable code is in Zend/zend_hash.h and Zend/zend_hash.c.
Zend_hash_add_empty_element adds an empty element to the function.
Zend_hash_del_key_or_index deletes elements based on the index.
Zend_hash_reverse_apply reverse traversal HashTable
Zend_hash_copy copy
_ Zend_hash_merge merge
Zend_hash_find string index lookup
Zend_hash_index_find numeric index method
Encapsulation of the two functions above zend_hash_quick_find
Zend_hash_exists index
Zend_hash_index_exists index
Encapsulation of the two methods above zend_hash_quick_exists
1.4 C extension commonly used HashTable functions
Although HashTable looks a little complicated, it is very convenient to use. we can use the following function to initialize and assign values to HashTable.
Number of local students enrolled in 2005
PHP syntax
C syntax
Meaning
$ Arr = array ()
Array_init (arr );
Initialize an array
$ Arr [] = NULL;
Add_next_index_null (arr );
$ Arr [] = 42;
Add_next_index_long (arr, 42 );
$ Arr [] = true;
Add_next_index_bool (arr, 1 );
$ Arr [] = 3.14;
Add_next_index_double (3.14 );
$ Arr [] = 'foo ';
Add_next_index_string (arr, "foo", 1 );
1 indicates copying strings.
$ Arr [] = $ myvar;
Add_next_index_zval (arr, myvar );
$ Arr [0] = NULL;
Add_index_null (arr, 0 );
$ Arr [1] = 42;
Add_index_long (arr, 1, 42 );
$ Arr [2] = true;
Add_index_bool (arr, 2, 1 );
$ Arr [3] = 3.14;
Add_index_double (arr, 3, 3, 14 );
$ Arr [4] = 'foo ';
Add_index_string (arr, 4, "foo", 1 );
$ Arr [5] = $ myvar;
Add_index_zval (arr, 5, myvar );
$ Arr ["abc"] = NULL;
Add_assoc_null (arr, "abc ");
$ Arr ["def"] = 711;
Add_assoc_long (arr, "def", 711 );
$ Arr ["ghi"] = true;
Add_assoc_bool (arr, ghi ", 1 );
$ Arr ["jkl"] = 1.44;
Add_assoc_double (arr, "jkl", 1.44 );
$ Arr ["mno"] = 'Baz ';
Add_assoc_string (arr, "mno", "baz", 1 );
$ Arr ['pqr '] = $ myvar;
Add_assoc_zval (arr, "pqr", myvar );
1.5 tasks and labs
Let's experiment with it.
Task: returns an array with the following data:
Array
(
[0] => for test
[42] = & gt; 123
[For test. for test.] => 1
[Array] => Array
(
[0] = & gt; 3.34
)
)
Code implementation:
PHP_FUNCTION (test)
{
Zval * t;
Array_init (return_value );
Add_next_index_string (return_value, "for test", 1 );
Add_index_long (return_value, 42,123 );
Add_assoc_double (return_value, "for test. for test.", 1.0 );
ALLOC_INIT_ZVAL (t );
Array_init (t );
Add_next_index_double (TB, 3.34 );
Add_assoc_zval (return_value, "array", t );
}
Simple, remember return_value?
Http://www.bkjia.com/PHPjc/477779.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/477779.htmlTechArticle1 array in this section we will talk about the php array, in php, the array is implemented using HashTable. This section describes HashTable in detail, and then describes how to use HastTable 1. 1...