From 91601deb844848dc02959679fd41e1441a76aff4 Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Sun, 14 Dec 2014 12:30:27 +0100 Subject: the all-in-one-file version of unqlite --- common/unqlite/jx9_hashmap.c | 2989 ------------------------------------------ 1 file changed, 2989 deletions(-) delete mode 100644 common/unqlite/jx9_hashmap.c (limited to 'common/unqlite/jx9_hashmap.c') diff --git a/common/unqlite/jx9_hashmap.c b/common/unqlite/jx9_hashmap.c deleted file mode 100644 index e97d41d..0000000 --- a/common/unqlite/jx9_hashmap.c +++ /dev/null @@ -1,2989 +0,0 @@ -/* - * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. - * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ - * Version 1.7.2 - * 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://jx9.symisc.net/ - */ - /* $SymiscID: hashmap.c v2.6 Win7 2012-12-11 00:50 stable $ */ -#ifndef JX9_AMALGAMATION -#include "jx9Int.h" -#endif -/* This file implement generic hashmaps used to represent JSON arrays and objects */ -/* 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 */ -/* - * 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(jx9_hashmap *pMap, int bRecursive, int iRecCount) -{ - sxi64 iCount = 0; - if( !bRecursive ){ - iCount = pMap->nEntry; - }else{ - /* Recursive hashmap walk */ - jx9_hashmap_node *pEntry = pMap->pLast; - jx9_value *pElem; - sxu32 n = 0; - for(;;){ - if( n >= pMap->nEntry ){ - break; - } - /* Point to the element value */ - pElem = (jx9_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((jx9_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 [jx9_hashmap_node] instance is returned. - */ -static jx9_hashmap_node * HashmapNewIntNode(jx9_hashmap *pMap, sxi64 iKey, sxu32 nHash, sxu32 nValIdx) -{ - jx9_hashmap_node *pNode; - /* Allocate a new node */ - pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node)); - if( pNode == 0 ){ - return 0; - } - /* Zero the stucture */ - SyZero(pNode, sizeof(jx9_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 [jx9_hashmap_node] instance is returned. - */ -static jx9_hashmap_node * HashmapNewBlobNode(jx9_hashmap *pMap, const void *pKey, sxu32 nKeyLen, sxu32 nHash, sxu32 nValIdx) -{ - jx9_hashmap_node *pNode; - /* Allocate a new node */ - pNode = (jx9_hashmap_node *)SyMemBackendPoolAlloc(&pMap->pVm->sAllocator, sizeof(jx9_hashmap_node)); - if( pNode == 0 ){ - return 0; - } - /* Zero the stucture */ - SyZero(pNode, sizeof(jx9_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(jx9_hashmap *pMap, jx9_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. - */ -static void jx9HashmapUnlinkNode(jx9_hashmap_node *pNode) -{ - jx9_hashmap *pMap = pNode->pMap; - jx9_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); - /* Restore to the free list */ - jx9VmUnsetMemObj(pVm, pNode->nValIdx); - if( pNode->iType == HASHMAP_BLOB_NODE ){ - SyBlobRelease(&pNode->xKey.sKey); - } - SyMemBackendPoolFree(&pVm->sAllocator, pNode); - pMap->nEntry--; - if( pMap->nEntry < 1 ){ - /* 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(jx9_hashmap *pMap) -{ - if( pMap->nEntry >= pMap->nSize * HASHMAP_FILL_FACTOR ){ - jx9_hashmap_node **apOld = pMap->apBucket; - jx9_hashmap_node *pEntry, **apNew; - sxu32 nNew = pMap->nSize << 1; - sxu32 nBucket; - sxu32 n; - if( nNew < 1 ){ - nNew = 16; - } - /* Allocate a new bucket */ - apNew = (jx9_hashmap_node **)SyMemBackendAlloc(&pMap->pVm->sAllocator, nNew * sizeof(jx9_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(jx9_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(jx9_hashmap *pMap,sxi64 iKey,jx9_value *pValue) -{ - jx9_hashmap_node *pNode; - jx9_value *pObj; - sxu32 nIdx; - sxu32 nHash; - sxi32 rc; - /* Reserve a jx9_value for the value */ - pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx); - if( pObj == 0 ){ - return SXERR_MEM; - } - if( pValue ){ - /* Duplicate the value */ - jx9MemObjStore(pValue, pObj); - } - /* Hash the key */ - nHash = pMap->xIntHash(iKey); - /* Allocate a new int node */ - pNode = HashmapNewIntNode(&(*pMap), iKey, nHash, nIdx); - if( pNode == 0 ){ - return SXERR_MEM; - } - /* 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)); - /* All done */ - return SXRET_OK; -} -/* - * Insert a BLOB key and it's associated value (if any) in the given - * hashmap. - */ -static sxi32 HashmapInsertBlobKey(jx9_hashmap *pMap,const void *pKey,sxu32 nKeyLen,jx9_value *pValue) -{ - jx9_hashmap_node *pNode; - jx9_value *pObj; - sxu32 nHash; - sxu32 nIdx; - sxi32 rc; - /* Reserve a jx9_value for the value */ - pObj = jx9VmReserveMemObj(pMap->pVm,&nIdx); - if( pObj == 0 ){ - return SXERR_MEM; - } - if( pValue ){ - /* Duplicate the value */ - jx9MemObjStore(pValue, pObj); - } - /* 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; - } - /* 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)); - /* 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( - jx9_hashmap *pMap, /* Target hashmap */ - sxi64 iKey, /* lookup key */ - jx9_hashmap_node **ppNode /* OUT: target node on success */ - ) -{ - jx9_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( - jx9_hashmap *pMap, /* Target hashmap */ - const void *pKey, /* Lookup key */ - sxu32 nKeyLen, /* Key length in bytes */ - jx9_hashmap_node **ppNode /* OUT: target node on success */ - ) -{ - jx9_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( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_hashmap_node **ppNode /* OUT: target node on success */ - ) -{ - jx9_hashmap_node *pNode = 0; /* cc -O6 warning */ - sxi32 rc; - if( pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES) ){ - if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(&(*pKey)); - } - if( SyBlobLength(&pKey->sBlob) > 0 ){ - /* 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 */ - jx9MemObjToInteger(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( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_value *pVal /* Node value */ - ) -{ - jx9_hashmap_node *pNode = 0; - sxi32 rc = SXRET_OK; - if( pMap->nEntry < 1 && pKey && (pKey->iFlags & MEMOBJ_STRING) ){ - pMap->iFlags |= HASHMAP_JSON_OBJECT; - } - if( pKey && (pKey->iFlags & (MEMOBJ_STRING|MEMOBJ_HASHMAP|MEMOBJ_RES)) ){ - if( (pKey->iFlags & MEMOBJ_STRING) == 0 ){ - /* Force a string cast */ - jx9MemObjToString(&(*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 */ - jx9_value *pElem; - pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx); - if( pElem ){ - if( pVal ){ - jx9MemObjStore(pVal, pElem); - }else{ - /* Nullify the entry */ - jx9MemObjToNull(pElem); - } - } - return SXRET_OK; - } - /* Perform a blob-key insertion */ - rc = HashmapInsertBlobKey(&(*pMap),SyBlobData(&pKey->sBlob),SyBlobLength(&pKey->sBlob),&(*pVal)); - return rc; - } -IntKey: - if( pKey ){ - if((pKey->iFlags & MEMOBJ_INT) == 0 ){ - /* Force an integer cast */ - jx9MemObjToInteger(pKey); - } - if( SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode) ){ - /* Overwrite the old value */ - jx9_value *pElem; - pElem = (jx9_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx); - if( pElem ){ - if( pVal ){ - jx9MemObjStore(pVal, pElem); - }else{ - /* Nullify the entry */ - jx9MemObjToNull(pElem); - } - } - return SXRET_OK; - } - /* Perform a 64-bit-int-key insertion */ - rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal)); - 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,&(*pVal)); - if( rc == SXRET_OK ){ - ++pMap->iNextIdx; - } - } - /* Insertion result */ - return rc; -} -/* - * Extract node value. - */ -static jx9_value * HashmapExtractNodeValue(jx9_hashmap_node *pNode) -{ - /* Point to the desired object */ - jx9_value *pObj; - pObj = (jx9_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(jx9_hashmap *pMap, jx9_hashmap_node *pNode, int bPreserve) -{ - jx9_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); - } - }else{ - /* Blob key */ - rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey), - SyBlobLength(&pNode->xKey.sKey), pObj); - } - 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 jx9_values comparison, refer to the implementation - * of the [jx9MemObjCmp()] function defined in memobj.c or the official - * documenation. - */ -static sxi32 HashmapNodeCmp(jx9_hashmap_node *pLeft, jx9_hashmap_node *pRight, int bStrict) -{ - jx9_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 */ - jx9MemObjInit(pLeft->pMap->pVm, &sObj1); - jx9MemObjInit(pLeft->pMap->pVm, &sObj2); - jx9HashmapExtractNodeValue(pLeft, &sObj1, FALSE); - jx9HashmapExtractNodeValue(pRight, &sObj2, FALSE); - rc = jx9MemObjCmp(&sObj1, &sObj2, bStrict, 0); - jx9MemObjRelease(&sObj1); - jx9MemObjRelease(&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(jx9_hashmap_node *pEntry) -{ - jx9_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( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pNeedle, /* Lookup key */ - jx9_hashmap_node **ppNode, /* OUT: target node on success */ - int bStrict /* TRUE for strict comparison */ - ) -{ - jx9_hashmap_node *pEntry; - jx9_value sVal, *pVal; - jx9_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; - jx9MemObjInit(pMap->pVm, &sVal); - jx9MemObjInit(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; - sxi32 iF2 = pNeedle->iFlags; - if( iF1 == iF2 ){ - /* NULL values are equals */ - if( ppNode ){ - *ppNode = pEntry; - } - return SXRET_OK; - } - }else{ - /* Duplicate value */ - jx9MemObjLoad(pVal, &sVal); - jx9MemObjLoad(pNeedle, &sNeedle); - rc = jx9MemObjCmp(&sNeedle, &sVal, bStrict, 0); - jx9MemObjRelease(&sVal); - jx9MemObjRelease(&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; -} -/* - * Compare two hashmaps. - * Return 0 if the hashmaps are equals.Any other value indicates inequality. - * Note on array comparison operators. - * According to the JX9 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. - * "apple", "b" => "banana"); - * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry"); - * $c = $a + $b; // Union of $a and $b - * print "Union of \$a and \$b: \n"; - * dump($c); - * $c = $b + $a; // Union of $b and $a - * print "Union of \$b and \$a: \n"; - * 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. - */ -JX9_PRIVATE sxi32 jx9HashmapCmp( - jx9_hashmap *pLeft, /* Left hashmap */ - jx9_hashmap *pRight, /* Right hashmap */ - int bStrict /* TRUE for strict comparison */ - ) -{ - jx9_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 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 JX9 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(jx9_hashmap *pSrc, jx9_hashmap *pDest) -{ - jx9_hashmap_node *pEntry; - jx9_value sKey, *pVal; - sxi32 rc; - sxu32 n; - if( pSrc == pDest ){ - /* Same map. This can easily happen since hashmaps are passed by reference. - * Unlike the 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 */ - jx9MemObjInitFromString(pDest->pVm, &sKey, 0); - jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey)); - rc = jx9HashmapInsert(&(*pDest), &sKey, pVal); - jx9MemObjRelease(&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; -} -/* - * Duplicate the contents of a hashmap. Store the copy in pDest. - * Refer to the [array_pad(), array_copy(), ...] implementation for more information. - */ -JX9_PRIVATE sxi32 jx9HashmapDup(jx9_hashmap *pSrc, jx9_hashmap *pDest) -{ - jx9_hashmap_node *pEntry; - jx9_value sKey, *pVal; - sxi32 rc; - sxu32 n; - if( pSrc == pDest ){ - /* Same map. This can easily happen since hashmaps are passed by reference. - * Unlike the 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 */ - jx9MemObjInitFromString(pDest->pVm, &sKey, 0); - jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey)); - rc = jx9HashmapInsert(&(*pDest), &sKey, pVal); - jx9MemObjRelease(&sKey); - }else{ - /* Int key insertion */ - rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal); - } - 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: - * "apple", "b" => "banana"); - * $b = array("a" => "pear", "b" => "strawberry", "c" => "cherry"); - * $c = $a + $b; // Union of $a and $b - * print "Union of \$a and \$b: \n"; - * dump($c); - * $c = $b + $a; // Union of $b and $a - * print "Union of \$b and \$a: \n"; - * 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. - */ -JX9_PRIVATE sxi32 jx9HashmapUnion(jx9_hashmap *pLeft, jx9_hashmap *pRight) -{ - jx9_hashmap_node *pEntry; - sxi32 rc = SXRET_OK; - jx9_value *pObj; - sxu32 n; - if( pLeft == pRight ){ - /* Same map. This can easily happen since hashmaps are passed by reference. - * Unlike the 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); - 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); - 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. - */ -JX9_PRIVATE jx9_hashmap * jx9NewHashmap( - jx9_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 */ - ) -{ - jx9_hashmap *pMap; - /* Allocate a new instance */ - pMap = (jx9_hashmap *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(jx9_hashmap)); - if( pMap == 0 ){ - return 0; - } - /* Zero the structure */ - SyZero(pMap, sizeof(jx9_hashmap)); - /* Fill in the structure */ - pMap->pVm = &(*pVm); - pMap->iRef = 1; - /* pMap->iFlags = 0; */ - /* 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 JX9 language reference manual. - * Superglobals are built-in variables that are always available in all scopes. -* Description -* All predefined variables in JX9 are "superglobals", which means they -* are available in all scopes throughout a script. -* These variables are: -* $_SERVER -* $_GET -* $_POST -* $_FILES -* $_REQUEST -* $_ENV -*/ -JX9_PRIVATE sxi32 jx9HashmapLoadBuiltin(jx9_vm *pVm) -{ - static const char * azSuper[] = { - "_SERVER", /* $_SERVER */ - "_GET", /* $_GET */ - "_POST", /* $_POST */ - "_FILES", /* $_FILES */ - "_REQUEST", /* $_REQUEST */ - "_COOKIE", /* $_COOKIE */ - "_ENV", /* $_ENV */ - "_HEADER", /* $_HEADER */ - "argv" /* $argv */ - }; - SyString *pFile; - sxi32 rc; - sxu32 n; - /* Install globals variable now */ - for( n = 0 ; n < SX_ARRAYSIZE(azSuper) ; n++ ){ - jx9_value *pSuper; - /* Request an empty array */ - pSuper = jx9_new_array(&(*pVm)); - if( pSuper == 0 ){ - return SXERR_MEM; - } - /* Install */ - rc = jx9_vm_config(&(*pVm),JX9_VM_CONFIG_CREATE_VAR, azSuper[n]/* Super-global name*/, pSuper/* Super-global value */); - if( rc != SXRET_OK ){ - return rc; - } - /* Release the value now it have been installed */ - jx9_release_value(&(*pVm), pSuper); - } - /* Set some $_SERVER entries */ - pFile = (SyString *)SySetPeek(&pVm->aFiles); - /* - * 'SCRIPT_FILENAME' - * The absolute pathname of the currently executing script. - */ - jx9_vm_config(pVm, JX9_VM_CONFIG_SERVER_ATTR, - "SCRIPT_FILENAME", - pFile ? pFile->zString : ":Memory:", - pFile ? pFile->nByte : sizeof(":Memory:") - 1 - ); - /* All done, all global variables are installed now */ - return SXRET_OK; -} -/* - * Release a hashmap. - */ -JX9_PRIVATE sxi32 jx9HashmapRelease(jx9_hashmap *pMap, int FreeDS) -{ - jx9_hashmap_node *pEntry, *pNext; - jx9_vm *pVm = pMap->pVm; - sxu32 n; - /* Start the release process */ - n = 0; - pEntry = pMap->pFirst; - for(;;){ - if( n >= pMap->nEntry ){ - break; - } - pNext = pEntry->pPrev; /* Reverse link */ - /* Restore the jx9_value to the free list */ - jx9VmUnsetMemObj(pVm, pEntry->nValIdx); - /* 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. - */ -JX9_PRIVATE void jx9HashmapUnref(jx9_hashmap *pMap) -{ - pMap->iRef--; - if( pMap->iRef < 1 ){ - jx9HashmapRelease(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. - */ -JX9_PRIVATE sxi32 jx9HashmapLookup( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_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. - */ -JX9_PRIVATE sxi32 jx9HashmapInsert( - jx9_hashmap *pMap, /* Target hashmap */ - jx9_value *pKey, /* Lookup key */ - jx9_value *pVal /* Node value.NULL otherwise */ - ) -{ - sxi32 rc; - rc = HashmapInsert(&(*pMap), &(*pKey), &(*pVal)); - return rc; -} -/* - * Reset the node cursor of a given hashmap. - */ -JX9_PRIVATE void jx9HashmapResetLoopCursor(jx9_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. - */ -JX9_PRIVATE jx9_hashmap_node * jx9HashmapGetNextEntry(jx9_hashmap *pMap) -{ - jx9_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. - */ -JX9_PRIVATE jx9_value * jx9HashmapGetNodeValue(jx9_hashmap_node *pNode) -{ - jx9_value *pValue; - pValue = HashmapExtractNodeValue(pNode); - return pValue; -} -/* - * Extract a node value (Second). - */ -JX9_PRIVATE void jx9HashmapExtractNodeValue(jx9_hashmap_node *pNode, jx9_value *pValue, int bStore) -{ - jx9_value *pEntry = HashmapExtractNodeValue(pNode); - if( pEntry ){ - if( bStore ){ - jx9MemObjStore(pEntry, pValue); - }else{ - jx9MemObjLoad(pEntry, pValue); - } - }else{ - jx9MemObjRelease(pValue); - } -} -/* - * Extract a node key. - */ -JX9_PRIVATE void jx9HashmapExtractNodeKey(jx9_hashmap_node *pNode,jx9_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 JX9_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. - */ -JX9_PRIVATE int jx9HashmapValuesToSet(jx9_hashmap *pMap, SySet *pOut) -{ - jx9_hashmap_node *pEntry = pMap->pFirst; - jx9_value *pValue; - sxu32 n; - /* Initialize the container */ - SySetInit(pOut, &pMap->pVm->sAllocator, sizeof(jx9_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 /* JX9_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)(jx9_hashmap_node *, jx9_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 jx9_hashmap_node * HashmapNodeMerge(jx9_hashmap_node *pA, jx9_hashmap_node *pB, ProcNodeCmp xCmp, void *pCmpData) -{ - jx9_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(jx9_hashmap *pMap, ProcNodeCmp xCmp, void *pCmpData) -{ - jx9_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; ipNext = 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(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData) -{ - jx9_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 */ - jx9MemObjInit(pA->pMap->pVm, &sA); - jx9MemObjInit(pA->pMap->pVm, &sB); - jx9HashmapExtractNodeValue(pA, &sA, FALSE); - jx9HashmapExtractNodeValue(pB, &sB, FALSE); - if( iFlags == 5 ){ - /* String cast */ - if( (sA.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sA); - } - if( (sB.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sB); - } - }else{ - /* Numeric cast */ - jx9MemObjToNumeric(&sA); - jx9MemObjToNumeric(&sB); - } - /* Perform the comparison */ - rc = jx9MemObjCmp(&sA, &sB, FALSE, 0); - jx9MemObjRelease(&sA); - jx9MemObjRelease(&sB); - return rc; -} -/* - * Node comparison callback. - * Used by: [rsort(), arsort()]; - */ -static sxi32 HashmapCmpCallback3(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData) -{ - jx9_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 */ - jx9MemObjInit(pA->pMap->pVm, &sA); - jx9MemObjInit(pA->pMap->pVm, &sB); - jx9HashmapExtractNodeValue(pA, &sA, FALSE); - jx9HashmapExtractNodeValue(pB, &sB, FALSE); - if( iFlags == 5 ){ - /* String cast */ - if( (sA.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sA); - } - if( (sB.iFlags & MEMOBJ_STRING) == 0 ){ - jx9MemObjToString(&sB); - } - }else{ - /* Numeric cast */ - jx9MemObjToNumeric(&sA); - jx9MemObjToNumeric(&sB); - } - /* Perform the comparison */ - rc = jx9MemObjCmp(&sA, &sB, FALSE, 0); - jx9MemObjRelease(&sA); - jx9MemObjRelease(&sB); - return -rc; -} -/* - * Node comparison callback: Invoke an user-defined callback for the purpose of node comparison. - * used-by: [usort(), uasort()] - */ -static sxi32 HashmapCmpCallback4(jx9_hashmap_node *pA, jx9_hashmap_node *pB, void *pCmpData) -{ - jx9_value sResult, *pCallback; - jx9_value *pV1, *pV2; - jx9_value *apArg[2]; /* Callback arguments */ - sxi32 rc; - /* Point to the desired callback */ - pCallback = (jx9_value *)pCmpData; - /* initialize the result value */ - jx9MemObjInit(pA->pMap->pVm, &sResult); - /* Extract nodes values */ - pV1 = HashmapExtractNodeValue(pA); - pV2 = HashmapExtractNodeValue(pB); - apArg[0] = pV1; - apArg[1] = pV2; - /* Invoke the callback */ - rc = jx9VmCallUserFunction(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 */ - jx9MemObjToInteger(&sResult); - } - rc = (sxi32)sResult.x.iVal; - } - jx9MemObjRelease(&sResult); - /* Callback result */ - return rc; -} -/* - * Rehash all nodes keys after a merge-sort have been applied. - * Used by [sort(), usort() and rsort()]. - */ -static void HashmapSortRehash(jx9_hashmap *pMap) -{ - jx9_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 JX9 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://jx9.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 jx9_hashmap_sort(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - /* Make sure we are dealing with a valid hashmap */ - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry > 1 ){ - sxi32 iCmpFlags = 0; - if( nArg > 1 ){ - /* Extract comparison flags */ - iCmpFlags = jx9_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 JX9 specification] */ - HashmapSortRehash(pMap); - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_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 jx9_hashmap_rsort(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - /* Make sure we are dealing with a valid hashmap */ - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry > 1 ){ - sxi32 iCmpFlags = 0; - if( nArg > 1 ){ - /* Extract comparison flags */ - iCmpFlags = jx9_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 JX9 specification] */ - HashmapSortRehash(pMap); - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_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 jx9_hashmap_usort(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - /* Make sure we are dealing with a valid hashmap */ - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing/Invalid arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry > 1 ){ - jx9_value *pCallback = 0; - ProcNodeCmp xCmp; - xCmp = HashmapCmpCallback4; /* User-defined function as the comparison callback */ - if( nArg > 1 && jx9_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 JX9 specification] */ - HashmapSortRehash(pMap); - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_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 jx9_hashmap_count(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - int bRecursive = FALSE; - sxi64 iCount; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( !jx9_value_is_json_array(apArg[0]) ){ - /* TICKET 1433-19: Handle objects */ - int res = !jx9_value_is_null(apArg[0]); - jx9_result_int(pCtx, res); - return JX9_OK; - } - if( nArg > 1 ){ - /* Recursive count? */ - bRecursive = jx9_value_to_int(apArg[1]) == 1 /* COUNT_RECURSIVE */; - } - /* Count */ - iCount = HashmapCount((jx9_hashmap *)apArg[0]->x.pOther, bRecursive, 0); - jx9_result_int64(pCtx, iCount); - return JX9_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 jx9_hashmap_key_exists(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - sxi32 rc; - if( nArg < 2 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[1]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Perform the lookup */ - rc = jx9HashmapLookup((jx9_hashmap *)apArg[1]->x.pOther, apArg[0], 0); - /* lookup result */ - jx9_result_bool(pCtx, rc == SXRET_OK ? 1 : 0); - return JX9_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 jx9_hashmap_pop(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Noting to pop, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_hashmap_node *pLast = pMap->pLast; - jx9_value *pObj; - pObj = HashmapExtractNodeValue(pLast); - if( pObj ){ - /* Node value */ - jx9_result_value(pCtx, pObj); - /* Unlink the node */ - jx9HashmapUnlinkNode(pLast); - }else{ - jx9_result_null(pCtx); - } - /* Reset the cursor */ - pMap->pCur = pMap->pFirst; - } - return JX9_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 jx9_hashmap_push(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - sxi32 rc; - int i; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Start pushing given values */ - for( i = 1 ; i < nArg ; ++i ){ - rc = jx9HashmapInsert(pMap, 0, apArg[i]); - if( rc != SXRET_OK ){ - break; - } - } - /* Return the new count */ - jx9_result_int64(pCtx, (sxi64)pMap->nEntry); - return JX9_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 jx9_hashmap_shift(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return null */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Empty hashmap, return NULL */ - jx9_result_null(pCtx); - }else{ - jx9_hashmap_node *pEntry = pMap->pFirst; - jx9_value *pObj; - sxu32 n; - pObj = HashmapExtractNodeValue(pEntry); - if( pObj ){ - /* Node value */ - jx9_result_value(pCtx, pObj); - /* Unlink the first node */ - jx9HashmapUnlinkNode(pEntry); - }else{ - jx9_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 JX9_OK; -} -/* - * Extract the node cursor value. - */ -static sxi32 HashmapCurrentValue(jx9_context *pCtx, jx9_hashmap *pMap, int iDirection) -{ - jx9_hashmap_node *pCur = pMap->pCur; - jx9_value *pVal; - if( pCur == 0 ){ - /* Cursor does not point to anything, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_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 */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - } - /* Point to the desired element */ - pVal = HashmapExtractNodeValue(pCur); - if( pVal ){ - jx9_result_value(pCtx, pVal); - }else{ - jx9_result_bool(pCtx, 0); - } - return JX9_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 jx9_hashmap_current(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 0); - return JX9_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 jx9_hashmap_next(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, 1); - return JX9_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 jx9_hashmap_prev(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - HashmapCurrentValue(&(*pCtx), (jx9_hashmap *)apArg[0]->x.pOther, -1); - return JX9_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 jx9_hashmap_end(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Point to the last node */ - pMap->pCur = pMap->pLast; - /* Return the last node value */ - HashmapCurrentValue(&(*pCtx), pMap, 0); - return JX9_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 jx9_hashmap_reset(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_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 JX9_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 jx9_hashmap_simple_key(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pCur; - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - pCur = pMap->pCur; - if( pCur == 0 ){ - /* Cursor does not point to anything, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( pCur->iType == HASHMAP_INT_NODE){ - /* Key is integer */ - jx9_result_int64(pCtx, pCur->xKey.iKey); - }else{ - /* Key is blob */ - jx9_result_string(pCtx, - (const char *)SyBlobData(&pCur->xKey.sKey), (int)SyBlobLength(&pCur->xKey.sKey)); - } - return JX9_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 jx9_hashmap_each(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pCur; - jx9_hashmap *pMap; - jx9_value *pArray; - jx9_value *pVal; - jx9_value sKey; - if( nArg < 1 ){ - /* Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the internal representation that describe the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->pCur == 0 ){ - /* Cursor does not point to anything, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pCur = pMap->pCur; - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pVal = HashmapExtractNodeValue(pCur); - /* Insert the current value */ - jx9_array_add_strkey_elem(pArray, "1", pVal); - jx9_array_add_strkey_elem(pArray, "value", pVal); - /* Make the key */ - if( pCur->iType == HASHMAP_INT_NODE ){ - jx9MemObjInitFromInt(pMap->pVm, &sKey, pCur->xKey.iKey); - }else{ - jx9MemObjInitFromString(pMap->pVm, &sKey, 0); - jx9MemObjStringAppend(&sKey, (const char *)SyBlobData(&pCur->xKey.sKey), SyBlobLength(&pCur->xKey.sKey)); - } - /* Insert the current key */ - jx9_array_add_elem(pArray, 0, &sKey); - jx9_array_add_strkey_elem(pArray, "key", &sKey); - jx9MemObjRelease(&sKey); - /* Advance the cursor */ - pMap->pCur = pCur->pPrev; /* Reverse link */ - /* Return the current entry */ - jx9_result_value(pCtx, pArray); - return JX9_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 jx9_hashmap_values(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pNode; - jx9_hashmap *pMap; - jx9_value *pArray; - jx9_value *pObj; - sxu32 n; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation that describe the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Perform the requested operation */ - pNode = pMap->pFirst; - for( n = 0 ; n < pMap->nEntry ; ++n ){ - pObj = HashmapExtractNodeValue(pNode); - if( pObj ){ - /* perform the insertion */ - jx9_array_add_elem(pArray, 0/* Automatic index assign */, pObj); - } - /* Point to the next entry */ - pNode = pNode->pPrev; /* Reverse link */ - } - /* return the new array */ - jx9_result_value(pCtx, pArray); - return JX9_OK; -} -/* - * bool array_same(array $arr1, array $arr2) - * Return TRUE if the given arrays are the same instance. - * This function is useful under JX9 since arrays and objects - * are passed by reference. - * 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 jx9_hashmap_same(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *p1, *p2; - int rc; - if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ - /* Missing or invalid arguments, return FALSE*/ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the hashmaps */ - p1 = (jx9_hashmap *)apArg[0]->x.pOther; - p2 = (jx9_hashmap *)apArg[1]->x.pOther; - rc = (p1 == p2); - /* Same instance? */ - jx9_result_bool(pCtx, rc); - return JX9_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 jx9_hashmap_merge(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap, *pSrc; - jx9_value *pArray; - int i; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)pArray->x.pOther; - /* Start merging */ - for( i = 0 ; i < nArg ; i++ ){ - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[i]) ){ - /* Insert scalar value */ - jx9_array_add_elem(pArray, 0, apArg[i]); - }else{ - pSrc = (jx9_hashmap *)apArg[i]->x.pOther; - /* Merge the two hashmaps */ - HashmapMerge(pSrc, pMap); - } - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_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 jx9_hashmap_in_array(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pNeedle; - int bStrict; - int rc; - if( nArg < 2 ){ - /* Missing argument, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pNeedle = apArg[0]; - bStrict = 0; - if( nArg > 2 ){ - bStrict = jx9_value_to_bool(apArg[2]); - } - if( !jx9_value_is_json_array(apArg[1]) ){ - /* haystack must be an array, perform a standard comparison */ - rc = jx9_value_compare(pNeedle, apArg[1], bStrict); - /* Set the comparison result */ - jx9_result_bool(pCtx, rc == 0); - return JX9_OK; - } - /* Perform the lookup */ - rc = HashmapFindValue((jx9_hashmap *)apArg[1]->x.pOther, pNeedle, 0, bStrict); - /* Lookup result */ - jx9_result_bool(pCtx, rc == SXRET_OK); - return JX9_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 jx9_hashmap_copy(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - jx9_value *pArray; - if( nArg < 1 ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_hashmap *)pArray->x.pOther; - if( jx9_value_is_json_array(apArg[0])){ - /* Point to the internal representation of the source */ - jx9_hashmap *pSrc = (jx9_hashmap *)apArg[0]->x.pOther; - /* Perform the copy */ - jx9HashmapDup(pSrc, pMap); - }else{ - /* Simple insertion */ - jx9HashmapInsert(pMap, 0/* Automatic index assign*/, apArg[0]); - } - /* Return the duplicated array */ - jx9_result_value(pCtx, pArray); - return JX9_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 jx9_hashmap_erase(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - if( nArg < 1 ){ - /* Missing arguments */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - /* Point to the target hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - /* Erase */ - jx9HashmapRelease(pMap, FALSE); - return JX9_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 jx9_hashmap_diff(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pEntry; - jx9_hashmap *pSrc, *pMap; - jx9_value *pArray; - jx9_value *pVal; - sxi32 rc; - sxu32 n; - int i; - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( nArg == 1 ){ - /* Return the first array since we cannot perform a diff */ - jx9_result_value(pCtx, apArg[0]); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the source hashmap */ - pSrc = (jx9_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( !jx9_value_is_json_array(apArg[i])) { - /* ignore */ - continue; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_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((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE); - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n--; - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_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 jx9_hashmap_intersect(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap_node *pEntry; - jx9_hashmap *pSrc, *pMap; - jx9_value *pArray; - jx9_value *pVal; - sxi32 rc; - sxu32 n; - int i; - if( nArg < 1 || !jx9_value_is_json_array(apArg[0]) ){ - /* Missing arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - if( nArg == 1 ){ - /* Return the first array since we cannot perform a diff */ - jx9_result_value(pCtx, apArg[0]); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the source hashmap */ - pSrc = (jx9_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( !jx9_value_is_json_array(apArg[i])) { - /* ignore */ - continue; - } - /* Point to the internal representation of the hashmap */ - pMap = (jx9_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((jx9_hashmap *)pArray->x.pOther, pEntry, TRUE); - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - n--; - } - /* Return the freshly created array */ - jx9_result_value(pCtx, pArray); - return JX9_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(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_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_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - dSum += pObj->x.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 */ - jx9_result_double(pCtx, dSum); -} -static void Int64Sum(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_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_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - nSum += (sxi64)pObj->x.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 */ - jx9_result_int64(pCtx, nSum); -} -/* number array_sum(array $array ) - * (See block-coment above) - */ -static int jx9_hashmap_sum(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - jx9_value *pObj; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Nothing to compute, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_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 ){ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( pObj->iFlags & MEMOBJ_REAL ){ - DoubleSum(pCtx, pMap); - }else{ - Int64Sum(pCtx, pMap); - } - return JX9_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(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_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_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - dProd *= pObj->x.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 */ - jx9_result_double(pCtx, dProd); -} -static void Int64Prod(jx9_context *pCtx, jx9_hashmap *pMap) -{ - jx9_hashmap_node *pEntry; - jx9_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_RES)) == 0){ - if( pObj->iFlags & MEMOBJ_REAL ){ - nProd *= (sxi64)pObj->x.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 */ - jx9_result_int64(pCtx, nProd); -} -/* number array_product(array $array ) - * (See block-block comment above) - */ -static int jx9_hashmap_product(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_hashmap *pMap; - jx9_value *pObj; - if( nArg < 1 ){ - /* Missing arguments, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - /* Make sure we are dealing with a valid hashmap */ - if( !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid argument, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - if( pMap->nEntry < 1 ){ - /* Nothing to compute, return 0 */ - jx9_result_int(pCtx, 0); - return JX9_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 ){ - jx9_result_int(pCtx, 0); - return JX9_OK; - } - if( pObj->iFlags & MEMOBJ_REAL ){ - DoubleProd(pCtx, pMap); - }else{ - Int64Prod(pCtx, pMap); - } - return JX9_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 jx9_hashmap_map(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pArray, *pValue, sKey, sResult; - jx9_hashmap_node *pEntry; - jx9_hashmap *pMap; - sxu32 n; - if( nArg < 2 || !jx9_value_is_json_array(apArg[1]) ){ - /* Invalid arguments, return NULL */ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Create a new array */ - pArray = jx9_context_new_array(pCtx); - if( pArray == 0 ){ - jx9_result_null(pCtx); - return JX9_OK; - } - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[1]->x.pOther; - jx9MemObjInit(pMap->pVm, &sResult); - jx9MemObjInit(pMap->pVm, &sKey); - /* 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 = jx9VmCallUserFunction(pMap->pVm, apArg[0], 1, &pValue, &sResult); - /* Extract the node key */ - jx9HashmapExtractNodeKey(pEntry, &sKey); - if( rc != SXRET_OK ){ - /* An error occured while invoking the supplied callback [i.e: not defined] */ - jx9_array_add_elem(pArray, &sKey, pValue); /* Keep the same value */ - }else{ - /* Insert the callback return value */ - jx9_array_add_elem(pArray, &sKey, &sResult); - } - jx9MemObjRelease(&sKey); - jx9MemObjRelease(&sResult); - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - jx9_result_value(pCtx, pArray); - return JX9_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 jx9_hashmap_walk(jx9_context *pCtx, int nArg, jx9_value **apArg) -{ - jx9_value *pValue, *pUserData, sKey; - jx9_hashmap_node *pEntry; - jx9_hashmap *pMap; - sxi32 rc; - sxu32 n; - if( nArg < 2 || !jx9_value_is_json_array(apArg[0]) ){ - /* Invalid/Missing arguments, return FALSE */ - jx9_result_bool(pCtx, 0); - return JX9_OK; - } - pUserData = nArg > 2 ? apArg[2] : 0; - /* Point to the internal representation of the input hashmap */ - pMap = (jx9_hashmap *)apArg[0]->x.pOther; - jx9MemObjInit(pMap->pVm, &sKey); - /* 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 */ - jx9HashmapExtractNodeKey(pEntry, &sKey); - /* Invoke the supplied callback */ - rc = jx9VmCallUserFunctionAp(pMap->pVm, apArg[1], 0, pValue, &sKey, pUserData, 0); - jx9MemObjRelease(&sKey); - if( rc != SXRET_OK ){ - /* An error occured while invoking the supplied callback [i.e: not defined] */ - jx9_result_bool(pCtx, 0); /* return FALSE */ - return JX9_OK; - } - } - /* Point to the next entry */ - pEntry = pEntry->pPrev; /* Reverse link */ - } - /* All done, return TRUE */ - jx9_result_bool(pCtx, 1); - return JX9_OK; -} -/* - * Table of built-in hashmap functions. - */ -static const jx9_builtin_func aHashmapFunc[] = { - {"count", jx9_hashmap_count }, - {"sizeof", jx9_hashmap_count }, - {"array_key_exists", jx9_hashmap_key_exists }, - {"array_pop", jx9_hashmap_pop }, - {"array_push", jx9_hashmap_push }, - {"array_shift", jx9_hashmap_shift }, - {"array_product", jx9_hashmap_product }, - {"array_sum", jx9_hashmap_sum }, - {"array_values", jx9_hashmap_values }, - {"array_same", jx9_hashmap_same }, - {"array_merge", jx9_hashmap_merge }, - {"array_diff", jx9_hashmap_diff }, - {"array_intersect", jx9_hashmap_intersect}, - {"in_array", jx9_hashmap_in_array }, - {"array_copy", jx9_hashmap_copy }, - {"array_erase", jx9_hashmap_erase }, - {"array_map", jx9_hashmap_map }, - {"array_walk", jx9_hashmap_walk }, - {"sort", jx9_hashmap_sort }, - {"rsort", jx9_hashmap_rsort }, - {"usort", jx9_hashmap_usort }, - {"current", jx9_hashmap_current }, - {"each", jx9_hashmap_each }, - {"pos", jx9_hashmap_current }, - {"next", jx9_hashmap_next }, - {"prev", jx9_hashmap_prev }, - {"end", jx9_hashmap_end }, - {"reset", jx9_hashmap_reset }, - {"key", jx9_hashmap_simple_key } -}; -/* - * Register the built-in hashmap functions defined above. - */ -JX9_PRIVATE void jx9RegisterHashmapFunctions(jx9_vm *pVm) -{ - sxu32 n; - for( n = 0 ; n < SX_ARRAYSIZE(aHashmapFunc) ; n++ ){ - jx9_create_function(&(*pVm), aHashmapFunc[n].zName, aHashmapFunc[n].xFunc, 0); - } -} -/* - * 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 JX9_OK. - * Refer to [jx9_array_walk()] for more information. - */ -JX9_PRIVATE sxi32 jx9HashmapWalk( - jx9_hashmap *pMap, /* Target hashmap */ - int (*xWalk)(jx9_value *, jx9_value *, void *), /* Walker callback */ - void *pUserData /* Last argument to xWalk() */ - ) -{ - jx9_hashmap_node *pEntry; - jx9_value sKey, sValue; - sxi32 rc; - sxu32 n; - /* Initialize walker parameter */ - rc = SXRET_OK; - jx9MemObjInit(pMap->pVm, &sKey); - jx9MemObjInit(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 */ - jx9HashmapExtractNodeKey(pEntry, &sKey); - jx9HashmapExtractNodeValue(pEntry, &sValue, FALSE); - /* Invoke the user callback */ - rc = xWalk(&sKey, &sValue, pUserData); - /* Release the copy of the key and the value */ - jx9MemObjRelease(&sKey); - jx9MemObjRelease(&sValue); - if( rc != JX9_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; -} -- cgit v1.2.3