diff --git a/src/knx/association_table_object.cpp b/src/knx/association_table_object.cpp index 2afbc7b..0517085 100644 --- a/src/knx/association_table_object.cpp +++ b/src/knx/association_table_object.cpp @@ -39,46 +39,97 @@ uint16_t AssociationTableObject::getASAP(uint16_t idx) return ntohs(_tableData[2 * idx + 2]); } +// after any table change the table is checked if it allows +// binary search access. If not, sortedEntryCount stays 0, +// otherwise sortedEntryCount represents size of bin search array +void AssociationTableObject::prepareBinarySearch() +{ + sortedEntryCount = 0; +#ifdef USE_BINSEARCH + uint16_t lastASAP = 0; + uint16_t currentASAP = 0; + uint16_t lookupIdx = 0; + uint16_t lookupASAP = 0; + // we iterate through all ASAP + // the first n ASAP are sorted (strictly increasing number), these are assigning sending TSAP + // the remaining ASAP have to be all repetitions, otherwise we set sortedEntryCount to 0, which forces linear search + for (uint16_t idx = 0; idx < entryCount(); idx++) + { + currentASAP = getASAP(idx); + if (sortedEntryCount) + { + // look if the remaining ASAP exist in the previously sorted list. + while (lookupIdx < sortedEntryCount) + { + lookupASAP = getASAP(lookupIdx); + if (currentASAP <= lookupASAP) + break; // while + else + lookupIdx++; + } + if (currentASAP < lookupASAP || lookupIdx >= sortedEntryCount) + { + // a new ASAP found, we force linear search + sortedEntryCount = 0; + break; // for + } + } + else + { + // check for strictly increasing ASAP + if (currentASAP > lastASAP) + lastASAP = currentASAP; + else + { + sortedEntryCount = idx; // last found index indicates end of sorted list + idx--; // current item has to be handled as remaining ASAP + } + } + } + // in case complete table is strictly increasing + if (lookupIdx == 0 && sortedEntryCount == 0) + sortedEntryCount = entryCount(); +#endif +} + const uint8_t* AssociationTableObject::restore(const uint8_t* buffer) { buffer = TableObject::restore(buffer); _tableData = (uint16_t*)data(); + prepareBinarySearch(); return buffer; } // return type is int32 so that we can return uint16 and -1 int32_t AssociationTableObject::translateAsap(uint16_t asap) { - uint16_t entries = entryCount(); - #ifdef USE_BINSEARCH - uint16_t low,high,i; - low = 0; - high = entries-1; + // sortedEntryCount is determined in prepareBinarySearch() + // if ETS provides strictly increasing numbers for ASAP + // represents the size of the array to search + if (sortedEntryCount) + { + uint16_t low = 0; + uint16_t high = sortedEntryCount - 1; - while(low <= high) - { - i = (low+high)/2; - uint16_t asap_i = getASAP(i); - if (asap_i == asap) + while(low <= high) { - // as the binary search does not hit the first element in a list with identical items, - // search downwards to return the first occurence in the table - while(getASAP(--i) == asap) - ; - return getTSAP(i+1); + uint16_t i = (low + high) / 2; + uint16_t asap_i = getASAP(i); + if (asap_i == asap) + return getTSAP(i); + if(asap_i > asap) + high = i - 1; + else + low = i + 1 ; } - if(asap_i > asap) - high = i - 1; - else - low = i + 1 ; } - #else - for (uint16_t i = 0; i < entries; i++) + else { - if (getASAP(i) == asap) - return getTSAP(i); + // if ASAP numbers are not strictly increasing linear seach is used + for (uint16_t i = 0; i < entryCount(); i++) + if (getASAP(i) == asap) + return getTSAP(i); } - #endif return -1; } @@ -89,6 +140,7 @@ void AssociationTableObject::beforeStateChange(LoadState& newState) return; _tableData = (uint16_t*)data(); + prepareBinarySearch(); } int32_t AssociationTableObject::nextAsap(uint16_t tsap, uint16_t& startIdx) diff --git a/src/knx/association_table_object.h b/src/knx/association_table_object.h index 1b5fde5..2e68a00 100644 --- a/src/knx/association_table_object.h +++ b/src/knx/association_table_object.h @@ -19,5 +19,7 @@ class AssociationTableObject : public TableObject uint16_t entryCount(); uint16_t getTSAP(uint16_t idx); uint16_t getASAP(uint16_t idx); + void prepareBinarySearch(); uint16_t* _tableData = 0; + uint16_t sortedEntryCount; };