5682 строки
		
	
	
		
			164 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			5682 строки
		
	
	
		
			164 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language.
 | 
						|
 * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/
 | 
						|
 * Version 2.1.4
 | 
						|
 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES
 | 
						|
 * please contact Symisc Systems via:
 | 
						|
 *       legal@symisc.net
 | 
						|
 *       licensing@symisc.net
 | 
						|
 *       contact@symisc.net
 | 
						|
 * or visit:
 | 
						|
 *      http://ph7.symisc.net/
 | 
						|
 */
 | 
						|
/* $SymiscID: hashmap.c v3.5 FreeBSD 2012-08-07 08:29 stable <chm@symisc.net> $ */
 | 
						|
#include "ph7int.h"
 | 
						|
/* This file implement generic hashmaps known as 'array' in the PHP world */
 | 
						|
/* Allowed node types */
 | 
						|
#define HASHMAP_INT_NODE   1  /* Node with an int [i.e: 64-bit integer] key */
 | 
						|
#define HASHMAP_BLOB_NODE  2  /* Node with a string/BLOB key */
 | 
						|
/* Node control flags */
 | 
						|
#define HASHMAP_NODE_FOREIGN_OBJ 0x001 /* Node hold a reference to a foreign ph7_value
 | 
						|
                                        * [i.e: array(&var)/$a[] =& $var ]
 | 
						|
										*/
 | 
						|
/*
 | 
						|
 * Default hash function for int [i.e; 64-bit integer] keys.
 | 
						|
 */
 | 
						|
