| /* ------------------------------------------------------------------ |
| * Copyright (C) 1998-2009 PacketVideo |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| * express or implied. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * ------------------------------------------------------------------- |
| */ |
| // class for encapsulating string based key-value pair handlings |
| #include "string_keyvalue_store.h" |
| #include "oscl_variablesize_mem_pool.h" |
| |
| #define STRINGKEYVALUESTOREOVERHEAD 100 |
| |
| uint32 StringKeyValueStore::getCurrentMemoryUsage() |
| { |
| return iVariableSizeMemPool->getCurrMemoryUsage(); |
| } |
| |
| uint32 StringKeyValueStore::getStoreSize() |
| { |
| return iVariableSizeMemPool->getPoolSize(); |
| } |
| |
| uint32 StringKeyValueStore::getAvailableSize() |
| { |
| return (iVariableSizeMemPool->getTotalAvailableSize() - STRINGKEYVALUESTOREOVERHEAD); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| bool StringKeyValueStore::storeNewKeyValueItem(const char *aItem, const int32 aItemLength, char *&aNewLocation) |
| { |
| int32 err = 0; |
| OSCL_TRY(err, aNewLocation = (char*)iVariableSizeMemPool->allocate(aItemLength + 1);); |
| if (err || aNewLocation == NULL) return false; |
| oscl_memcpy(aNewLocation, aItem, aItemLength); |
| aNewLocation[aItemLength] = 0; |
| return true; |
| } |
| |
| void StringKeyValueStore::releaseOldKeyValueItem(const char *aItem, const int32 aItemLength) |
| { |
| OSCL_UNUSED_ARG(aItemLength); |
| |
| int32 err = 0; |
| OSCL_TRY(err, iVariableSizeMemPool->deallocate((OsclAny*)aItem);); |
| } |
| |
| int32 StringKeyValueStore::addKeyToStore(const StrCSumPtrLen &aNewKey, int32 tableIndex) |
| { |
| if (!iFieldKeys[tableIndex].empty()) return StringKeyValueStore_Success; |
| |
| // new field |
| // save this checksum |
| int32 err = 0; |
| OSCL_TRY(err, iFieldKeyTableIndexVector.push_back(tableIndex)); |
| if (err) return StringKeyValueStore_NoMemory; |
| |
| // add new field key |
| char *newLocation = NULL; |
| int32 aKeyLength = aNewKey.length(); |
| if (!storeNewKeyValueItem(aNewKey.c_str(), aKeyLength, newLocation)) return StringKeyValueStore_NoMemory; |
| iFieldKeys[tableIndex].setPtrLen(newLocation, aKeyLength); |
| iTotalKeyValueLength += aKeyLength; |
| return StringKeyValueStore_Success; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| int32 StringKeyValueStore::addKeyValuePair(const StrCSumPtrLen &aNewKey, const StrPtrLen &aNewValue, const bool aNeedReplaceOldValue) |
| { |
| int32 tableIndex; |
| if ((tableIndex = getHashTableIndex(aNewKey, false)) < 0) return false; // false in getHashTableIndex means for adding an element |
| |
| // add the field key if it is new |
| if (addKeyToStore(aNewKey, tableIndex)) return StringKeyValueStore_NoMemory; |
| |
| // add or append new field value |
| char *newLocation = NULL; |
| int32 aValueLength = aNewValue.length(); |
| if (!storeNewKeyValueItem(aNewValue.c_str(), aValueLength, newLocation)) return StringKeyValueStore_NoMemory; |
| |
| if (iFieldVals[tableIndex].length() == 0) |
| { |
| // empty value field |
| |
| iFieldVals[tableIndex].setPtrLen(newLocation, aValueLength); |
| } |
| else if (aNeedReplaceOldValue) |
| { |
| // overrite |
| // first, release the memory for the old value |
| releaseOldKeyValueItem(iFieldVals[tableIndex].c_str(), aValueLength); |
| // then replace the old value with the new value |
| iTotalKeyValueLength -= iFieldVals[tableIndex].length(); |
| iFieldVals[tableIndex].setPtrLen(newLocation, aValueLength); |
| iTotalNumberOfKeyValuePairs--; // for the following iTotalNumberOfKeyValuePairs++; |
| } |
| else |
| { |
| // append |
| // create StrCSumPtrLenWrapper |
| StrCSumPtrLenWrapper aMewValueWrapper(newLocation, aValueLength); |
| int32 err = 0; |
| OSCL_TRY(err, iStrCSumPtrLenWrapperVector.push_back(aMewValueWrapper)); |
| if (err) return StringKeyValueStore_NoMemory; |
| // link this new field value to the existing field key |
| iFieldKeys[tableIndex].push_back(&iStrCSumPtrLenWrapperVector[iStrCSumPtrLenWrapperVector.size()-1]); |
| iTotalKeyValueLength += iFieldKeys[tableIndex].length(); // field key still needs to be taken into account |
| } |
| |
| // update iTotalKeyValueLength and iTotalNumberOfKeyValuePairs |
| iTotalKeyValueLength += aValueLength; |
| iTotalNumberOfKeyValuePairs++; |
| return StringKeyValueStore_Success; |
| } |
| |
| int32 StringKeyValueStore::addKeyValuePair(const StrCSumPtrLen &aNewKey, const char *aNewValue, const bool aNeedReplaceOldValue) |
| { |
| if (!aNewValue) return StringKeyValueStore_Failure; |
| StrPtrLen newValue(aNewValue); |
| return addKeyValuePair(aNewKey, newValue, aNeedReplaceOldValue); |
| } |
| |
| int32 StringKeyValueStore::addKeyValuePair(const char *aNewKey, const uint32 aKeyLength, const char *aNewValue, const uint32 aValueLength, |
| const bool aNeedReplaceOldValue) |
| { |
| StrCSumPtrLen newKey(aNewKey, aKeyLength); |
| StrPtrLen newValue(aNewValue, aValueLength); |
| return addKeyValuePair(newKey, newValue, aNeedReplaceOldValue); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| // get |
| uint32 StringKeyValueStore::getCurrentKeyList(StrPtrLen *&aFieldKeyList, const uint32 aListSize) |
| { |
| uint32 requestListSize = (aListSize == 0 ? iFieldKeyTableIndexVector.size() : |
| OSCL_MIN(aListSize, iFieldKeyTableIndexVector.size())); |
| uint32 i; |
| for (i = 0; i < requestListSize; i++) |
| { |
| aFieldKeyList[i].setPtrLen(iFieldKeys[iFieldKeyTableIndexVector[i]].c_str(), |
| iFieldKeys[iFieldKeyTableIndexVector[i]].length()); |
| } |
| |
| return requestListSize; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| bool StringKeyValueStore::getValueByKey(const StrCSumPtrLen &aKey, StrPtrLen &aValue, uint32 index) |
| { |
| // reset aValue |
| aValue.setPtrLen("", 0); |
| |
| int32 tableIndex = getHashTableIndex(aKey); |
| if (tableIndex < 0 || tableIndex > KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS) return false; // no such key in the store |
| |
| if (index == 0) |
| { |
| aValue = iFieldVals[tableIndex]; |
| return true; |
| } |
| else |
| { |
| // index > 0 |
| uint32 count = 1; |
| StrCSumPtrLenWrapper *pStrWrapper = iFieldKeys[tableIndex].getNext(); |
| while (count < index && pStrWrapper != NULL) |
| { |
| pStrWrapper = pStrWrapper->getNext(); |
| count++; |
| } |
| |
| if (!pStrWrapper) return false; |
| if (count == index && pStrWrapper != NULL) |
| { |
| // find the right one |
| aValue.setPtrLen(pStrWrapper->c_str(), pStrWrapper->length()); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| uint32 StringKeyValueStore::getNumberOfValuesByKey(const StrCSumPtrLen &aKey) |
| { |
| int32 tableIndex = getHashTableIndex(aKey); |
| if (tableIndex < 0) return 0; // no such key in the store |
| |
| uint32 count = 1; |
| StrCSumPtrLenWrapper *pStrWrapper = iFieldKeys[tableIndex].getNext(); |
| while (pStrWrapper) |
| { |
| count++; |
| pStrWrapper = pStrWrapper->getNext(); |
| } |
| return count; |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| // search |
| bool StringKeyValueStore::isKeyValueAvailable(const StrCSumPtrLen &aKey) |
| { |
| return (getHashTableIndex(aKey) >= 0); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| // remove |
| bool StringKeyValueStore::removeKeyValuePair(const StrCSumPtrLen &aKey) |
| { |
| // update iTotalNumberOfKeyValuePairs |
| uint32 numValues = getNumberOfValuesByKey(aKey); |
| if (numValues == 0) return true; //false; // no such key |
| iTotalNumberOfKeyValuePairs -= numValues; |
| |
| // update iTotalKeyValueLength |
| int32 tableIndex = getHashTableIndex(aKey); |
| iTotalKeyValueLength -= (iFieldKeys[tableIndex].length() * numValues + iFieldVals[tableIndex].length()); |
| StrCSumPtrLenWrapper *pStrWrapper = iFieldKeys[tableIndex].getNext(); |
| while (pStrWrapper) // for the mulitple values |
| { |
| // release the key and all appending values |
| releaseOldKeyValueItem(pStrWrapper->c_str(), pStrWrapper->length()); |
| iTotalKeyValueLength -= pStrWrapper->length(); |
| pStrWrapper = pStrWrapper->getNext(); |
| } |
| // release the value |
| releaseOldKeyValueItem(iFieldVals[tableIndex].c_str(), iFieldVals[tableIndex].length()); |
| |
| // remove the index in iFieldKeyTableIndexVector |
| uint32 i = 0; |
| for (i = 0; i < iFieldKeyTableIndexVector.size(); i++) |
| { |
| if (iFieldKeys[iFieldKeyTableIndexVector[i]].isCIEquivalentTo(aKey)) |
| { |
| iFieldKeyTableIndexVector.erase(iFieldKeyTableIndexVector.begin() + i); |
| } |
| } |
| |
| // clear key-value |
| iFieldKeys[tableIndex].clear(); |
| iFieldVals[tableIndex].setPtrLen("", 0); |
| |
| // note that we need to upgrade to use resizable memory pool to return the memory for the key-value pair; |
| // otherwise we waste the memory. |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| // clear |
| void StringKeyValueStore::clear() |
| { |
| iTotalNumberOfKeyValuePairs = 0; |
| iTotalKeyValueLength = 0; |
| iNumConflicts = 0; |
| |
| // clear field key and value tables |
| uint32 i; |
| for (i = 0; i < KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS; i++) |
| { |
| iFieldKeys[i].clear(); |
| iFieldVals[i].setPtrLen("", 0); |
| } |
| iStrCSumPtrLenWrapperVector.clear(); |
| iFieldKeyTableIndexVector.clear(); |
| if (iVariableSizeMemPool) iVariableSizeMemPool->clear(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| // copy |
| bool StringKeyValueStore::copy(StringKeyValueStore& aStore) |
| { |
| uint32 numKeyValuePairs = aStore.getNumberOfKeyValuePairs(); |
| uint32 numKeys = aStore.getNumberOfKeys(); |
| if (numKeyValuePairs == 0 || numKeys == 0) return true; |
| OSCL_ASSERT(numKeyValuePairs >= numKeys); |
| |
| StrPtrLen *keyList = OSCL_ARRAY_NEW(StrPtrLen, numKeys); |
| if (!keyList) return false; |
| aStore.getCurrentKeyList(keyList); |
| for (uint32 i = 0; i < numKeyValuePairs; i++) |
| { |
| uint32 numValuesByKey = aStore.getNumberOfValuesByKey(keyList[i]); |
| for (uint32 j = 0; j < numValuesByKey; j++) |
| { |
| StrPtrLen fieldValue; |
| if (!aStore.getValueByKey(keyList[i], fieldValue, j)) |
| { |
| OSCL_ARRAY_DELETE(keyList); |
| return false; |
| } |
| if (addKeyValuePair(keyList[i], fieldValue)) |
| { |
| OSCL_ARRAY_DELETE(keyList); |
| return false; |
| } |
| } |
| } |
| |
| OSCL_ARRAY_DELETE(keyList); |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| // factory method |
| StringKeyValueStore* StringKeyValueStore::create(const uint32 aStoreSize) |
| { |
| StringKeyValueStore *store = OSCL_NEW(StringKeyValueStore, ()); |
| if (!store) return NULL; |
| if (!store->construct(aStoreSize)) |
| { |
| OSCL_DELETE(store); |
| return NULL; |
| } |
| return store; |
| } |
| |
| bool StringKeyValueStore::construct(const uint32 aStoreSize) |
| { |
| clear(); |
| |
| // create two vectors |
| int32 err = 0; |
| OSCL_TRY(err, iStrCSumPtrLenWrapperVector.reserve(KEYVALUESTORE_VECTOR_RESERVE_VALUE); |
| iFieldKeyTableIndexVector.reserve(KEYVALUESTORE_VECTOR_RESERVE_VALUE); |
| ); |
| if (err) |
| { |
| iStrCSumPtrLenWrapperVector.clear(); |
| iFieldKeyTableIndexVector.clear(); |
| return false; |
| } |
| |
| // create iVariableSizeMemPool |
| OSCL_TRY(err, iVariableSizeMemPool = new OsclMemPoolVariableChunkAllocator(aStoreSize);); |
| if (err || iVariableSizeMemPool == NULL) return false; |
| |
| |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| // destructor |
| StringKeyValueStore::~StringKeyValueStore() |
| { |
| clear(); |
| |
| // delete memory pool |
| OSCL_DELETE(iVariableSizeMemPool); |
| iVariableSizeMemPool = NULL; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////// |
| uint32 StringKeyValueStore::calculateChecksum(const char *aBuffer, uint32 aBufferLength) |
| { |
| uint32 checkSum = 0; |
| for (uint32 i = 0; i < aBufferLength; i++) |
| { |
| if (oscl_isLetter(aBuffer[i])) |
| { |
| checkSum += (uint32)(aBuffer[i] | OSCL_ASCII_CASE_MAGIC_BIT); |
| } |
| else |
| { |
| checkSum += aBuffer[i]; |
| } |
| } |
| |
| return (checkSum % KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS) / 2; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| int32 StringKeyValueStore::getHashTableIndex(const StrCSumPtrLen &aKey, const bool aFindKey) |
| { |
| StrCSumPtrLenWrapper keyWrapper(aKey); |
| int32 aTableIndex = keyWrapper.getChecksum(); |
| |
| if (iFieldKeys[aTableIndex].empty()) |
| { |
| if (!aFindKey) |
| { |
| return aTableIndex; // for add, new key to be added |
| } |
| return query(aKey); // for search, need to check linear search table |
| } |
| |
| if (iFieldKeys[aTableIndex].isCIEquivalentTo(aKey)) |
| { |
| return aTableIndex; |
| } |
| int32 index = query(aKey); |
| if (aFindKey) |
| { |
| return index; // for search |
| } |
| // for add |
| if (index >= 0) |
| { |
| return index; // find an existing one to add |
| } |
| // create a new index for this new conllision |
| if (iNumConflicts + 1 >= KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS / 2) |
| { |
| return -1; |
| } |
| return KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS / 2 + (iNumConflicts++); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| int32 StringKeyValueStore::query(const StrCSumPtrLen &aKey) |
| { |
| StrCSumPtrLenWrapper *LSTable = iFieldKeys + KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS / 2; // LSTable= Linear Search Table |
| uint32 i = 0; |
| for (i = 0; i < iNumConflicts; i++) |
| { |
| if (LSTable[i].isCIEquivalentTo(aKey)) |
| { |
| return (int32)(KEYVALUESTORE_HASH_TABLE_SIZE_FOR_KEYS / 2 + i); |
| } |
| } |
| return -1; |
| } |
| |