| #include "bej_dictionary.h" |
| |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| /** |
| * @brief Get the index for a property offset. First property will be at index |
| * 0. |
| * |
| * @param[in] propertyOffset - a valid property offset. |
| * @return index of the property. |
| */ |
| static uint16_t bejGetPropertyEntryIndex(uint16_t propertyOffset) |
| { |
| return (propertyOffset - bejDictGetPropertyHeadOffset()) / |
| sizeof(struct BejDictionaryProperty); |
| } |
| |
| /** |
| * @brief Validate a property offset. |
| * |
| * @param[in] dictionary - pointer to the dictionary. |
| * @param[in] propertyOffset - offset needed to be validated. |
| * @return true if propertyOffset is a valid offset. |
| */ |
| static bool bejValidatePropertyOffset(const uint8_t* dictionary, |
| uint16_t propertyOffset) |
| { |
| // propertyOffset should be greater than or equal to first property offset. |
| if (propertyOffset < bejDictGetPropertyHeadOffset()) |
| { |
| fprintf( |
| stderr, |
| "Invalid property offset. Pointing to Dictionary header data\n"); |
| return false; |
| } |
| |
| // propertyOffset should be a multiple of sizeof(BejDictionaryProperty) |
| // starting from first property within the dictionary. |
| if ((propertyOffset - bejDictGetPropertyHeadOffset()) % |
| sizeof(struct BejDictionaryProperty)) |
| { |
| fprintf(stderr, "Invalid property offset. Does not point to beginning " |
| "of property\n"); |
| return false; |
| } |
| |
| const struct BejDictionaryHeader* header = |
| (const struct BejDictionaryHeader*)dictionary; |
| uint16_t propertyIndex = bejGetPropertyEntryIndex(propertyOffset); |
| if (propertyIndex >= header->entryCount) |
| { |
| fprintf(stderr, |
| "Invalid property offset %u. It falls outside of dictionary " |
| "properties\n", |
| propertyOffset); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool bejValidatePropertyNameLength( |
| const uint8_t* dictionary, uint32_t dictionarySize, uint16_t nameOffset, |
| uint8_t nameLength) |
| { |
| if (nameLength == 0) |
| { |
| return true; |
| } |
| // After the property names, the dictionary has at least the |
| // uint8 CopyrightLength field. So we compare with dictionarySize - 1. |
| // nameOffset is uint16_t and nameLength is uint8_t so it cannot overflow |
| // uint32_t dictionarySize here. |
| if (((uint32_t)nameOffset) + (uint32_t)(nameLength) > (dictionarySize - 1)) |
| { |
| fprintf(stderr, "Property name length goes beyond dictionary size\n"); |
| return false; |
| } |
| |
| // nameLength includes the NULL terminating. So the actual string size |
| // should be nameLength - 1. |
| const char* name = (const char*)dictionary + nameOffset; |
| const void* nullTerminator = memchr(name, '\0', nameLength); |
| if (nullTerminator == NULL || |
| ((const char*)nullTerminator - name) != (ptrdiff_t)(nameLength - 1)) |
| { |
| fprintf(stderr, "Property name length doesn't match actual length\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| uint16_t bejDictGetPropertyHeadOffset(void) |
| { |
| // First property is present soon after the dictionary header. |
| return sizeof(struct BejDictionaryHeader); |
| } |
| |
| uint16_t bejDictGetFirstAnnotatedPropertyOffset(void) |
| { |
| // The first property available is the "Annotations" set which is the parent |
| // for all properties. Next immediate property is the first property we |
| // need. |
| return sizeof(struct BejDictionaryHeader) + |
| sizeof(struct BejDictionaryProperty); |
| } |
| |
| int bejDictGetProperty(const uint8_t* dictionary, |
| uint16_t startingPropertyOffset, uint16_t sequenceNumber, |
| const struct BejDictionaryProperty** property, |
| uint16_t* propertyOffset) |
| { |
| NULL_CHECK(property, "property in bejDictGetProperty"); |
| |
| uint16_t curPropertyOffset = startingPropertyOffset; |
| const struct BejDictionaryHeader* header = |
| (const struct BejDictionaryHeader*)dictionary; |
| |
| if (!bejValidatePropertyOffset(dictionary, curPropertyOffset)) |
| { |
| return bejErrorInvalidPropertyOffset; |
| } |
| uint16_t propertyIndex = bejGetPropertyEntryIndex(curPropertyOffset); |
| |
| for (uint16_t index = propertyIndex; index < header->entryCount; ++index) |
| { |
| const struct BejDictionaryProperty* p = |
| (const struct BejDictionaryProperty*)(dictionary + |
| curPropertyOffset); |
| if (p->sequenceNumber == sequenceNumber) |
| { |
| if (!bejValidatePropertyNameLength(dictionary, |
| header->dictionarySize, |
| p->nameOffset, p->nameLength)) |
| { |
| return bejErrorInvalidSize; |
| } |
| |
| *property = p; |
| // propertyOffset is an optional output. |
| if (propertyOffset != NULL) |
| { |
| *propertyOffset = curPropertyOffset; |
| } |
| return 0; |
| } |
| curPropertyOffset += sizeof(struct BejDictionaryProperty); |
| } |
| return bejErrorUnknownProperty; |
| } |
| |
| const char* bejDictGetPropertyName(const uint8_t* dictionary, |
| uint16_t nameOffset, uint8_t nameLength) |
| { |
| if (nameLength == 0) |
| { |
| return ""; |
| } |
| return (const char*)(dictionary + nameOffset); |
| } |
| |
| int bejDictGetPropertyByName( |
| const uint8_t* dictionary, uint16_t startingPropertyOffset, |
| const char* propertyName, const struct BejDictionaryProperty** property, |
| uint16_t* propertyOffset) |
| { |
| NULL_CHECK(property, "property in bejDictGetPropertyByName"); |
| |
| uint16_t curPropertyOffset = startingPropertyOffset; |
| const struct BejDictionaryHeader* header = |
| (const struct BejDictionaryHeader*)dictionary; |
| |
| if (!bejValidatePropertyOffset(dictionary, curPropertyOffset)) |
| { |
| return bejErrorInvalidPropertyOffset; |
| } |
| uint16_t propertyIndex = bejGetPropertyEntryIndex(curPropertyOffset); |
| |
| for (uint16_t index = propertyIndex; index < header->entryCount; ++index) |
| { |
| const struct BejDictionaryProperty* p = |
| (const struct BejDictionaryProperty*)(dictionary + |
| curPropertyOffset); |
| if (strcmp(propertyName, |
| bejDictGetPropertyName(dictionary, p->nameOffset, |
| p->nameLength)) == 0) |
| { |
| *property = p; |
| // propertyOffset is an optional output. |
| if (propertyOffset != NULL) |
| { |
| *propertyOffset = curPropertyOffset; |
| } |
| return 0; |
| } |
| curPropertyOffset += sizeof(struct BejDictionaryProperty); |
| } |
| return bejErrorUnknownProperty; |
| } |
| |
| int bejDictEntryByBejLocator( |
| const uint8_t* dictionary, const uint8_t* bejLocator, uint8_t locatorLength, |
| const struct BejDictionaryProperty** property, uint16_t* propertyOffset) |
| { |
| NULL_CHECK(propertyOffset, "propertyOffset in bejDictEntryByBejLocator"); |
| |
| // bejLocator offset 0 provides a nnint which provides the total size of the |
| // sequence numbers. |
| const uint8_t* lengthBytesNnint = bejLocator; |
| // Get the total length of the sequence number series. This is done for a |
| // data validity check. We do not really need this seqNumbersLen for the |
| // rest of the calculations. |
| uint8_t seqNumbersLen = (uint8_t)bejGetNnint(lengthBytesNnint); |
| if (seqNumbersLen + bejGetNnintSize(lengthBytesNnint) != locatorLength) |
| { |
| fprintf(stderr, |
| "Invalid LengthBytes field in the bejLocator. LengthBytes: %u " |
| "locatorLength: %u\n", |
| seqNumbersLen, locatorLength); |
| return -1; |
| } |
| |
| const struct BejDictionaryProperty* curProperty; |
| uint16_t curPropertyOffset; |
| |
| // The first bejTupleS starts after the first nnint that provides the length |
| // bytes. |
| uint8_t tupleSOffset = bejGetNnintSize(bejLocator); |
| // Get the offset to the first property of the dictionary. |
| uint16_t dictOffset = bejDictGetPropertyHeadOffset(); |
| |
| // Go through the series of sequence numbers and reach the property. |
| while (tupleSOffset < locatorLength) |
| { |
| const uint8_t* tupleS = bejLocator + tupleSOffset; |
| // Dictionary sequence numnber is 16bits. But the bejTupleS type also |
| // adds the dictionary selection bit to the LSB. So we need to use |
| // uint32_t. |
| uint32_t seqWithDictFlag = (uint32_t)bejGetNnint(tupleS); |
| // Extract the sequence number. |
| uint16_t seq = (seqWithDictFlag >> 1) & 0xFFFF; |
| |
| int ret = bejDictGetProperty(dictionary, dictOffset, seq, &curProperty, |
| &curPropertyOffset); |
| if (ret != 0) |
| { |
| fprintf(stderr, |
| "Failed to find a property for the sequence number %u. " |
| "Started at dictionary offset: 0x%x index: %u\n", |
| seq, dictOffset, bejGetPropertyEntryIndex(dictOffset)); |
| return ret; |
| } |
| |
| // Goto next tuple offset/. |
| tupleSOffset += bejGetNnintSize(tupleS); |
| |
| // Check wether this is the last sequence number in the list. |
| // (tupleSOffset > locatorLength), shouldn't happen. If it happens, |
| // probably the input is invalid. |
| if (tupleSOffset == locatorLength) |
| { |
| *propertyOffset = curPropertyOffset; |
| // property is an optional output. |
| if (property != NULL) |
| { |
| *property = curProperty; |
| } |
| return 0; |
| } |
| |
| // Next sequence number is the sequence number of the child property of |
| // the current property. So jump the child of the current dictionary |
| // entry. |
| dictOffset = curProperty->childPointerOffset; |
| } |
| |
| fprintf(stderr, "Couldn't reach the expected property. Input is invalid or " |
| "something went wrong during calculation"); |
| return -1; |
| } |