static sxu32 IntHash(sxi64 iKey) {
 | 
						|
	return (sxu32)(iKey ^ (iKey << 8) ^ (iKey >> 8));
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Default hash function for string/BLOB keys.
 | 
						|
 */
 | 
						|
static sxu32 BinHash(const void *pSrc, sxu32 nLen) {
 | 
						|
	register unsigned char *zIn = (unsigned char *)pSrc;
 | 
						|
	unsigned char *zEnd;
 | 
						|
	sxu32 nH = 5381;
 | 
						|
	zEnd = &zIn[nLen];
 | 
						|
	for(;;) {
 | 
						|
		if(zIn >= zEnd) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		nH = nH * 33 + zIn[0] ;
 | 
						|
		zIn++;
 | 
						|
		if(zIn >= zEnd) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		nH = nH * 33 + zIn[0] ;
 | 
						|
		zIn++;
 | 
						|
		if(zIn >= zEnd) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		nH = nH * 33 + zIn[0] ;
 | 
						|
		zIn++;
 | 
						|
		if(zIn >= zEnd) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		nH = nH * 33 + zIn[0] ;
 | 
						|
		zIn++;
 | 
						|
	}
 | 
						|
	return nH;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Return the total number of entries in a given hashmap.
 | 
						|
 * If bRecurisve is set to TRUE then recurse on hashmap entries.
 | 
						|
 * If the nesting limit is reached,this function abort immediately.
 | 
						|
 */
 | 
						|
static sxi64 HashmapCount(ph7_hashmap *pMap, int bRecursive, int iRecCount) {
 | 
						|
	sxi64 iCount = 0;
 | 
						|
	if(!bRecursive) {
 | 
						|
		iCount = pMap->nEntry;
 | 
						|
	} else {
 | 
						|
		/* Recursive hashmap walk */
 | 
						|
		ph7_hashmap_node *pEntry = pMap->pLast;
 | 
						|
		ph7_value *pElem;
 | 
						|
		sxu32 n = 0;
 | 
						|
		for(;;) {
 | 
						|
			if(n >= pMap->nEntry) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			/* Point to the element value */
 | 
						|
			pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj, pEntry->nValIdx);
 | 
						|
			if(pElem) {
 | 
						|
				if(pElem->iFlags & MEMOBJ_HASHMAP) {
 | 
						|
					if(iRecCount > 31) {
 | 
						|
						/* Nesting limit reached */
 | 
						|
						return iCount;
 | 
						|
					}
 | 
						|
					/* Recurse */
 | 
						|
					iRecCount++;
 | 
						|
					iCount += HashmapCount((ph7_hashmap *)pElem->x.pOther, TRUE, iRecCount);
 | 
						|
					iRecCount--;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			/* Point to the next entry */
 | 
						|
			pEntry = pEntry->pNext;
 | 
						|
			++n;
 | 
						|
		}
 | 
						|
		/* Update count */
 | 
						|
		iCount += pMap->nEntry;
 | 
						|
	}
 | 
						|
	return iCount;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Allocate a new hashmap node with a 64-bit integer key.
 | 
						|
 * If something goes wrong [i.e: out of memory],this function return NULL.
 | 
						|
 * Otherwise a fresh [ph7_hashmap_node] instance is returned.
 | 
						|
 */
 | 
						|
static ph7_hashmap_node *HashmapNewIntNode(ph7_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	/* Allocate a new node */
 | 
						|
	pNode = (ph7_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(ph7_hashmap_node));
 | 
						|
	if(pNode == 0) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	/* Zero the stucture */
 | 
						|
	SyZero(pNode, sizeof(ph7_hashmap_node));
 | 
						|
	/* Fill in the structure */
 | 
						|
	pNode->pMap  = &(*pMap);
 | 
						|
	pNode->iType = HASHMAP_INT_NODE;
 | 
						|
	pNode->nHash = nHash;
 | 
						|
	pNode->xKey.iKey = iKey;
 | 
						|
	pNode->nValIdx  = nValIdx;
 | 
						|
	return pNode;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Allocate a new hashmap node with a BLOB key.
 | 
						|
 * If something goes wrong [i.e: out of memory],this function return NULL.
 | 
						|
 * Otherwise a fresh [ph7_hashmap_node] instance is returned.
 | 
						|
 */
 | 
						|
static ph7_hashmap_node *HashmapNewBlobNode(ph7_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	/* Allocate a new node */
 | 
						|
	pNode = (ph7_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(ph7_hashmap_node));
 | 
						|
	if(pNode == 0) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	/* Zero the stucture */
 | 
						|
	SyZero(pNode, sizeof(ph7_hashmap_node));
 | 
						|
	/* Fill in the structure */
 | 
						|
	pNode->pMap  = &(*pMap);
 | 
						|
	pNode->iType = HASHMAP_BLOB_NODE;
 | 
						|
	pNode->nHash = nHash;
 | 
						|
	SyBlobInit(&pNode->xKey.sKey, &pMap->pVm->sAllocator);
 | 
						|
	SyBlobAppend(&pNode->xKey.sKey, pKey, nKeyLen);
 | 
						|
	pNode->nValIdx = nValIdx;
 | 
						|
	return pNode;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * link a hashmap node to the given bucket index (last argument to this function).
 | 
						|
 */
 | 
						|
static void HashmapNodeLink(ph7_hashmap *pMap, ph7_hashmap_node *pNode, sxu32 nBucketIdx) {
 | 
						|
	/* Link */
 | 
						|
	if(pMap->apBucket[nBucketIdx] != 0) {
 | 
						|
		pNode->pNextCollide = pMap->apBucket[nBucketIdx];
 | 
						|
		pMap->apBucket[nBucketIdx]->pPrevCollide = pNode;
 | 
						|
	}
 | 
						|
	pMap->apBucket[nBucketIdx] = pNode;
 | 
						|
	/* Link to the map list */
 | 
						|
	if(pMap->pFirst == 0) {
 | 
						|
		pMap->pFirst = pMap->pLast = pNode;
 | 
						|
		/* Point to the first inserted node */
 | 
						|
		pMap->pCur = pNode;
 | 
						|
	} else {
 | 
						|
		MACRO_LD_PUSH(pMap->pLast, pNode);
 | 
						|
	}
 | 
						|
	++pMap->nEntry;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Unlink a node from the hashmap.
 | 
						|
 * If the node count reaches zero then release the whole hash-bucket.
 | 
						|
 */
 | 
						|
PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode, int bRestore) {
 | 
						|
	ph7_hashmap *pMap = pNode->pMap;
 | 
						|
	ph7_vm *pVm = pMap->pVm;
 | 
						|
	/* Unlink from the corresponding bucket */
 | 
						|
	if(pNode->pPrevCollide == 0) {
 | 
						|
		pMap->apBucket[pNode->nHash & (pMap->nSize - 1)] = pNode->pNextCollide;
 | 
						|
	} else {
 | 
						|
		pNode->pPrevCollide->pNextCollide = pNode->pNextCollide;
 | 
						|
	}
 | 
						|
	if(pNode->pNextCollide) {
 | 
						|
		pNode->pNextCollide->pPrevCollide = pNode->pPrevCollide;
 | 
						|
	}
 | 
						|
	if(pMap->pFirst == pNode) {
 | 
						|
		pMap->pFirst = pNode->pPrev;
 | 
						|
	}
 | 
						|
	if(pMap->pCur == pNode) {
 | 
						|
		/* Advance the node cursor */
 | 
						|
		pMap->pCur = pMap->pCur->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Unlink from the map list */
 | 
						|
	MACRO_LD_REMOVE(pMap->pLast, pNode);
 | 
						|
	if(bRestore) {
 | 
						|
		/* Remove the ph7_value associated with this node from the reference table */
 | 
						|
		PH7_VmRefObjRemove(pVm, pNode->nValIdx, 0, pNode);
 | 
						|
		/* Restore to the freelist */
 | 
						|
		if((pNode->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0) {
 | 
						|
			PH7_VmUnsetMemObj(pVm, pNode->nValIdx, FALSE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if(pNode->iType == HASHMAP_BLOB_NODE) {
 | 
						|
		SyBlobRelease(&pNode->xKey.sKey);
 | 
						|
	}
 | 
						|
	SyMemBackendPoolFree(&pVm->sAllocator, pNode);
 | 
						|
	pMap->nEntry--;
 | 
						|
	if(pMap->nEntry < 1 && pMap != pVm->pGlobal) {
 | 
						|
		/* Free the hash-bucket */
 | 
						|
		SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
 | 
						|
		pMap->apBucket = 0;
 | 
						|
		pMap->nSize = 0;
 | 
						|
		pMap->pFirst = pMap->pLast = pMap->pCur = 0;
 | 
						|
	}
 | 
						|
}
 | 
						|
#define HASHMAP_FILL_FACTOR 3
 | 
						|
/*
 | 
						|
 * Grow the hash-table and rehash all entries.
 | 
						|
 */
 | 
						|
static sxi32 HashmapGrowBucket(ph7_hashmap *pMap) {
 | 
						|
	if(pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR) {
 | 
						|
		ph7_hashmap_node **apOld = pMap->apBucket;
 | 
						|
		ph7_hashmap_node *pEntry, **apNew;
 | 
						|
		sxu32 nNew = pMap->nSize << 1;
 | 
						|
		sxu32 nBucket;
 | 
						|
		sxu32 n;
 | 
						|
		if(nNew < 1) {
 | 
						|
			nNew = 16;
 | 
						|
		}
 | 
						|
		/* Allocate a new bucket */
 | 
						|
		apNew = (ph7_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(ph7_hashmap_node *));
 | 
						|
		if(apNew == 0) {
 | 
						|
			if(pMap->nSize < 1) {
 | 
						|
				return SXERR_MEM; /* Fatal */
 | 
						|
			}
 | 
						|
			/* Not so fatal here,simply a performance hit */
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Zero the table */
 | 
						|
		SyZero((void *)apNew, nNew * sizeof(ph7_hashmap_node *));
 | 
						|
		/* Reflect the change */
 | 
						|
		pMap->apBucket = apNew;
 | 
						|
		pMap->nSize = nNew;
 | 
						|
		if(apOld == 0) {
 | 
						|
			/* First allocated table [i.e: no entry],return immediately */
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Rehash old entries */
 | 
						|
		pEntry = pMap->pFirst;
 | 
						|
		n = 0;
 | 
						|
		for(;;) {
 | 
						|
			if(n >= pMap->nEntry) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			/* Clear the old collision link */
 | 
						|
			pEntry->pNextCollide = pEntry->pPrevCollide = 0;
 | 
						|
			/* Link to the new bucket */
 | 
						|
			nBucket = pEntry->nHash & (nNew - 1);
 | 
						|
			if(pMap->apBucket[nBucket] != 0) {
 | 
						|
				pEntry->pNextCollide = pMap->apBucket[nBucket];
 | 
						|
				pMap->apBucket[nBucket]->pPrevCollide = pEntry;
 | 
						|
			}
 | 
						|
			pMap->apBucket[nBucket] = pEntry;
 | 
						|
			/* Point to the next entry */
 | 
						|
			pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
			n++;
 | 
						|
		}
 | 
						|
		/* Free the old table */
 | 
						|
		SyMemBackendFree(&pMap->pVm->sAllocator, (void *)apOld);
 | 
						|
	}
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Insert a 64-bit integer key and it's associated value (if any) in the given
 | 
						|
 * hashmap.
 | 
						|
 */
 | 
						|
static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValue, sxu32 nRefIdx, int isForeign) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	sxu32 nIdx;
 | 
						|
	sxu32 nHash;
 | 
						|
	sxi32 rc;
 | 
						|
	if(!isForeign) {
 | 
						|
		ph7_value *pObj;
 | 
						|
		/* Reserve a ph7_value for the value */
 | 
						|
		pObj = PH7_ReserveMemObj(pMap->pVm);
 | 
						|
		if(pObj == 0) {
 | 
						|
			return SXERR_MEM;
 | 
						|
		}
 | 
						|
		if(pValue) {
 | 
						|
			/* Duplicate the value */
 | 
						|
			PH7_MemObjStore(pValue, pObj);
 | 
						|
		}
 | 
						|
		nIdx = pObj->nIdx;
 | 
						|
	} else {
 | 
						|
		nIdx = nRefIdx;
 | 
						|
	}
 | 
						|
	/* Hash the key */
 | 
						|
	nHash = pMap->xIntHash(iKey);
 | 
						|
	/* Allocate a new int node */
 | 
						|
	pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx);
 | 
						|
	if(pNode == 0) {
 | 
						|
		return SXERR_MEM;
 | 
						|
	}
 | 
						|
	if(isForeign) {
 | 
						|
		/* Mark as a foregin entry */
 | 
						|
		pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ;
 | 
						|
	}
 | 
						|
	/* Make sure the bucket is big enough to hold the new entry */
 | 
						|
	rc = HashmapGrowBucket(&(*pMap));
 | 
						|
	if(rc != SXRET_OK) {
 | 
						|
		SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	/* Perform the insertion */
 | 
						|
	HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
 | 
						|
	/* Install in the reference table */
 | 
						|
	PH7_VmRefObjInstall(pMap->pVm, nIdx, 0, pNode, 0);
 | 
						|
	/* All done */
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Insert a BLOB key and it's associated value (if any) in the given
 | 
						|
 * hashmap.
 | 
						|
 */
 | 
						|
static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKeyLen, ph7_value *pValue, sxu32 nRefIdx, int isForeign) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	sxu32 nHash;
 | 
						|
	sxu32 nIdx;
 | 
						|
	sxi32 rc;
 | 
						|
	if(!isForeign) {
 | 
						|
		ph7_value *pObj;
 | 
						|
		/* Reserve a ph7_value for the value */
 | 
						|
		pObj = PH7_ReserveMemObj(pMap->pVm);
 | 
						|
		if(pObj == 0) {
 | 
						|
			return SXERR_MEM;
 | 
						|
		}
 | 
						|
		if(pValue) {
 | 
						|
			/* Duplicate the value */
 | 
						|
			PH7_MemObjStore(pValue, pObj);
 | 
						|
		}
 | 
						|
		nIdx = pObj->nIdx;
 | 
						|
	} else {
 | 
						|
		nIdx = nRefIdx;
 | 
						|
	}
 | 
						|
	/* Hash the key */
 | 
						|
	nHash = pMap->xBlobHash(pKey, nKeyLen);
 | 
						|
	/* Allocate a new blob node */
 | 
						|
	pNode = HashmapNewBlobNode(&(*pMap), pKey, nKeyLen, nHash, nIdx);
 | 
						|
	if(pNode == 0) {
 | 
						|
		return SXERR_MEM;
 | 
						|
	}
 | 
						|
	if(isForeign) {
 | 
						|
		/* Mark as a foregin entry */
 | 
						|
		pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ;
 | 
						|
	}
 | 
						|
	/* Make sure the bucket is big enough to hold the new entry */
 | 
						|
	rc = HashmapGrowBucket(&(*pMap));
 | 
						|
	if(rc != SXRET_OK) {
 | 
						|
		SyMemBackendPoolFree(&pMap->pVm->sAllocator, pNode);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	/* Perform the insertion */
 | 
						|
	HashmapNodeLink(&(*pMap), pNode, nHash & (pMap->nSize - 1));
 | 
						|
	/* Install in the reference table */
 | 
						|
	PH7_VmRefObjInstall(pMap->pVm, nIdx, 0, pNode, 0);
 | 
						|
	/* All done */
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Check if a given 64-bit integer key exists in the given hashmap.
 | 
						|
 * Write a pointer to the target node on success. Otherwise
 | 
						|
 * SXERR_NOTFOUND is returned on failure.
 | 
						|
 */
 | 
						|
static sxi32 HashmapLookupIntKey(
 | 
						|
	ph7_hashmap *pMap,         /* Target hashmap */
 | 
						|
	sxi64 iKey,                /* lookup key */
 | 
						|
	ph7_hashmap_node **ppNode  /* OUT: target node on success */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	sxu32 nHash;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* Don't bother hashing,there is no entry anyway */
 | 
						|
		return SXERR_NOTFOUND;
 | 
						|
	}
 | 
						|
	/* Hash the key first */
 | 
						|
	nHash = pMap->xIntHash(iKey);
 | 
						|
	/* Point to the appropriate bucket */
 | 
						|
	pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
 | 
						|
	/* Perform the lookup */
 | 
						|
	for(;;) {
 | 
						|
		if(pNode == 0) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if(pNode->iType == HASHMAP_INT_NODE
 | 
						|
				&& pNode->nHash == nHash
 | 
						|
				&& pNode->xKey.iKey == iKey) {
 | 
						|
			/* Node found */
 | 
						|
			if(ppNode) {
 | 
						|
				*ppNode = pNode;
 | 
						|
			}
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Follow the collision link */
 | 
						|
		pNode = pNode->pNextCollide;
 | 
						|
	}
 | 
						|
	/* No such entry */
 | 
						|
	return SXERR_NOTFOUND;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Check if a given BLOB key exists in the given hashmap.
 | 
						|
 * Write a pointer to the target node on success. Otherwise
 | 
						|
 * SXERR_NOTFOUND is returned on failure.
 | 
						|
 */
 | 
						|
static sxi32 HashmapLookupBlobKey(
 | 
						|
	ph7_hashmap *pMap,          /* Target hashmap */
 | 
						|
	const void *pKey,           /* Lookup key */
 | 
						|
	sxu32 nKeyLen,              /* Key length in bytes */
 | 
						|
	ph7_hashmap_node **ppNode   /* OUT: target node on success */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	sxu32 nHash;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* Don't bother hashing,there is no entry anyway */
 | 
						|
		return SXERR_NOTFOUND;
 | 
						|
	}
 | 
						|
	/* Hash the key first */
 | 
						|
	nHash = pMap->xBlobHash(pKey, nKeyLen);
 | 
						|
	/* Point to the appropriate bucket */
 | 
						|
	pNode = pMap->apBucket[nHash & (pMap->nSize - 1)];
 | 
						|
	/* Perform the lookup */
 | 
						|
	for(;;) {
 | 
						|
		if(pNode == 0) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if(pNode->iType == HASHMAP_BLOB_NODE
 | 
						|
				&& pNode->nHash == nHash
 | 
						|
				&& SyBlobLength(&pNode->xKey.sKey) == nKeyLen
 | 
						|
				&& SyMemcmp(SyBlobData(&pNode->xKey.sKey), pKey, nKeyLen) == 0) {
 | 
						|
			/* Node found */
 | 
						|
			if(ppNode) {
 | 
						|
				*ppNode = pNode;
 | 
						|
			}
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Follow the collision link */
 | 
						|
		pNode = pNode->pNextCollide;
 | 
						|
	}
 | 
						|
	/* No such entry */
 | 
						|
	return SXERR_NOTFOUND;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Check if the given BLOB key looks like a decimal number.
 | 
						|
 * Retrurn TRUE on success.FALSE otherwise.
 | 
						|
 */
 | 
						|
static int HashmapIsIntKey(SyBlob *pKey) {
 | 
						|
	const char *zIn  = (const char *)SyBlobData(pKey);
 | 
						|
	const char *zEnd = &zIn[SyBlobLength(pKey)];
 | 
						|
	if((int)(zEnd - zIn) > 1 && zIn[0] == '0') {
 | 
						|
		/* Octal not decimal number */
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	if((zIn[0] == '-' || zIn[0] == '+') && &zIn[1] < zEnd) {
 | 
						|
		zIn++;
 | 
						|
	}
 | 
						|
	for(;;) {
 | 
						|
		if(zIn >= zEnd) {
 | 
						|
			return TRUE;
 | 
						|
		}
 | 
						|
		if((unsigned char)zIn[0] >= 0xc0 /* UTF-8 stream */  || !SyisDigit(zIn[0])) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		zIn++;
 | 
						|
	}
 | 
						|
	/* Key does not look like a decimal number */
 | 
						|
	return FALSE;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Check if a given key exists in the given hashmap.
 | 
						|
 * Write a pointer to the target node on success.
 | 
						|
 * Otherwise SXERR_NOTFOUND is returned on failure.
 | 
						|
 */
 | 
						|
static sxi32 HashmapLookup(
 | 
						|
	ph7_hashmap *pMap,          /* Target hashmap */
 | 
						|
	ph7_value *pKey,            /* Lookup key */
 | 
						|
	ph7_hashmap_node **ppNode   /* OUT: target node on success */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pNode = 0; /* cc -O6 warning */
 | 
						|
	sxi32 rc;
 | 
						|
	if(pKey->iFlags & (MEMOBJ_STRING | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) {
 | 
						|
		if((pKey->iFlags & MEMOBJ_STRING) == 0) {
 | 
						|
			/* Force a string cast */
 | 
						|
			PH7_MemObjToString(&(*pKey));
 | 
						|
		}
 | 
						|
		if(SyBlobLength(&pKey->sBlob) > 0 && !HashmapIsIntKey(&pKey->sBlob)) {
 | 
						|
			/* Perform a blob lookup */
 | 
						|
			rc = HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &pNode);
 | 
						|
			goto result;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Perform an int lookup */
 | 
						|
	if((pKey->iFlags & MEMOBJ_INT) == 0) {
 | 
						|
		/* Force an integer cast */
 | 
						|
		PH7_MemObjToInteger(pKey);
 | 
						|
	}
 | 
						|
	/* Perform an int lookup */
 | 
						|
	rc = HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode);
 | 
						|
result:
 | 
						|
	if(rc == SXRET_OK) {
 | 
						|
		/* Node found */
 | 
						|
		if(ppNode) {
 | 
						|
			*ppNode = pNode;
 | 
						|
		}
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	/* No such entry */
 | 
						|
	return SXERR_NOTFOUND;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Insert a given key and it's associated value (if any) in the given
 | 
						|
 * hashmap.
 | 
						|
 * If a node with the given key already exists in the database
 | 
						|
 * then this function overwrite the old value.
 | 
						|
 */
 | 
						|
static sxi32 HashmapInsert(
 | 
						|
	ph7_hashmap *pMap, /* Target hashmap */
 | 
						|
	ph7_value *pKey,   /* Lookup key  */
 | 
						|
	ph7_value *pVal    /* Node value */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pNode = 0;
 | 
						|
	sxi32 rc = SXRET_OK;
 | 
						|
	if(pKey && pKey->iFlags & (MEMOBJ_STRING | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) {
 | 
						|
		if((pKey->iFlags & MEMOBJ_STRING) == 0) {
 | 
						|
			/* Force a string cast */
 | 
						|
			PH7_MemObjToString(&(*pKey));
 | 
						|
		}
 | 
						|
		if(SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob)) {
 | 
						|
			if(SyBlobLength(&pKey->sBlob) < 1) {
 | 
						|
				/* Automatic index assign */
 | 
						|
				pKey = 0;
 | 
						|
			}
 | 
						|
			goto IntKey;
 | 
						|
		}
 | 
						|
		if(SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob),
 | 
						|
											SyBlobLength(&pKey->sBlob), &pNode)) {
 | 
						|
			/* Overwrite the old value */
 | 
						|
			ph7_value *pElem;
 | 
						|
			pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
 | 
						|
			if(pElem) {
 | 
						|
				if(pVal) {
 | 
						|
					PH7_MemObjStore(pVal, pElem);
 | 
						|
				} else {
 | 
						|
					/* Nullify the entry */
 | 
						|
					PH7_MemObjToNull(pElem);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		if(pMap == pMap->pVm->pGlobal) {
 | 
						|
			/* Forbidden */
 | 
						|
			PH7_VmThrowError(pMap->pVm, 0, PH7_CTX_NOTICE, "$GLOBALS is a read-only array,insertion is forbidden");
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Perform a blob-key insertion */
 | 
						|
		rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &(*pVal), 0, FALSE);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
IntKey:
 | 
						|
	if(pKey) {
 | 
						|
		if((pKey->iFlags & MEMOBJ_INT) == 0) {
 | 
						|
			/* Force an integer cast */
 | 
						|
			PH7_MemObjToInteger(pKey);
 | 
						|
		}
 | 
						|
		if(SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode)) {
 | 
						|
			/* Overwrite the old value */
 | 
						|
			ph7_value *pElem;
 | 
						|
			pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
 | 
						|
			if(pElem) {
 | 
						|
				if(pVal) {
 | 
						|
					PH7_MemObjStore(pVal, pElem);
 | 
						|
				} else {
 | 
						|
					/* Nullify the entry */
 | 
						|
					PH7_MemObjToNull(pElem);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		if(pMap == pMap->pVm->pGlobal) {
 | 
						|
			/* Forbidden */
 | 
						|
			PH7_VmThrowError(pMap->pVm, 0, PH7_CTX_NOTICE, "$GLOBALS is a read-only array,insertion is forbidden");
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Perform a 64-bit-int-key insertion */
 | 
						|
		rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal), 0, FALSE);
 | 
						|
		if(rc == SXRET_OK) {
 | 
						|
			if(pKey->x.iVal >= pMap->iNextIdx) {
 | 
						|
				/* Increment the automatic index */
 | 
						|
				pMap->iNextIdx = pKey->x.iVal + 1;
 | 
						|
				/* Make sure the automatic index is not reserved */
 | 
						|
				while(SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0)) {
 | 
						|
					pMap->iNextIdx++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if(pMap == pMap->pVm->pGlobal) {
 | 
						|
			/* Forbidden */
 | 
						|
			PH7_VmThrowError(pMap->pVm, 0, PH7_CTX_NOTICE, "$GLOBALS is a read-only array,insertion is forbidden");
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Assign an automatic index */
 | 
						|
		rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, &(*pVal), 0, FALSE);
 | 
						|
		if(rc == SXRET_OK) {
 | 
						|
			++pMap->iNextIdx;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Insertion result */
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Insert a given key and it's associated value (foreign index) in the given
 | 
						|
 * hashmap.
 | 
						|
 * This is insertion by reference so be careful to mark the node
 | 
						|
 * with the HASHMAP_NODE_FOREIGN_OBJ flag being set.
 | 
						|
 * The insertion by reference is triggered when the following
 | 
						|
 * expression is encountered.
 | 
						|
 * $var = 10;
 | 
						|
 *  $a = array(&var);
 | 
						|
 * OR
 | 
						|
 *  $a[] =& $var;
 | 
						|
 * That is,$var is a foreign ph7_value and the $a array have no control
 | 
						|
 * over it's contents.
 | 
						|
 * Note that the node that hold the foreign ph7_value is automatically
 | 
						|
 * removed when the foreign ph7_value is unset.
 | 
						|
 * Example:
 | 
						|
 *  $var = 10;
 | 
						|
 *  $a[] =& $var;
 | 
						|
 *  echo count($a).PHP_EOL; //1
 | 
						|
 *  //Unset the foreign ph7_value now
 | 
						|
 *  unset($var);
 | 
						|
 *  echo count($a); //0
 | 
						|
 * Note that this is a PH7 eXtension.
 | 
						|
 * Refer to the official documentation for more information.
 | 
						|
 * If a node with the given key already exists in the database
 | 
						|
 * then this function overwrite the old value.
 | 
						|
 */
 | 
						|
static sxi32 HashmapInsertByRef(
 | 
						|
	ph7_hashmap *pMap,   /* Target hashmap */
 | 
						|
	ph7_value *pKey,     /* Lookup key */
 | 
						|
	sxu32 nRefIdx        /* Foreign ph7_value index */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pNode = 0;
 | 
						|
	sxi32 rc = SXRET_OK;
 | 
						|
	if(pKey && pKey->iFlags & (MEMOBJ_STRING | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) {
 | 
						|
		if((pKey->iFlags & MEMOBJ_STRING) == 0) {
 | 
						|
			/* Force a string cast */
 | 
						|
			PH7_MemObjToString(&(*pKey));
 | 
						|
		}
 | 
						|
		if(SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob)) {
 | 
						|
			if(SyBlobLength(&pKey->sBlob) < 1) {
 | 
						|
				/* Automatic index assign */
 | 
						|
				pKey = 0;
 | 
						|
			}
 | 
						|
			goto IntKey;
 | 
						|
		}
 | 
						|
		if(SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob),
 | 
						|
											SyBlobLength(&pKey->sBlob), &pNode)) {
 | 
						|
			/* Overwrite */
 | 
						|
			PH7_VmRefObjRemove(pMap->pVm, pNode->nValIdx, 0, pNode);
 | 
						|
			pNode->nValIdx = nRefIdx;
 | 
						|
			/* Install in the reference table */
 | 
						|
			PH7_VmRefObjInstall(pMap->pVm, nRefIdx, 0, pNode, 0);
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Perform a blob-key insertion */
 | 
						|
		rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), 0, nRefIdx, TRUE);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
IntKey:
 | 
						|
	if(pKey) {
 | 
						|
		if((pKey->iFlags & MEMOBJ_INT) == 0) {
 | 
						|
			/* Force an integer cast */
 | 
						|
			PH7_MemObjToInteger(pKey);
 | 
						|
		}
 | 
						|
		if(SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode)) {
 | 
						|
			/* Overwrite */
 | 
						|
			PH7_VmRefObjRemove(pMap->pVm, pNode->nValIdx, 0, pNode);
 | 
						|
			pNode->nValIdx = nRefIdx;
 | 
						|
			/* Install in the reference table */
 | 
						|
			PH7_VmRefObjInstall(pMap->pVm, nRefIdx, 0, pNode, 0);
 | 
						|
			return SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Perform a 64-bit-int-key insertion */
 | 
						|
		rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, 0, nRefIdx, TRUE);
 | 
						|
		if(rc == SXRET_OK) {
 | 
						|
			if(pKey->x.iVal >= pMap->iNextIdx) {
 | 
						|
				/* Increment the automatic index */
 | 
						|
				pMap->iNextIdx = pKey->x.iVal + 1;
 | 
						|
				/* Make sure the automatic index is not reserved */
 | 
						|
				while(SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0)) {
 | 
						|
					pMap->iNextIdx++;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/* Assign an automatic index */
 | 
						|
		rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, 0, nRefIdx, TRUE);
 | 
						|
		if(rc == SXRET_OK) {
 | 
						|
			++pMap->iNextIdx;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Insertion result */
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Extract node value.
 | 
						|
 */
 | 
						|
static ph7_value *HashmapExtractNodeValue(ph7_hashmap_node *pNode) {
 | 
						|
	/* Point to the desired object */
 | 
						|
	ph7_value *pObj;
 | 
						|
	pObj = (ph7_value *)SySetAt(&pNode->pMap->pVm->aMemObj, pNode->nValIdx);
 | 
						|
	return pObj;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Insert a node in the given hashmap.
 | 
						|
 * If a node with the given key already exists in the database
 | 
						|
 * then this function overwrite the old value.
 | 
						|
 */
 | 
						|
static sxi32 HashmapInsertNode(ph7_hashmap *pMap, ph7_hashmap_node *pNode, int bPreserve) {
 | 
						|
	ph7_value *pObj;
 | 
						|
	sxi32 rc;
 | 
						|
	/* Extract the node value */
 | 
						|
	pObj = HashmapExtractNodeValue(&(*pNode));
 | 
						|
	if(pObj == 0) {
 | 
						|
		return SXERR_EMPTY;
 | 
						|
	}
 | 
						|
	/* Preserve key */
 | 
						|
	if(pNode->iType == HASHMAP_INT_NODE) {
 | 
						|
		/* Int64 key */
 | 
						|
		if(!bPreserve) {
 | 
						|
			/* Assign an automatic index */
 | 
						|
			rc = HashmapInsert(&(*pMap), 0, pObj);
 | 
						|
		} else {
 | 
						|
			rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj, 0, FALSE);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/* Blob key */
 | 
						|
		rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey),
 | 
						|
								  SyBlobLength(&pNode->xKey.sKey), pObj, 0, FALSE);
 | 
						|
	}
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Compare two node values.
 | 
						|
 * Return 0 if the node values are equals, > 0 if pLeft is greater than pRight
 | 
						|
 * or < 0 if pRight is greater than pLeft.
 | 
						|
 * For a full description on ph7_values comparison,refer to the implementation
 | 
						|
 * of the [PH7_MemObjCmp()] function defined in memobj.c or the official
 | 
						|
 * documenation.
 | 
						|
 */
 | 
						|
static sxi32 HashmapNodeCmp(ph7_hashmap_node *pLeft, ph7_hashmap_node *pRight, int bStrict) {
 | 
						|
	ph7_value sObj1, sObj2;
 | 
						|
	sxi32 rc;
 | 
						|
	if(pLeft == pRight) {
 | 
						|
		/*
 | 
						|
		 * Same node.Refer to the sort() implementation defined
 | 
						|
		 * below for more information on this sceanario.
 | 
						|
		 */
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	/* Do the comparison */
 | 
						|
	PH7_MemObjInit(pLeft->pMap->pVm, &sObj1);
 | 
						|
	PH7_MemObjInit(pLeft->pMap->pVm, &sObj2);
 | 
						|
	PH7_HashmapExtractNodeValue(pLeft, &sObj1, FALSE);
 | 
						|
	PH7_HashmapExtractNodeValue(pRight, &sObj2, FALSE);
 | 
						|
	rc = PH7_MemObjCmp(&sObj1, &sObj2, bStrict, 0);
 | 
						|
	PH7_MemObjRelease(&sObj1);
 | 
						|
	PH7_MemObjRelease(&sObj2);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Rehash a node with a 64-bit integer key.
 | 
						|
 * Refer to [merge_sort(),array_shift()] implementations for more information.
 | 
						|
 */
 | 
						|
static void HashmapRehashIntNode(ph7_hashmap_node *pEntry) {
 | 
						|
	ph7_hashmap *pMap = pEntry->pMap;
 | 
						|
	sxu32 nBucket;
 | 
						|
	/* Remove old collision links */
 | 
						|
	if(pEntry->pPrevCollide) {
 | 
						|
		pEntry->pPrevCollide->pNextCollide = pEntry->pNextCollide;
 | 
						|
	} else {
 | 
						|
		pMap->apBucket[pEntry->nHash & (pMap->nSize - 1)] = pEntry->pNextCollide;
 | 
						|
	}
 | 
						|
	if(pEntry->pNextCollide) {
 | 
						|
		pEntry->pNextCollide->pPrevCollide = pEntry->pPrevCollide;
 | 
						|
	}
 | 
						|
	pEntry->pNextCollide = pEntry->pPrevCollide = 0;
 | 
						|
	/* Compute the new hash */
 | 
						|
	pEntry->nHash = pMap->xIntHash(pMap->iNextIdx);
 | 
						|
	pEntry->xKey.iKey = pMap->iNextIdx;
 | 
						|
	nBucket = pEntry->nHash & (pMap->nSize - 1);
 | 
						|
	/* Link to the new bucket */
 | 
						|
	pEntry->pNextCollide = pMap->apBucket[nBucket];
 | 
						|
	if(pMap->apBucket[nBucket]) {
 | 
						|
		pMap->apBucket[nBucket]->pPrevCollide = pEntry;
 | 
						|
	}
 | 
						|
	pEntry->pNextCollide = pMap->apBucket[nBucket];
 | 
						|
	pMap->apBucket[nBucket] = pEntry;
 | 
						|
	/* Increment the automatic index */
 | 
						|
	pMap->iNextIdx++;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Perform a linear search on a given hashmap.
 | 
						|
 * Write a pointer to the target node on success.
 | 
						|
 * Otherwise SXERR_NOTFOUND is returned on failure.
 | 
						|
 * Refer to [array_intersect(),array_diff(),in_array(),...] implementations
 | 
						|
 * for more information.
 | 
						|
 */
 | 
						|
static int HashmapFindValue(
 | 
						|
	ph7_hashmap *pMap,   /* Target hashmap */
 | 
						|
	ph7_value *pNeedle,  /* Lookup key */
 | 
						|
	ph7_hashmap_node **ppNode, /* OUT: target node on success  */
 | 
						|
	int bStrict      /* TRUE for strict comparison */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value sVal, *pVal;
 | 
						|
	ph7_value sNeedle;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	/* Perform a linear search since we cannot sort the hashmap based on values */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	n = pMap->nEntry;
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sVal);
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sNeedle);
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			if((pVal->iFlags | pNeedle->iFlags) & MEMOBJ_NULL) {
 | 
						|
				sxi32 iF1 = pVal->iFlags & ~MEMOBJ_AUX;
 | 
						|
				sxi32 iF2 = pNeedle->iFlags & ~MEMOBJ_AUX;
 | 
						|
				if(iF1 == iF2) {
 | 
						|
					/* NULL values are equals */
 | 
						|
					if(ppNode) {
 | 
						|
						*ppNode = pEntry;
 | 
						|
					}
 | 
						|
					return SXRET_OK;
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				/* Duplicate value */
 | 
						|
				PH7_MemObjLoad(pVal, &sVal);
 | 
						|
				PH7_MemObjLoad(pNeedle, &sNeedle);
 | 
						|
				rc = PH7_MemObjCmp(&sNeedle, &sVal, bStrict, 0);
 | 
						|
				PH7_MemObjRelease(&sVal);
 | 
						|
				PH7_MemObjRelease(&sNeedle);
 | 
						|
				if(rc == 0) {
 | 
						|
					if(ppNode) {
 | 
						|
						*ppNode = pEntry;
 | 
						|
					}
 | 
						|
					/* Match found*/
 | 
						|
					return SXRET_OK;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* No such entry */
 | 
						|
	return SXERR_NOTFOUND;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Perform a linear search on a given hashmap but use an user-defined callback
 | 
						|
 * for values comparison.
 | 
						|
 * Write a pointer to the target node on success.
 | 
						|
 * Otherwise SXERR_NOTFOUND is returned on failure.
 | 
						|
 * Refer to [array_uintersect(),array_udiff()...] implementations
 | 
						|
 * for more information.
 | 
						|
 */
 | 
						|
static int HashmapFindValueByCallback(
 | 
						|
	ph7_hashmap *pMap,     /* Target hashmap */
 | 
						|
	ph7_value *pNeedle,    /* Lookup key */
 | 
						|
	ph7_value *pCallback,  /* User defined callback */
 | 
						|
	ph7_hashmap_node **ppNode /* OUT: target node on success */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value sResult, *pVal;
 | 
						|
	ph7_value *apArg[2];    /* Callback arguments */
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	/* Perform a linear search since we cannot sort the array based on values */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	n = pMap->nEntry;
 | 
						|
	/* Store callback result here */
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sResult);
 | 
						|
	/* First argument to the callback */
 | 
						|
	apArg[0] = pNeedle;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			/* Invoke the user callback */
 | 
						|
			apArg[1] = pVal; /* Second argument to the callback */
 | 
						|
			rc = PH7_VmCallUserFunction(pMap->pVm, pCallback, 2, apArg, &sResult);
 | 
						|
			if(rc == SXRET_OK) {
 | 
						|
				/* Extract callback result */
 | 
						|
				if((sResult.iFlags & MEMOBJ_INT) == 0) {
 | 
						|
					/* Perform an int cast */
 | 
						|
					PH7_MemObjToInteger(&sResult);
 | 
						|
				}
 | 
						|
				rc = (sxi32)sResult.x.iVal;
 | 
						|
				PH7_MemObjRelease(&sResult);
 | 
						|
				if(rc == 0) {
 | 
						|
					/* Match found*/
 | 
						|
					if(ppNode) {
 | 
						|
						*ppNode = pEntry;
 | 
						|
					}
 | 
						|
					return SXRET_OK;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* No such entry */
 | 
						|
	return SXERR_NOTFOUND;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Compare two hashmaps.
 | 
						|
 * Return 0 if the hashmaps are equals.Any other value indicates inequality.
 | 
						|
 * Note on array comparison operators.
 | 
						|
 *  According to the PHP language reference manual.
 | 
						|
 *  Array Operators Example 	Name 	Result
 | 
						|
 *  $a + $b 	Union 	Union of $a and $b.
 | 
						|
 *  $a == $b 	Equality 	TRUE if $a and $b have the same key/value pairs.
 | 
						|
 *  $a === $b 	Identity 	TRUE if $a and $b have the same key/value pairs in the same
 | 
						|
 *                          order and of the same types.
 | 
						|
 *  $a != $b 	Inequality 	TRUE if $a is not equal to $b.
 | 
						|
 *  $a <> $b 	Inequality 	TRUE if $a is not equal to $b.
 | 
						|
 *  $a !== $b 	Non-identity 	TRUE if $a is not identical to $b.
 | 
						|
 * The + operator returns the right-hand array appended to the left-hand array;
 | 
						|
 * For keys that exist in both arrays, the elements from the left-hand array will be used
 | 
						|
 * and the matching elements from the right-hand array will be ignored.
 | 
						|
 * <?php
 | 
						|
 * $a = array("a" => "apple", "b" => "banana");
 | 
						|
 * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
 | 
						|
 * $c = $a + $b; // Union of $a and $b
 | 
						|
 * echo "Union of \$a and \$b: \n";
 | 
						|
 * var_dump($c);
 | 
						|
 * $c = $b + $a; // Union of $b and $a
 | 
						|
 * echo "Union of \$b and \$a: \n";
 | 
						|
 * var_dump($c);
 | 
						|
 * ?>
 | 
						|
 * When executed, this script will print the following:
 | 
						|
 * Union of $a and $b:
 | 
						|
 * array(3) {
 | 
						|
 *  ["a"]=>
 | 
						|
 *  string(5) "apple"
 | 
						|
 *  ["b"]=>
 | 
						|
 * string(6) "banana"
 | 
						|
 *  ["c"]=>
 | 
						|
 * string(6) "cherry"
 | 
						|
 * }
 | 
						|
 * Union of $b and $a:
 | 
						|
 * array(3) {
 | 
						|
 * ["a"]=>
 | 
						|
 * string(4) "pear"
 | 
						|
 * ["b"]=>
 | 
						|
 * string(10) "strawberry"
 | 
						|
 * ["c"]=>
 | 
						|
 * string(6) "cherry"
 | 
						|
 * }
 | 
						|
 * Elements of arrays are equal for the comparison if they have the same key and value.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapCmp(
 | 
						|
	ph7_hashmap *pLeft,  /* Left hashmap */
 | 
						|
	ph7_hashmap *pRight, /* Right hashmap */
 | 
						|
	int bStrict          /* TRUE for strict comparison */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pLe, *pRe;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	if(pLeft == pRight) {
 | 
						|
		/* Same hashmap instance. This can easily happen since hashmaps are passed by reference.
 | 
						|
		 * Unlike the zend engine.
 | 
						|
		 */
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	if(pLeft->nEntry != pRight->nEntry) {
 | 
						|
		/* Must have the same number of entries */
 | 
						|
		return pLeft->nEntry > pRight->nEntry ? 1 : -1;
 | 
						|
	}
 | 
						|
	/* Point to the first inserted entry of the left hashmap */
 | 
						|
	pLe = pLeft->pFirst;
 | 
						|
	pRe = 0; /* cc warning */
 | 
						|
	/* Perform the comparison */
 | 
						|
	n = pLeft->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if(pLe->iType == HASHMAP_INT_NODE) {
 | 
						|
			/* Int key */
 | 
						|
			rc = HashmapLookupIntKey(&(*pRight), pLe->xKey.iKey, &pRe);
 | 
						|
		} else {
 | 
						|
			SyBlob *pKey = &pLe->xKey.sKey;
 | 
						|
			/* Blob key */
 | 
						|
			rc = HashmapLookupBlobKey(&(*pRight), SyBlobData(pKey), SyBlobLength(pKey), &pRe);
 | 
						|
		}
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			/* No such entry in the right side */
 | 
						|
			return 1;
 | 
						|
		}
 | 
						|
		rc = 0;
 | 
						|
		if(bStrict) {
 | 
						|
			/* Make sure,the keys are of the same type */
 | 
						|
			if(pLe->iType != pRe->iType) {
 | 
						|
				rc = 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(!rc) {
 | 
						|
			/* Compare nodes */
 | 
						|
			rc = HashmapNodeCmp(pLe, pRe, bStrict);
 | 
						|
		}
 | 
						|
		if(rc != 0) {
 | 
						|
			/* Nodes key/value differ */
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pLe = pLe->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	return 0; /* Hashmaps are equals */
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Merge two hashmaps.
 | 
						|
 * Note on the merge process
 | 
						|
 * According to the PHP language reference manual.
 | 
						|
 *  Merges the elements of two arrays together so that the values of one are appended
 | 
						|
 *  to the end of the previous one. It returns the resulting array (pDest).
 | 
						|
 *  If the input arrays have the same string keys, then the later value for that key
 | 
						|
 *  will overwrite the previous one. If, however, the arrays contain numeric keys
 | 
						|
 *  the later value will not overwrite the original value, but will be appended.
 | 
						|
 *  Values in the input array with numeric keys will be renumbered with incrementing
 | 
						|
 *  keys starting from zero in the result array.
 | 
						|
 */
 | 
						|
static sxi32 HashmapMerge(ph7_hashmap *pSrc, ph7_hashmap *pDest) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value sKey, *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	if(pSrc == pDest) {
 | 
						|
		/* Same map. This can easily happen since hashmaps are passed by reference.
 | 
						|
		 * Unlike the zend engine.
 | 
						|
		 */
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	/* Point to the first inserted entry in the source */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	/* Perform the merge */
 | 
						|
	for(n = 0 ; n < pSrc->nEntry ; ++n) {
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pEntry->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Blob key insertion */
 | 
						|
			PH7_MemObjInitFromString(pDest->pVm, &sKey, 0);
 | 
						|
			PH7_MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
 | 
						|
			rc = PH7_HashmapInsert(&(*pDest), &sKey, pVal);
 | 
						|
			PH7_MemObjRelease(&sKey);
 | 
						|
		} else {
 | 
						|
			rc = HashmapInsert(&(*pDest), 0/* Automatic index assign */, pVal);
 | 
						|
		}
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Overwrite entries with the same key.
 | 
						|
 * Refer to the [array_replace()] implementation for more information.
 | 
						|
 *  According to the PHP language reference manual.
 | 
						|
 *  array_replace() replaces the values of the first array with the same values
 | 
						|
 *  from all the following arrays. If a key from the first array exists in the second
 | 
						|
 *  array, its value will be replaced by the value from the second array. If the key
 | 
						|
 *  exists in the second array, and not the first, it will be created in the first array.
 | 
						|
 *  If a key only exists in the first array, it will be left as is. If several arrays
 | 
						|
 *  are passed for replacement, they will be processed in order, the later arrays
 | 
						|
 *  overwriting the previous values.
 | 
						|
 *  array_replace() is not recursive : it will replace values in the first array
 | 
						|
 *  by whatever type is in the second array.
 | 
						|
 */
 | 
						|
static sxi32 HashmapOverwrite(ph7_hashmap *pSrc, ph7_hashmap *pDest) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value sKey, *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	if(pSrc == pDest) {
 | 
						|
		/* Same map. This can easily happen since hashmaps are passed by reference.
 | 
						|
		 * Unlike the zend engine.
 | 
						|
		 */
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	/* Point to the first inserted entry in the source */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	/* Perform the merge */
 | 
						|
	for(n = 0 ; n < pSrc->nEntry ; ++n) {
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pEntry->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Blob key insertion */
 | 
						|
			PH7_MemObjInitFromString(pDest->pVm, &sKey, 0);
 | 
						|
			PH7_MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
 | 
						|
		} else {
 | 
						|
			/* Int key insertion */
 | 
						|
			PH7_MemObjInitFromInt(pDest->pVm, &sKey, pEntry->xKey.iKey);
 | 
						|
		}
 | 
						|
		rc = PH7_HashmapInsert(&(*pDest), &sKey, pVal);
 | 
						|
		PH7_MemObjRelease(&sKey);
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Duplicate the contents of a hashmap. Store the copy in pDest.
 | 
						|
 * Refer to the [array_pad(),array_copy(),...] implementation for more information.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc, ph7_hashmap *pDest) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value sKey, *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	if(pSrc == pDest) {
 | 
						|
		/* Same map. This can easily happen since hashmaps are passed by reference.
 | 
						|
		 * Unlike the zend engine.
 | 
						|
		 */
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	/* Point to the first inserted entry in the source */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	/* Perform the duplication */
 | 
						|
	for(n = 0 ; n < pSrc->nEntry ; ++n) {
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pEntry->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Blob key insertion */
 | 
						|
			PH7_MemObjInitFromString(pDest->pVm, &sKey, 0);
 | 
						|
			PH7_MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
 | 
						|
			rc = PH7_HashmapInsert(&(*pDest), &sKey, pVal);
 | 
						|
			PH7_MemObjRelease(&sKey);
 | 
						|
		} else {
 | 
						|
			/* Int key insertion */
 | 
						|
			rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal, 0, FALSE);
 | 
						|
		}
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Perform the union of two hashmaps.
 | 
						|
 * This operation is performed only if the user uses the '+' operator
 | 
						|
 * with a variable holding an array as follows:
 | 
						|
 * <?php
 | 
						|
 * $a = array("a" => "apple", "b" => "banana");
 | 
						|
 * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");
 | 
						|
 * $c = $a + $b; // Union of $a and $b
 | 
						|
 * echo "Union of \$a and \$b: \n";
 | 
						|
 * var_dump($c);
 | 
						|
 * $c = $b + $a; // Union of $b and $a
 | 
						|
 * echo "Union of \$b and \$a: \n";
 | 
						|
 * var_dump($c);
 | 
						|
 * ?>
 | 
						|
 * When executed, this script will print the following:
 | 
						|
 * Union of $a and $b:
 | 
						|
 * array(3) {
 | 
						|
 *  ["a"]=>
 | 
						|
 *  string(5) "apple"
 | 
						|
 *  ["b"]=>
 | 
						|
 * string(6) "banana"
 | 
						|
 *  ["c"]=>
 | 
						|
 * string(6) "cherry"
 | 
						|
 * }
 | 
						|
 * Union of $b and $a:
 | 
						|
 * array(3) {
 | 
						|
 * ["a"]=>
 | 
						|
 * string(4) "pear"
 | 
						|
 * ["b"]=>
 | 
						|
 * string(10) "strawberry"
 | 
						|
 * ["c"]=>
 | 
						|
 * string(6) "cherry"
 | 
						|
 * }
 | 
						|
 * The + operator returns the right-hand array appended to the left-hand array;
 | 
						|
 * For keys that exist in both arrays, the elements from the left-hand array will be used
 | 
						|
 * and the matching elements from the right-hand array will be ignored.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	sxi32 rc = SXRET_OK;
 | 
						|
	ph7_value *pObj;
 | 
						|
	sxu32 n;
 | 
						|
	if(pLeft == pRight) {
 | 
						|
		/* Same map. This can easily happen since hashmaps are passed by reference.
 | 
						|
		 * Unlike the zend engine.
 | 
						|
		 */
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	/* Perform the union */
 | 
						|
	pEntry = pRight->pFirst;
 | 
						|
	for(n = 0 ; n < pRight->nEntry ; ++n) {
 | 
						|
		/* Make sure the given key does not exists in the left array */
 | 
						|
		if(pEntry->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* BLOB key */
 | 
						|
			if(SXRET_OK !=
 | 
						|
					HashmapLookupBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), 0)) {
 | 
						|
				pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
				if(pObj) {
 | 
						|
					/* Perform the insertion */
 | 
						|
					rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey),
 | 
						|
											  pObj, 0, FALSE);
 | 
						|
					if(rc != SXRET_OK) {
 | 
						|
						return rc;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			/* INT key */
 | 
						|
			if(SXRET_OK != HashmapLookupIntKey(&(*pLeft), pEntry->xKey.iKey, 0)) {
 | 
						|
				pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
				if(pObj) {
 | 
						|
					/* Perform the insertion */
 | 
						|
					rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj, 0, FALSE);
 | 
						|
					if(rc != SXRET_OK) {
 | 
						|
						return rc;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Allocate a new hashmap.
 | 
						|
 * Return a pointer to the freshly allocated hashmap on success.NULL otherwise.
 | 
						|
 */
 | 
						|
PH7_PRIVATE ph7_hashmap *PH7_NewHashmap(
 | 
						|
	ph7_vm *pVm,              /* VM that trigger the hashmap creation */
 | 
						|
	sxu32(*xIntHash)(sxi64),  /* Hash function for int keys.NULL otherwise*/
 | 
						|
	sxu32(*xBlobHash)(const void *, sxu32) /* Hash function for BLOB keys.NULL otherwise */
 | 
						|
) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Allocate a new instance */
 | 
						|
	pMap = (ph7_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_hashmap));
 | 
						|
	if(pMap == 0) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	/* Zero the structure */
 | 
						|
	SyZero(pMap, sizeof(ph7_hashmap));
 | 
						|
	/* Fill in the structure */
 | 
						|
	pMap->pVm = &(*pVm);
 | 
						|
	pMap->iRef = 1;
 | 
						|
	/* Default hash functions */
 | 
						|
	pMap->xIntHash  = xIntHash ? xIntHash : IntHash;
 | 
						|
	pMap->xBlobHash = xBlobHash ? xBlobHash : BinHash;
 | 
						|
	return pMap;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Install superglobals in the given virtual machine.
 | 
						|
 * Note on superglobals.
 | 
						|
 *  According to the PHP language reference manual.
 | 
						|
 *  Superglobals are built-in variables that are always available in all scopes.
 | 
						|
*   Description
 | 
						|
*   Several predefined variables in PHP are "superglobals", which means they
 | 
						|
*   are available in all scopes throughout a script. There is no need to do
 | 
						|
*   global $variable; to access them within functions or methods.
 | 
						|
*   These superglobal variables are:
 | 
						|
*    $GLOBALS
 | 
						|
*    $_SERVER
 | 
						|
*    $_GET
 | 
						|
*    $_POST
 | 
						|
*    $_FILES
 | 
						|
*    $_COOKIE
 | 
						|
*    $_SESSION
 | 
						|
*    $_REQUEST
 | 
						|
*    $_ENV
 | 
						|
*/
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapCreateSuper(ph7_vm *pVm) {
 | 
						|
	static const char *azSuper[] = {
 | 
						|
		"_SERVER",   /* $_SERVER */
 | 
						|
		"_GET",      /* $_GET */
 | 
						|
		"_POST",     /* $_POST */
 | 
						|
		"_FILES",    /* $_FILES */
 | 
						|
		"_COOKIE",   /* $_COOKIE */
 | 
						|
		"_SESSION",  /* $_SESSION */
 | 
						|
		"_REQUEST",  /* $_REQUEST */
 | 
						|
		"_ENV",      /* $_ENV */
 | 
						|
		"_HEADER",   /* $_HEADER */
 | 
						|
		"argv"       /* $argv */
 | 
						|
	};
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pObj;
 | 
						|
	SyString *pFile;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	/* Allocate a new hashmap for the $GLOBALS array */
 | 
						|
	pMap = PH7_NewHashmap(&(*pVm), 0, 0);
 | 
						|
	if(pMap == 0) {
 | 
						|
		return SXERR_MEM;
 | 
						|
	}
 | 
						|
	pVm->pGlobal = pMap;
 | 
						|
	/* Reserve a ph7_value for the $GLOBALS array*/
 | 
						|
	pObj = PH7_ReserveMemObj(&(*pVm));
 | 
						|
	if(pObj == 0) {
 | 
						|
		return SXERR_MEM;
 | 
						|
	}
 | 
						|
	PH7_MemObjInitFromArray(&(*pVm), pObj, pMap);
 | 
						|
	/* Record object index */
 | 
						|
	pVm->nGlobalIdx = pObj->nIdx;
 | 
						|
	/* Install the special $GLOBALS array */
 | 
						|
	rc = SyHashInsert(&pVm->hSuper, (const void *)"GLOBALS", sizeof("GLOBALS") - 1, SX_INT_TO_PTR(pVm->nGlobalIdx));
 | 
						|
	if(rc != SXRET_OK) {
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	/* Install superglobals now */
 | 
						|
	for(n =  0 ; n < SX_ARRAYSIZE(azSuper)  ; n++) {
 | 
						|
		ph7_value *pSuper;
 | 
						|
		/* Request an empty array */
 | 
						|
		pSuper = ph7_new_array(&(*pVm));
 | 
						|
		if(pSuper == 0) {
 | 
						|
			return SXERR_MEM;
 | 
						|
		}
 | 
						|
		/* Install */
 | 
						|
		rc = ph7_vm_config(&(*pVm), PH7_VM_CONFIG_CREATE_SUPER, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */);
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			return rc;
 | 
						|
		}
 | 
						|
		/* Release the value now it have been installed */
 | 
						|
		ph7_release_value(&(*pVm), pSuper);
 | 
						|
	}
 | 
						|
	/* Set some $_SERVER entries */
 | 
						|
	pFile = (SyString *)SySetPeek(&pVm->aFiles);
 | 
						|
	/*
 | 
						|
	 * 'SCRIPT_FILENAME'
 | 
						|
	 * The absolute pathname of the currently executing script.
 | 
						|
	 */
 | 
						|
	ph7_vm_config(pVm, PH7_VM_CONFIG_SERVER_ATTR,
 | 
						|
				  "SCRIPT_FILENAME",
 | 
						|
				  pFile ? pFile->zString : ":Memory:",
 | 
						|
				  pFile ? pFile->nByte : sizeof(":Memory:") - 1
 | 
						|
				 );
 | 
						|
	/* All done,all super-global are installed now */
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Release a hashmap.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapRelease(ph7_hashmap *pMap, int FreeDS) {
 | 
						|
	ph7_hashmap_node *pEntry, *pNext;
 | 
						|
	ph7_vm *pVm = pMap->pVm;
 | 
						|
	sxu32 n;
 | 
						|
	if(pMap == pVm->pGlobal) {
 | 
						|
		/* Cannot delete the $GLOBALS array */
 | 
						|
		PH7_VmThrowError(pMap->pVm, 0, PH7_CTX_NOTICE, "$GLOBALS is a read-only array,deletion is forbidden");
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	/* Start the release process */
 | 
						|
	n = 0;
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	for(;;) {
 | 
						|
		if(n >= pMap->nEntry) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		pNext = pEntry->pPrev; /* Reverse link */
 | 
						|
		/* Remove the reference from the foreign table */
 | 
						|
		PH7_VmRefObjRemove(pVm, pEntry->nValIdx, 0, pEntry);
 | 
						|
		if((pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0) {
 | 
						|
			/* Restore the ph7_value to the free list */
 | 
						|
			PH7_VmUnsetMemObj(pVm, pEntry->nValIdx, FALSE);
 | 
						|
		}
 | 
						|
		/* Release the node */
 | 
						|
		if(pEntry->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			SyBlobRelease(&pEntry->xKey.sKey);
 | 
						|
		}
 | 
						|
		SyMemBackendPoolFree(&pVm->sAllocator, pEntry);
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pNext;
 | 
						|
		n++;
 | 
						|
	}
 | 
						|
	if(pMap->nEntry > 0) {
 | 
						|
		/* Release the hash bucket */
 | 
						|
		SyMemBackendFree(&pVm->sAllocator, pMap->apBucket);
 | 
						|
	}
 | 
						|
	if(FreeDS) {
 | 
						|
		/* Free the whole instance */
 | 
						|
		SyMemBackendPoolFree(&pVm->sAllocator, pMap);
 | 
						|
	} else {
 | 
						|
		/* Keep the instance but reset it's fields */
 | 
						|
		pMap->apBucket = 0;
 | 
						|
		pMap->iNextIdx = 0;
 | 
						|
		pMap->nEntry = pMap->nSize = 0;
 | 
						|
		pMap->pFirst = pMap->pLast = pMap->pCur = 0;
 | 
						|
	}
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Decrement the reference count of a given hashmap.
 | 
						|
 * If the count reaches zero which mean no more variables
 | 
						|
 * are pointing to this hashmap,then release the whole instance.
 | 
						|
 */
 | 
						|
PH7_PRIVATE void  PH7_HashmapUnref(ph7_hashmap *pMap) {
 | 
						|
	ph7_vm *pVm = pMap->pVm;
 | 
						|
	/* TICKET 1432-49: $GLOBALS is not subject to garbage collection */
 | 
						|
	pMap->iRef--;
 | 
						|
	if(pMap->iRef < 1 && pMap != pVm->pGlobal) {
 | 
						|
		PH7_HashmapRelease(pMap, TRUE);
 | 
						|
	}
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Check if a given key exists in the given hashmap.
 | 
						|
 * Write a pointer to the target node on success.
 | 
						|
 * Otherwise SXERR_NOTFOUND is returned on failure.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapLookup(
 | 
						|
	ph7_hashmap *pMap,        /* Target hashmap */
 | 
						|
	ph7_value *pKey,          /* Lookup key */
 | 
						|
	ph7_hashmap_node **ppNode /* OUT: Target node on success */
 | 
						|
) {
 | 
						|
	sxi32 rc;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* TICKET 1433-25: Don't bother hashing,the hashmap is empty anyway.
 | 
						|
		 */
 | 
						|
		return SXERR_NOTFOUND;
 | 
						|
	}
 | 
						|
	rc = HashmapLookup(&(*pMap), &(*pKey), ppNode);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Insert a given key and it's associated value (if any) in the given
 | 
						|
 * hashmap.
 | 
						|
 * If a node with the given key already exists in the database
 | 
						|
 * then this function overwrite the old value.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapInsert(
 | 
						|
	ph7_hashmap *pMap, /* Target hashmap */
 | 
						|
	ph7_value *pKey,   /* Lookup key */
 | 
						|
	ph7_value *pVal    /* Node value.NULL otherwise */
 | 
						|
) {
 | 
						|
	sxi32 rc;
 | 
						|
	if(pVal && (pVal->iFlags & MEMOBJ_HASHMAP) && (ph7_hashmap *)pVal->x.pOther == pMap->pVm->pGlobal) {
 | 
						|
		/*
 | 
						|
		 * TICKET 1433-35: Insertion in the $GLOBALS array is forbidden.
 | 
						|
		 */
 | 
						|
		PH7_VmThrowError(pMap->pVm, 0, PH7_CTX_ERR, "$GLOBALS is a read-only array,insertion is forbidden");
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal));
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Insert a given key and it's associated value (foreign index) in the given
 | 
						|
 * hashmap.
 | 
						|
 * This is insertion by reference so be careful to mark the node
 | 
						|
 * with the HASHMAP_NODE_FOREIGN_OBJ flag being set.
 | 
						|
 * The insertion by reference is triggered when the following
 | 
						|
 * expression is encountered.
 | 
						|
 * $var = 10;
 | 
						|
 *  $a = array(&var);
 | 
						|
 * OR
 | 
						|
 *  $a[] =& $var;
 | 
						|
 * That is,$var is a foreign ph7_value and the $a array have no control
 | 
						|
 * over it's contents.
 | 
						|
 * Note that the node that hold the foreign ph7_value is automatically
 | 
						|
 * removed when the foreign ph7_value is unset.
 | 
						|
 * Example:
 | 
						|
 *  $var = 10;
 | 
						|
 *  $a[] =& $var;
 | 
						|
 *  echo count($a).PHP_EOL; //1
 | 
						|
 *  //Unset the foreign ph7_value now
 | 
						|
 *  unset($var);
 | 
						|
 *  echo count($a); //0
 | 
						|
 * Note that this is a PH7 eXtension.
 | 
						|
 * Refer to the official documentation for more information.
 | 
						|
 * If a node with the given key already exists in the database
 | 
						|
 * then this function overwrite the old value.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapInsertByRef(
 | 
						|
	ph7_hashmap *pMap, /* Target hashmap */
 | 
						|
	ph7_value *pKey,   /* Lookup key */
 | 
						|
	sxu32 nRefIdx      /* Foreign ph7_value index */
 | 
						|
) {
 | 
						|
	sxi32 rc;
 | 
						|
	if(nRefIdx == pMap->pVm->nGlobalIdx) {
 | 
						|
		/*
 | 
						|
		 * TICKET 1433-35: Insertion in the $GLOBALS array is forbidden.
 | 
						|
		 */
 | 
						|
		PH7_VmThrowError(pMap->pVm, 0, PH7_CTX_ERR, "$GLOBALS is a read-only array,insertion is forbidden");
 | 
						|
		return SXRET_OK;
 | 
						|
	}
 | 
						|
	rc = HashmapInsertByRef(&(*pMap), &(*pKey), nRefIdx);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Reset the node cursor of a given hashmap.
 | 
						|
 */
 | 
						|
PH7_PRIVATE void PH7_HashmapResetLoopCursor(ph7_hashmap *pMap) {
 | 
						|
	/* Reset the loop cursor */
 | 
						|
	pMap->pCur = pMap->pFirst;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Return a pointer to the node currently pointed by the node cursor.
 | 
						|
 * If the cursor reaches the end of the list,then this function
 | 
						|
 * return NULL.
 | 
						|
 * Note that the node cursor is automatically advanced by this function.
 | 
						|
 */
 | 
						|
PH7_PRIVATE ph7_hashmap_node *PH7_HashmapGetNextEntry(ph7_hashmap *pMap) {
 | 
						|
	ph7_hashmap_node *pCur = pMap->pCur;
 | 
						|
	if(pCur == 0) {
 | 
						|
		/* End of the list,return null */
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	/* Advance the node cursor */
 | 
						|
	pMap->pCur = pCur->pPrev; /* Reverse link */
 | 
						|
	return pCur;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Extract a node value.
 | 
						|
 */
 | 
						|
PH7_PRIVATE void PH7_HashmapExtractNodeValue(ph7_hashmap_node *pNode, ph7_value *pValue, int bStore) {
 | 
						|
	ph7_value *pEntry = HashmapExtractNodeValue(pNode);
 | 
						|
	if(pEntry) {
 | 
						|
		if(bStore) {
 | 
						|
			PH7_MemObjStore(pEntry, pValue);
 | 
						|
		} else {
 | 
						|
			PH7_MemObjLoad(pEntry, pValue);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		PH7_MemObjRelease(pValue);
 | 
						|
	}
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Extract a node key.
 | 
						|
 */
 | 
						|
PH7_PRIVATE void PH7_HashmapExtractNodeKey(ph7_hashmap_node *pNode, ph7_value *pKey) {
 | 
						|
	/* Fill with the current key */
 | 
						|
	if(pNode->iType == HASHMAP_INT_NODE) {
 | 
						|
		if(SyBlobLength(&pKey->sBlob) > 0) {
 | 
						|
			SyBlobRelease(&pKey->sBlob);
 | 
						|
		}
 | 
						|
		pKey->x.iVal = pNode->xKey.iKey;
 | 
						|
		MemObjSetType(pKey, MEMOBJ_INT);
 | 
						|
	} else {
 | 
						|
		SyBlobReset(&pKey->sBlob);
 | 
						|
		SyBlobAppend(&pKey->sBlob, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey));
 | 
						|
		MemObjSetType(pKey, MEMOBJ_STRING);
 | 
						|
	}
 | 
						|
}
 | 
						|
#ifndef PH7_DISABLE_BUILTIN_FUNC
 | 
						|
/*
 | 
						|
 * Store the address of nodes value in the given container.
 | 
						|
 * Refer to the [vfprintf(),vprintf(),vsprintf()] implementations
 | 
						|
 * defined in 'builtin.c' for more information.
 | 
						|
 */
 | 
						|
PH7_PRIVATE int PH7_HashmapValuesToSet(ph7_hashmap *pMap, SySet *pOut) {
 | 
						|
	ph7_hashmap_node *pEntry = pMap->pFirst;
 | 
						|
	ph7_value *pValue;
 | 
						|
	sxu32 n;
 | 
						|
	/* Initialize the container */
 | 
						|
	SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(ph7_value *));
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		/* Extract node value */
 | 
						|
		pValue = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pValue) {
 | 
						|
			SySetPut(pOut, (const void *)&pValue);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Total inserted entries */
 | 
						|
	return (int)SySetUsed(pOut);
 | 
						|
}
 | 
						|
#endif /* PH7_DISABLE_BUILTIN_FUNC */
 | 
						|
/*
 | 
						|
 * Merge sort.
 | 
						|
 * The merge sort implementation is based on the one found in the SQLite3 source tree.
 | 
						|
 * Status: Public domain
 | 
						|
 */
 | 
						|
/* Node comparison callback signature */
 | 
						|
typedef sxi32(*ProcNodeCmp)(ph7_hashmap_node *, ph7_hashmap_node *, void *);
 | 
						|
/*
 | 
						|
** Inputs:
 | 
						|
**   a:       A sorted, null-terminated linked list.  (May be null).
 | 
						|
**   b:       A sorted, null-terminated linked list.  (May be null).
 | 
						|
**   cmp:     A pointer to the comparison function.
 | 
						|
**
 | 
						|
** Return Value:
 | 
						|
**   A pointer to the head of a sorted list containing the elements
 | 
						|
**   of both a and b.
 | 
						|
**
 | 
						|
** Side effects:
 | 
						|
**   The "next","prev" pointers for elements in the lists a and b are
 | 
						|
**   changed.
 | 
						|
*/
 | 
						|
static ph7_hashmap_node *HashmapNodeMerge(ph7_hashmap_node *pA, ph7_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData) {
 | 
						|
	ph7_hashmap_node result, *pTail;
 | 
						|
	/* Prevent compiler warning */
 | 
						|
	result.pNext = result.pPrev = 0;
 | 
						|
	pTail = &result;
 | 
						|
	while(pA && pB) {
 | 
						|
		if(xCmp(pA, pB, pCmpData) < 0) {
 | 
						|
			pTail->pPrev = pA;
 | 
						|
			pA->pNext = pTail;
 | 
						|
			pTail = pA;
 | 
						|
			pA = pA->pPrev;
 | 
						|
		} else {
 | 
						|
			pTail->pPrev = pB;
 | 
						|
			pB->pNext = pTail;
 | 
						|
			pTail = pB;
 | 
						|
			pB = pB->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if(pA) {
 | 
						|
		pTail->pPrev = pA;
 | 
						|
		pA->pNext = pTail;
 | 
						|
	} else if(pB) {
 | 
						|
		pTail->pPrev = pB;
 | 
						|
		pB->pNext = pTail;
 | 
						|
	} else {
 | 
						|
		pTail->pPrev = pTail->pNext = 0;
 | 
						|
	}
 | 
						|
	return result.pPrev;
 | 
						|
}
 | 
						|
/*
 | 
						|
** Inputs:
 | 
						|
**   Map:       Input hashmap
 | 
						|
**   cmp:       A comparison function.
 | 
						|
**
 | 
						|
** Return Value:
 | 
						|
**   Sorted hashmap.
 | 
						|
**
 | 
						|
** Side effects:
 | 
						|
**   The "next" pointers for elements in list are changed.
 | 
						|
*/
 | 
						|
#define N_SORT_BUCKET  32
 | 
						|
static sxi32 HashmapMergeSort(ph7_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData) {
 | 
						|
	ph7_hashmap_node *a[N_SORT_BUCKET], *p, *pIn;
 | 
						|
	sxu32 i;
 | 
						|
	SyZero(a, sizeof(a));
 | 
						|
	/* Point to the first inserted entry */
 | 
						|
	pIn = pMap->pFirst;
 | 
						|
	while(pIn) {
 | 
						|
		p = pIn;
 | 
						|
		pIn = p->pPrev;
 | 
						|
		p->pPrev = 0;
 | 
						|
		for(i = 0; i < N_SORT_BUCKET - 1; i++) {
 | 
						|
			if(a[i] == 0) {
 | 
						|
				a[i] = p;
 | 
						|
				break;
 | 
						|
			} else {
 | 
						|
				p = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
 | 
						|
				a[i] = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(i == N_SORT_BUCKET - 1) {
 | 
						|
			/* To get here, there need to be 2^(N_SORT_BUCKET) elements in he input list.
 | 
						|
			 * But that is impossible.
 | 
						|
			 */
 | 
						|
			a[i] = HashmapNodeMerge(a[i], p, xCmp, pCmpData);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	p = a[0];
 | 
						|
	for(i = 1; i < N_SORT_BUCKET; i++) {
 | 
						|
		p = HashmapNodeMerge(p, a[i], xCmp, pCmpData);
 | 
						|
	}
 | 
						|
	p->pNext = 0;
 | 
						|
	/* Reflect the change */
 | 
						|
	pMap->pFirst = p;
 | 
						|
	/* Reset the loop cursor */
 | 
						|
	pMap->pCur = pMap->pFirst;
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Node comparison callback.
 | 
						|
 * used-by: [sort(),asort(),...]
 | 
						|
 */
 | 
						|
static sxi32 HashmapCmpCallback1(ph7_hashmap_node *pA, ph7_hashmap_node *pB, void *pCmpData) {
 | 
						|
	ph7_value sA, sB;
 | 
						|
	sxi32 iFlags;
 | 
						|
	int rc;
 | 
						|
	if(pCmpData == 0) {
 | 
						|
		/* Perform a standard comparison */
 | 
						|
		rc = HashmapNodeCmp(pA, pB, FALSE);
 | 
						|
		return rc;
 | 
						|
	}
 | 
						|
	iFlags = SX_PTR_TO_INT(pCmpData);
 | 
						|
	/* Duplicate node values */
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sA);
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sB);
 | 
						|
	PH7_HashmapExtractNodeValue(pA, &sA, FALSE);
 | 
						|
	PH7_HashmapExtractNodeValue(pB, &sB, FALSE);
 | 
						|
	if(iFlags == 5) {
 | 
						|
		/* String cast */
 | 
						|
		if((sA.iFlags & MEMOBJ_STRING) == 0) {
 | 
						|
			PH7_MemObjToString(&sA);
 | 
						|
		}
 | 
						|
		if((sB.iFlags & MEMOBJ_STRING) == 0) {
 | 
						|
			PH7_MemObjToString(&sB);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/* Numeric cast */
 | 
						|
		PH7_MemObjToNumeric(&sA);
 | 
						|
		PH7_MemObjToNumeric(&sB);
 | 
						|
	}
 | 
						|
	/* Perform the comparison */
 | 
						|
	rc = PH7_MemObjCmp(&sA, &sB, FALSE, 0);
 | 
						|
	PH7_MemObjRelease(&sA);
 | 
						|
	PH7_MemObjRelease(&sB);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Node comparison callback: Compare nodes by keys only.
 | 
						|
 * used-by: [ksort()]
 | 
						|
 */
 | 
						|
static sxi32 HashmapCmpCallback2(ph7_hashmap_node *pA, ph7_hashmap_node *pB, void *pCmpData) {
 | 
						|
	sxi32 rc;
 | 
						|
	SXUNUSED(pCmpData); /* cc warning */
 | 
						|
	if(pA->iType == HASHMAP_BLOB_NODE && pB->iType == HASHMAP_BLOB_NODE) {
 | 
						|
		/* Perform a string comparison */
 | 
						|
		rc = SyBlobCmp(&pA->xKey.sKey, &pB->xKey.sKey);
 | 
						|
	} else {
 | 
						|
		SyString sStr;
 | 
						|
		sxi64 iA, iB;
 | 
						|
		/* Perform a numeric comparison */
 | 
						|
		if(pA->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Cast to 64-bit integer */
 | 
						|
			SyStringInitFromBuf(&sStr, SyBlobData(&pA->xKey.sKey), SyBlobLength(&pA->xKey.sKey));
 | 
						|
			if(sStr.nByte < 1) {
 | 
						|
				iA = 0;
 | 
						|
			} else {
 | 
						|
				SyStrToInt64(sStr.zString, sStr.nByte, (void *)&iA, 0);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			iA = pA->xKey.iKey;
 | 
						|
		}
 | 
						|
		if(pB->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Cast to 64-bit integer */
 | 
						|
			SyStringInitFromBuf(&sStr, SyBlobData(&pB->xKey.sKey), SyBlobLength(&pB->xKey.sKey));
 | 
						|
			if(sStr.nByte < 1) {
 | 
						|
				iB = 0;
 | 
						|
			} else {
 | 
						|
				SyStrToInt64(sStr.zString, sStr.nByte, (void *)&iB, 0);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			iB = pB->xKey.iKey;
 | 
						|
		}
 | 
						|
		rc = (sxi32)(iA - iB);
 | 
						|
	}
 | 
						|
	/* Comparison result */
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Node comparison callback.
 | 
						|
 * Used by: [rsort(),arsort()];
 | 
						|
 */
 | 
						|
static sxi32 HashmapCmpCallback3(ph7_hashmap_node *pA, ph7_hashmap_node *pB, void *pCmpData) {
 | 
						|
	ph7_value sA, sB;
 | 
						|
	sxi32 iFlags;
 | 
						|
	int rc;
 | 
						|
	if(pCmpData == 0) {
 | 
						|
		/* Perform a standard comparison */
 | 
						|
		rc = HashmapNodeCmp(pA, pB, FALSE);
 | 
						|
		return -rc;
 | 
						|
	}
 | 
						|
	iFlags = SX_PTR_TO_INT(pCmpData);
 | 
						|
	/* Duplicate node values */
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sA);
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sB);
 | 
						|
	PH7_HashmapExtractNodeValue(pA, &sA, FALSE);
 | 
						|
	PH7_HashmapExtractNodeValue(pB, &sB, FALSE);
 | 
						|
	if(iFlags == 5) {
 | 
						|
		/* String cast */
 | 
						|
		if((sA.iFlags & MEMOBJ_STRING) == 0) {
 | 
						|
			PH7_MemObjToString(&sA);
 | 
						|
		}
 | 
						|
		if((sB.iFlags & MEMOBJ_STRING) == 0) {
 | 
						|
			PH7_MemObjToString(&sB);
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		/* Numeric cast */
 | 
						|
		PH7_MemObjToNumeric(&sA);
 | 
						|
		PH7_MemObjToNumeric(&sB);
 | 
						|
	}
 | 
						|
	/* Perform the comparison */
 | 
						|
	rc = PH7_MemObjCmp(&sA, &sB, FALSE, 0);
 | 
						|
	PH7_MemObjRelease(&sA);
 | 
						|
	PH7_MemObjRelease(&sB);
 | 
						|
	return -rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison.
 | 
						|
 * used-by: [usort(),uasort()]
 | 
						|
 */
 | 
						|
static sxi32 HashmapCmpCallback4(ph7_hashmap_node *pA, ph7_hashmap_node *pB, void *pCmpData) {
 | 
						|
	ph7_value sResult, *pCallback;
 | 
						|
	ph7_value *pV1, *pV2;
 | 
						|
	ph7_value *apArg[2];  /* Callback arguments */
 | 
						|
	sxi32 rc;
 | 
						|
	/* Point to the desired callback */
 | 
						|
	pCallback = (ph7_value *)pCmpData;
 | 
						|
	/* initialize the result value */
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sResult);
 | 
						|
	/* Extract nodes values */
 | 
						|
	pV1 = HashmapExtractNodeValue(pA);
 | 
						|
	pV2 = HashmapExtractNodeValue(pB);
 | 
						|
	apArg[0] = pV1;
 | 
						|
	apArg[1] = pV2;
 | 
						|
	/* Invoke the callback */
 | 
						|
	rc = PH7_VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult);
 | 
						|
	if(rc != SXRET_OK) {
 | 
						|
		/* An error occured while calling user defined function [i.e: not defined] */
 | 
						|
		rc = -1; /* Set a dummy result */
 | 
						|
	} else {
 | 
						|
		/* Extract callback result */
 | 
						|
		if((sResult.iFlags & MEMOBJ_INT) == 0) {
 | 
						|
			/* Perform an int cast */
 | 
						|
			PH7_MemObjToInteger(&sResult);
 | 
						|
		}
 | 
						|
		rc = (sxi32)sResult.x.iVal;
 | 
						|
	}
 | 
						|
	PH7_MemObjRelease(&sResult);
 | 
						|
	/* Callback result */
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Node comparison callback: Compare nodes by keys only.
 | 
						|
 * used-by: [krsort()]
 | 
						|
 */
 | 
						|
static sxi32 HashmapCmpCallback5(ph7_hashmap_node *pA, ph7_hashmap_node *pB, void *pCmpData) {
 | 
						|
	sxi32 rc;
 | 
						|
	SXUNUSED(pCmpData); /* cc warning */
 | 
						|
	if(pA->iType == HASHMAP_BLOB_NODE && pB->iType == HASHMAP_BLOB_NODE) {
 | 
						|
		/* Perform a string comparison */
 | 
						|
		rc = SyBlobCmp(&pA->xKey.sKey, &pB->xKey.sKey);
 | 
						|
	} else {
 | 
						|
		SyString sStr;
 | 
						|
		sxi64 iA, iB;
 | 
						|
		/* Perform a numeric comparison */
 | 
						|
		if(pA->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Cast to 64-bit integer */
 | 
						|
			SyStringInitFromBuf(&sStr, SyBlobData(&pA->xKey.sKey), SyBlobLength(&pA->xKey.sKey));
 | 
						|
			if(sStr.nByte < 1) {
 | 
						|
				iA = 0;
 | 
						|
			} else {
 | 
						|
				SyStrToInt64(sStr.zString, sStr.nByte, (void *)&iA, 0);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			iA = pA->xKey.iKey;
 | 
						|
		}
 | 
						|
		if(pB->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Cast to 64-bit integer */
 | 
						|
			SyStringInitFromBuf(&sStr, SyBlobData(&pB->xKey.sKey), SyBlobLength(&pB->xKey.sKey));
 | 
						|
			if(sStr.nByte < 1) {
 | 
						|
				iB = 0;
 | 
						|
			} else {
 | 
						|
				SyStrToInt64(sStr.zString, sStr.nByte, (void *)&iB, 0);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			iB = pB->xKey.iKey;
 | 
						|
		}
 | 
						|
		rc = (sxi32)(iA - iB);
 | 
						|
	}
 | 
						|
	return -rc; /* Reverse result */
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison.
 | 
						|
 * used-by: [uksort()]
 | 
						|
 */
 | 
						|
static sxi32 HashmapCmpCallback6(ph7_hashmap_node *pA, ph7_hashmap_node *pB, void *pCmpData) {
 | 
						|
	ph7_value sResult, *pCallback;
 | 
						|
	ph7_value *apArg[2];  /* Callback arguments */
 | 
						|
	ph7_value sK1, sK2;
 | 
						|
	sxi32 rc;
 | 
						|
	/* Point to the desired callback */
 | 
						|
	pCallback = (ph7_value *)pCmpData;
 | 
						|
	/* initialize the result value */
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sResult);
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sK1);
 | 
						|
	PH7_MemObjInit(pA->pMap->pVm, &sK2);
 | 
						|
	/* Extract nodes keys */
 | 
						|
	PH7_HashmapExtractNodeKey(pA, &sK1);
 | 
						|
	PH7_HashmapExtractNodeKey(pB, &sK2);
 | 
						|
	apArg[0] = &sK1;
 | 
						|
	apArg[1] = &sK2;
 | 
						|
	/* Mark keys as constants */
 | 
						|
	sK1.nIdx = SXU32_HIGH;
 | 
						|
	sK2.nIdx = SXU32_HIGH;
 | 
						|
	/* Invoke the callback */
 | 
						|
	rc = PH7_VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult);
 | 
						|
	if(rc != SXRET_OK) {
 | 
						|
		/* An error occured while calling user defined function [i.e: not defined] */
 | 
						|
		rc = -1; /* Set a dummy result */
 | 
						|
	} else {
 | 
						|
		/* Extract callback result */
 | 
						|
		if((sResult.iFlags & MEMOBJ_INT) == 0) {
 | 
						|
			/* Perform an int cast */
 | 
						|
			PH7_MemObjToInteger(&sResult);
 | 
						|
		}
 | 
						|
		rc = (sxi32)sResult.x.iVal;
 | 
						|
	}
 | 
						|
	PH7_MemObjRelease(&sResult);
 | 
						|
	PH7_MemObjRelease(&sK1);
 | 
						|
	PH7_MemObjRelease(&sK2);
 | 
						|
	/* Callback result */
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Node comparison callback: Random node comparison.
 | 
						|
 * used-by: [shuffle()]
 | 
						|
 */
 | 
						|
static sxi32 HashmapCmpCallback7(ph7_hashmap_node *pA, ph7_hashmap_node *pB, void *pCmpData) {
 | 
						|
	sxu32 n;
 | 
						|
	SXUNUSED(pB); /* cc warning */
 | 
						|
	SXUNUSED(pCmpData);
 | 
						|
	/* Grab a random number */
 | 
						|
	n = PH7_VmRandomNum(pA->pMap->pVm);
 | 
						|
	/* if the random number is odd then the first node 'pA' is greater then
 | 
						|
	 * the second node 'pB'. Otherwise the reverse is assumed.
 | 
						|
	 */
 | 
						|
	return n & 1 ? 1 : -1;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Rehash all nodes keys after a merge-sort have been applied.
 | 
						|
 * Used by [sort(),usort() and rsort()].
 | 
						|
 */
 | 
						|
static void HashmapSortRehash(ph7_hashmap *pMap) {
 | 
						|
	ph7_hashmap_node *p, *pLast;
 | 
						|
	sxu32 i;
 | 
						|
	/* Rehash all entries */
 | 
						|
	pLast = p = pMap->pFirst;
 | 
						|
	pMap->iNextIdx = 0; /* Reset the automatic index */
 | 
						|
	i = 0;
 | 
						|
	for(;;) {
 | 
						|
		if(i >= pMap->nEntry) {
 | 
						|
			pMap->pLast = pLast; /* Fix the last link broken by the merge-sort */
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if(p->iType == HASHMAP_BLOB_NODE) {
 | 
						|
			/* Do not maintain index association as requested by the PHP specification */
 | 
						|
			SyBlobRelease(&p->xKey.sKey);
 | 
						|
			/* Change key type */
 | 
						|
			p->iType = HASHMAP_INT_NODE;
 | 
						|
		}
 | 
						|
		HashmapRehashIntNode(p);
 | 
						|
		/* Point to the next entry */
 | 
						|
		i++;
 | 
						|
		pLast = p;
 | 
						|
		p = p->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Array functions implementation.
 | 
						|
 * Authors:
 | 
						|
 *  Symisc Systems,devel@symisc.net.
 | 
						|
 *  Copyright (C) Symisc Systems,http://ph7.symisc.net
 | 
						|
 * Status:
 | 
						|
 *  Stable.
 | 
						|
 */
 | 
						|
/*
 | 
						|
 * bool sort(array &$array[,int $sort_flags = SORT_REGULAR ] )
 | 
						|
 * Sort an array.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $sort_flags
 | 
						|
 *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 | 
						|
 *  Sorting type flags:
 | 
						|
 *   SORT_REGULAR - compare items normally (don't change types)
 | 
						|
 *   SORT_NUMERIC - compare items numerically
 | 
						|
 *   SORT_STRING - compare items as strings
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int ph7_hashmap_sort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		sxi32 iCmpFlags = 0;
 | 
						|
		if(nArg > 1) {
 | 
						|
			/* Extract comparison flags */
 | 
						|
			iCmpFlags = ph7_value_to_int(apArg[1]);
 | 
						|
			if(iCmpFlags == 3 /* SORT_REGULAR */) {
 | 
						|
				iCmpFlags = 0; /* Standard comparison */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags));
 | 
						|
		/* Rehash [Do not maintain index association as requested by the PHP specification] */
 | 
						|
		HashmapSortRehash(pMap);
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool asort(array &$array[,int $sort_flags = SORT_REGULAR ] )
 | 
						|
 *  Sort an array and maintain index association.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $sort_flags
 | 
						|
 *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 | 
						|
 *  Sorting type flags:
 | 
						|
 *   SORT_REGULAR - compare items normally (don't change types)
 | 
						|
 *   SORT_NUMERIC - compare items numerically
 | 
						|
 *   SORT_STRING - compare items as strings
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_asort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		sxi32 iCmpFlags = 0;
 | 
						|
		if(nArg > 1) {
 | 
						|
			/* Extract comparison flags */
 | 
						|
			iCmpFlags = ph7_value_to_int(apArg[1]);
 | 
						|
			if(iCmpFlags == 3 /* SORT_REGULAR */) {
 | 
						|
				iCmpFlags = 0; /* Standard comparison */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, HashmapCmpCallback1, SX_INT_TO_PTR(iCmpFlags));
 | 
						|
		/* Fix the last link broken by the merge */
 | 
						|
		while(pMap->pLast->pPrev) {
 | 
						|
			pMap->pLast = pMap->pLast->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool arsort(array &$array[,int $sort_flags = SORT_REGULAR ] )
 | 
						|
 *  Sort an array in reverse order and maintain index association.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $sort_flags
 | 
						|
 *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 | 
						|
 *  Sorting type flags:
 | 
						|
 *   SORT_REGULAR - compare items normally (don't change types)
 | 
						|
 *   SORT_NUMERIC - compare items numerically
 | 
						|
 *   SORT_STRING - compare items as strings
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_arsort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		sxi32 iCmpFlags = 0;
 | 
						|
		if(nArg > 1) {
 | 
						|
			/* Extract comparison flags */
 | 
						|
			iCmpFlags = ph7_value_to_int(apArg[1]);
 | 
						|
			if(iCmpFlags == 3 /* SORT_REGULAR */) {
 | 
						|
				iCmpFlags = 0; /* Standard comparison */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags));
 | 
						|
		/* Fix the last link broken by the merge */
 | 
						|
		while(pMap->pLast->pPrev) {
 | 
						|
			pMap->pLast = pMap->pLast->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool ksort(array &$array[,int $sort_flags = SORT_REGULAR ] )
 | 
						|
 *  Sort an array by key.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $sort_flags
 | 
						|
 *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 | 
						|
 *  Sorting type flags:
 | 
						|
 *   SORT_REGULAR - compare items normally (don't change types)
 | 
						|
 *   SORT_NUMERIC - compare items numerically
 | 
						|
 *   SORT_STRING - compare items as strings
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_ksort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		sxi32 iCmpFlags = 0;
 | 
						|
		if(nArg > 1) {
 | 
						|
			/* Extract comparison flags */
 | 
						|
			iCmpFlags = ph7_value_to_int(apArg[1]);
 | 
						|
			if(iCmpFlags == 3 /* SORT_REGULAR */) {
 | 
						|
				iCmpFlags = 0; /* Standard comparison */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, HashmapCmpCallback2, SX_INT_TO_PTR(iCmpFlags));
 | 
						|
		/* Fix the last link broken by the merge */
 | 
						|
		while(pMap->pLast->pPrev) {
 | 
						|
			pMap->pLast = pMap->pLast->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool krsort(array &$array[,int $sort_flags = SORT_REGULAR ] )
 | 
						|
 *  Sort an array by key in reverse order.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $sort_flags
 | 
						|
 *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 | 
						|
 *  Sorting type flags:
 | 
						|
 *   SORT_REGULAR - compare items normally (don't change types)
 | 
						|
 *   SORT_NUMERIC - compare items numerically
 | 
						|
 *   SORT_STRING - compare items as strings
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_krsort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		sxi32 iCmpFlags = 0;
 | 
						|
		if(nArg > 1) {
 | 
						|
			/* Extract comparison flags */
 | 
						|
			iCmpFlags = ph7_value_to_int(apArg[1]);
 | 
						|
			if(iCmpFlags == 3 /* SORT_REGULAR */) {
 | 
						|
				iCmpFlags = 0; /* Standard comparison */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, HashmapCmpCallback5, SX_INT_TO_PTR(iCmpFlags));
 | 
						|
		/* Fix the last link broken by the merge */
 | 
						|
		while(pMap->pLast->pPrev) {
 | 
						|
			pMap->pLast = pMap->pLast->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool rsort(array &$array[,int $sort_flags = SORT_REGULAR ] )
 | 
						|
 * Sort an array in reverse order.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $sort_flags
 | 
						|
 *  The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 | 
						|
 *  Sorting type flags:
 | 
						|
 *   SORT_REGULAR - compare items normally (don't change types)
 | 
						|
 *   SORT_NUMERIC - compare items numerically
 | 
						|
 *   SORT_STRING - compare items as strings
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_rsort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		sxi32 iCmpFlags = 0;
 | 
						|
		if(nArg > 1) {
 | 
						|
			/* Extract comparison flags */
 | 
						|
			iCmpFlags = ph7_value_to_int(apArg[1]);
 | 
						|
			if(iCmpFlags == 3 /* SORT_REGULAR */) {
 | 
						|
				iCmpFlags = 0; /* Standard comparison */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, HashmapCmpCallback3, SX_INT_TO_PTR(iCmpFlags));
 | 
						|
		/* Rehash [Do not maintain index association as requested by the PHP specification] */
 | 
						|
		HashmapSortRehash(pMap);
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool usort(array &$array,callable $cmp_function)
 | 
						|
 *  Sort an array by values using a user-defined comparison function.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $cmp_function
 | 
						|
 *  The comparison function must return an integer less than, equal to, or greater
 | 
						|
 *  than zero if the first argument is considered to be respectively less than, equal
 | 
						|
 *  to, or greater than the second.
 | 
						|
 *    int callback ( mixed $a, mixed $b )
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_usort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		ph7_value *pCallback = 0;
 | 
						|
		ProcNodeCmp xCmp;
 | 
						|
		xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */
 | 
						|
		if(nArg > 1 && ph7_value_is_callable(apArg[1])) {
 | 
						|
			/* Point to the desired callback */
 | 
						|
			pCallback = apArg[1];
 | 
						|
		} else {
 | 
						|
			/* Use the default comparison function */
 | 
						|
			xCmp = HashmapCmpCallback1;
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, xCmp, pCallback);
 | 
						|
		/* Rehash [Do not maintain index association as requested by the PHP specification] */
 | 
						|
		HashmapSortRehash(pMap);
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool uasort(array &$array,callable $cmp_function)
 | 
						|
 *  Sort an array by values using a user-defined comparison function
 | 
						|
 *  and maintain index association.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $cmp_function
 | 
						|
 *  The comparison function must return an integer less than, equal to, or greater
 | 
						|
 *  than zero if the first argument is considered to be respectively less than, equal
 | 
						|
 *  to, or greater than the second.
 | 
						|
 *    int callback ( mixed $a, mixed $b )
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_uasort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		ph7_value *pCallback = 0;
 | 
						|
		ProcNodeCmp xCmp;
 | 
						|
		xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */
 | 
						|
		if(nArg > 1 && ph7_value_is_callable(apArg[1])) {
 | 
						|
			/* Point to the desired callback */
 | 
						|
			pCallback = apArg[1];
 | 
						|
		} else {
 | 
						|
			/* Use the default comparison function */
 | 
						|
			xCmp = HashmapCmpCallback1;
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, xCmp, pCallback);
 | 
						|
		/* Fix the last link broken by the merge */
 | 
						|
		while(pMap->pLast->pPrev) {
 | 
						|
			pMap->pLast = pMap->pLast->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool uksort(array &$array,callable $cmp_function)
 | 
						|
 *  Sort an array by keys using a user-defined comparison
 | 
						|
 *  function and maintain index association.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $cmp_function
 | 
						|
 *  The comparison function must return an integer less than, equal to, or greater
 | 
						|
 *  than zero if the first argument is considered to be respectively less than, equal
 | 
						|
 *  to, or greater than the second.
 | 
						|
 *    int callback ( mixed $a, mixed $b )
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_uksort(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		ph7_value *pCallback = 0;
 | 
						|
		ProcNodeCmp xCmp;
 | 
						|
		xCmp = HashmapCmpCallback6; /* User-defined function as the comparison callback */
 | 
						|
		if(nArg > 1 && ph7_value_is_callable(apArg[1])) {
 | 
						|
			/* Point to the desired callback */
 | 
						|
			pCallback = apArg[1];
 | 
						|
		} else {
 | 
						|
			/* Use the default comparison function */
 | 
						|
			xCmp = HashmapCmpCallback2;
 | 
						|
		}
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, xCmp, pCallback);
 | 
						|
		/* Fix the last link broken by the merge */
 | 
						|
		while(pMap->pLast->pPrev) {
 | 
						|
			pMap->pLast = pMap->pLast->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool shuffle(array &$array)
 | 
						|
 *  shuffles (randomizes the order of the elements in) an array.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 *
 | 
						|
 */
 | 
						|
static int ph7_hashmap_shuffle(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry > 1) {
 | 
						|
		/* Do the merge sort */
 | 
						|
		HashmapMergeSort(pMap, HashmapCmpCallback7, 0);
 | 
						|
		/* Fix the last link broken by the merge */
 | 
						|
		while(pMap->pLast->pPrev) {
 | 
						|
			pMap->pLast = pMap->pLast->pPrev;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * int count(array $var [, int $mode = COUNT_NORMAL ])
 | 
						|
 *   Count all elements in an array, or something in an object.
 | 
						|
 * Parameters
 | 
						|
 *  $var
 | 
						|
 *   The array or the object.
 | 
						|
 * $mode
 | 
						|
 *  If the optional mode parameter is set to COUNT_RECURSIVE (or 1), count()
 | 
						|
 *  will recursively count the array. This is particularly useful for counting
 | 
						|
 *  all the elements of a multidimensional array. count() does not detect infinite
 | 
						|
 *  recursion.
 | 
						|
 * Return
 | 
						|
 *  Returns the number of elements in the array.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_count(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	int bRecursive = FALSE;
 | 
						|
	sxi64 iCount;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* TICKET 1433-19: Handle objects */
 | 
						|
		int res = !ph7_value_is_null(apArg[0]);
 | 
						|
		ph7_result_int(pCtx, res);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg > 1) {
 | 
						|
		/* Recursive count? */
 | 
						|
		bRecursive = ph7_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */;
 | 
						|
	}
 | 
						|
	/* Count */
 | 
						|
	iCount = HashmapCount((ph7_hashmap *)apArg[0]->x.pOther, bRecursive, 0);
 | 
						|
	ph7_result_int64(pCtx, iCount);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool array_key_exists(value $key,array $search)
 | 
						|
 *  Checks if the given key or index exists in the array.
 | 
						|
 * Parameters
 | 
						|
 * $key
 | 
						|
 *   Value to check.
 | 
						|
 * $search
 | 
						|
 *  An array with keys to check.
 | 
						|
 * Return
 | 
						|
 *  TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_key_exists(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	sxi32 rc;
 | 
						|
	if(nArg < 2) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[1])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the lookup */
 | 
						|
	rc = PH7_HashmapLookup((ph7_hashmap *)apArg[1]->x.pOther, apArg[0], 0);
 | 
						|
	/* lookup result */
 | 
						|
	ph7_result_bool(pCtx, rc == SXRET_OK ? 1 : 0);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value array_pop(array $array)
 | 
						|
 *   POP the last inserted element from the array.
 | 
						|
 * Parameter
 | 
						|
 *  The array to get the value from.
 | 
						|
 * Return
 | 
						|
 *  Poped value or NULL on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_pop(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return null */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return null */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* Noting to pop,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
	} else {
 | 
						|
		ph7_hashmap_node *pLast = pMap->pLast;
 | 
						|
		ph7_value *pObj;
 | 
						|
		pObj = HashmapExtractNodeValue(pLast);
 | 
						|
		if(pObj) {
 | 
						|
			/* Node value */
 | 
						|
			ph7_result_value(pCtx, pObj);
 | 
						|
			/* Unlink the node */
 | 
						|
			PH7_HashmapUnlinkNode(pLast, TRUE);
 | 
						|
		} else {
 | 
						|
			ph7_result_null(pCtx);
 | 
						|
		}
 | 
						|
		/* Reset the cursor */
 | 
						|
		pMap->pCur = pMap->pFirst;
 | 
						|
	}
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * int array_push($array,$var,...)
 | 
						|
 *   Push one or more elements onto the end of array. (Stack insertion)
 | 
						|
 * Parameters
 | 
						|
 *  array
 | 
						|
 *    The input array.
 | 
						|
 *  var
 | 
						|
 *   On or more value to push.
 | 
						|
 * Return
 | 
						|
 *  New array count (including old items).
 | 
						|
 */
 | 
						|
static int ph7_hashmap_push(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	sxi32 rc;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Start pushing given values */
 | 
						|
	for(i = 1 ; i < nArg ; ++i) {
 | 
						|
		rc = PH7_HashmapInsert(pMap, 0, apArg[i]);
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Return the new count */
 | 
						|
	ph7_result_int64(pCtx, (sxi64)pMap->nEntry);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value array_shift(array $array)
 | 
						|
 *   Shift an element off the beginning of array.
 | 
						|
 * Parameter
 | 
						|
 *  The array to get the value from.
 | 
						|
 * Return
 | 
						|
 *  Shifted value or NULL on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_shift(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return null */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return null */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* Empty hashmap,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
	} else {
 | 
						|
		ph7_hashmap_node *pEntry = pMap->pFirst;
 | 
						|
		ph7_value *pObj;
 | 
						|
		sxu32 n;
 | 
						|
		pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pObj) {
 | 
						|
			/* Node value */
 | 
						|
			ph7_result_value(pCtx, pObj);
 | 
						|
			/* Unlink the first node */
 | 
						|
			PH7_HashmapUnlinkNode(pEntry, TRUE);
 | 
						|
		} else {
 | 
						|
			ph7_result_null(pCtx);
 | 
						|
		}
 | 
						|
		/* Rehash all int keys */
 | 
						|
		n = pMap->nEntry;
 | 
						|
		pEntry = pMap->pFirst;
 | 
						|
		pMap->iNextIdx = 0; /* Reset the automatic index */
 | 
						|
		for(;;) {
 | 
						|
			if(n < 1) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if(pEntry->iType == HASHMAP_INT_NODE) {
 | 
						|
				HashmapRehashIntNode(pEntry);
 | 
						|
			}
 | 
						|
			/* Point to the next entry */
 | 
						|
			pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
			n--;
 | 
						|
		}
 | 
						|
		/* Reset the cursor */
 | 
						|
		pMap->pCur = pMap->pFirst;
 | 
						|
	}
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Extract the node cursor value.
 | 
						|
 */
 | 
						|
static sxi32 HashmapCurrentValue(ph7_context *pCtx, ph7_hashmap *pMap, int iDirection) {
 | 
						|
	ph7_hashmap_node *pCur = pMap->pCur;
 | 
						|
	ph7_value *pVal;
 | 
						|
	if(pCur == 0) {
 | 
						|
		/* Cursor does not point to anything,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(iDirection != 0) {
 | 
						|
		if(iDirection > 0) {
 | 
						|
			/* Point to the next entry */
 | 
						|
			pMap->pCur = pCur->pPrev; /* Reverse link */
 | 
						|
			pCur = pMap->pCur;
 | 
						|
		} else {
 | 
						|
			/* Point to the previous entry */
 | 
						|
			pMap->pCur = pCur->pNext; /* Reverse link */
 | 
						|
			pCur = pMap->pCur;
 | 
						|
		}
 | 
						|
		if(pCur == 0) {
 | 
						|
			/* End of input reached,return FALSE */
 | 
						|
			ph7_result_bool(pCtx, 0);
 | 
						|
			return PH7_OK;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Point to the desired element */
 | 
						|
	pVal = HashmapExtractNodeValue(pCur);
 | 
						|
	if(pVal) {
 | 
						|
		ph7_result_value(pCtx, pVal);
 | 
						|
	} else {
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
	}
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value current(array $array)
 | 
						|
 *  Return the current element in an array.
 | 
						|
 * Parameter
 | 
						|
 *  $input: The input array.
 | 
						|
 * Return
 | 
						|
 *  The current() function simply returns the value of the array element that's currently
 | 
						|
 *  being pointed to by the internal pointer. It does not move the pointer in any way.
 | 
						|
 *  If the internal pointer points beyond the end of the elements list or the array
 | 
						|
 *  is empty, current() returns FALSE.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_current(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	HashmapCurrentValue(&(*pCtx), (ph7_hashmap *)apArg[0]->x.pOther, 0);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value next(array $input)
 | 
						|
 *  Advance the internal array pointer of an array.
 | 
						|
 * Parameter
 | 
						|
 *  $input: The input array.
 | 
						|
 * Return
 | 
						|
 *  next() behaves like current(), with one difference. It advances the internal array
 | 
						|
 *  pointer one place forward before returning the element value. That means it returns
 | 
						|
 *  the next array value and advances the internal array pointer by one.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_next(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	HashmapCurrentValue(&(*pCtx), (ph7_hashmap *)apArg[0]->x.pOther, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value prev(array $input)
 | 
						|
 *  Rewind the internal array pointer.
 | 
						|
 * Parameter
 | 
						|
 *  $input: The input array.
 | 
						|
 * Return
 | 
						|
 *  Returns the array value in the previous place that's pointed
 | 
						|
 *  to by the internal array pointer, or FALSE if there are no more
 | 
						|
 *  elements.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_prev(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	HashmapCurrentValue(&(*pCtx), (ph7_hashmap *)apArg[0]->x.pOther, -1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value end(array $input)
 | 
						|
 *  Set the internal pointer of an array to its last element.
 | 
						|
 * Parameter
 | 
						|
 *  $input: The input array.
 | 
						|
 * Return
 | 
						|
 *  Returns the value of the last element or FALSE for empty array.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_end(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Point to the last node */
 | 
						|
	pMap->pCur = pMap->pLast;
 | 
						|
	/* Return the last node value */
 | 
						|
	HashmapCurrentValue(&(*pCtx), pMap, 0);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value reset(array $array )
 | 
						|
 *  Set the internal pointer of an array to its first element.
 | 
						|
 * Parameter
 | 
						|
 *  $input: The input array.
 | 
						|
 * Return
 | 
						|
 *  Returns the value of the first array element,or FALSE if the array is empty.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_reset(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Point to the first node */
 | 
						|
	pMap->pCur = pMap->pFirst;
 | 
						|
	/* Return the last node value if available */
 | 
						|
	HashmapCurrentValue(&(*pCtx), pMap, 0);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value key(array $array)
 | 
						|
 *   Fetch a key from an array
 | 
						|
 * Parameter
 | 
						|
 *  $input
 | 
						|
 *   The input array.
 | 
						|
 * Return
 | 
						|
 *  The key() function simply returns the key of the array element that's currently
 | 
						|
 *  being pointed to by the internal pointer. It does not move the pointer in any way.
 | 
						|
 *  If the internal pointer points beyond the end of the elements list or the array
 | 
						|
 *  is empty, key() returns NULL.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_simple_key(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pCur;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	pCur = pMap->pCur;
 | 
						|
	if(pCur == 0) {
 | 
						|
		/* Cursor does not point to anything,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(pCur->iType == HASHMAP_INT_NODE) {
 | 
						|
		/* Key is integer */
 | 
						|
		ph7_result_int64(pCtx, pCur->xKey.iKey);
 | 
						|
	} else {
 | 
						|
		/* Key is blob */
 | 
						|
		ph7_result_string(pCtx,
 | 
						|
						  (const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey));
 | 
						|
	}
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array each(array $input)
 | 
						|
 *  Return the current key and value pair from an array and advance the array cursor.
 | 
						|
 * Parameter
 | 
						|
 *  $input
 | 
						|
 *    The input array.
 | 
						|
 * Return
 | 
						|
 *  Returns the current key and value pair from the array array. This pair is returned
 | 
						|
 *  in a four-element array, with the keys 0, 1, key, and value. Elements 0 and key
 | 
						|
 *  contain the key name of the array element, and 1 and value contain the data.
 | 
						|
 *  If the internal pointer for the array points past the end of the array contents
 | 
						|
 *  each() returns FALSE.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_each(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pCur;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	ph7_value sKey;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation that describe the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->pCur == 0) {
 | 
						|
		/* Cursor does not point to anything,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pCur = pMap->pCur;
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pVal = HashmapExtractNodeValue(pCur);
 | 
						|
	/* Insert the current value */
 | 
						|
	ph7_array_add_intkey_elem(pArray, 1, pVal);
 | 
						|
	ph7_array_add_strkey_elem(pArray, "value", pVal);
 | 
						|
	/* Make the key */
 | 
						|
	if(pCur->iType == HASHMAP_INT_NODE) {
 | 
						|
		PH7_MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey);
 | 
						|
	} else {
 | 
						|
		PH7_MemObjInitFromString(pMap->pVm, &sKey, 0);
 | 
						|
		PH7_MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey));
 | 
						|
	}
 | 
						|
	/* Insert the current key */
 | 
						|
	ph7_array_add_intkey_elem(pArray, 0, &sKey);
 | 
						|
	ph7_array_add_strkey_elem(pArray, "key", &sKey);
 | 
						|
	PH7_MemObjRelease(&sKey);
 | 
						|
	/* Advance the cursor */
 | 
						|
	pMap->pCur = pCur->pPrev; /* Reverse link */
 | 
						|
	/* Return the current entry */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array range(int $start,int $limit,int $step)
 | 
						|
 *  Create an array containing a range of elements
 | 
						|
 * Parameter
 | 
						|
 *  start
 | 
						|
 *   First value of the sequence.
 | 
						|
 *  limit
 | 
						|
 *   The sequence is ended upon reaching the limit value.
 | 
						|
 *  step
 | 
						|
 *  If a step value is given, it will be used as the increment between elements in the sequence.
 | 
						|
 *  step should be given as a positive number. If not specified, step will default to 1.
 | 
						|
 * Return
 | 
						|
 *  An array of elements from start to limit, inclusive.
 | 
						|
 * NOTE:
 | 
						|
 *  Only 32/64 bit integer key is supported.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_range(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_value *pValue, *pArray;
 | 
						|
	sxi64 iOfft, iLimit;
 | 
						|
	int iStep = 1;
 | 
						|
	iOfft = iLimit = 0; /* cc -O6 */
 | 
						|
	if(nArg > 0) {
 | 
						|
		/* Extract the offset */
 | 
						|
		iOfft = ph7_value_to_int64(apArg[0]);
 | 
						|
		if(nArg > 1) {
 | 
						|
			/* Extract the limit */
 | 
						|
			iLimit = ph7_value_to_int64(apArg[1]);
 | 
						|
			if(nArg > 2) {
 | 
						|
				/* Extract the increment */
 | 
						|
				iStep = ph7_value_to_int(apArg[2]);
 | 
						|
				if(iStep < 1) {
 | 
						|
					/* Only positive number are allowed */
 | 
						|
					iStep = 1;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Element container */
 | 
						|
	pValue = ph7_context_new_scalar(pCtx);
 | 
						|
	/* Create the new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Start filling */
 | 
						|
	while(iOfft <= iLimit) {
 | 
						|
		ph7_value_int64(pValue, iOfft);
 | 
						|
		/* Perform the insertion */
 | 
						|
		ph7_array_add_elem(pArray, 0/* Automatic index assign*/, pValue);
 | 
						|
		/* Increment */
 | 
						|
		iOfft += iStep;
 | 
						|
	}
 | 
						|
	/* Return the new array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	/* Dont'worry about freeing 'pValue',it will be released automatically
 | 
						|
	 * by the virtual machine as soon we return from this foreign function.
 | 
						|
	 */
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_values(array $input)
 | 
						|
 *   Returns all the values from the input array and indexes numerically the array.
 | 
						|
 * Parameters
 | 
						|
 *   input: The input array.
 | 
						|
 * Return
 | 
						|
 *  An indexed array of values or NULL on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_values(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pObj;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation that describe the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pNode = pMap->pFirst;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; ++n) {
 | 
						|
		pObj = HashmapExtractNodeValue(pNode);
 | 
						|
		if(pObj) {
 | 
						|
			/* perform the insertion */
 | 
						|
			ph7_array_add_elem(pArray, 0/* Automatic index assign */, pObj);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pNode = pNode->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* return the new array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_keys(array $input [, val $search_value [, bool $strict = false ]] )
 | 
						|
 *  Return all the keys or a subset of the keys of an array.
 | 
						|
 * Parameters
 | 
						|
 *  $input
 | 
						|
 *   An array containing keys to return.
 | 
						|
 * $search_value
 | 
						|
 *   If specified, then only keys containing these values are returned.
 | 
						|
 * $strict
 | 
						|
 *   Determines if strict comparison (===) should be used during the search.
 | 
						|
 * Return
 | 
						|
 *  An array of all the keys in input or NULL on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_keys(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value sObj;
 | 
						|
	ph7_value sVal;
 | 
						|
	SyString sKey;
 | 
						|
	int bStrict;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	bStrict = FALSE;
 | 
						|
	if(nArg > 2 && ph7_value_is_bool(apArg[2])) {
 | 
						|
		bStrict = ph7_value_to_bool(apArg[2]);
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pNode = pMap->pFirst;
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sVal);
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; ++n) {
 | 
						|
		if(pNode->iType == HASHMAP_INT_NODE) {
 | 
						|
			PH7_MemObjInitFromInt(pMap->pVm, &sObj, pNode->xKey.iKey);
 | 
						|
		} else {
 | 
						|
			SyStringInitFromBuf(&sKey, SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey));
 | 
						|
			PH7_MemObjInitFromString(pMap->pVm, &sObj, &sKey);
 | 
						|
		}
 | 
						|
		rc = 0;
 | 
						|
		if(nArg > 1) {
 | 
						|
			ph7_value *pValue = HashmapExtractNodeValue(pNode);
 | 
						|
			if(pValue) {
 | 
						|
				PH7_MemObjLoad(pValue, &sVal);
 | 
						|
				/* Filter key */
 | 
						|
				rc = ph7_value_compare(&sVal, apArg[1], bStrict);
 | 
						|
				PH7_MemObjRelease(pValue);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(rc == 0) {
 | 
						|
			/* Perform the insertion */
 | 
						|
			ph7_array_add_elem(pArray, 0, &sObj);
 | 
						|
		}
 | 
						|
		PH7_MemObjRelease(&sObj);
 | 
						|
		/* Point to the next entry */
 | 
						|
		pNode = pNode->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* return the new array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool array_same(array $arr1,array $arr2)
 | 
						|
 *  Return TRUE if the given arrays are the same instance.
 | 
						|
 *  This function is useful under PH7 since arrays are passed
 | 
						|
 *  by reference unlike the zend engine which use pass by values.
 | 
						|
 * Parameters
 | 
						|
 *  $arr1
 | 
						|
 *   First array
 | 
						|
 *  $arr2
 | 
						|
 *   Second array
 | 
						|
 * Return
 | 
						|
 *  TRUE if the arrays are the same instance.FALSE otherwise.
 | 
						|
 * Note
 | 
						|
 *  This function is a symisc eXtension.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_same(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *p1, *p2;
 | 
						|
	int rc;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0]) || !ph7_value_is_array(apArg[1])) {
 | 
						|
		/* Missing or invalid arguments,return FALSE*/
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the hashmaps */
 | 
						|
	p1 = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	p2 = (ph7_hashmap *)apArg[1]->x.pOther;
 | 
						|
	rc = (p1 == p2);
 | 
						|
	/* Same instance? */
 | 
						|
	ph7_result_bool(pCtx, rc);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_merge(array $array1,...)
 | 
						|
 *  Merge one or more arrays.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    Initial array to merge.
 | 
						|
 *  ...
 | 
						|
 *   More array to merge.
 | 
						|
 * Return
 | 
						|
 *  The resulting array.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_merge(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap, *pSrc;
 | 
						|
	ph7_value *pArray;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the hashmap */
 | 
						|
	pMap = (ph7_hashmap *)pArray->x.pOther;
 | 
						|
	/* Start merging */
 | 
						|
	for(i = 0 ; i < nArg ; i++) {
 | 
						|
		/* Make sure we are dealing with a valid hashmap */
 | 
						|
		if(!ph7_value_is_array(apArg[i])) {
 | 
						|
			/* Insert scalar value */
 | 
						|
			ph7_array_add_elem(pArray, 0, apArg[i]);
 | 
						|
		} else {
 | 
						|
			pSrc = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
			/* Merge the two hashmaps */
 | 
						|
			HashmapMerge(pSrc, pMap);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_copy(array $source)
 | 
						|
 *  Make a blind copy of the target array.
 | 
						|
 * Parameters
 | 
						|
 *  $source
 | 
						|
 *   Target array
 | 
						|
 * Return
 | 
						|
 *  Copy of the target array on success.NULL otherwise.
 | 
						|
 * Note
 | 
						|
 *  This function is a symisc eXtension.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_copy(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the hashmap */
 | 
						|
	pMap = (ph7_hashmap *)pArray->x.pOther;
 | 
						|
	if(ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Point to the internal representation of the source */
 | 
						|
		ph7_hashmap *pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
		/* Perform the copy */
 | 
						|
		PH7_HashmapDup(pSrc, pMap);
 | 
						|
	} else {
 | 
						|
		/* Simple insertion */
 | 
						|
		PH7_HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]);
 | 
						|
	}
 | 
						|
	/* Return the duplicated array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool array_erase(array $source)
 | 
						|
 *  Remove all elements from a given array.
 | 
						|
 * Parameters
 | 
						|
 *  $source
 | 
						|
 *   Target array
 | 
						|
 * Return
 | 
						|
 *  TRUE on success.FALSE otherwise.
 | 
						|
 * Note
 | 
						|
 *  This function is a symisc eXtension.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_erase(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the target hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Erase */
 | 
						|
	PH7_HashmapRelease(pMap, FALSE);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_slice(array $array,int $offset [,int $length [, bool $preserve_keys = false ]])
 | 
						|
 *  Extract a slice of the array.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *    The input array.
 | 
						|
 * $offset
 | 
						|
 *    If offset is non-negative, the sequence will start at that offset in the array.
 | 
						|
 *    If offset is negative, the sequence will start that far from the end of the array.
 | 
						|
 * $length (optional)
 | 
						|
 *    If length is given and is positive, then the sequence will have that many elements
 | 
						|
 *    in it. If length is given and is negative then the sequence will stop that many
 | 
						|
 *   elements from the end of the array. If it is omitted, then the sequence will have
 | 
						|
 *   everything from offset up until the end of the array.
 | 
						|
 * $preserve_keys (optional)
 | 
						|
 *    Note that array_slice() will reorder and reset the array indices by default.
 | 
						|
 *    You can change this behaviour by setting preserve_keys to TRUE.
 | 
						|
 * Return
 | 
						|
 *   The new slice.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_slice(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap, *pSrc;
 | 
						|
	ph7_hashmap_node *pCur;
 | 
						|
	ph7_value *pArray;
 | 
						|
	int iLength, iOfft;
 | 
						|
	int bPreserve;
 | 
						|
	sxi32 rc;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point the internal representation of the target array */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	bPreserve = FALSE;
 | 
						|
	/* Get the offset */
 | 
						|
	iOfft = ph7_value_to_int(apArg[1]);
 | 
						|
	if(iOfft < 0) {
 | 
						|
		iOfft = (int)pSrc->nEntry + iOfft;
 | 
						|
	}
 | 
						|
	if(iOfft < 0 || iOfft > (int)pSrc->nEntry) {
 | 
						|
		/* Invalid offset,return the last entry */
 | 
						|
		iOfft = (int)pSrc->nEntry - 1;
 | 
						|
	}
 | 
						|
	/* Get the length */
 | 
						|
	iLength = (int)pSrc->nEntry - iOfft;
 | 
						|
	if(nArg > 2) {
 | 
						|
		iLength = ph7_value_to_int(apArg[2]);
 | 
						|
		if(iLength < 0) {
 | 
						|
			iLength = ((int)pSrc->nEntry + iLength) - iOfft;
 | 
						|
		}
 | 
						|
		if(iLength < 0 || iOfft + iLength >= (int)pSrc->nEntry) {
 | 
						|
			iLength = (int)pSrc->nEntry - iOfft;
 | 
						|
		}
 | 
						|
		if(nArg > 3 && ph7_value_is_bool(apArg[3])) {
 | 
						|
			bPreserve = ph7_value_to_bool(apArg[3]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(iLength < 1) {
 | 
						|
		/* Don't bother processing,return the empty array */
 | 
						|
		ph7_result_value(pCtx, pArray);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the desired entry */
 | 
						|
	pCur = pSrc->pFirst;
 | 
						|
	for(;;) {
 | 
						|
		if(iOfft < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pCur = pCur->pPrev; /* Reverse link */
 | 
						|
		iOfft--;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the hashmap */
 | 
						|
	pMap = (ph7_hashmap *)pArray->x.pOther;
 | 
						|
	for(;;) {
 | 
						|
		if(iLength < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		rc = HashmapInsertNode(pMap, pCur, bPreserve);
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pCur = pCur->pPrev; /* Reverse link */
 | 
						|
		iLength--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_splice(array $array,int $offset [,int $length [,value $replacement ]])
 | 
						|
 *  Remove a portion of the array and replace it with something else.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *    The input array.
 | 
						|
 * $offset
 | 
						|
 *    If offset is positive then the start of removed portion is at that offset from
 | 
						|
 *    the beginning of the input array. If offset is negative then it starts that far
 | 
						|
 *    from the end of the input array.
 | 
						|
 * $length (optional)
 | 
						|
 *    If length is omitted, removes everything from offset to the end of the array.
 | 
						|
 *    If length is specified and is positive, then that many elements will be removed.
 | 
						|
 *    If length is specified and is negative then the end of the removed portion will
 | 
						|
 *    be that many elements from the end of the array.
 | 
						|
 * $replacement (optional)
 | 
						|
 *  If replacement array is specified, then the removed elements are replaced
 | 
						|
 *  with elements from this array.
 | 
						|
 *  If offset and length are such that nothing is removed, then the elements
 | 
						|
 *  from the replacement array are inserted in the place specified by the offset.
 | 
						|
 *  Note that keys in replacement array are not preserved.
 | 
						|
 *  If replacement is just one element it is not necessary to put array() around
 | 
						|
 *  it, unless the element is an array itself, an object or NULL.
 | 
						|
 * Return
 | 
						|
 *   A new array consisting of the extracted elements.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_splice(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pCur, *pPrev, *pRnode;
 | 
						|
	ph7_value *pArray, *pRvalue, *pOld;
 | 
						|
	ph7_hashmap *pMap, *pSrc, *pRep;
 | 
						|
	int iLength, iOfft;
 | 
						|
	sxi32 rc;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point the internal representation of the target array */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Get the offset */
 | 
						|
	iOfft = ph7_value_to_int(apArg[1]);
 | 
						|
	if(iOfft < 0) {
 | 
						|
		iOfft = (int)pSrc->nEntry + iOfft;
 | 
						|
	}
 | 
						|
	if(iOfft < 0 || iOfft > (int)pSrc->nEntry) {
 | 
						|
		/* Invalid offset,remove the last entry */
 | 
						|
		iOfft = (int)pSrc->nEntry - 1;
 | 
						|
	}
 | 
						|
	/* Get the length */
 | 
						|
	iLength = (int)pSrc->nEntry - iOfft;
 | 
						|
	if(nArg > 2) {
 | 
						|
		iLength = ph7_value_to_int(apArg[2]);
 | 
						|
		if(iLength < 0) {
 | 
						|
			iLength = ((int)pSrc->nEntry + iLength) - iOfft;
 | 
						|
		}
 | 
						|
		if(iLength < 0 || iOfft + iLength >= (int)pSrc->nEntry) {
 | 
						|
			iLength = (int)pSrc->nEntry - iOfft;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(iLength < 1) {
 | 
						|
		/* Don't bother processing,return the empty array */
 | 
						|
		ph7_result_value(pCtx, pArray);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the desired entry */
 | 
						|
	pCur = pSrc->pFirst;
 | 
						|
	for(;;) {
 | 
						|
		if(iOfft < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pCur = pCur->pPrev; /* Reverse link */
 | 
						|
		iOfft--;
 | 
						|
	}
 | 
						|
	pRep = 0;
 | 
						|
	if(nArg > 3) {
 | 
						|
		if(!ph7_value_is_array(apArg[3])) {
 | 
						|
			/* Perform an array cast */
 | 
						|
			PH7_MemObjToHashmap(apArg[3]);
 | 
						|
			if(ph7_value_is_array(apArg[3])) {
 | 
						|
				pRep = (ph7_hashmap *)apArg[3]->x.pOther;
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			pRep = (ph7_hashmap *)apArg[3]->x.pOther;
 | 
						|
		}
 | 
						|
		if(pRep) {
 | 
						|
			/* Reset the loop cursor */
 | 
						|
			pRep->pCur = pRep->pFirst;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the hashmap */
 | 
						|
	pMap = (ph7_hashmap *)pArray->x.pOther;
 | 
						|
	for(;;) {
 | 
						|
		if(iLength < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		pPrev = pCur->pPrev;
 | 
						|
		rc = HashmapInsertNode(pMap, pCur, FALSE);
 | 
						|
		if(pRep && (pRnode = PH7_HashmapGetNextEntry(pRep)) != 0) {
 | 
						|
			/* Extract node value */
 | 
						|
			pRvalue = HashmapExtractNodeValue(pRnode);
 | 
						|
			/* Replace the old node */
 | 
						|
			pOld = HashmapExtractNodeValue(pCur);
 | 
						|
			if(pRvalue && pOld) {
 | 
						|
				PH7_MemObjStore(pRvalue, pOld);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			/* Unlink the node from the source hashmap */
 | 
						|
			PH7_HashmapUnlinkNode(pCur, TRUE);
 | 
						|
		}
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pCur = pPrev; /* Reverse link */
 | 
						|
		iLength--;
 | 
						|
	}
 | 
						|
	if(pRep) {
 | 
						|
		while((pRnode = PH7_HashmapGetNextEntry(pRep)) != 0) {
 | 
						|
			HashmapInsertNode(pSrc, pRnode, FALSE);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool in_array(value $needle,array $haystack[,bool $strict = FALSE ])
 | 
						|
 *  Checks if a value exists in an array.
 | 
						|
 * Parameters
 | 
						|
 *  $needle
 | 
						|
 *   The searched value.
 | 
						|
 *   Note:
 | 
						|
 *    If needle is a string, the comparison is done in a case-sensitive manner.
 | 
						|
 * $haystack
 | 
						|
 *  The target array.
 | 
						|
 * $strict
 | 
						|
 *  If the third parameter strict is set to TRUE then the in_array() function
 | 
						|
 *  will also check the types of the needle in the haystack.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_in_array(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_value *pNeedle;
 | 
						|
	int bStrict;
 | 
						|
	int rc;
 | 
						|
	if(nArg < 2) {
 | 
						|
		/* Missing argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pNeedle = apArg[0];
 | 
						|
	bStrict = 0;
 | 
						|
	if(nArg > 2) {
 | 
						|
		bStrict = ph7_value_to_bool(apArg[2]);
 | 
						|
	}
 | 
						|
	if(!ph7_value_is_array(apArg[1])) {
 | 
						|
		/* haystack must be an array,perform a standard comparison */
 | 
						|
		rc = ph7_value_compare(pNeedle, apArg[1], bStrict);
 | 
						|
		/* Set the comparison result */
 | 
						|
		ph7_result_bool(pCtx, rc == 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the lookup */
 | 
						|
	rc = HashmapFindValue((ph7_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict);
 | 
						|
	/* Lookup result */
 | 
						|
	ph7_result_bool(pCtx, rc == SXRET_OK);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value array_search(value $needle,array $haystack[,bool $strict = false ])
 | 
						|
 *  Searches the array for a given value and returns the corresponding key if successful.
 | 
						|
 * Parameters
 | 
						|
 * $needle
 | 
						|
 *   The searched value.
 | 
						|
 * $haystack
 | 
						|
 *   The array.
 | 
						|
 * $strict
 | 
						|
 *  If the third parameter strict is set to TRUE then the array_search() function
 | 
						|
 *  will search for identical elements in the haystack. This means it will also check
 | 
						|
 *  the types of the needle in the haystack, and objects must be the same instance.
 | 
						|
 * Return
 | 
						|
 *  Returns the key for needle if it is found in the array, FALSE otherwise.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_search(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pVal, sNeedle;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value sVal;
 | 
						|
	int bStrict;
 | 
						|
	sxu32 n;
 | 
						|
	int rc;
 | 
						|
	if(nArg < 2) {
 | 
						|
		/* Missing argument,return FALSE*/
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	bStrict = FALSE;
 | 
						|
	if(!ph7_value_is_array(apArg[1])) {
 | 
						|
		/* hasystack must be an array,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg > 2 && ph7_value_is_bool(apArg[2])) {
 | 
						|
		bStrict = ph7_value_to_bool(apArg[2]);
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the internal hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[1]->x.pOther;
 | 
						|
	/* Perform a linear search since we cannot sort the hashmap based on values */
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sVal);
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sNeedle);
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	n = pMap->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(!n) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			/* Make a copy of the vuurent values since the comparison routine
 | 
						|
			 * can change their type.
 | 
						|
			 */
 | 
						|
			PH7_MemObjLoad(pVal, &sVal);
 | 
						|
			PH7_MemObjLoad(apArg[0], &sNeedle);
 | 
						|
			rc = PH7_MemObjCmp(&sNeedle, &sVal, bStrict, 0);
 | 
						|
			PH7_MemObjRelease(&sVal);
 | 
						|
			PH7_MemObjRelease(&sNeedle);
 | 
						|
			if(rc == 0) {
 | 
						|
				/* Match found,return key */
 | 
						|
				if(pEntry->iType == HASHMAP_INT_NODE) {
 | 
						|
					/* INT key */
 | 
						|
					ph7_result_int64(pCtx, pEntry->xKey.iKey);
 | 
						|
				} else {
 | 
						|
					SyBlob *pKey = &pEntry->xKey.sKey;
 | 
						|
					/* Blob key */
 | 
						|
					ph7_result_string(pCtx, (const char *)SyBlobData(pKey), (int)SyBlobLength(pKey));
 | 
						|
				}
 | 
						|
				return PH7_OK;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* No such value,return FALSE */
 | 
						|
	ph7_result_bool(pCtx, 0);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_diff(array $array1,array $array2,...)
 | 
						|
 *  Computes the difference of arrays.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all the entries from array1 that
 | 
						|
 *  are not present in any of the other arrays.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_diff(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg == 1) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the source hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the diff */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			for(i = 1 ; i < nArg ; i++) {
 | 
						|
				if(!ph7_value_is_array(apArg[i])) {
 | 
						|
					/* ignore */
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
				/* Point to the internal representation of the hashmap */
 | 
						|
				pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
				/* Perform the lookup */
 | 
						|
				rc = HashmapFindValue(pMap, pVal, 0, TRUE);
 | 
						|
				if(rc == SXRET_OK) {
 | 
						|
					/* Value exist */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if(i >= nArg) {
 | 
						|
				/* Perform the insertion */
 | 
						|
				HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_udiff(array $array1,array $array2,...,$callback)
 | 
						|
 *  Computes the difference of arrays by using a callback function for data comparison.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against.
 | 
						|
 * $callback
 | 
						|
 *  The callback comparison function.
 | 
						|
 *  The comparison function must return an integer less than, equal to, or greater than zero
 | 
						|
 *  if the first argument is considered to be respectively less than, equal to, or greater
 | 
						|
 *  than the second.
 | 
						|
 *     int callback ( mixed $a, mixed $b )
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all the entries from array1 that
 | 
						|
 *  are not present in any of the other arrays.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_udiff(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pCallback;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the callback */
 | 
						|
	pCallback = apArg[nArg - 1];
 | 
						|
	if(nArg == 2) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the source hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the diff */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			for(i = 1 ; i < nArg - 1; i++) {
 | 
						|
				if(!ph7_value_is_array(apArg[i])) {
 | 
						|
					/* ignore */
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
				/* Point to the internal representation of the hashmap */
 | 
						|
				pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
				/* Perform the lookup */
 | 
						|
				rc = HashmapFindValueByCallback(pMap, pVal, pCallback, 0);
 | 
						|
				if(rc == SXRET_OK) {
 | 
						|
					/* Value exist */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if(i >= (nArg - 1)) {
 | 
						|
				/* Perform the insertion */
 | 
						|
				HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_diff_assoc(array $array1,array $array2,...)
 | 
						|
 *  Computes the difference of arrays with additional index check.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all the entries from array1 that
 | 
						|
 *  are not present in any of the other arrays.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_diff_assoc(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pN1, *pN2, *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg == 1) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the source hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the diff */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	pN1 = pN2 = 0;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		for(i = 1 ; i < nArg ; i++) {
 | 
						|
			if(!ph7_value_is_array(apArg[i])) {
 | 
						|
				/* ignore */
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			/* Point to the internal representation of the hashmap */
 | 
						|
			pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
			/* Perform a key lookup first */
 | 
						|
			if(pEntry->iType == HASHMAP_INT_NODE) {
 | 
						|
				rc = HashmapLookupIntKey(pMap, pEntry->xKey.iKey, &pN1);
 | 
						|
			} else {
 | 
						|
				rc = HashmapLookupBlobKey(pMap, SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), &pN1);
 | 
						|
			}
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				/* No such key,break immediately */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			/* Extract node value */
 | 
						|
			pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
			if(pVal) {
 | 
						|
				/* Perform the lookup */
 | 
						|
				rc = HashmapFindValue(pMap, pVal, &pN2, TRUE);
 | 
						|
				if(rc != SXRET_OK || pN1 != pN2) {
 | 
						|
					/* Value does not exist */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(i < nArg) {
 | 
						|
			/* Perform the insertion */
 | 
						|
			HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_diff_uassoc(array $array1,array $array2,...,callback $key_compare_func)
 | 
						|
 *  Computes the difference of arrays with additional index check which is performed
 | 
						|
 *  by a user supplied callback function.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against.
 | 
						|
 *  $key_compare_func
 | 
						|
 *   Callback function to use. The callback function must return an integer
 | 
						|
 *   less than, equal to, or greater than zero if the first argument is considered
 | 
						|
 *   to be respectively less than, equal to, or greater than the second.
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all the entries from array1 that
 | 
						|
 *  are not present in any of the other arrays.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_diff_uassoc(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pN1, *pN2, *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pCallback;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the callback */
 | 
						|
	pCallback = apArg[nArg - 1];
 | 
						|
	if(nArg == 2) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the source hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the diff */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	pN1 = pN2 = 0; /* cc warning */
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		for(i = 1 ; i < nArg - 1; i++) {
 | 
						|
			if(!ph7_value_is_array(apArg[i])) {
 | 
						|
				/* ignore */
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			/* Point to the internal representation of the hashmap */
 | 
						|
			pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
			/* Perform a key lookup first */
 | 
						|
			if(pEntry->iType == HASHMAP_INT_NODE) {
 | 
						|
				rc = HashmapLookupIntKey(pMap, pEntry->xKey.iKey, &pN1);
 | 
						|
			} else {
 | 
						|
				rc = HashmapLookupBlobKey(pMap, SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), &pN1);
 | 
						|
			}
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				/* No such key,break immediately */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			/* Extract node value */
 | 
						|
			pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
			if(pVal) {
 | 
						|
				/* Invoke the user callback */
 | 
						|
				rc = HashmapFindValueByCallback(pMap, pVal, pCallback, &pN2);
 | 
						|
				if(rc != SXRET_OK || pN1 != pN2) {
 | 
						|
					/* Value does not exist */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(i < (nArg - 1)) {
 | 
						|
			/* Perform the insertion */
 | 
						|
			HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_diff_key(array $array1 ,array $array2,...)
 | 
						|
 *  Computes the difference of arrays using keys for comparison.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all the entries from array1 whose keys are not present
 | 
						|
 *  in any of the other arrays.
 | 
						|
 * Note that NULL is returned on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_diff_key(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg == 1) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the main hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perfrom the diff */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		for(i = 1 ; i < nArg ; i++) {
 | 
						|
			if(!ph7_value_is_array(apArg[i])) {
 | 
						|
				/* ignore */
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
			if(pEntry->iType == HASHMAP_BLOB_NODE) {
 | 
						|
				SyBlob *pKey = &pEntry->xKey.sKey;
 | 
						|
				/* Blob lookup */
 | 
						|
				rc = HashmapLookupBlobKey(pMap, SyBlobData(pKey), SyBlobLength(pKey), 0);
 | 
						|
			} else {
 | 
						|
				/* Int lookup */
 | 
						|
				rc = HashmapLookupIntKey(pMap, pEntry->xKey.iKey, 0);
 | 
						|
			}
 | 
						|
			if(rc == SXRET_OK) {
 | 
						|
				/* Key exists,break immediately */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(i >= nArg) {
 | 
						|
			/* Perform the insertion */
 | 
						|
			HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_intersect(array $array1 ,array $array2,...)
 | 
						|
 *  Computes the intersection of arrays.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all of the values in array1 whose values exist
 | 
						|
 *  in all of the parameters. .
 | 
						|
 * Note that NULL is returned on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_intersect(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg == 1) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the source hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the intersection */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			for(i = 1 ; i < nArg ; i++) {
 | 
						|
				if(!ph7_value_is_array(apArg[i])) {
 | 
						|
					/* ignore */
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
				/* Point to the internal representation of the hashmap */
 | 
						|
				pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
				/* Perform the lookup */
 | 
						|
				rc = HashmapFindValue(pMap, pVal, 0, TRUE);
 | 
						|
				if(rc != SXRET_OK) {
 | 
						|
					/* Value does not exist */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if(i >= nArg) {
 | 
						|
				/* Perform the insertion */
 | 
						|
				HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_intersect_assoc(array $array1 ,array $array2,...)
 | 
						|
 *  Computes the intersection of arrays.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all of the values in array1 whose values exist
 | 
						|
 *  in all of the parameters. .
 | 
						|
 * Note that NULL is returned on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_intersect_assoc(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry, *pN1, *pN2;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg == 1) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the source hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the intersection */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	pN1 = pN2 = 0; /* cc warning */
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			for(i = 1 ; i < nArg ; i++) {
 | 
						|
				if(!ph7_value_is_array(apArg[i])) {
 | 
						|
					/* ignore */
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
				/* Point to the internal representation of the hashmap */
 | 
						|
				pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
				/* Perform a key lookup first */
 | 
						|
				if(pEntry->iType == HASHMAP_INT_NODE) {
 | 
						|
					rc = HashmapLookupIntKey(pMap, pEntry->xKey.iKey, &pN1);
 | 
						|
				} else {
 | 
						|
					rc = HashmapLookupBlobKey(pMap, SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), &pN1);
 | 
						|
				}
 | 
						|
				if(rc != SXRET_OK) {
 | 
						|
					/* No such key,break immediately */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				/* Perform the lookup */
 | 
						|
				rc = HashmapFindValue(pMap, pVal, &pN2, TRUE);
 | 
						|
				if(rc != SXRET_OK || pN1 != pN2) {
 | 
						|
					/* Value does not exist */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if(i >= nArg) {
 | 
						|
				/* Perform the insertion */
 | 
						|
				HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_intersect_key(array $array1 ,array $array2,...)
 | 
						|
 *  Computes the intersection of arrays using keys for comparison.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against
 | 
						|
 * Return
 | 
						|
 *  Returns an associative array containing all the entries of array1 which
 | 
						|
 *  have keys that are present in all arguments.
 | 
						|
 * Note that NULL is returned on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_intersect_key(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg == 1) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the main hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perfrom the intersection */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		for(i = 1 ; i < nArg ; i++) {
 | 
						|
			if(!ph7_value_is_array(apArg[i])) {
 | 
						|
				/* ignore */
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
			if(pEntry->iType == HASHMAP_BLOB_NODE) {
 | 
						|
				SyBlob *pKey = &pEntry->xKey.sKey;
 | 
						|
				/* Blob lookup */
 | 
						|
				rc = HashmapLookupBlobKey(pMap, SyBlobData(pKey), SyBlobLength(pKey), 0);
 | 
						|
			} else {
 | 
						|
				/* Int key */
 | 
						|
				rc = HashmapLookupIntKey(pMap, pEntry->xKey.iKey, 0);
 | 
						|
			}
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				/* Key does not exists,break immediately */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(i >= nArg) {
 | 
						|
			/* Perform the insertion */
 | 
						|
			HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_uintersect(array $array1 ,array $array2,...,$callback)
 | 
						|
 *  Computes the intersection of arrays.
 | 
						|
 * Parameters
 | 
						|
 *  $array1
 | 
						|
 *    The array to compare from
 | 
						|
 *  $array2
 | 
						|
 *    An array to compare against
 | 
						|
 *  $...
 | 
						|
 *   More arrays to compare against
 | 
						|
 * $callback
 | 
						|
 *  The callback comparison function.
 | 
						|
 *  The comparison function must return an integer less than, equal to, or greater than zero
 | 
						|
 *  if the first argument is considered to be respectively less than, equal to, or greater
 | 
						|
 *  than the second.
 | 
						|
 *     int callback ( mixed $a, mixed $b )
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all of the values in array1 whose values exist
 | 
						|
 *  in all of the parameters. .
 | 
						|
 * Note that NULL is returned on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_uintersect(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc, *pMap;
 | 
						|
	ph7_value *pCallback;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pVal;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	int i;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Missing/Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the callback */
 | 
						|
	pCallback = apArg[nArg - 1];
 | 
						|
	if(nArg == 2) {
 | 
						|
		/* Return the first array since we cannot perform a diff */
 | 
						|
		ph7_result_value(pCtx, apArg[0]);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the source hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the intersection */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	n = pSrc->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract the node value */
 | 
						|
		pVal = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pVal) {
 | 
						|
			for(i = 1 ; i < nArg - 1; i++) {
 | 
						|
				if(!ph7_value_is_array(apArg[i])) {
 | 
						|
					/* ignore */
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
				/* Point to the internal representation of the hashmap */
 | 
						|
				pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
				/* Perform the lookup */
 | 
						|
				rc = HashmapFindValueByCallback(pMap, pVal, pCallback, 0);
 | 
						|
				if(rc != SXRET_OK) {
 | 
						|
					/* Value does not exist */
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if(i >= (nArg - 1)) {
 | 
						|
				/* Perform the insertion */
 | 
						|
				HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_fill(int $start_index,int $num,var $value)
 | 
						|
 *  Fill an array with values.
 | 
						|
 * Parameters
 | 
						|
 *  $start_index
 | 
						|
 *    The first index of the returned array.
 | 
						|
 *  $num
 | 
						|
 *   Number of elements to insert.
 | 
						|
 *  $value
 | 
						|
 *    Value to use for filling.
 | 
						|
 * Return
 | 
						|
 *  The filled array or null on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_fill(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_value *pArray;
 | 
						|
	int i, nEntry;
 | 
						|
	if(nArg < 3) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Total number of entries to insert */
 | 
						|
	nEntry = ph7_value_to_int(apArg[1]);
 | 
						|
	/* Insert the first entry alone because it have it's own key */
 | 
						|
	ph7_array_add_intkey_elem(pArray, ph7_value_to_int(apArg[0]), apArg[2]);
 | 
						|
	/* Repeat insertion of the desired value */
 | 
						|
	for(i = 1 ; i < nEntry ; i++) {
 | 
						|
		ph7_array_add_elem(pArray, 0/*Automatic index assign */, apArg[2]);
 | 
						|
	}
 | 
						|
	/* Return the filled array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_fill_keys(array $input,var $value)
 | 
						|
 *  Fill an array with values, specifying keys.
 | 
						|
 * Parameters
 | 
						|
 *  $input
 | 
						|
 *   Array of values that will be used as key.
 | 
						|
 *  $value
 | 
						|
 *    Value to use for filling.
 | 
						|
 * Return
 | 
						|
 *  The filled array or null on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_fill_keys(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc;
 | 
						|
	ph7_value *pArray;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 2) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	for(n = 0 ; n < pSrc->nEntry ; n++) {
 | 
						|
		ph7_array_add_elem(pArray, HashmapExtractNodeValue(pEntry), apArg[1]);
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Return the filled array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_combine(array $keys,array $values)
 | 
						|
 *  Creates an array by using one array for keys and another for its values.
 | 
						|
 * Parameters
 | 
						|
 *  $keys
 | 
						|
 *    Array of keys to be used.
 | 
						|
 * $values
 | 
						|
 *   Array of values to be used.
 | 
						|
 * Return
 | 
						|
 *  Returns the combined array. Otherwise FALSE if the number of elements
 | 
						|
 *  for each array isn't equal or if one of the given arguments is
 | 
						|
 *  not an array.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_combine(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pKe, *pVe;
 | 
						|
	ph7_hashmap *pKey, *pValue;
 | 
						|
	ph7_value *pArray;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 2) {
 | 
						|
		/* Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0]) || !ph7_value_is_array(apArg[1])) {
 | 
						|
		/* Invalid argument,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmaps */
 | 
						|
	pKey   = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	pValue = (ph7_hashmap *)apArg[1]->x.pOther;
 | 
						|
	if(pKey->nEntry != pValue->nEntry) {
 | 
						|
		/* Array length differs,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pKe = pKey->pFirst;
 | 
						|
	pVe = pValue->pFirst;
 | 
						|
	for(n = 0 ; n < pKey->nEntry ; n++) {
 | 
						|
		ph7_array_add_elem(pArray, HashmapExtractNodeValue(pKe), HashmapExtractNodeValue(pVe));
 | 
						|
		/* Point to the next entry */
 | 
						|
		pKe = pKe->pPrev; /* Reverse link */
 | 
						|
		pVe = pVe->pPrev;
 | 
						|
	}
 | 
						|
	/* Return the filled array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_reverse(array $array [,bool $preserve_keys = false ])
 | 
						|
 *  Return an array with elements in reverse order.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 *  $preserve_keys (optional)
 | 
						|
 *   If set to TRUE keys are preserved.
 | 
						|
 * Return
 | 
						|
 *  The reversed array.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_reverse(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc;
 | 
						|
	ph7_value *pArray;
 | 
						|
	int bPreserve;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	bPreserve = FALSE;
 | 
						|
	if(nArg > 1 && ph7_value_is_bool(apArg[1])) {
 | 
						|
		bPreserve = ph7_value_to_bool(apArg[1]);
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pEntry = pSrc->pLast;
 | 
						|
	for(n = 0 ; n < pSrc->nEntry ; n++) {
 | 
						|
		HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, bPreserve);
 | 
						|
		/* Point to the previous entry */
 | 
						|
		pEntry = pEntry->pNext; /* Reverse link */
 | 
						|
	}
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_unique(array $array[,int $sort_flags = SORT_STRING ])
 | 
						|
 *  Removes duplicate values from an array
 | 
						|
 * Parameter
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 *  $sort_flags
 | 
						|
 *    The optional second parameter sort_flags may be used to modify the sorting behavior using these values:
 | 
						|
 *    Sorting type flags:
 | 
						|
 *       SORT_REGULAR - compare items normally (don't change types)
 | 
						|
 *       SORT_NUMERIC - compare items numerically
 | 
						|
 *       SORT_STRING - compare items as strings
 | 
						|
 *       SORT_LOCALE_STRING - compare items as
 | 
						|
 * Return
 | 
						|
 *  Filtered array or NULL on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_unique(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pNeedle;
 | 
						|
	ph7_hashmap *pSrc;
 | 
						|
	ph7_value *pArray;
 | 
						|
	int bStrict;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	bStrict = FALSE;
 | 
						|
	if(nArg > 1) {
 | 
						|
		bStrict = ph7_value_to_int(apArg[1]) == 3 /* SORT_REGULAR */ ? 1 : 0;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	for(n = 0 ; n < pSrc->nEntry ; n++) {
 | 
						|
		pNeedle = HashmapExtractNodeValue(pEntry);
 | 
						|
		rc = SXERR_NOTFOUND;
 | 
						|
		if(pNeedle) {
 | 
						|
			rc = HashmapFindValue((ph7_hashmap *)pArray->x.pOther, pNeedle, 0, bStrict);
 | 
						|
		}
 | 
						|
		if(rc != SXRET_OK) {
 | 
						|
			/* Perform the insertion */
 | 
						|
			HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_flip(array $input)
 | 
						|
 *  Exchanges all keys with their associated values in an array.
 | 
						|
 * Parameter
 | 
						|
 *  $input
 | 
						|
 *   Input array.
 | 
						|
 * Return
 | 
						|
 *   The flipped array on success or NULL on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_flip(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pSrc;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value *pKey;
 | 
						|
	ph7_value sVal;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pSrc = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Start processing */
 | 
						|
	pEntry = pSrc->pFirst;
 | 
						|
	for(n = 0 ; n < pSrc->nEntry ; n++) {
 | 
						|
		/* Extract the node value */
 | 
						|
		pKey = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pKey && (pKey->iFlags & MEMOBJ_NULL) == 0) {
 | 
						|
			/* Prepare the value for insertion */
 | 
						|
			if(pEntry->iType == HASHMAP_INT_NODE) {
 | 
						|
				PH7_MemObjInitFromInt(pSrc->pVm, &sVal, pEntry->xKey.iKey);
 | 
						|
			} else {
 | 
						|
				SyString sStr;
 | 
						|
				SyStringInitFromBuf(&sStr, SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey));
 | 
						|
				PH7_MemObjInitFromString(pSrc->pVm, &sVal, &sStr);
 | 
						|
			}
 | 
						|
			/* Perform the insertion */
 | 
						|
			ph7_array_add_elem(pArray, pKey, &sVal);
 | 
						|
			/* Safely release the value because each inserted entry
 | 
						|
			 * have it's own private copy of the value.
 | 
						|
			 */
 | 
						|
			PH7_MemObjRelease(&sVal);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Return the freshly created array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * number array_sum(array $array )
 | 
						|
 *  Calculate the sum of values in an array.
 | 
						|
 * Parameters
 | 
						|
 *  $array: The input array.
 | 
						|
 * Return
 | 
						|
 *  Returns the sum of values as an integer or float.
 | 
						|
 */
 | 
						|
static void DoubleSum(ph7_context *pCtx, ph7_hashmap *pMap) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pObj;
 | 
						|
	double dSum = 0;
 | 
						|
	sxu32 n;
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pObj && (pObj->iFlags & (MEMOBJ_NULL | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) == 0) {
 | 
						|
			if(pObj->iFlags & MEMOBJ_REAL) {
 | 
						|
				dSum += pObj->rVal;
 | 
						|
			} else if(pObj->iFlags & (MEMOBJ_INT | MEMOBJ_BOOL)) {
 | 
						|
				dSum += (double)pObj->x.iVal;
 | 
						|
			} else if(pObj->iFlags & MEMOBJ_STRING) {
 | 
						|
				if(SyBlobLength(&pObj->sBlob) > 0) {
 | 
						|
					double dv = 0;
 | 
						|
					SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
 | 
						|
					dSum += dv;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Return sum */
 | 
						|
	ph7_result_double(pCtx, dSum);
 | 
						|
}
 | 
						|
static void Int64Sum(ph7_context *pCtx, ph7_hashmap *pMap) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pObj;
 | 
						|
	sxi64 nSum = 0;
 | 
						|
	sxu32 n;
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pObj && (pObj->iFlags & (MEMOBJ_NULL | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) == 0) {
 | 
						|
			if(pObj->iFlags & MEMOBJ_REAL) {
 | 
						|
				nSum += (sxi64)pObj->rVal;
 | 
						|
			} else if(pObj->iFlags & (MEMOBJ_INT | MEMOBJ_BOOL)) {
 | 
						|
				nSum += pObj->x.iVal;
 | 
						|
			} else if(pObj->iFlags & MEMOBJ_STRING) {
 | 
						|
				if(SyBlobLength(&pObj->sBlob) > 0) {
 | 
						|
					sxi64 nv = 0;
 | 
						|
					SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
 | 
						|
					nSum += nv;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Return sum */
 | 
						|
	ph7_result_int64(pCtx, nSum);
 | 
						|
}
 | 
						|
/* number array_sum(array $array )
 | 
						|
 * (See block-coment above)
 | 
						|
 */
 | 
						|
static int ph7_hashmap_sum(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pObj;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* Nothing to compute,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* If the first element is of type float,then perform floating
 | 
						|
	 * point computaion.Otherwise switch to int64 computaion.
 | 
						|
	 */
 | 
						|
	pObj = HashmapExtractNodeValue(pMap->pFirst);
 | 
						|
	if(pObj == 0) {
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(pObj->iFlags & MEMOBJ_REAL) {
 | 
						|
		DoubleSum(pCtx, pMap);
 | 
						|
	} else {
 | 
						|
		Int64Sum(pCtx, pMap);
 | 
						|
	}
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * number array_product(array $array )
 | 
						|
 *  Calculate the product of values in an array.
 | 
						|
 * Parameters
 | 
						|
 *  $array: The input array.
 | 
						|
 * Return
 | 
						|
 *  Returns the product of values as an integer or float.
 | 
						|
 */
 | 
						|
static void DoubleProd(ph7_context *pCtx, ph7_hashmap *pMap) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pObj;
 | 
						|
	double dProd;
 | 
						|
	sxu32 n;
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	dProd = 1;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pObj && (pObj->iFlags & (MEMOBJ_NULL | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) == 0) {
 | 
						|
			if(pObj->iFlags & MEMOBJ_REAL) {
 | 
						|
				dProd *= pObj->rVal;
 | 
						|
			} else if(pObj->iFlags & (MEMOBJ_INT | MEMOBJ_BOOL)) {
 | 
						|
				dProd *= (double)pObj->x.iVal;
 | 
						|
			} else if(pObj->iFlags & MEMOBJ_STRING) {
 | 
						|
				if(SyBlobLength(&pObj->sBlob) > 0) {
 | 
						|
					double dv = 0;
 | 
						|
					SyStrToReal((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&dv, 0);
 | 
						|
					dProd *= dv;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Return product */
 | 
						|
	ph7_result_double(pCtx, dProd);
 | 
						|
}
 | 
						|
static void Int64Prod(ph7_context *pCtx, ph7_hashmap *pMap) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pObj;
 | 
						|
	sxi64 nProd;
 | 
						|
	sxu32 n;
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	nProd = 1;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pObj && (pObj->iFlags & (MEMOBJ_NULL | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) == 0) {
 | 
						|
			if(pObj->iFlags & MEMOBJ_REAL) {
 | 
						|
				nProd *= (sxi64)pObj->rVal;
 | 
						|
			} else if(pObj->iFlags & (MEMOBJ_INT | MEMOBJ_BOOL)) {
 | 
						|
				nProd *= pObj->x.iVal;
 | 
						|
			} else if(pObj->iFlags & MEMOBJ_STRING) {
 | 
						|
				if(SyBlobLength(&pObj->sBlob) > 0) {
 | 
						|
					sxi64 nv = 0;
 | 
						|
					SyStrToInt64((const char *)SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), (void *)&nv, 0);
 | 
						|
					nProd *= nv;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* Return product */
 | 
						|
	ph7_result_int64(pCtx, nProd);
 | 
						|
}
 | 
						|
/* number array_product(array $array )
 | 
						|
 * (See block-block comment above)
 | 
						|
 */
 | 
						|
static int ph7_hashmap_product(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pObj;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing arguments,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with a valid hashmap */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid argument,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* Nothing to compute,return 0 */
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* If the first element is of type float,then perform floating
 | 
						|
	 * point computaion.Otherwise switch to int64 computaion.
 | 
						|
	 */
 | 
						|
	pObj = HashmapExtractNodeValue(pMap->pFirst);
 | 
						|
	if(pObj == 0) {
 | 
						|
		ph7_result_int(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(pObj->iFlags & MEMOBJ_REAL) {
 | 
						|
		DoubleProd(pCtx, pMap);
 | 
						|
	} else {
 | 
						|
		Int64Prod(pCtx, pMap);
 | 
						|
	}
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value array_rand(array $input[,int $num_req = 1 ])
 | 
						|
 *  Pick one or more random entries out of an array.
 | 
						|
 * Parameters
 | 
						|
 * $input
 | 
						|
 *  The input array.
 | 
						|
 * $num_req
 | 
						|
 *  Specifies how many entries you want to pick.
 | 
						|
 * Return
 | 
						|
 *  If you are picking only one entry, array_rand() returns the key for a random entry.
 | 
						|
 *  Otherwise, it returns an array of keys for the random entries.
 | 
						|
 *  NULL is returned on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_rand(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pNode;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	int nItem = 1;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Missing argument,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Make sure we are dealing with an array */
 | 
						|
	if(!ph7_value_is_array(apArg[0])) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	if(pMap->nEntry < 1) {
 | 
						|
		/* Empty hashmap,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nArg > 1) {
 | 
						|
		nItem = ph7_value_to_int(apArg[1]);
 | 
						|
	}
 | 
						|
	if(nItem < 2) {
 | 
						|
		sxu32 nEntry;
 | 
						|
		/* Select a random number */
 | 
						|
		nEntry = PH7_VmRandomNum(pMap->pVm) % pMap->nEntry;
 | 
						|
		/* Extract the desired entry.
 | 
						|
		 * Note that we perform a linear lookup here (later version must change this)
 | 
						|
		 */
 | 
						|
		if(nEntry > pMap->nEntry / 2) {
 | 
						|
			pNode = pMap->pLast;
 | 
						|
			nEntry = pMap->nEntry - nEntry;
 | 
						|
			if(nEntry > 1) {
 | 
						|
				for(;;) {
 | 
						|
					if(nEntry == 0) {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					/* Point to the previous entry */
 | 
						|
					pNode = pNode->pNext; /* Reverse link */
 | 
						|
					nEntry--;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			pNode = pMap->pFirst;
 | 
						|
			for(;;) {
 | 
						|
				if(nEntry == 0) {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				/* Point to the next entry */
 | 
						|
				pNode = pNode->pPrev; /* Reverse link */
 | 
						|
				nEntry--;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(pNode->iType == HASHMAP_INT_NODE) {
 | 
						|
			/* Int key */
 | 
						|
			ph7_result_int64(pCtx, pNode->xKey.iKey);
 | 
						|
		} else {
 | 
						|
			/* Blob key */
 | 
						|
			ph7_result_string(pCtx, (const char *)SyBlobData(&pNode->xKey.sKey), (int)SyBlobLength(&pNode->xKey.sKey));
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		ph7_value sKey, *pArray;
 | 
						|
		ph7_hashmap *pDest;
 | 
						|
		/* Create a new array */
 | 
						|
		pArray = ph7_context_new_array(pCtx);
 | 
						|
		if(pArray == 0) {
 | 
						|
			ph7_result_null(pCtx);
 | 
						|
			return PH7_OK;
 | 
						|
		}
 | 
						|
		/* Point to the internal representation of the hashmap */
 | 
						|
		pDest = (ph7_hashmap *)pArray->x.pOther;
 | 
						|
		PH7_MemObjInit(pDest->pVm, &sKey);
 | 
						|
		/* Copy the first n items */
 | 
						|
		pNode = pMap->pFirst;
 | 
						|
		if(nItem > (int)pMap->nEntry) {
 | 
						|
			nItem = (int)pMap->nEntry;
 | 
						|
		}
 | 
						|
		while(nItem > 0) {
 | 
						|
			PH7_HashmapExtractNodeKey(pNode, &sKey);
 | 
						|
			PH7_HashmapInsert(pDest, 0/* Automatic index assign*/, &sKey);
 | 
						|
			PH7_MemObjRelease(&sKey);
 | 
						|
			/* Point to the next entry */
 | 
						|
			pNode = pNode->pPrev; /* Reverse link */
 | 
						|
			nItem--;
 | 
						|
		}
 | 
						|
		/* Shuffle the array */
 | 
						|
		HashmapMergeSort(pDest, HashmapCmpCallback7, 0);
 | 
						|
		/* Rehash node */
 | 
						|
		HashmapSortRehash(pDest);
 | 
						|
		/* Return the random array */
 | 
						|
		ph7_result_value(pCtx, pArray);
 | 
						|
	}
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_chunk (array $input,int $size [,bool $preserve_keys = false ])
 | 
						|
 *  Split an array into chunks.
 | 
						|
 * Parameters
 | 
						|
 * $input
 | 
						|
 *   The array to work on
 | 
						|
 * $size
 | 
						|
 *   The size of each chunk
 | 
						|
 * $preserve_keys
 | 
						|
 *   When set to TRUE keys will be preserved. Default is FALSE which will reindex
 | 
						|
 *   the chunk numerically.
 | 
						|
 * Return
 | 
						|
 *  Returns a multidimensional numerically indexed array, starting with
 | 
						|
 *  zero, with each dimension containing size elements.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_chunk(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_value *pArray, *pChunk;
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	int bPreserve;
 | 
						|
	sxu32 nChunk;
 | 
						|
	sxu32 nSize;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Extract the chunk size */
 | 
						|
	nSize = (sxu32)ph7_value_to_int(apArg[1]);
 | 
						|
	if(nSize < 1) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	if(nSize >= pMap->nEntry) {
 | 
						|
		/* Return the whole array */
 | 
						|
		ph7_array_add_elem(pArray, 0, apArg[0]);
 | 
						|
		ph7_result_value(pCtx, pArray);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	bPreserve = 0;
 | 
						|
	if(nArg > 2) {
 | 
						|
		bPreserve = ph7_value_to_bool(apArg[2]);
 | 
						|
	}
 | 
						|
	/* Start processing */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	nChunk = 0;
 | 
						|
	pChunk = 0;
 | 
						|
	n = pMap->nEntry;
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			if(nChunk > 0) {
 | 
						|
				/* Insert the last chunk */
 | 
						|
				ph7_array_add_elem(pArray, 0, pChunk); /* Will have it's own copy */
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if(nChunk < 1) {
 | 
						|
			if(pChunk) {
 | 
						|
				/* Put the first chunk */
 | 
						|
				ph7_array_add_elem(pArray, 0, pChunk); /* Will have it's own copy */
 | 
						|
			}
 | 
						|
			/* Create a new dimension */
 | 
						|
			pChunk = ph7_context_new_array(pCtx); /* Don't worry about freeing memory here,everything
 | 
						|
												   * will be automatically released as soon we return
 | 
						|
												   * from this function */
 | 
						|
			if(pChunk == 0) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			nChunk = nSize;
 | 
						|
		}
 | 
						|
		/* Insert the entry */
 | 
						|
		HashmapInsertNode((ph7_hashmap *)pChunk->x.pOther, pEntry, bPreserve);
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		nChunk--;
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* Return the multidimensional array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_pad(array $input,int $pad_size,value $pad_value)
 | 
						|
 *  Pad array to the specified length with a value.
 | 
						|
 * $input
 | 
						|
 *   Initial array of values to pad.
 | 
						|
 * $pad_size
 | 
						|
 *   New size of the array.
 | 
						|
 * $pad_value
 | 
						|
 *   Value to pad if input is less than pad_size.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_pad(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	int nEntry;
 | 
						|
	if(nArg < 3 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Extract the total number of desired entry to insert */
 | 
						|
	nEntry = ph7_value_to_int(apArg[1]);
 | 
						|
	if(nEntry < 0) {
 | 
						|
		nEntry = -nEntry;
 | 
						|
		if(nEntry > 1048576) {
 | 
						|
			nEntry = 1048576; /* Limit imposed by PHP */
 | 
						|
		}
 | 
						|
		if(nEntry > (int)pMap->nEntry) {
 | 
						|
			nEntry -= (int)pMap->nEntry;
 | 
						|
			/* Insert given items first */
 | 
						|
			while(nEntry > 0) {
 | 
						|
				ph7_array_add_elem(pArray, 0, apArg[2]);
 | 
						|
				nEntry--;
 | 
						|
			}
 | 
						|
			/* Merge the two arrays */
 | 
						|
			HashmapMerge(pMap, (ph7_hashmap *)pArray->x.pOther);
 | 
						|
		} else {
 | 
						|
			PH7_HashmapDup(pMap, (ph7_hashmap *)pArray->x.pOther);
 | 
						|
		}
 | 
						|
	} else if(nEntry > 0) {
 | 
						|
		if(nEntry > 1048576) {
 | 
						|
			nEntry = 1048576; /* Limit imposed by PHP */
 | 
						|
		}
 | 
						|
		if(nEntry > (int)pMap->nEntry) {
 | 
						|
			nEntry -= (int)pMap->nEntry;
 | 
						|
			/* Merge the two arrays first */
 | 
						|
			HashmapMerge(pMap, (ph7_hashmap *)pArray->x.pOther);
 | 
						|
			/* Insert given items */
 | 
						|
			while(nEntry > 0) {
 | 
						|
				ph7_array_add_elem(pArray, 0, apArg[2]);
 | 
						|
				nEntry--;
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			PH7_HashmapDup(pMap, (ph7_hashmap *)pArray->x.pOther);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	/* Return the new array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_replace(array &$array,array &$array1,...)
 | 
						|
 *  Replaces elements from passed arrays into the first array.
 | 
						|
 * Parameters
 | 
						|
 * $array
 | 
						|
 *   The array in which elements are replaced.
 | 
						|
 * $array1
 | 
						|
 *   The array from which elements will be extracted.
 | 
						|
 * ....
 | 
						|
 *  More arrays from which elements will be extracted.
 | 
						|
 *  Values from later arrays overwrite the previous values.
 | 
						|
 * Return
 | 
						|
 *  Returns an array, or NULL if an error occurs.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_replace(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	int i;
 | 
						|
	if(nArg < 1) {
 | 
						|
		/* Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	for(i = 0 ; i < nArg ; i++) {
 | 
						|
		if(!ph7_value_is_array(apArg[i])) {
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		/* Point to the internal representation of the input hashmap */
 | 
						|
		pMap = (ph7_hashmap *)apArg[i]->x.pOther;
 | 
						|
		HashmapOverwrite(pMap, (ph7_hashmap *)pArray->x.pOther);
 | 
						|
	}
 | 
						|
	/* Return the new array */
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_filter(array $input [,callback $callback ])
 | 
						|
 *  Filters elements of an array using a callback function.
 | 
						|
 * Parameters
 | 
						|
 *  $input
 | 
						|
 *    The array to iterate over
 | 
						|
 * $callback
 | 
						|
 *    The callback function to use
 | 
						|
 *    If no callback is supplied, all entries of input equal to FALSE (see converting to boolean)
 | 
						|
 *    will be removed.
 | 
						|
 * Return
 | 
						|
 *  The filtered array.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_filter(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pArray;
 | 
						|
	ph7_value sResult;   /* Callback result */
 | 
						|
	ph7_value *pValue;
 | 
						|
	sxi32 rc;
 | 
						|
	int keep;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 1 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sResult);
 | 
						|
	sResult.nIdx = SXU32_HIGH; /* Mark as constant */
 | 
						|
	/* Perform the requested operation */
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		/* Extract node value */
 | 
						|
		pValue = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(nArg > 1 && pValue) {
 | 
						|
			/* Invoke the given callback */
 | 
						|
			keep = FALSE;
 | 
						|
			rc = PH7_VmCallUserFunction(pMap->pVm, apArg[1], 1, &pValue, &sResult);
 | 
						|
			if(rc == SXRET_OK) {
 | 
						|
				/* Perform a boolean cast */
 | 
						|
				keep = ph7_value_to_bool(&sResult);
 | 
						|
			}
 | 
						|
			PH7_MemObjRelease(&sResult);
 | 
						|
		} else {
 | 
						|
			/* No available callback,check for empty item */
 | 
						|
			keep = !PH7_MemObjIsEmpty(pValue);
 | 
						|
		}
 | 
						|
		if(keep) {
 | 
						|
			/* Perform the insertion,now the callback returned true */
 | 
						|
			HashmapInsertNode((ph7_hashmap *)pArray->x.pOther, pEntry, TRUE);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * array array_map(callback $callback,array $arr1)
 | 
						|
 *  Applies the callback to the elements of the given arrays.
 | 
						|
 * Parameters
 | 
						|
 *  $callback
 | 
						|
 *   Callback function to run for each element in each array.
 | 
						|
 * $arr1
 | 
						|
 *   An array to run through the callback function.
 | 
						|
 * Return
 | 
						|
 *  Returns an array containing all the elements of arr1 after applying
 | 
						|
 *  the callback function to each one.
 | 
						|
 * NOTE:
 | 
						|
 *  array_map() passes only a single value to the callback.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_map(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_value *pArray, *pValue, sKey, sResult;
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[1])) {
 | 
						|
		/* Invalid arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Create a new array */
 | 
						|
	pArray = ph7_context_new_array(pCtx);
 | 
						|
	if(pArray == 0) {
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[1]->x.pOther;
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sResult);
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sKey);
 | 
						|
	sResult.nIdx = SXU32_HIGH; /* Mark as constant */
 | 
						|
	sKey.nIdx    = SXU32_HIGH; /* Mark as constant */
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		/* Extrcat the node value */
 | 
						|
		pValue = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pValue) {
 | 
						|
			sxi32 rc;
 | 
						|
			/* Invoke the supplied callback */
 | 
						|
			rc = PH7_VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult);
 | 
						|
			/* Extract the node key */
 | 
						|
			PH7_HashmapExtractNodeKey(pEntry, &sKey);
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				/* An error occured while invoking the supplied callback [i.e: not defined] */
 | 
						|
				ph7_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */
 | 
						|
			} else {
 | 
						|
				/* Insert the callback return value */
 | 
						|
				ph7_array_add_elem(pArray, &sKey, &sResult);
 | 
						|
			}
 | 
						|
			PH7_MemObjRelease(&sKey);
 | 
						|
			PH7_MemObjRelease(&sResult);
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	ph7_result_value(pCtx, pArray);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * value array_reduce(array $input,callback $function[, value $initial = NULL ])
 | 
						|
 *  Iteratively reduce the array to a single value using a callback function.
 | 
						|
 * Parameters
 | 
						|
 *  $input
 | 
						|
 *   The input array.
 | 
						|
 *  $function
 | 
						|
 *  The callback function.
 | 
						|
 * $initial
 | 
						|
 *  If the optional initial is available, it will be used at the beginning
 | 
						|
 *  of the process, or as a final result in case the array is empty.
 | 
						|
 * Return
 | 
						|
 *  Returns the resulting value.
 | 
						|
 *  If the array is empty and initial is not passed, array_reduce() returns NULL.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_reduce(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	ph7_value *pValue;
 | 
						|
	ph7_value sResult;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid/Missing arguments,return NULL */
 | 
						|
		ph7_result_null(pCtx);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Assume a NULL initial value */
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sResult);
 | 
						|
	sResult.nIdx = SXU32_HIGH; /* Mark as constant */
 | 
						|
	if(nArg > 2) {
 | 
						|
		/* Set the initial value */
 | 
						|
		PH7_MemObjLoad(apArg[2], &sResult);
 | 
						|
	}
 | 
						|
	/* Perform the requested operation */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		/* Extract the node value */
 | 
						|
		pValue = HashmapExtractNodeValue(pEntry);
 | 
						|
		/* Invoke the supplied callback */
 | 
						|
		PH7_VmCallUserFunctionAp(pMap->pVm, apArg[1], &sResult, &sResult, pValue, 0);
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	ph7_result_value(pCtx, &sResult); /* Will make it's own copy */
 | 
						|
	PH7_MemObjRelease(&sResult);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool array_walk(array &$array,callback $funcname [, value $userdata ] )
 | 
						|
 *  Apply a user function to every member of an array.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $funcname
 | 
						|
 *  Typically, funcname takes on two parameters.The array parameter's value being
 | 
						|
 *  the first, and the key/index second.
 | 
						|
 * Note:
 | 
						|
 *  If funcname needs to be working with the actual values of the array,specify the first
 | 
						|
 *  parameter of funcname as a reference. Then, any changes made to those elements will
 | 
						|
 *  be made in the original array itself.
 | 
						|
 * $userdata
 | 
						|
 *  If the optional userdata parameter is supplied, it will be passed as the third parameter
 | 
						|
 *  to the callback funcname.
 | 
						|
 * Return
 | 
						|
 *  Returns TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_walk(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_value *pValue, *pUserData, sKey;
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid/Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	pUserData = nArg > 2 ? apArg[2] : 0;
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sKey);
 | 
						|
	sKey.nIdx = SXU32_HIGH; /* Mark as constant */
 | 
						|
	/* Perform the desired operation */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		/* Extract the node value */
 | 
						|
		pValue = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pValue) {
 | 
						|
			/* Extract the entry key */
 | 
						|
			PH7_HashmapExtractNodeKey(pEntry, &sKey);
 | 
						|
			/* Invoke the supplied callback */
 | 
						|
			rc = PH7_VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0);
 | 
						|
			PH7_MemObjRelease(&sKey);
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				/* An error occured while invoking the supplied callback [i.e: not defined] */
 | 
						|
				ph7_result_bool(pCtx, 0); /* return FALSE */
 | 
						|
				return PH7_OK;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	/* All done,return TRUE */
 | 
						|
	ph7_result_bool(pCtx, 1);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Apply a user function to every member of an array.(Recurse on array's).
 | 
						|
 * Refer to the [array_walk_recursive()] implementation for more information.
 | 
						|
 */
 | 
						|
static int HashmapWalkRecursive(
 | 
						|
	ph7_hashmap *pMap,    /* Target hashmap */
 | 
						|
	ph7_value *pCallback, /* User callback */
 | 
						|
	ph7_value *pUserData, /* Callback private data */
 | 
						|
	int iNest             /* Nesting level */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pValue, sKey;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	/* Iterate throw hashmap entries */
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sKey);
 | 
						|
	sKey.nIdx = SXU32_HIGH; /* Mark as constant */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	for(n = 0 ; n < pMap->nEntry ; n++) {
 | 
						|
		/* Extract the node value */
 | 
						|
		pValue = HashmapExtractNodeValue(pEntry);
 | 
						|
		if(pValue) {
 | 
						|
			if(pValue->iFlags & MEMOBJ_HASHMAP) {
 | 
						|
				if(iNest < 32) {
 | 
						|
					/* Recurse */
 | 
						|
					iNest++;
 | 
						|
					HashmapWalkRecursive((ph7_hashmap *)pValue->x.pOther, pCallback, pUserData, iNest);
 | 
						|
					iNest--;
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				/* Extract the node key */
 | 
						|
				PH7_HashmapExtractNodeKey(pEntry, &sKey);
 | 
						|
				/* Invoke the supplied callback */
 | 
						|
				rc = PH7_VmCallUserFunctionAp(pMap->pVm, pCallback, 0, pValue, &sKey, pUserData, 0);
 | 
						|
				PH7_MemObjRelease(&sKey);
 | 
						|
				if(rc != SXRET_OK) {
 | 
						|
					return rc;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	return SXRET_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * bool array_walk_recursive(array &$array,callback $funcname [, value $userdata ] )
 | 
						|
 *  Apply a user function recursively to every member of an array.
 | 
						|
 * Parameters
 | 
						|
 *  $array
 | 
						|
 *   The input array.
 | 
						|
 * $funcname
 | 
						|
 *  Typically, funcname takes on two parameters.The array parameter's value being
 | 
						|
 *  the first, and the key/index second.
 | 
						|
 * Note:
 | 
						|
 *  If funcname needs to be working with the actual values of the array,specify the first
 | 
						|
 *  parameter of funcname as a reference. Then, any changes made to those elements will
 | 
						|
 *  be made in the original array itself.
 | 
						|
 * $userdata
 | 
						|
 *  If the optional userdata parameter is supplied, it will be passed as the third parameter
 | 
						|
 *  to the callback funcname.
 | 
						|
 * Return
 | 
						|
 *  Returns TRUE on success or FALSE on failure.
 | 
						|
 */
 | 
						|
static int ph7_hashmap_walk_recursive(ph7_context *pCtx, int nArg, ph7_value **apArg) {
 | 
						|
	ph7_hashmap *pMap;
 | 
						|
	sxi32 rc;
 | 
						|
	if(nArg < 2 || !ph7_value_is_array(apArg[0])) {
 | 
						|
		/* Invalid/Missing arguments,return FALSE */
 | 
						|
		ph7_result_bool(pCtx, 0);
 | 
						|
		return PH7_OK;
 | 
						|
	}
 | 
						|
	/* Point to the internal representation of the input hashmap */
 | 
						|
	pMap = (ph7_hashmap *)apArg[0]->x.pOther;
 | 
						|
	/* Perform the desired operation */
 | 
						|
	rc = HashmapWalkRecursive(pMap, apArg[1], nArg > 2 ? apArg[2] : 0, 0);
 | 
						|
	/* All done */
 | 
						|
	ph7_result_bool(pCtx, rc == SXRET_OK);
 | 
						|
	return PH7_OK;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Table of hashmap functions.
 | 
						|
 */
 | 
						|
static const ph7_builtin_func aHashmapFunc[] = {
 | 
						|
	{"sizeof",            ph7_hashmap_count },
 | 
						|
	{"array_key_exists",  ph7_hashmap_key_exists },
 | 
						|
	{"array_pop",         ph7_hashmap_pop     },
 | 
						|
	{"array_push",        ph7_hashmap_push    },
 | 
						|
	{"array_shift",       ph7_hashmap_shift   },
 | 
						|
	{"array_product",     ph7_hashmap_product },
 | 
						|
	{"array_sum",         ph7_hashmap_sum     },
 | 
						|
	{"array_keys",        ph7_hashmap_keys    },
 | 
						|
	{"array_values",      ph7_hashmap_values  },
 | 
						|
	{"array_same",        ph7_hashmap_same    },  /* Symisc eXtension */
 | 
						|
	{"array_merge",       ph7_hashmap_merge   },
 | 
						|
	{"array_slice",       ph7_hashmap_slice   },
 | 
						|
	{"array_splice",      ph7_hashmap_splice  },
 | 
						|
	{"array_search",      ph7_hashmap_search  },
 | 
						|
	{"array_diff",        ph7_hashmap_diff    },
 | 
						|
	{"array_udiff",       ph7_hashmap_udiff   },
 | 
						|
	{"array_diff_assoc",  ph7_hashmap_diff_assoc },
 | 
						|
	{"array_diff_uassoc", ph7_hashmap_diff_uassoc },
 | 
						|
	{"array_diff_key",    ph7_hashmap_diff_key },
 | 
						|
	{"array_intersect",   ph7_hashmap_intersect},
 | 
						|
	{"array_intersect_assoc", ph7_hashmap_intersect_assoc},
 | 
						|
	{"array_uintersect",  ph7_hashmap_uintersect},
 | 
						|
	{"array_intersect_key",   ph7_hashmap_intersect_key},
 | 
						|
	{"array_copy",        ph7_hashmap_copy    },
 | 
						|
	{"array_erase",       ph7_hashmap_erase   },
 | 
						|
	{"array_fill",        ph7_hashmap_fill    },
 | 
						|
	{"array_fill_keys",   ph7_hashmap_fill_keys},
 | 
						|
	{"array_combine",     ph7_hashmap_combine },
 | 
						|
	{"array_reverse",     ph7_hashmap_reverse },
 | 
						|
	{"array_unique",      ph7_hashmap_unique  },
 | 
						|
	{"array_flip",        ph7_hashmap_flip    },
 | 
						|
	{"array_rand",        ph7_hashmap_rand    },
 | 
						|
	{"array_chunk",       ph7_hashmap_chunk   },
 | 
						|
	{"array_pad",         ph7_hashmap_pad     },
 | 
						|
	{"array_replace",     ph7_hashmap_replace },
 | 
						|
	{"array_filter",      ph7_hashmap_filter  },
 | 
						|
	{"array_map",         ph7_hashmap_map     },
 | 
						|
	{"array_reduce",      ph7_hashmap_reduce  },
 | 
						|
	{"array_walk",        ph7_hashmap_walk    },
 | 
						|
	{"array_walk_recursive", ph7_hashmap_walk_recursive },
 | 
						|
	{"in_array",          ph7_hashmap_in_array},
 | 
						|
	{"sort",              ph7_hashmap_sort    },
 | 
						|
	{"asort",             ph7_hashmap_asort   },
 | 
						|
	{"arsort",            ph7_hashmap_arsort  },
 | 
						|
	{"ksort",             ph7_hashmap_ksort   },
 | 
						|
	{"krsort",            ph7_hashmap_krsort  },
 | 
						|
	{"rsort",             ph7_hashmap_rsort   },
 | 
						|
	{"usort",             ph7_hashmap_usort   },
 | 
						|
	{"uasort",            ph7_hashmap_uasort  },
 | 
						|
	{"uksort",            ph7_hashmap_uksort  },
 | 
						|
	{"shuffle",           ph7_hashmap_shuffle },
 | 
						|
	{"range",             ph7_hashmap_range   },
 | 
						|
	{"current",           ph7_hashmap_current },
 | 
						|
	{"each",              ph7_hashmap_each    },
 | 
						|
	{"pos",               ph7_hashmap_current },
 | 
						|
	{"next",              ph7_hashmap_next    },
 | 
						|
	{"prev",              ph7_hashmap_prev    },
 | 
						|
	{"end",               ph7_hashmap_end     },
 | 
						|
	{"reset",             ph7_hashmap_reset   },
 | 
						|
	{"key",               ph7_hashmap_simple_key }
 | 
						|
};
 | 
						|
/*
 | 
						|
 * Register the built-in hashmap functions defined above.
 | 
						|
 */
 | 
						|
PH7_PRIVATE void PH7_RegisterHashmapFunctions(ph7_vm *pVm) {
 | 
						|
	sxu32 n;
 | 
						|
	for(n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++) {
 | 
						|
		ph7_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0);
 | 
						|
	}
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Dump a hashmap instance and it's entries and the store the dump in
 | 
						|
 * the BLOB given as the first argument.
 | 
						|
 * This function is typically invoked when the user issue a call to
 | 
						|
 * [var_dump(),var_export(),print_r(),...]
 | 
						|
 * This function SXRET_OK on success. Any other return value including
 | 
						|
 * SXERR_LIMIT(infinite recursion) indicates failure.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut, ph7_hashmap *pMap, int ShowType, int nTab, int nDepth) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value *pObj;
 | 
						|
	sxu32 n = 0;
 | 
						|
	int isRef;
 | 
						|
	sxi32 rc;
 | 
						|
	int i;
 | 
						|
	if(nDepth > 31) {
 | 
						|
		static const char zInfinite[] = "Nesting limit reached: Infinite recursion?";
 | 
						|
		/* Nesting limit reached */
 | 
						|
		SyBlobAppend(&(*pOut), zInfinite, sizeof(zInfinite) - 1);
 | 
						|
		if(ShowType) {
 | 
						|
			SyBlobAppend(&(*pOut), ")", sizeof(char));
 | 
						|
		}
 | 
						|
		return SXERR_LIMIT;
 | 
						|
	}
 | 
						|
	/* Point to the first inserted entry */
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	rc = SXRET_OK;
 | 
						|
	if(!ShowType) {
 | 
						|
		SyBlobAppend(&(*pOut), "Array(", sizeof("Array(") - 1);
 | 
						|
	}
 | 
						|
	/* Total entries */
 | 
						|
	SyBlobFormat(&(*pOut), "%u) {", pMap->nEntry);
 | 
						|
#ifdef __WINNT__
 | 
						|
	SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1);
 | 
						|
#else
 | 
						|
	SyBlobAppend(&(*pOut), "\n", sizeof(char));
 | 
						|
#endif
 | 
						|
	for(;;) {
 | 
						|
		if(n >= pMap->nEntry) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		for(i = 0 ; i < nTab ; i++) {
 | 
						|
			SyBlobAppend(&(*pOut), " ", sizeof(char));
 | 
						|
		}
 | 
						|
		/* Dump key */
 | 
						|
		if(pEntry->iType == HASHMAP_INT_NODE) {
 | 
						|
			SyBlobFormat(&(*pOut), "[%qd] =>", pEntry->xKey.iKey);
 | 
						|
		} else {
 | 
						|
			SyBlobFormat(&(*pOut), "[%.*s] =>",
 | 
						|
						 SyBlobLength(&pEntry->xKey.sKey), SyBlobData(&pEntry->xKey.sKey));
 | 
						|
		}
 | 
						|
#ifdef __WINNT__
 | 
						|
		SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1);
 | 
						|
#else
 | 
						|
		SyBlobAppend(&(*pOut), "\n", sizeof(char));
 | 
						|
#endif
 | 
						|
		/* Dump node value */
 | 
						|
		pObj = HashmapExtractNodeValue(pEntry);
 | 
						|
		isRef = 0;
 | 
						|
		if(pObj) {
 | 
						|
			if(pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ) {
 | 
						|
				/* Referenced object */
 | 
						|
				isRef = 1;
 | 
						|
			}
 | 
						|
			rc = PH7_MemObjDump(&(*pOut), pObj, ShowType, nTab + 1, nDepth, isRef);
 | 
						|
			if(rc == SXERR_LIMIT) {
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		n++;
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
	}
 | 
						|
	for(i = 0 ; i < nTab ; i++) {
 | 
						|
		SyBlobAppend(&(*pOut), " ", sizeof(char));
 | 
						|
	}
 | 
						|
	SyBlobAppend(&(*pOut), "}", sizeof(char));
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
/*
 | 
						|
 * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each
 | 
						|
 * retrieved entry.
 | 
						|
 * Note that argument are passed to the callback by copy. That is,any modification to
 | 
						|
 * the entry value in the callback body will not alter the real value.
 | 
						|
 * If the callback wishes to abort processing [i.e: it's invocation] it must return
 | 
						|
 * a value different from PH7_OK.
 | 
						|
 * Refer to [ph7_array_walk()] for more information.
 | 
						|
 */
 | 
						|
PH7_PRIVATE sxi32 PH7_HashmapWalk(
 | 
						|
	ph7_hashmap *pMap, /* Target hashmap */
 | 
						|
	int (*xWalk)(ph7_value *, ph7_value *, void *), /* Walker callback */
 | 
						|
	void *pUserData /* Last argument to xWalk() */
 | 
						|
) {
 | 
						|
	ph7_hashmap_node *pEntry;
 | 
						|
	ph7_value sKey, sValue;
 | 
						|
	sxi32 rc;
 | 
						|
	sxu32 n;
 | 
						|
	/* Initialize walker parameter */
 | 
						|
	rc = SXRET_OK;
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sKey);
 | 
						|
	PH7_MemObjInit(pMap->pVm, &sValue);
 | 
						|
	n = pMap->nEntry;
 | 
						|
	pEntry = pMap->pFirst;
 | 
						|
	/* Start the iteration process */
 | 
						|
	for(;;) {
 | 
						|
		if(n < 1) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Extract a copy of the key and a copy the current value */
 | 
						|
		PH7_HashmapExtractNodeKey(pEntry, &sKey);
 | 
						|
		PH7_HashmapExtractNodeValue(pEntry, &sValue, FALSE);
 | 
						|
		/* Invoke the user callback */
 | 
						|
		rc = xWalk(&sKey, &sValue, pUserData);
 | 
						|
		/* Release the copy of the key and the value */
 | 
						|
		PH7_MemObjRelease(&sKey);
 | 
						|
		PH7_MemObjRelease(&sValue);
 | 
						|
		if(rc != PH7_OK) {
 | 
						|
			/* Callback request an operation abort */
 | 
						|
			return SXERR_ABORT;
 | 
						|
		}
 | 
						|
		/* Point to the next entry */
 | 
						|
		pEntry = pEntry->pPrev; /* Reverse link */
 | 
						|
		n--;
 | 
						|
	}
 | 
						|
	/* All done */
 | 
						|
	return SXRET_OK;
 | 
						|
}
 |