From 7deb63c74b1bc391a620513a4f967ba02dbc3f1b Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 10 Apr 2024 07:10:00 +0100 Subject: [PATCH 01/73] OPTIM: Added two new fields to the evidence pair data structure. fieldLength sets the length of the field name in the data structure to avoid repeated calls to strlen to determine the length of the string. relative is a void pointer that can be used to relate use cases specific information to the evidence pair. For example; storing data related to the evidence pair that could take a long time to retrieve. --- evidence.c | 5 ++++- evidence.h | 2 ++ fiftyone.h | 1 + headers.c | 2 +- headers.h | 3 ++- pseudoheader.c | 28 ++++++++++++++-------------- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/evidence.c b/evidence.c index 0f628893..f6b1f1bc 100644 --- a/evidence.c +++ b/evidence.c @@ -59,6 +59,8 @@ fiftyoneDegreesEvidenceCreate(uint32_t capacity) { if (evidence != NULL) { for (i = 0; i < evidence->capacity; i++) { evidence->items[i].field = NULL; + evidence->items[i].fieldLength = 0; + evidence->items[i].relative = NULL; evidence->items[i].originalValue = NULL; evidence->items[i].parsedValue = NULL; evidence->items[i].prefix = FIFTYONE_DEGREES_EVIDENCE_IGNORE; @@ -83,6 +85,7 @@ fiftyoneDegreesEvidenceKeyValuePair* fiftyoneDegreesEvidenceAddString( pair = &evidence->items[evidence->count++]; pair->prefix = prefix; pair->field = field; + pair->fieldLength = strlen(field); pair->originalValue = (void*)originalValue; pair->parsedValue = NULL; } @@ -97,7 +100,7 @@ fiftyoneDegreesEvidenceKeyValuePair* fiftyoneDegreesEvidenceAddString( * @param prefixes the accepted evidence prefixes * @param state the state object to hold the current state of the process * @param cont indicate whether the iteration should continue. This normally - * indicate if error has occured. Upon return, this value is also updated so + * indicate if error has occurred. Upon return, this value is also updated so * that caller know whether to continue processing any other member set of * the evidence collection. * @param callback the method to call back when a matched evidence is found. diff --git a/evidence.h b/evidence.h index 53b21e81..c538e561 100644 --- a/evidence.h +++ b/evidence.h @@ -160,6 +160,8 @@ typedef struct fiftyone_degrees_evidence_prefix_map_t { typedef struct fiftyone_degrees_evidence_key_value_pair_t { fiftyoneDegreesEvidencePrefix prefix; /**< e.g. #FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING */ const char *field; /**< e.g. User-Agent or ScreenPixelsWidth */ + size_t fieldLength; /**< length of field */ + void* relative; /**< optional pointer to a related entity */ const void *originalValue; /**< The original unparsed value */ const void *parsedValue; /**< The parsed value which may not be a string */ } fiftyoneDegreesEvidenceKeyValuePair; diff --git a/fiftyone.h b/fiftyone.h index fb391bda..a1023032 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -178,6 +178,7 @@ MAP_TYPE(Coordinate) MAP_TYPE(HeaderSegment) MAP_TYPE(HeaderSegmentArray) MAP_TYPE(KeyValuePair) +MAP_TYPE(HeaderID) #define ProfileGetOffsetForProfileId fiftyoneDegreesProfileGetOffsetForProfileId /**< Synonym for #fiftyoneDegreesProfileGetOffsetForProfileId function. */ #define OverrideValuesAdd fiftyoneDegreesOverrideValuesAdd /**< Synonym for #fiftyoneDegreesOverrideValuesAdd function. */ diff --git a/headers.c b/headers.c index a64e7c1c..3368f03a 100644 --- a/headers.c +++ b/headers.c @@ -433,7 +433,7 @@ bool fiftyoneDegreesHeadersIsHttp( return HeaderGetIndex( (Headers*)state, pair->field, - strlen(pair->field)) >= 0; + pair->fieldLength) >= 0; } /** diff --git a/headers.h b/headers.h index 08bc92a4..3ac295e8 100644 --- a/headers.h +++ b/headers.h @@ -195,7 +195,8 @@ EXTERNAL fiftyoneDegreesHeaders* fiftyoneDegreesHeadersCreate( fiftyoneDegreesHeadersGetMethod get); /** - * Provides the integer index of the HTTP header name. + * Provides the integer index of the HTTP header name, or -1 if there is no + * matching header. * @param headers structure created by #fiftyoneDegreesHeadersCreate * @param httpHeaderName of the header whose index is required * @param length number of characters in httpHeaderName diff --git a/pseudoheader.c b/pseudoheader.c index 618e74d6..f4cca8cd 100644 --- a/pseudoheader.c +++ b/pseudoheader.c @@ -37,17 +37,15 @@ static const char* getEvidenceValueForHeader( HeaderSegment* segment, const EvidenceKeyValuePairArray *evidence, EvidencePrefix prefix) { - size_t length; EvidenceKeyValuePair *pair; for (uint32_t i = 0; i < evidence->count; i++) { pair = &evidence->items[i]; if (pair->prefix == prefix) { - length = strlen(pair->field); - if (length == segment->length && + if (pair->fieldLength == segment->length && StringCompareLength( pair->field, segment->segment, - length) == 0) { + segment->length) == 0) { return (char*)evidence->items[i].originalValue; } } @@ -136,7 +134,6 @@ static bool isEvidencePresentForHeader( const EvidencePrefix* acceptedPrefixes, size_t numberOfPrefixes) { bool matchPrefix = false; - size_t length; EvidenceKeyValuePair* pair; for (uint32_t i = 0; i < evidence->count; i++) { pair = &evidence->items[i]; @@ -152,8 +149,7 @@ static bool isEvidencePresentForHeader( // Compare the field name to the header name if the prefix matches. if (matchPrefix) { - length = strlen(pair->field); - if (length == header->nameLength && + if (pair->fieldLength == header->nameLength && StringCompare(header->name, pair->field) == 0) { return true; } @@ -206,13 +202,16 @@ fiftyoneDegreesPseudoHeadersAddEvidence( evidence->pseudoEvidence->items[ evidence->pseudoEvidence->count].field = header->name; - evidence->pseudoEvidence->items[ - evidence->pseudoEvidence->count].prefix = - orderOfPrecedence[j]; - evidence->pseudoEvidence->count++; - // Once a complete pseudo evidence is found - // move on the next pseudo header - break; + evidence->pseudoEvidence->items[ + evidence->pseudoEvidence->count].fieldLength = + header->nameLength; + evidence->pseudoEvidence->items[ + evidence->pseudoEvidence->count].prefix = + orderOfPrecedence[j]; + evidence->pseudoEvidence->count++; + // Once a complete pseudo evidence is found + // move on the next pseudo header + break; } else if (charAdded < 0) { PseudoHeadersRemoveEvidence( @@ -244,6 +243,7 @@ void fiftyoneDegreesPseudoHeadersRemoveEvidence( for (uint32_t i = 0; i < evidence->pseudoEvidence->count; i++) { pair = &evidence->pseudoEvidence->items[i]; pair->field = NULL; + pair->fieldLength = 0; memset((void*)pair->originalValue, '\0', bufferSize); } evidence->pseudoEvidence->count = 0; From 1352545efece0acde6126c3b8953b836146abada Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 10 Apr 2024 14:18:49 +0100 Subject: [PATCH 02/73] OPTIM: Removed check of the collection pointer. It will always be present and if not will be handlded in the macro COLLECTION_RELEASE. --- properties.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/properties.c b/properties.c index b276597c..68811284 100644 --- a/properties.c +++ b/properties.c @@ -54,9 +54,7 @@ static uint32_t countAvailableProperties(propertiesSource *source) { uint32_t i = 0; DataReset(&stringItem.data); while (source->getName(source->state, i, &stringItem) != NULL) { - if (stringItem.collection != NULL) { - COLLECTION_RELEASE(stringItem.collection, &stringItem); - } + COLLECTION_RELEASE(stringItem.collection, &stringItem); i++; } return i; @@ -129,14 +127,10 @@ static int getPropertyIndex( _strnicmp(&test->value, requiredPropertyName, requiredPropertyLength) == 0) { - if (stringItem.collection != NULL) { - COLLECTION_RELEASE(stringItem.collection, &stringItem); - } - return i; - } - if (stringItem.collection != NULL) { COLLECTION_RELEASE(stringItem.collection, &stringItem); + return i; } + COLLECTION_RELEASE(stringItem.collection, &stringItem); test = source->getName(source->state, ++i, &stringItem); } return -1; From dc1acf7ce1ce89e77fa142a77ab2da16fddf9289 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 19 Apr 2024 08:15:39 +0100 Subject: [PATCH 03/73] FEAT/PERF: Added a flag to the configuration called propertyValueIndex. When this flag is true then the data set post init method can be used to build an index to the first value for each property and profile which can be used to improve the performance of subsequent retrieval operations. There is a small penalty at startup to build the index. indexes.h/c contain the bulk of the impelementation. profile.h/c has been changed to add a new method called fiftyoneDegreesProfileIterateValuesForPropertyWithIndex that can use the index rather than a binary search to find the first value. --- ConfigBase.cpp | 8 + ConfigBase.hpp | 14 ++ .../FiftyOne.Common.C.vcxproj | 2 + .../FiftyOne.Common.C.vcxproj.filters | 6 + config.h | 5 +- dataset.c | 9 +- dataset.h | 4 + fiftyone.h | 7 + indexes.c | 220 ++++++++++++++++++ indexes.h | 113 +++++++++ profile.c | 42 ++-- profile.h | 25 ++ 12 files changed, 439 insertions(+), 16 deletions(-) create mode 100644 indexes.c create mode 100644 indexes.h diff --git a/ConfigBase.cpp b/ConfigBase.cpp index e557c6ea..f37fbade 100644 --- a/ConfigBase.cpp +++ b/ConfigBase.cpp @@ -52,6 +52,10 @@ void ConfigBase::setTempDirectories( mapTempDirectories(); } +void ConfigBase::setPropertyValueIndex(bool index) { + this->config->propertyValueIndex = index; +} + bool ConfigBase::getUseUpperPrefixHeaders() const { return config->usesUpperPrefixedHeaders; } @@ -68,6 +72,10 @@ vector ConfigBase::getTempDirectories() const { return tempDirs; } +bool ConfigBase::getPropertyValueIndex() const { + return config->propertyValueIndex; +} + uint16_t ConfigBase::getConcurrency() const { return 0; } diff --git a/ConfigBase.hpp b/ConfigBase.hpp index 3a326a5a..1232599f 100644 --- a/ConfigBase.hpp +++ b/ConfigBase.hpp @@ -113,6 +113,13 @@ namespace FiftyoneDegrees { */ void setTempDirectories(vector tempDirs); + /** + * Set whether or not an index to speed up the retrieval of values + * for profiles and properties is created. + * @param index should create an index + */ + void setPropertyValueIndex(bool index); + /** * @} * @name Getters @@ -149,6 +156,13 @@ namespace FiftyoneDegrees { */ vector getTempDirectories() const; + /** + * Gets a flag indicating if an index of values for properties and + * profile is created. + * @return true if an index should be created, or false if not. + */ + bool getPropertyValueIndex() const; + /** * Get the expected number of concurrent accessors of the data set. * @return concurrency diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj index 87d6872b..a1ca0b31 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj @@ -20,6 +20,7 @@ + @@ -54,6 +55,7 @@ + diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters index 35793208..e7d1f12e 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters @@ -126,6 +126,9 @@ Header Files + + Header Files + @@ -218,5 +221,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/config.h b/config.h index 94bfdb2a..524b0f3d 100644 --- a/config.h +++ b/config.h @@ -63,6 +63,8 @@ typedef struct fiftyone_degrees_config_base_t { const char **tempDirs; /**< Array of temp directories which can be used in order of preference. */ int tempDirCount; /**< Number of directories in the tempDirs array. */ + bool propertyValueIndex; /**< Indicates if an index to values for property + and profiles should be created. */ } fiftyoneDegreesConfigBase; /** Default value for the #FIFTYONE_DEGREES_CONFIG_USE_TEMP_FILE macro. */ @@ -99,7 +101,8 @@ FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY_DEFAULT FIFTYONE_DEGREES_CONFIG_USE_TEMP_FILE, /* useTempFile */ \ false, /* reuseTempFile */ \ NULL, /* tempDirs */ \ - 0 /* tempDirCount */ + 0, /* tempDirCount */ \ + true /* propertyValueIndex */ /** * @} diff --git a/dataset.c b/dataset.c index 0d63fce4..5959d07b 100644 --- a/dataset.c +++ b/dataset.c @@ -70,13 +70,19 @@ static StatusCode initWithTempFile( void fiftyoneDegreesDataSetFree(fiftyoneDegreesDataSetBase *dataSet) { + // Free the memory used for the index of property and profile values. + if (dataSet->indexPropertyProfile != NULL) { + IndexPropertyProfileFree(dataSet->indexPropertyProfile); + dataSet->indexPropertyProfile = NULL; + } + // Free the memory used by the unique headers. HeadersFree(dataSet->uniqueHeaders); dataSet->uniqueHeaders = NULL; // Free the override properties if any. if (dataSet->overridable != NULL) { - fiftyoneDegreesOverridePropertiesFree(dataSet->overridable); + OverridePropertiesFree(dataSet->overridable); dataSet->overridable = NULL; } @@ -109,6 +115,7 @@ void fiftyoneDegreesDataSetReset(fiftyoneDegreesDataSetBase *dataSet) { dataSet->uniqueHeaders = NULL; dataSet->available = NULL; dataSet->overridable = NULL; + dataSet->indexPropertyProfile = NULL; dataSet->config = NULL; dataSet->handle = NULL; } diff --git a/dataset.h b/dataset.h index f043e96e..4bd99751 100644 --- a/dataset.h +++ b/dataset.h @@ -95,6 +95,7 @@ #include "config.h" #include "overrides.h" #include "common.h" +#include "indexes.h" /** * Base data set structure which contains the 'must have's for all data sets. @@ -126,6 +127,9 @@ typedef struct fiftyone_degrees_dataset_base_t { fiftyoneDegreesOverridePropertyArray *overridable; /**< Array of properties that can be overridden */ + fiftyoneDegreesIndexPropertyProfile* indexPropertyProfile; /**< Index to + look up profile + values */ const void *config; /**< Pointer to the config used to create the dataset */ } fiftyoneDegreesDataSetBase; diff --git a/fiftyone.h b/fiftyone.h index a1023032..bb404700 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -85,6 +85,7 @@ #include "process.h" #include "pair.h" #include "yamlfile.h" +#include "indexes.h" /** * Macro used to support synonym implementation. Creates a typedef which @@ -179,6 +180,7 @@ MAP_TYPE(HeaderSegment) MAP_TYPE(HeaderSegmentArray) MAP_TYPE(KeyValuePair) MAP_TYPE(HeaderID) +MAP_TYPE(IndexPropertyProfile) #define ProfileGetOffsetForProfileId fiftyoneDegreesProfileGetOffsetForProfileId /**< Synonym for #fiftyoneDegreesProfileGetOffsetForProfileId function. */ #define OverrideValuesAdd fiftyoneDegreesOverrideValuesAdd /**< Synonym for #fiftyoneDegreesOverrideValuesAdd function. */ @@ -187,10 +189,12 @@ MAP_TYPE(HeaderID) #define ProfileGetByIndex fiftyoneDegreesProfileGetByIndex /**< Synonym for #fiftyoneDegreesProfileGetByIndex function. */ #define OverridesAdd fiftyoneDegreesOverridesAdd /**< Synonym for #fiftyoneDegreesOverridesAdd function. */ #define OverrideProfileIds fiftyoneDegreesOverrideProfileIds /**< Synonym for #fiftyoneDegreesOverrideProfileIds function. */ +#define OverridePropertiesFree fiftyoneDegreesOverridePropertiesFree /**< Synonym for #fiftyoneDegreesOverridePropertiesFree function. */ #define ComponentInitList fiftyoneDegreesComponentInitList /**< Synonym for #fiftyoneDegreesComponentInitList function. */ #define CollectionGetInteger32 fiftyoneDegreesCollectionGetInteger32 /**< Synonym for #fiftyoneDegreesCollectionGetInteger32 function. */ #define PropertyGet fiftyoneDegreesPropertyGet /**< Synonym for #fiftyoneDegreesPropertyGet function. */ #define ProfileIterateValuesForProperty fiftyoneDegreesProfileIterateValuesForProperty /**< Synonym for #fiftyoneDegreesProfileIterateValuesForProperty function. */ +#define ProfileIterateValuesForPropertyWithIndex fiftyoneDegreesProfileIterateValuesForPropertyWithIndex /**< Synonym for #fiftyoneDegreesProfileIterateValuesForPropertyWithIndex function. */ #define ProfileIterateProfilesForPropertyAndValue fiftyoneDegreesProfileIterateProfilesForPropertyAndValue /**< Synonym for #fiftyoneDegreesProfileIterateProfilesForPropertyAndValue function. */ #define PropertiesGetPropertyIndexFromName fiftyoneDegreesPropertiesGetPropertyIndexFromName /**< Synonym for #fiftyoneDegreesPropertiesGetPropertyIndexFromName function. */ #define TreeIterate fiftyoneDegreesTreeIterateNodes /**< Synonym for #fiftyoneDegreesTreeIterateNodes function. */ @@ -332,6 +336,9 @@ MAP_TYPE(HeaderID) #define ProcessGetId fiftyoneDegreesProcessGetId /**< Synonym for fiftyoneDegreesProcessGetId */ #define YamlFileIterate fiftyoneDegreesYamlFileIterate /**< Synonym for fiftyoneDegreesYamlFileIterate */ #define YamlFileIterateWithLimit fiftyoneDegreesYamlFileIterateWithLimit /**< Synonym for fiftyoneDegreesYamlFileIterateWithLimit */ +#define IndexPropertyProfileCreate fiftyoneDegreesIndexPropertyProfileCreate /**< Synonym for fiftyoneDegreesIndexPropertyProfileCreate */ +#define IndexPropertyProfileFree fiftyoneDegreesIndexPropertyProfileFree /**< Synonym for fiftyoneDegreesIndexPropertyProfileFree */ +#define IndexPropertyProfileLookup fiftyoneDegreesIndexPropertyProfileLookup /**< Synonym for fiftyoneDegreesIndexPropertyProfileLookup */ /* <-- only one asterisk to avoid inclusion in documentation * Shortened macros. diff --git a/indexes.c b/indexes.c new file mode 100644 index 00000000..618affd2 --- /dev/null +++ b/indexes.c @@ -0,0 +1,220 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#include "indexes.h" +#include "fiftyone.h" + +// State structure when building the index. +typedef struct iterate_method_add_state_t { + IndexPropertyProfile* index; // index in use or null if not available + fiftyoneDegreesCollection* values; // collection of values +} iterateMethodAddState; + +// Callback method used when iterating the available profiles. +typedef void(*iterateMethod)( + void* state, + Profile* profile, + Exception* exception); + +// Loops through the values associated with the profile setting the index at +// the position for the property and profile to the first value index from the +// profile. +static void addProfileValuesMethod( + void* state, + Profile* profile, + Exception* exception) { + iterateMethodAddState* s = (iterateMethodAddState*)state; + Item valueItem; // The current value memory + Value* value; // The current value pointer + DataReset(&valueItem.data); + + uint32_t* first = (uint32_t*)(profile + 1); // First value for the profile + int16_t nextPropertyIndex = 0; // The next property index to find a value + uint32_t currentIndex = s->index->propertyCount * profile->profileId; + + // For each of the values associated with the profile check to see if it + // relates to a new property index. If it does then record the first value + // index and advance the current index to the next pointer. + for (uint32_t i = 0; i < profile->valueCount && EXCEPTION_OKAY; i++) { + value = s->values->get(s->values, *(first + i), &valueItem, exception); + if (value != NULL && EXCEPTION_OKAY) { + + // If the values skip property indexes then set the value to zero + // and move to the next one. + while (nextPropertyIndex < value->propertyIndex) { + s->index->valueIndexes[currentIndex] = 0; + currentIndex++; + nextPropertyIndex++; + } + + // If this is the first value for the property index then record + // the value. + if (value->propertyIndex == nextPropertyIndex) { + s->index->valueIndexes[currentIndex] = *(first + i); + currentIndex++; + nextPropertyIndex++; + s->index->filled++; + } + COLLECTION_RELEASE(s->values, &valueItem); + } + } + + // Set any remaining values to zero. + while (nextPropertyIndex < (int16_t)s->index->propertyCount) { + s->index->valueIndexes[currentIndex] = 0; + currentIndex++; + nextPropertyIndex++; + } +} + +static void iterateProfiles( + fiftyoneDegreesCollection* profiles, + fiftyoneDegreesCollection* profileOffsets, + iterateMethodAddState* state, + iterateMethod callback, + Exception *exception) { + Profile* profile; // The current profile pointer + Item profileItem; // The current profile memory + ProfileOffset* profileOffset; // The current profile offset pointer + Item profileOffsetItem; // The current profile offset memory + DataReset(&profileItem.data); + DataReset(&profileOffsetItem.data); + for (uint32_t i = 0; + i < state->index->profileCount && EXCEPTION_OKAY; + i++) { + profileOffset = profileOffsets->get( + profileOffsets, + i, + &profileOffsetItem, + exception); + if (profileOffset != NULL && EXCEPTION_OKAY) { + profile = profiles->get( + profiles, + profileOffset->offset, + &profileItem, + exception); + if (profile != NULL && EXCEPTION_OKAY) { + callback(state, profile, exception); + COLLECTION_RELEASE(profiles, &profileItem); + } + COLLECTION_RELEASE(profileOffsets, &profileOffsetItem); + } + } +} + +// Gets the last profile in the collection and returns the profile id. As the +// profileOffsets collection is ordered in ascending profile id this is always +// the maximum profile id. +static uint32_t getMaxProfileId( + fiftyoneDegreesCollection* profileOffsets, + uint32_t profileCount, + Exception* exception) { + uint32_t maxProfileId = 0; + ProfileOffset* profileOffset; // The profile offset pointer + Item profileOffsetItem; // The profile offset memory + DataReset(&profileOffsetItem.data); + profileOffset = profileOffsets->get( + profileOffsets, + profileCount - 1, + &profileOffsetItem, + exception); + if (profileOffset != NULL && EXCEPTION_OKAY) { + maxProfileId = profileOffset->profileId; + COLLECTION_RELEASE(profileOffsets, &profileOffsetItem); + } + return maxProfileId; +} + +fiftyoneDegreesIndexPropertyProfile* +fiftyoneDegreesIndexPropertyProfileCreate( + fiftyoneDegreesCollection* profiles, + fiftyoneDegreesCollection* profileOffsets, + fiftyoneDegreesCollection* properties, + fiftyoneDegreesCollection* values, + fiftyoneDegreesException* exception) { + + // Get the count of available profiles. + uint32_t profileCount = CollectionGetCount(profileOffsets); + + uint32_t maxProfileId = getMaxProfileId( + profileOffsets, + profileCount, + exception); + if (!EXCEPTION_OKAY) { + return NULL; + } + + // Allocate memory for the number of properties multiplied by the maximum + // profile id. + IndexPropertyProfile* index = (IndexPropertyProfile*)Malloc( + sizeof(IndexPropertyProfile)); + if (index == NULL) { + EXCEPTION_SET(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + return NULL; + } + index->filled = 0; + index->profileCount = profileCount; + index->size = (maxProfileId + 1) * properties->count; + index->propertyCount = properties->count; + index->valueIndexes =(uint32_t*)Malloc(sizeof(uint32_t) * index->size); + if (index->valueIndexes == NULL) { + EXCEPTION_SET(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + Free(index); + return NULL; + } + + // For each of the profiles in the collection call add the property value + // indexes to the index array. + iterateMethodAddState state = { index, values }; + iterateProfiles( + profiles, + profileOffsets, + &state, + addProfileValuesMethod, + exception); + + // Return the index or free the memory if there was an exception. + if (EXCEPTION_OKAY) { + return index; + } + else { + Free(index->valueIndexes); + Free(index); + return NULL; + } +} + +void fiftyoneDegreesIndexPropertyProfileFree( + fiftyoneDegreesIndexPropertyProfile* index) { + Free(index->valueIndexes); + Free(index); +} + +uint32_t fiftyoneDegreesIndexPropertyProfileLookup( + fiftyoneDegreesIndexPropertyProfile* index, + uint32_t profileId, + uint32_t propertyIndex) { + uint32_t valueIndex = + (profileId * index->propertyCount) + propertyIndex; + assert(valueIndex < index->size); + return index->valueIndexes[valueIndex]; +} \ No newline at end of file diff --git a/indexes.h b/indexes.h new file mode 100644 index 00000000..7fe99db4 --- /dev/null +++ b/indexes.h @@ -0,0 +1,113 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#ifndef FIFTYONE_DEGREES_INDEXES_H_INCLUDED +#define FIFTYONE_DEGREES_INDEXES_H_INCLUDED + + /** + * @ingroup FiftyOneDegreesCommon + * @defgroup FiftyOneDegreesIndexes Indexes + * + * A look up list for profile and property index to the first value index. + * + * ## Introduction + * + * @{ + */ + +#include +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 5105) +#include +#pragma warning (default: 5105) +#pragma warning (pop) +#endif +#include "data.h" +#include "exceptions.h" +#include "collection.h" +#include "property.h" +#include "common.h" + +/** + * Maps the profile index and the property index to the first value index of + * the profile for the property. Is an array of uint32_t with entries equal to + * the number of properties multiplied by the number of profiles. + */ +typedef struct fiftyoneDegrees_index_property_profile{ + uint32_t* valueIndexes; // array of value indexes + uint32_t propertyCount; // number of properties + uint32_t profileCount; // number of profiles in profileOffsets / profiles + uint32_t size; // number elements in the valueIndexes array + uint32_t filled; // number of elements with values +} fiftyoneDegreesIndexPropertyProfile; + +/** + * Create an index for the profiles, properties, and values provided such that + * given the index to a property and profile the index of the first value can + * be returned by calling fiftyoneDegreesIndexPropertyProfileLookup. + * @param profiles collection of variable sized profiles to be indexed + * @param profileOffsets collection of fixed offsets to profiles to be indexed + * @param properties collection to be indexed + * @param values collection to be indexed + * @param exception pointer to an exception data structure to be used if an + * exception occurs. See exceptions.h + * @return pointer to the index memory structure + */ +EXTERNAL fiftyoneDegreesIndexPropertyProfile* +fiftyoneDegreesIndexPropertyProfileCreate( + fiftyoneDegreesCollection* profiles, + fiftyoneDegreesCollection* profileOffsets, + fiftyoneDegreesCollection* properties, + fiftyoneDegreesCollection* values, + fiftyoneDegreesException* exception); + +/** + * Frees an index previously created by + * fiftyoneDegreesIndexPropertyProfileCreate. + * @param index to be freed + */ +EXTERNAL void fiftyoneDegreesIndexPropertyProfileFree( + fiftyoneDegreesIndexPropertyProfile* index); + +/** + * For a given profile id and property index returns the first value index, or + * null if a first index can not be determined from the index. The indexes + * relate to the collections for profiles, properties, and values provided to + * the fiftyoneDegreesIndexPropertyProfileCreate method when the index was + * created. + * @param index to use for the lookup + * @param profileId the values relate to + * @param propertyIndex in the collection of properties + * @return the index in the list of values for the profile for the first value + * associated with the property + */ +EXTERNAL uint32_t fiftyoneDegreesIndexPropertyProfileLookup( + fiftyoneDegreesIndexPropertyProfile* index, + uint32_t profileId, + uint32_t propertyIndex); + +/** + * @} + */ + +#endif \ No newline at end of file diff --git a/profile.c b/profile.c index 905fcdc4..654642e9 100644 --- a/profile.c +++ b/profile.c @@ -107,7 +107,6 @@ static uint32_t* getFirstValueForProfileAndProperty( */ static uint32_t iterateValues( Collection *values, - Profile *profile, Property *property, void *state, ProfileIterateMethod callback, @@ -118,13 +117,6 @@ static uint32_t iterateValues( uint32_t count = 0; bool cont = true; - // Move back through the values until the first one for the property is - // found. - while ((void*)valueIndex > (void*)(profile + 1) && - *(valueIndex - 1) >= property->firstValueIndex) { - valueIndex--; - } - // Loop through until the last value for the property has been returned // or the callback doesn't need to continue. while (cont == true && @@ -135,8 +127,8 @@ static uint32_t iterateValues( // Reset the items as they should never share the same memory. DataReset(&valueItem.data); - // Get the value from the value index and call the callback. Do not free - // the item as the calling function is responsible for this. + // Get the value from the value index and call the callback. Do not + // free the item as the calling function is responsible for this. if (values->get(values, *valueIndex, &valueItem, exception) != NULL && EXCEPTION_OKAY) { cont = callback(state, &valueItem); @@ -251,24 +243,46 @@ uint32_t fiftyoneDegreesProfileIterateValuesForProperty( void *state, fiftyoneDegreesProfileIterateMethod callback, fiftyoneDegreesException *exception) { - uint32_t *valueIndex = getFirstValueForProfileAndProperty( + uint32_t *firstValueIndex = getFirstValueForProfileAndProperty( profile, property); uint32_t count = 0; - if (valueIndex != NULL) { + if (firstValueIndex != NULL) { count = iterateValues( values, - profile, property, state, callback, - valueIndex, + firstValueIndex, ((uint32_t*)(profile + 1)) + profile->valueCount, exception); } return count; } +uint32_t fiftyoneDegreesProfileIterateValuesForPropertyWithIndex( + fiftyoneDegreesCollection* values, + fiftyoneDegreesIndexPropertyProfile* index, + uint32_t propertyIndex, + fiftyoneDegreesProfile* profile, + fiftyoneDegreesProperty* property, + void* state, + fiftyoneDegreesProfileIterateMethod callback, + fiftyoneDegreesException* exception) { + uint32_t firstValueIndex = IndexPropertyProfileLookup( + index, + profile->profileId, + propertyIndex); + return iterateValues( + values, + property, + state, + callback, + &firstValueIndex, + ((uint32_t*)(profile + 1)) + profile->valueCount, + exception); +} + uint32_t fiftyoneDegreesProfileIterateProfilesForPropertyAndValue( fiftyoneDegreesCollection *strings, fiftyoneDegreesCollection *properties, diff --git a/profile.h b/profile.h index 727957f4..df2927a3 100644 --- a/profile.h +++ b/profile.h @@ -79,6 +79,7 @@ #include "property.h" #include "value.h" #include "common.h" +#include "indexes.h" /** * Encapsulates a profile stored within a data set. A profile pertains to a @@ -196,6 +197,30 @@ EXTERNAL uint32_t fiftyoneDegreesProfileIterateValuesForProperty( fiftyoneDegreesProfileIterateMethod callback, fiftyoneDegreesException *exception); +/** + * Iterate over all values contained in the profile which relate to the + * specified property and profile, calling the callback method for each. + * @param values collection containing all values + * @param index array of property and profile first value indexes + * @param profileIndex the index of the profile + * @param propertyIndex the index of the property + * @param property which the values must relate to + * @param state pointer containing data needed for the callback method + * @param callback method to be called for each value + * @param exception pointer to an exception data structure to be used if an + * exception occurs. See exceptions.h + * @return the number of matching values which have been iterated + */ +EXTERNAL uint32_t fiftyoneDegreesProfileIterateValuesForPropertyWithIndex( + fiftyoneDegreesCollection* values, + fiftyoneDegreesIndexPropertyProfile* index, + uint32_t propertyIndex, + fiftyoneDegreesProfile* profile, + fiftyoneDegreesProperty* property, + void* state, + fiftyoneDegreesProfileIterateMethod callback, + fiftyoneDegreesException* exception); + /** * Iterate all profiles which contain the specified value, calling the callback * method for each. From 97233b1ad3f0461e06de97371cff6d6114e39e19 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sat, 20 Apr 2024 14:33:41 +0100 Subject: [PATCH 04/73] BUG/OPTIM: List properties were not previously returned properly. Uses the available properties collection to reduce the memory used for the index when fewer than the total number of properties are initialised. Offsets the profile indexes by the minimum profile id value to further reduce the memory used for the index. Provides two default configurations with and without the index flag set. --- config.h | 17 +++++- indexes.c | 179 ++++++++++++++++++++++++++++++++---------------------- indexes.h | 17 +++--- profile.c | 26 ++++---- profile.h | 4 +- 5 files changed, 150 insertions(+), 93 deletions(-) diff --git a/config.h b/config.h index 524b0f3d..cb5b518e 100644 --- a/config.h +++ b/config.h @@ -92,9 +92,9 @@ FIFTYONE_DEGREES_CONFIG_USE_TEMP_FILE_DEFAULT FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY_DEFAULT /** - * Default value for the #fiftyoneDegreesConfigBase structure. + * Default value for the #fiftyoneDegreesConfigBase structure with index. */ -#define FIFTYONE_DEGREES_CONFIG_DEFAULT \ +#define FIFTYONE_DEGREES_CONFIG_DEFAULT_WITH_INDEX \ FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY, /* allInMemory */ \ true, /* usesUpperPrefixedHeaders */ \ false, /* freeData */ \ @@ -104,6 +104,19 @@ FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY_DEFAULT 0, /* tempDirCount */ \ true /* propertyValueIndex */ + /** + * Default value for the #fiftyoneDegreesConfigBase structure without index. + */ +#define FIFTYONE_DEGREES_CONFIG_DEFAULT_NO_INDEX \ + FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY, /* allInMemory */ \ + true, /* usesUpperPrefixedHeaders */ \ + false, /* freeData */ \ + FIFTYONE_DEGREES_CONFIG_USE_TEMP_FILE, /* useTempFile */ \ + false, /* reuseTempFile */ \ + NULL, /* tempDirs */ \ + 0, /* tempDirCount */ \ + false /* propertyValueIndex */ + /** * @} */ diff --git a/indexes.c b/indexes.c index 618affd2..4c0732ed 100644 --- a/indexes.c +++ b/indexes.c @@ -23,74 +23,71 @@ #include "indexes.h" #include "fiftyone.h" -// State structure when building the index. -typedef struct iterate_method_add_state_t { - IndexPropertyProfile* index; // index in use or null if not available - fiftyoneDegreesCollection* values; // collection of values -} iterateMethodAddState; - -// Callback method used when iterating the available profiles. -typedef void(*iterateMethod)( - void* state, - Profile* profile, - Exception* exception); +typedef struct map_t { + uint32_t availableProperty; // available property index + int16_t propertyIndex; // index in the properties collection +} map; + +// Gets the index of the profile id in the property profile index. +static uint32_t getProfileIndex( + IndexPropertyProfile* index, + uint32_t profileId) { + return profileId - index->minProfileId; +} // Loops through the values associated with the profile setting the index at // the position for the property and profile to the first value index from the // profile. static void addProfileValuesMethod( - void* state, + IndexPropertyProfile* index, // index in use or null if not available + map* propertyIndexes, // property indexes in ascending order + fiftyoneDegreesCollection* values, // collection of values Profile* profile, Exception* exception) { - iterateMethodAddState* s = (iterateMethodAddState*)state; + uint32_t valueIndex; Item valueItem; // The current value memory Value* value; // The current value pointer DataReset(&valueItem.data); uint32_t* first = (uint32_t*)(profile + 1); // First value for the profile - int16_t nextPropertyIndex = 0; // The next property index to find a value - uint32_t currentIndex = s->index->propertyCount * profile->profileId; + uint32_t base = getProfileIndex(index, profile->profileId) * + index->availablePropertyCount; // For each of the values associated with the profile check to see if it // relates to a new property index. If it does then record the first value // index and advance the current index to the next pointer. - for (uint32_t i = 0; i < profile->valueCount && EXCEPTION_OKAY; i++) { - value = s->values->get(s->values, *(first + i), &valueItem, exception); + for (uint32_t i = 0, p = 0; + i < profile->valueCount && EXCEPTION_OKAY; + i++) { + value = values->get(values, *(first + i), &valueItem, exception); if (value != NULL && EXCEPTION_OKAY) { - - // If the values skip property indexes then set the value to zero - // and move to the next one. - while (nextPropertyIndex < value->propertyIndex) { - s->index->valueIndexes[currentIndex] = 0; - currentIndex++; - nextPropertyIndex++; + + // If the value doesn't relate to the next property index then + // move to the next property index. + while (propertyIndexes[p].propertyIndex < value->propertyIndex) { + p++; } - // If this is the first value for the property index then record - // the value. - if (value->propertyIndex == nextPropertyIndex) { - s->index->valueIndexes[currentIndex] = *(first + i); - currentIndex++; - nextPropertyIndex++; - s->index->filled++; + // If the value relates to the next property index being sought + // then record the first value in the profile associated with the + // property. + if (value->propertyIndex == propertyIndexes[p].propertyIndex) { + valueIndex = base + propertyIndexes[p].availableProperty; + index->valueIndexes[valueIndex] = i; + p++; + index->filled++; } - COLLECTION_RELEASE(s->values, &valueItem); + COLLECTION_RELEASE(values, &valueItem); } } - - // Set any remaining values to zero. - while (nextPropertyIndex < (int16_t)s->index->propertyCount) { - s->index->valueIndexes[currentIndex] = 0; - currentIndex++; - nextPropertyIndex++; - } } static void iterateProfiles( fiftyoneDegreesCollection* profiles, fiftyoneDegreesCollection* profileOffsets, - iterateMethodAddState* state, - iterateMethod callback, + IndexPropertyProfile* index, // index in use or null if not available + map* propertyIndexes, // property indexes in ascending order + fiftyoneDegreesCollection* values, // collection of values Exception *exception) { Profile* profile; // The current profile pointer Item profileItem; // The current profile memory @@ -99,7 +96,7 @@ static void iterateProfiles( DataReset(&profileItem.data); DataReset(&profileOffsetItem.data); for (uint32_t i = 0; - i < state->index->profileCount && EXCEPTION_OKAY; + i < index->profileCount && EXCEPTION_OKAY; i++) { profileOffset = profileOffsets->get( profileOffsets, @@ -113,7 +110,12 @@ static void iterateProfiles( &profileItem, exception); if (profile != NULL && EXCEPTION_OKAY) { - callback(state, profile, exception); + addProfileValuesMethod( + index, + propertyIndexes, + values, + profile, + exception); COLLECTION_RELEASE(profiles, &profileItem); } COLLECTION_RELEASE(profileOffsets, &profileOffsetItem); @@ -121,50 +123,64 @@ static void iterateProfiles( } } -// Gets the last profile in the collection and returns the profile id. As the -// profileOffsets collection is ordered in ascending profile id this is always -// the maximum profile id. -static uint32_t getMaxProfileId( +// As the profileOffsets collection is ordered in ascending profile id the +// first and last entries are the min and max available profile ids. +static uint32_t getProfileId( fiftyoneDegreesCollection* profileOffsets, - uint32_t profileCount, + uint32_t index, Exception* exception) { - uint32_t maxProfileId = 0; + uint32_t profileId = 0; ProfileOffset* profileOffset; // The profile offset pointer Item profileOffsetItem; // The profile offset memory DataReset(&profileOffsetItem.data); profileOffset = profileOffsets->get( profileOffsets, - profileCount - 1, + index, &profileOffsetItem, exception); if (profileOffset != NULL && EXCEPTION_OKAY) { - maxProfileId = profileOffset->profileId; + profileId = profileOffset->profileId; COLLECTION_RELEASE(profileOffsets, &profileOffsetItem); } - return maxProfileId; + return profileId; +} + +static int comparePropertyIndexes(const void* a, const void* b) { + return ((map*)a)->propertyIndex - ((map*)b)->propertyIndex; +} + +// Build an ascending ordered array of the property indexes. +static map* createPropertyIndexes( + PropertiesAvailable* available, + Exception* exception) { + map* index = (map*)Malloc(sizeof(map) * available->count); + if (index == NULL) { + EXCEPTION_SET(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + return NULL; + } + for (uint32_t i = 0; i < available->count; i++) { + index[i].availableProperty = i; + index[i].propertyIndex = (int16_t)available->items[i].propertyIndex; + } + qsort(index, available->count, sizeof(map*), comparePropertyIndexes); + return index; } fiftyoneDegreesIndexPropertyProfile* fiftyoneDegreesIndexPropertyProfileCreate( fiftyoneDegreesCollection* profiles, fiftyoneDegreesCollection* profileOffsets, - fiftyoneDegreesCollection* properties, + fiftyoneDegreesPropertiesAvailable* available, fiftyoneDegreesCollection* values, fiftyoneDegreesException* exception) { - // Get the count of available profiles. - uint32_t profileCount = CollectionGetCount(profileOffsets); - - uint32_t maxProfileId = getMaxProfileId( - profileOffsets, - profileCount, - exception); - if (!EXCEPTION_OKAY) { + // Create the ordered list of property indexes. + map* propertyIndexes = createPropertyIndexes(available, exception); + if (propertyIndexes == NULL) { return NULL; } - // Allocate memory for the number of properties multiplied by the maximum - // profile id. + // Allocate memory for the index and set the fields. IndexPropertyProfile* index = (IndexPropertyProfile*)Malloc( sizeof(IndexPropertyProfile)); if (index == NULL) { @@ -172,25 +188,45 @@ fiftyoneDegreesIndexPropertyProfileCreate( return NULL; } index->filled = 0; - index->profileCount = profileCount; - index->size = (maxProfileId + 1) * properties->count; - index->propertyCount = properties->count; + index->profileCount = CollectionGetCount(profileOffsets); + index->minProfileId = getProfileId(profileOffsets, 0, exception); + if (!EXCEPTION_OKAY) { + Free(index); + Free(propertyIndexes); + return NULL; + } + index->maxProfileId = getProfileId( + profileOffsets, + index->profileCount - 1, + exception); + if (!EXCEPTION_OKAY) { + Free(index); + Free(propertyIndexes); + return NULL; + } + index->availablePropertyCount = available->count; + index->size = (index->maxProfileId - index->minProfileId + 1) * + available->count; + + // Allocate memory for the values index and set the fields. index->valueIndexes =(uint32_t*)Malloc(sizeof(uint32_t) * index->size); if (index->valueIndexes == NULL) { EXCEPTION_SET(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); Free(index); + Free(propertyIndexes); return NULL; } // For each of the profiles in the collection call add the property value // indexes to the index array. - iterateMethodAddState state = { index, values }; iterateProfiles( profiles, profileOffsets, - &state, - addProfileValuesMethod, + index, + propertyIndexes, + values, exception); + Free(propertyIndexes); // Return the index or free the memory if there was an exception. if (EXCEPTION_OKAY) { @@ -212,9 +248,10 @@ void fiftyoneDegreesIndexPropertyProfileFree( uint32_t fiftyoneDegreesIndexPropertyProfileLookup( fiftyoneDegreesIndexPropertyProfile* index, uint32_t profileId, - uint32_t propertyIndex) { + uint32_t availablePropertyIndex) { uint32_t valueIndex = - (profileId * index->propertyCount) + propertyIndex; + (getProfileIndex(index, profileId) * index->availablePropertyCount) + + availablePropertyIndex; assert(valueIndex < index->size); return index->valueIndexes[valueIndex]; } \ No newline at end of file diff --git a/indexes.h b/indexes.h index 7fe99db4..84890260 100644 --- a/indexes.h +++ b/indexes.h @@ -46,6 +46,7 @@ #include "exceptions.h" #include "collection.h" #include "property.h" +#include "properties.h" #include "common.h" /** @@ -55,19 +56,21 @@ */ typedef struct fiftyoneDegrees_index_property_profile{ uint32_t* valueIndexes; // array of value indexes - uint32_t propertyCount; // number of properties - uint32_t profileCount; // number of profiles in profileOffsets / profiles + uint32_t availablePropertyCount; // number of available properties + uint32_t minProfileId; // minimum profile id + uint32_t maxProfileId; // maximum profile id + uint32_t profileCount; // total number of profiles uint32_t size; // number elements in the valueIndexes array uint32_t filled; // number of elements with values } fiftyoneDegreesIndexPropertyProfile; /** - * Create an index for the profiles, properties, and values provided such that - * given the index to a property and profile the index of the first value can - * be returned by calling fiftyoneDegreesIndexPropertyProfileLookup. + * Create an index for the profiles, available properties, and values provided + * such that given the index to a property and profile the index of the first + * value can be returned by calling fiftyoneDegreesIndexPropertyProfileLookup. * @param profiles collection of variable sized profiles to be indexed * @param profileOffsets collection of fixed offsets to profiles to be indexed - * @param properties collection to be indexed + * @param available properties provided by the caller * @param values collection to be indexed * @param exception pointer to an exception data structure to be used if an * exception occurs. See exceptions.h @@ -77,7 +80,7 @@ EXTERNAL fiftyoneDegreesIndexPropertyProfile* fiftyoneDegreesIndexPropertyProfileCreate( fiftyoneDegreesCollection* profiles, fiftyoneDegreesCollection* profileOffsets, - fiftyoneDegreesCollection* properties, + fiftyoneDegreesPropertiesAvailable* available, fiftyoneDegreesCollection* values, fiftyoneDegreesException* exception); diff --git a/profile.c b/profile.c index 654642e9..049c16b1 100644 --- a/profile.c +++ b/profile.c @@ -263,24 +263,28 @@ uint32_t fiftyoneDegreesProfileIterateValuesForProperty( uint32_t fiftyoneDegreesProfileIterateValuesForPropertyWithIndex( fiftyoneDegreesCollection* values, fiftyoneDegreesIndexPropertyProfile* index, - uint32_t propertyIndex, + uint32_t availablePropertyIndex, fiftyoneDegreesProfile* profile, fiftyoneDegreesProperty* property, void* state, fiftyoneDegreesProfileIterateMethod callback, fiftyoneDegreesException* exception) { - uint32_t firstValueIndex = IndexPropertyProfileLookup( + uint32_t i = IndexPropertyProfileLookup( index, profile->profileId, - propertyIndex); - return iterateValues( - values, - property, - state, - callback, - &firstValueIndex, - ((uint32_t*)(profile + 1)) + profile->valueCount, - exception); + availablePropertyIndex); + if (i < profile->valueCount) { + uint32_t* firstValueIndex = (uint32_t*)(profile + 1) + i; + return iterateValues( + values, + property, + state, + callback, + firstValueIndex, + ((uint32_t*)(profile + 1)) + profile->valueCount, + exception); + } + return 0; } uint32_t fiftyoneDegreesProfileIterateProfilesForPropertyAndValue( diff --git a/profile.h b/profile.h index df2927a3..4462ca24 100644 --- a/profile.h +++ b/profile.h @@ -203,7 +203,7 @@ EXTERNAL uint32_t fiftyoneDegreesProfileIterateValuesForProperty( * @param values collection containing all values * @param index array of property and profile first value indexes * @param profileIndex the index of the profile - * @param propertyIndex the index of the property + * @param availablePropertyIndex the index of the available property * @param property which the values must relate to * @param state pointer containing data needed for the callback method * @param callback method to be called for each value @@ -214,7 +214,7 @@ EXTERNAL uint32_t fiftyoneDegreesProfileIterateValuesForProperty( EXTERNAL uint32_t fiftyoneDegreesProfileIterateValuesForPropertyWithIndex( fiftyoneDegreesCollection* values, fiftyoneDegreesIndexPropertyProfile* index, - uint32_t propertyIndex, + uint32_t availablePropertyIndex, fiftyoneDegreesProfile* profile, fiftyoneDegreesProperty* property, void* state, From b1b12c029171afedd24a922b34686d2384c0854b Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sat, 20 Apr 2024 14:44:16 +0100 Subject: [PATCH 05/73] BUG: Check that the available properties have not been exhausted when creating the index. --- indexes.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/indexes.c b/indexes.c index 4c0732ed..69e7d69d 100644 --- a/indexes.c +++ b/indexes.c @@ -57,21 +57,25 @@ static void addProfileValuesMethod( // relates to a new property index. If it does then record the first value // index and advance the current index to the next pointer. for (uint32_t i = 0, p = 0; - i < profile->valueCount && EXCEPTION_OKAY; + i < profile->valueCount && + p < index->availablePropertyCount && + EXCEPTION_OKAY; i++) { value = values->get(values, *(first + i), &valueItem, exception); if (value != NULL && EXCEPTION_OKAY) { // If the value doesn't relate to the next property index then // move to the next property index. - while (propertyIndexes[p].propertyIndex < value->propertyIndex) { + while (propertyIndexes[p].propertyIndex < value->propertyIndex && + p < index->availablePropertyCount) { p++; } // If the value relates to the next property index being sought // then record the first value in the profile associated with the // property. - if (value->propertyIndex == propertyIndexes[p].propertyIndex) { + if (p < index->availablePropertyCount && + value->propertyIndex == propertyIndexes[p].propertyIndex) { valueIndex = base + propertyIndexes[p].availableProperty; index->valueIndexes[valueIndex] = i; p++; From 07dc0e703607ba997546424569c3c8a6e21d7425 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 23 Apr 2024 16:18:28 +0100 Subject: [PATCH 06/73] PERF/MINOR: Avoid setting up the memory if there are no values to process. --- overrides.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overrides.c b/overrides.c index 213ab8fe..00ee7cbd 100644 --- a/overrides.c +++ b/overrides.c @@ -321,7 +321,7 @@ uint32_t fiftyoneDegreesOverrideValuesAdd( uint32_t i, count = 0; OverrideValue *current; Item valueItem; - if (values != NULL) { + if (values != NULL && values->count > 0) { // Use a dummy collection so that the call to release will work // if the client respects the collection pattern. From 5578ab1340cf4aa886fd1c3a3319692ba91bc9a3 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 23 Apr 2024 16:18:53 +0100 Subject: [PATCH 07/73] DOC: Align the comment to the macro. --- fiftyone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fiftyone.h b/fiftyone.h index bb404700..2400e7f8 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -299,7 +299,7 @@ MAP_TYPE(IndexPropertyProfile) #define FileWrite fiftyoneDegreesFileWrite /**< Synonym for #fiftyoneDegreesFileWrite function. */ #define FilePoolInit fiftyoneDegreesFilePoolInit /**< Synonym for #fiftyoneDegreesFilePoolInit function. */ #define FileCreateDirectory fiftyoneDegreesFileCreateDirectory /**< Synonym for #fiftyoneDegreesFileCreateDirectory function. */ -#define TextFileIterateWithLimit fiftyoneDegreesTextFileIterateWithLimit /**< Synonym for #fiftyoneDegreesTextFileIterateIterate function. */ +#define TextFileIterateWithLimit fiftyoneDegreesTextFileIterateWithLimit /**< Synonym for #fiftyoneDegreesTextFileIterateWithLimit function. */ #define TextFileIterate fiftyoneDegreesTextFileIterate /**< Synonym for #fiftyoneDegreesTextFileIterate function. */ #define ResourceManagerInit fiftyoneDegreesResourceManagerInit /**< Synonym for #fiftyoneDegreesResourceManagerInit function. */ #define PropertiesGetPropertyIndexFromRequiredIndex fiftyoneDegreesPropertiesGetPropertyIndexFromRequiredIndex /**< Synonym for #fiftyoneDegreesPropertiesGetPropertyIndexFromRequiredIndex function. */ From 431f1b7549e242be4f98fcb8d463895a68d8e00a Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 8 May 2024 13:07:50 +0100 Subject: [PATCH 08/73] PERF: Optimised headers and evidence for improved performance. --- .../FiftyOne.Common.C.vcxproj | 2 - component.c | 39 +- component.h | 17 +- dataset.c | 6 +- dataset.h | 5 +- evidence.c | 327 +++++++++-- evidence.h | 57 +- fiftyone.h | 9 +- headers.c | 518 ++++++++++++------ headers.h | 77 ++- pseudoheader.c | 251 --------- pseudoheader.h | 78 --- 12 files changed, 780 insertions(+), 606 deletions(-) delete mode 100644 pseudoheader.c delete mode 100644 pseudoheader.h diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj index a1ca0b31..3cc5186c 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj @@ -29,7 +29,6 @@ - @@ -63,7 +62,6 @@ - diff --git a/component.c b/component.c index e5a2a3f3..3626aae9 100644 --- a/component.c +++ b/component.c @@ -26,7 +26,8 @@ static uint32_t getFinalComponentSize(void *initial) { Component *component = (Component*)initial; - int32_t trailing = (component->keyValuesCount - 1) * sizeof(fiftyoneDegreesComponentKeyValuePair); + int32_t trailing = (component->keyValuesCount - 1) * + sizeof(fiftyoneDegreesComponentKeyValuePair); return (uint32_t)(sizeof(Component) + trailing); } @@ -62,7 +63,8 @@ fiftyoneDegreesString* fiftyoneDegreesComponentGetName( exception); } -const fiftyoneDegreesComponentKeyValuePair* fiftyoneDegreesComponentGetKeyValuePair( +const fiftyoneDegreesComponentKeyValuePair* +fiftyoneDegreesComponentGetKeyValuePair( fiftyoneDegreesComponent *component, uint16_t index, fiftyoneDegreesException *exception) { @@ -122,3 +124,36 @@ void fiftyoneDegreesComponentInitList( } } } + +fiftyoneDegreesHeaderPtrs* fiftyoneDegreesComponentGetHeaders( + fiftyoneDegreesComponent* component, + fiftyoneDegreesHeaders* headers, + fiftyoneDegreesException* exception) { + const ComponentKeyValuePair* keyValue; + HeaderPtrs* componentHeaders; + + // Create an array of header pointers. + FIFTYONE_DEGREES_ARRAY_CREATE( + fiftyoneDegreesHeaderPtr, + componentHeaders, + component->keyValuesCount); + if (componentHeaders == NULL) { + EXCEPTION_SET(INSUFFICIENT_MEMORY); + return NULL; + } + + // Add the header from the headers array that relate to each header if the + // component considers. + for (uint32_t i = 0; i < component->keyValuesCount; i++) { + keyValue = ComponentGetKeyValuePair( + component, + (uint16_t)i, + exception); + componentHeaders->items[i] = + HeadersGetHeaderFromUniqueId(headers, keyValue->key); + componentHeaders->count++; + } + assert(componentHeaders->count == componentHeaders->capacity); + + return componentHeaders; +} \ No newline at end of file diff --git a/component.h b/component.h index 143b37a3..d555e1d8 100644 --- a/component.h +++ b/component.h @@ -60,6 +60,7 @@ #include "list.h" #include "string.h" #include "common.h" +#include "headers.h" /** * Key value pair contained in each component. This can point to anything. For @@ -117,7 +118,8 @@ EXTERNAL fiftyoneDegreesString* fiftyoneDegreesComponentGetName( * exception occurs. See exceptions.h * @return pointer to a key value pair */ -const fiftyoneDegreesComponentKeyValuePair* fiftyoneDegreesComponentGetKeyValuePair( +const fiftyoneDegreesComponentKeyValuePair* +fiftyoneDegreesComponentGetKeyValuePair( fiftyoneDegreesComponent *component, uint16_t index, fiftyoneDegreesException *exception); @@ -170,6 +172,19 @@ EXTERNAL uint32_t fiftyoneDegreesComponentGetDefaultProfileId( fiftyoneDegreesComponent *component, fiftyoneDegreesException *exception); +/** + * Where the component's key value pairs relate to headers creates an array of + * pointers to the relevant headers in the same order as the key value pairs. + * @param component to get the headers for + * @param headers array to find the corresponding header in + * @param exception pointer to an exception data structure to be used if an + * exception occurs. See exceptions.h + */ +EXTERNAL fiftyoneDegreesHeaderPtrs* fiftyoneDegreesComponentGetHeaders( + fiftyoneDegreesComponent* component, + fiftyoneDegreesHeaders* headers, + fiftyoneDegreesException* exception); + /** * @} */ diff --git a/dataset.c b/dataset.c index 5959d07b..7bf87650 100644 --- a/dataset.c +++ b/dataset.c @@ -171,13 +171,15 @@ fiftyoneDegreesStatusCode fiftyoneDegreesDataSetInitProperties( fiftyoneDegreesStatusCode fiftyoneDegreesDataSetInitHeaders( fiftyoneDegreesDataSetBase *dataSet, void *state, - fiftyoneDegreesHeadersGetMethod getHeaderMethod) { + fiftyoneDegreesHeadersGetMethod getHeaderMethod, + fiftyoneDegreesException* exception) { // Initialise the unique HTTP headers. dataSet->uniqueHeaders = HeadersCreate( CONFIG(dataSet)->usesUpperPrefixedHeaders, state, - getHeaderMethod); + getHeaderMethod, + exception); // Check both the headers and properties were initialised. if (dataSet->uniqueHeaders == NULL) { diff --git a/dataset.h b/dataset.h index 4bd99751..570467d0 100644 --- a/dataset.h +++ b/dataset.h @@ -212,6 +212,8 @@ fiftyoneDegreesStatusCode fiftyoneDegreesDataSetInitProperties( * @param state pointer to data which is needed by getPropertymethod * @param getHeaderMethod method used to retrieve the unique id and name of a * header at a specified index from the data set + * @param exception pointer to an exception data structure to be used if an + * exception occurs. See exceptions.h. * @return the status associated with the header initialisation. Any value * other than #FIFTYONE_DEGREES_STATUS_SUCCESS means the headers were not * initialised correctly @@ -219,7 +221,8 @@ fiftyoneDegreesStatusCode fiftyoneDegreesDataSetInitProperties( fiftyoneDegreesStatusCode fiftyoneDegreesDataSetInitHeaders( fiftyoneDegreesDataSetBase *dataSet, void *state, - fiftyoneDegreesHeadersGetMethod getHeaderMethod); + fiftyoneDegreesHeadersGetMethod getHeaderMethod, + fiftyoneDegreesException* exception); /** * Initialses the data set from data stored on file. diff --git a/evidence.c b/evidence.c index f6b1f1bc..c3334236 100644 --- a/evidence.c +++ b/evidence.c @@ -47,10 +47,238 @@ static void parsePair(EvidenceKeyValuePair *pair) { case FIFTYONE_DEGREES_EVIDENCE_COOKIE: default: pair->parsedValue = pair->originalValue; + pair->parsedLength = strlen(pair->parsedValue); break; } } +// If a string comparison of the pair field and the header indicates a match +// then set the header to avoid a string comparison in future iterations. +static void setPairHeader(EvidenceKeyValuePair* pair, Header* header) { + if (pair->fieldLength == header->length && + StringCompareLength(pair->field, header->name, header->length) == 0) { + pair->header = header; + } +} + +/* + * Iterate through an evidence collection and perform callback on the evidence + * whose prefix matches the input prefixes. + * + * @param evidence the evidence collection to process + * @param prefixes the accepted evidence prefixes + * @param state the state object to hold the current state of the process + * @param callback the method to call back when a matched evidence is found. + * @return number of evidence processed. + */ +static uint32_t evidenceIterate( + fiftyoneDegreesEvidenceKeyValuePairArray* evidence, + int prefixes, + void* state, + fiftyoneDegreesEvidenceIterateMethod callback) { + uint32_t i = 0, count = 0; + EvidenceKeyValuePair* pair; + bool cont = true; + while (cont && i < evidence->count) { + pair = &evidence->items[i++]; + if ((pair->prefix & prefixes) == pair->prefix) { + if (pair->parsedValue == NULL) { + parsePair(pair); + } + cont = callback(state, pair); + count++; + } + } + return count; +} + +/** + * Finds the evidence pair that matches the header. Returns null if a pair does + * not exist. + */ +static EvidenceKeyValuePair* findHeaderEvidence( + EvidenceKeyValuePairArray* evidence, + int prefixes, + Header* header) { + EvidenceKeyValuePair* pair; + + // For each of the evidence pairs available. + for (uint32_t i = 0; i < evidence->count; i++) { + pair = &evidence->items[i++]; + + // Check that the prefix is one that is being considered. + if ((pair->prefix & prefixes) == pair->prefix) { + + // If the header has been assigned to the pair check to see if this + // one is a match. + if (pair->header == NULL) { + setPairHeader(pair, header); + } + + // If the pair's header and the required header are the same. + if (pair->header == header) { + + // Ensure the parsed value is populated before returning. + if (pair->parsedValue == NULL) { + parsePair(pair); + } + return pair; + } + } + } + return NULL; +} + +// Copies the pair parsed value to the buffer checking that there are +// sufficient bytes remaining in the buffer for the parsed value. Returns NULL +// if there is insufficient bytes remaining in the buffer, or the new pointer +// in buffer for the next character. +static char* addPairValueToBuffer( + char* buffer, + size_t length, + EvidenceKeyValuePair* pair) { + if (length < pair->parsedLength) { + return NULL; + } + else { + return memcpy(buffer, (char*)pair->parsedValue, pair->parsedLength); + } +} + +// For the header finds the corresponding evidence in the array of evidence. If +// found then copies the parsed value into the buffer considering the remaining +// length available. Returns the next character in the buffer to add more +// characters, or NULL if either not found or insufficient buffer length +// remaining. +static char* addHeaderValueToBuffer( + fiftyoneDegreesEvidenceKeyValuePairArray* evidence, + int prefixes, + fiftyoneDegreesHeader* header, + char* buffer, + size_t length) { + char* current = buffer; + + // Get the evidence that corresponds to the header. If it doesn't exist + // then there is no evidence for the header and a call back will not be + // possible. + EvidenceKeyValuePair* pair = findHeaderEvidence( + evidence, + prefixes, + header); + if (pair == NULL) { + return NULL; + } + + // Copy the value of the evidence pair in to the buffer advancing the + // current character in the buffer. + current = addPairValueToBuffer(buffer, length, pair); + + // If the buffer is not sufficiently large to hold the pair value then + // return. + if (current == NULL) { + return NULL; + } + + return current; +} + +// Adds the character value checking sufficient capacity in the buffer is +// available. Returns the next character in the buffer to add more characters, +// or NULL if either not found or insufficient buffer length remaining. +static char* addCharacter(char* buffer, size_t length, char value) { + if (length == 0) { + return NULL; + } + *buffer = value; + return buffer + 1; +} + +// Assembles a pseudo header in the buffer. If this can not be achieved returns +// true to indicate that processing should continue. If a pseudo header can be +// created then returns the result of the callback which might decide not to +// continue processing. +static bool processPseudoHeader( + EvidenceKeyValuePairArray* evidence, + int prefixes, + Header* header, + char* buffer, + size_t length, + void* state, + fiftyoneDegreesEvidenceIterateForHeadersMethod callback) { + char* current = buffer; + size_t remaining = length; + + // For each of the headers that form the pseudo header. + for (uint32_t i = 0; i < header->segmentHeaders->count; i++) { + + // Add the header evidence that forms the segment if available updating + // the current buffer position if available. + current = addHeaderValueToBuffer( + evidence, + prefixes, + header->segmentHeaders->items[i], + current, + remaining); + + // If the pseudo header wasn't found, or insufficient space was + // available to copy it, then return. + if (current == NULL) { + return true; + } + + // Update the remaining capacity of the buffer. + remaining = length - (current - buffer); + + // Add the pseudo header separator. + current = addCharacter(current, remaining, PSEUDO_HEADER_SEP); + + // If there was insufficient space for the separator then return. + if (current == NULL) { + return true; + } + + // Update the remaining capacity of the buffer. + remaining = length - (current - buffer); + } + + // Switch the last pseudo header separator to a null terminating character. + *(current - 1) = '\0'; + + // A full header has been formed so call the callback with the buffer and + // the number of characters populated. + return callback(state, header, (const char*)buffer, length - remaining); +} + +// Finds the header in the evidence, and if available calls the callback. +// Returns true if further processing should continue, otherwise false to stop +// further processing. +static bool processHeader( + EvidenceKeyValuePairArray* evidence, + int prefixes, + Header* header, + void* state, + fiftyoneDegreesEvidenceIterateForHeadersMethod callback) { + + // Get the evidence that corresponds to the header. If it doesn't exist + // then there is no evidence for the header and a call back will not be + // possible. + EvidenceKeyValuePair* pair = findHeaderEvidence( + evidence, + prefixes, + header); + if (pair == NULL) { + return true; + } + + // A full header has been formed so call the callback with the buffer and + // the number of characters populated. + return callback( + state, + header, + (const char*)pair->parsedValue, + pair->parsedLength); +} + fiftyoneDegreesEvidenceKeyValuePairArray* fiftyoneDegreesEvidenceCreate(uint32_t capacity) { EvidenceKeyValuePairArray *evidence; @@ -60,12 +288,11 @@ fiftyoneDegreesEvidenceCreate(uint32_t capacity) { for (i = 0; i < evidence->capacity; i++) { evidence->items[i].field = NULL; evidence->items[i].fieldLength = 0; - evidence->items[i].relative = NULL; + evidence->items[i].header = NULL; evidence->items[i].originalValue = NULL; evidence->items[i].parsedValue = NULL; evidence->items[i].prefix = FIFTYONE_DEGREES_EVIDENCE_IGNORE; } - evidence->pseudoEvidence = NULL; } return evidence; } @@ -88,68 +315,21 @@ fiftyoneDegreesEvidenceKeyValuePair* fiftyoneDegreesEvidenceAddString( pair->fieldLength = strlen(field); pair->originalValue = (void*)originalValue; pair->parsedValue = NULL; + pair->header = NULL; } return pair; } -/* - * Iterate through an evidence collection and perform callback on the evidence - * whose prefix matches the input prefixes. - * - * @param evidence the evidence collection to process - * @param prefixes the accepted evidence prefixes - * @param state the state object to hold the current state of the process - * @param cont indicate whether the iteration should continue. This normally - * indicate if error has occurred. Upon return, this value is also updated so - * that caller know whether to continue processing any other member set of - * the evidence collection. - * @param callback the method to call back when a matched evidence is found. - * @return number of evidence processed. - */ -static uint32_t evidenceIterateSub( - fiftyoneDegreesEvidenceKeyValuePairArray* evidence, - int prefixes, - void* state, - bool* cont, - fiftyoneDegreesEvidenceIterateMethod callback) { - uint32_t i = 0, count = 0; - EvidenceKeyValuePair* pair; - while (*cont == true && i < evidence->count) { - pair = &evidence->items[i++]; - if ((pair->prefix & prefixes) == pair->prefix) { - if (pair->parsedValue == NULL) { - parsePair(pair); - } - *cont = callback(state, pair); - count++; - } - } - return count; -} - uint32_t fiftyoneDegreesEvidenceIterate( fiftyoneDegreesEvidenceKeyValuePairArray *evidence, int prefixes, void *state, fiftyoneDegreesEvidenceIterateMethod callback) { - bool cont = true; - uint32_t count = evidenceIterateSub( + return evidenceIterate( evidence, prefixes, state, - &cont, callback); - - // Continue if there is a secondary evidence - if (cont == true && evidence->pseudoEvidence != NULL) { - count += evidenceIterateSub( - evidence->pseudoEvidence, - prefixes, - state, - &cont, - callback); - } - return count; } fiftyoneDegreesEvidencePrefixMap* fiftyoneDegreesEvidenceMapPrefix( @@ -178,4 +358,49 @@ EXTERNAL const char* fiftyoneDegreesEvidencePrefixString( } } return NULL; +} + +bool fiftyoneDegreesEvidenceIterateForHeaders( + fiftyoneDegreesEvidenceKeyValuePairArray* evidence, + int prefixes, + fiftyoneDegreesHeaderPtrs* headers, + char* buffer, + size_t length, + void* state, + fiftyoneDegreesEvidenceIterateForHeadersMethod callback) { + Header* header; + + // For each of the headers process as either a standard header, or a pseudo + // header. + for (uint32_t i = 0; i < headers->count; i++) { + header = headers->items[i]; + + // Try and process the header as a standard header. + if (processHeader( + evidence, + prefixes, + header, + state, + callback) == false) { + return true; + } + + // If the header is a pseudo header then attempt to assemble a complete + // value from the evidence and process it. + if (header->segmentHeaders != NULL && + header->segmentHeaders->count > 0) { + if (processPseudoHeader( + evidence, + prefixes, + header, + buffer, + length, + state, + callback) == false) { + return true; + } + } + } + + return false; } \ No newline at end of file diff --git a/evidence.h b/evidence.h index c538e561..27943c02 100644 --- a/evidence.h +++ b/evidence.h @@ -125,6 +125,7 @@ #include "string.h" #include "array.h" #include "common.h" +#include "headers.h" /** * Evidence prefixes used to determine the category a piece of evidence @@ -161,18 +162,16 @@ typedef struct fiftyone_degrees_evidence_key_value_pair_t { fiftyoneDegreesEvidencePrefix prefix; /**< e.g. #FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING */ const char *field; /**< e.g. User-Agent or ScreenPixelsWidth */ size_t fieldLength; /**< length of field */ - void* relative; /**< optional pointer to a related entity */ - const void *originalValue; /**< The original unparsed value */ - const void *parsedValue; /**< The parsed value which may not be a string */ + const void *originalValue; /**< original unparsed value */ + const void *parsedValue; /**< parsed value which may not be a string */ + size_t parsedLength; /**< length of parsedValue string */ + fiftyoneDegreesHeader* header; /**< Unique header in the data set, or + null if not related to a header */ } fiftyoneDegreesEvidenceKeyValuePair; -#define EVIDENCE_KEY_VALUE_MEMBERS \ -struct fiftyone_degrees_array_fiftyoneDegreesEvidenceKeyValuePair_t* pseudoEvidence; /**< The pseudo evidence. #fiftyoneDegreesEvidenceKeyValuePairArray type */ - FIFTYONE_DEGREES_ARRAY_TYPE( fiftyoneDegreesEvidenceKeyValuePair, - EVIDENCE_KEY_VALUE_MEMBERS) - + ) /** * Callback method used to iterate evidence key value pairs. @@ -181,9 +180,24 @@ FIFTYONE_DEGREES_ARRAY_TYPE( * @return true if the iteration should continue otherwise false */ typedef bool(*fiftyoneDegreesEvidenceIterateMethod)( - void *state, + void *state, fiftyoneDegreesEvidenceKeyValuePair *pair); +/** + * Callback method used to iterate evidence key value pairs for an array of + * headers. + * @param state pointer provided to the iterator + * @param header that the value is related to + * @param value from the evidence for the header + * @param length of the value + * @return true if the iteration should continue otherwise false + */ +typedef bool(*fiftyoneDegreesEvidenceIterateForHeadersMethod)( + void* state, + fiftyoneDegreesHeader* header, + const char* value, + size_t length); + /** * Creates a new evidence array with the capacity requested. * @param capacity maximum number of evidence items @@ -247,6 +261,31 @@ EXTERNAL uint32_t fiftyoneDegreesEvidenceIterate( void *state, fiftyoneDegreesEvidenceIterateMethod callback); +/** + * Iterates over the headers assembling the evidence values, considering the + * prefixes, in the buffer if available. The call back method is called for + * each header or pseudo header available. The buffer is only used with pseudo + * headers where multiple header values need to be combined into a single + * value. + * + * @param evidence key value pairs including prefixes + * @param prefixes one or more prefix flags to return values for + * @param state pointer passed to the callback method + * @param headers to return evidence for if available + * @param buffer that MIGHT be used with the callback + * @param length of the buffer + * @param callback method called when a matching prefix is found + * @return true if the callback was called successfully, otherwise false + */ +EXTERNAL bool fiftyoneDegreesEvidenceIterateForHeaders( + fiftyoneDegreesEvidenceKeyValuePairArray* evidence, + int prefixes, + fiftyoneDegreesHeaderPtrs* headers, + char* buffer, + size_t length, + void* state, + fiftyoneDegreesEvidenceIterateForHeadersMethod callback); + /** * @} */ diff --git a/fiftyone.h b/fiftyone.h index 2400e7f8..1bc9d86d 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -166,7 +166,10 @@ MAP_TYPE(PropertiesAvailable) MAP_TYPE(EvidencePropertyIndex) MAP_TYPE(EvidenceProperties) MAP_TYPE(Header) -MAP_TYPE(HeaderArray) +MAP_TYPE(Headers) +MAP_TYPE(HeaderID) +MAP_TYPE(HeaderPtr) +MAP_TYPE(HeaderPtrs) MAP_TYPE(OverridesFilterMethod) MAP_TYPE(Mutex) MAP_TYPE(Signal) @@ -176,8 +179,6 @@ MAP_TYPE(ProfileOffset) MAP_TYPE(ProfileIterateMethod) MAP_TYPE(Float) MAP_TYPE(Coordinate) -MAP_TYPE(HeaderSegment) -MAP_TYPE(HeaderSegmentArray) MAP_TYPE(KeyValuePair) MAP_TYPE(HeaderID) MAP_TYPE(IndexPropertyProfile) @@ -191,6 +192,7 @@ MAP_TYPE(IndexPropertyProfile) #define OverrideProfileIds fiftyoneDegreesOverrideProfileIds /**< Synonym for #fiftyoneDegreesOverrideProfileIds function. */ #define OverridePropertiesFree fiftyoneDegreesOverridePropertiesFree /**< Synonym for #fiftyoneDegreesOverridePropertiesFree function. */ #define ComponentInitList fiftyoneDegreesComponentInitList /**< Synonym for #fiftyoneDegreesComponentInitList function. */ +#define ComponentGetHeaders fiftyoneDegreesComponentGetHeaders /**< Synonym for #fiftyoneDegreesComponentGetHeaders function. */ #define CollectionGetInteger32 fiftyoneDegreesCollectionGetInteger32 /**< Synonym for #fiftyoneDegreesCollectionGetInteger32 function. */ #define PropertyGet fiftyoneDegreesPropertyGet /**< Synonym for #fiftyoneDegreesPropertyGet function. */ #define ProfileIterateValuesForProperty fiftyoneDegreesProfileIterateValuesForProperty /**< Synonym for #fiftyoneDegreesProfileIterateValuesForProperty function. */ @@ -215,6 +217,7 @@ MAP_TYPE(IndexPropertyProfile) #define StringSubString fiftyoneDegreesStringSubString /**< Synonym for #fiftyoneDegreesSubString function. */ #define OverridesExtractFromEvidence fiftyoneDegreesOverridesExtractFromEvidence /**< Synonym for #fiftyoneDegreesOverridesExtractFromEvidence function. */ #define EvidenceIterate fiftyoneDegreesEvidenceIterate /**< Synonym for #fiftyoneDegreesEvidenceIterate function. */ +#define EvidenceIterateForHeaders fiftyoneDegreesEvidenceIterateForHeaders /**< Synonym for #fiftyoneDegreesEvidenceIterateForHeaders function. */ #define CacheRelease fiftyoneDegreesCacheRelease /**< Synonym for #fiftyoneDegreesCacheRelease function. */ #define DataReset fiftyoneDegreesDataReset /**< Synonym for #fiftyoneDegreesDataReset function. */ #define CacheFree fiftyoneDegreesCacheFree /**< Synonym for #fiftyoneDegreesCacheFree function. */ diff --git a/headers.c b/headers.c index 3368f03a..7226948e 100644 --- a/headers.c +++ b/headers.c @@ -27,7 +27,36 @@ /* HTTP header prefix used when processing collections of parameters. */ #define HTTP_PREFIX_UPPER "HTTP_" -MAP_TYPE(HeaderID) +/** + * Free the members of the header. + */ +static void freeHeader(Header* header) { + if (header->name != NULL) { + Free((void*)header->name); + } + if (header->pseudoHeaders != NULL) { + Free((void*)header->pseudoHeaders); + } + if (header->segmentHeaders != NULL) { + Free((void*)header->segmentHeaders); + } +} + +/** + * Sets all the header elements to default settings. + */ +static void initHeaders(Headers* headers) { + for (uint32_t i = 0; i < headers->capacity; i++) { + Header* h = &headers->items[i]; + h->index = i; + h->headerId = 0; + h->isDataSet = false; + h->length = 0; + h->name = NULL; + h->pseudoHeaders = NULL; + h->segmentHeaders = NULL; + } +} /** * Counts the number of segments in a header name. @@ -82,98 +111,38 @@ static int countAllSegments(void* state, HeadersGetMethod get) { } /** - * Counts the number of headers that have more than 1 segment indicating - * they are pseudo headers. + * Relates the pseudoHeader index to the source. */ -static int countPseudoHeaders(Headers* headers) { - Header* header; - int pseudoCount = 0; - for (uint32_t i = 0; i < headers->count; i++) { - header = &headers->items[i]; - if (header->segments->count > 1) { - pseudoCount++; - } - } - return pseudoCount; +static void relateSegmentHeaderToPseudoHeader( + Header* source, + Header* pseudoHeader) { + HeaderPtrs* array = source->pseudoHeaders; + array->items[array->count++] = pseudoHeader; } /** - * Adds the segment to the array of segments returning the character position - * for the next segment. - * If there was not enough space in the array, the exception is set. - * @param segments pre allocated array to populate - * @param start pointer to the first character of the segment string - * @param end pointer to the last character of the segment string - * @param exception set if there was not enough space in the array - * @return char* pointer to the first character of the next segment + * Relates the pseudoHeader index to the source. */ -static char* addSegment( - HeaderSegmentArray* segments, - char* start, - char* end, - Exception *exception) { - if (segments->count >= segments->capacity) { - EXCEPTION_SET(POINTER_OUT_OF_BOUNDS); - return end; - } - HeaderSegment* segment = &segments->items[segments->count++]; - segment->segment = start; - segment->length = end - start; - return end + 1; -} - -/** - * Create the array of segments from the name string, or NULL if there are no - * segments or the memory can't be allocated. - * All headers should have at least one segment, so a result of NULL indicates - * something is wrong. - */ -static HeaderSegmentArray* createSegmentArray(const char* name) { - HeaderSegmentArray* segments; - EXCEPTION_CREATE; - int count = countHeaderSegments(name); - char* current, *last; - FIFTYONE_DEGREES_ARRAY_CREATE( - HeaderSegment, - segments, - count); - if (segments != NULL) { - current = (char*)name; - last = current; - while (*current != '\0' && EXCEPTION_OKAY) { - if (*current == PSEUDO_HEADER_SEP) { - if (current != last) { - last = addSegment(segments, last, current, exception); - } - else { - last++; - } - } - current++; - } - if (current != last && EXCEPTION_OKAY) { - last = addSegment(segments, last, current, exception); - } - if (EXCEPTION_FAILED) { - Free(segments); - return NULL; - } - } - return segments; +static void relatePseudoHeaderToSegmentHeader( + Header* pseudoHeader, + Header* segmentHeader) { + HeaderPtrs* array = pseudoHeader->segmentHeaders; + array->items[array->count++] = segmentHeader; } /** * Copies the length of the source string characters to a new string array * associated with the header provided. */ -static bool copyHeaderName(Header* header, const char* source, size_t length) { +static bool setHeaderName( + Header* header, + const char* source, + size_t length, + Exception* exception) { size_t size = length + 1; char* name = (char*)Malloc(sizeof(char) * size); if (name == NULL) { - return false; - } - if (memset(name, '\0', size) == NULL) { - Free(name); + EXCEPTION_SET(INSUFFICIENT_MEMORY); return false; } header->name = memcpy(name, source, length); @@ -181,41 +150,62 @@ static bool copyHeaderName(Header* header, const char* source, size_t length) { Free(name); return false; } - header->nameLength = length; + name[length] = '\0'; + header->length = length; return true; } /** - * Sets the header from the data set including segments. + * Sets the header with the name, and pseudo and related headers with the + * capacity provided. */ -static bool setHeaderFromDataSet( +static bool setHeader( Header* header, const char* name, - size_t nameLength, - HeaderID uniqueId) { - if (copyHeaderName(header, name, nameLength) == false) { + size_t length, + uint32_t capacity, + Exception* exception) { + if (setHeaderName(header, name, length, exception) == false) { return false; } - header->isDataSet = true; - header->uniqueId = uniqueId; - header->segments = createSegmentArray(header->name); - return header->segments != NULL; + FIFTYONE_DEGREES_ARRAY_CREATE( + fiftyoneDegreesHeaderPtr, + header->pseudoHeaders, + capacity); + if (header->pseudoHeaders == NULL) { + EXCEPTION_SET(INSUFFICIENT_MEMORY); + freeHeader(header); + return false; + } + FIFTYONE_DEGREES_ARRAY_CREATE( + fiftyoneDegreesHeaderPtr, + header->segmentHeaders, + capacity); + if (header->segmentHeaders == NULL) { + EXCEPTION_SET(INSUFFICIENT_MEMORY); + freeHeader(header); + return false; + } + return true; } /** - * Sets the header from the source header and source segment. + * Sets the header from the data set copying the data set string to new memory + * specifically assigned for the header. Sets the capacity of the pseudo + * headers that might contain this header to the value provided. */ -static bool setHeaderFromSegment(Header* header, HeaderSegment* segment) { - if (copyHeaderName(header, segment->segment, segment->length) == false) { - return false; - } - header->segments = createSegmentArray(header->name); - if (header->segments == NULL) { - Free((void*)header->name); +static bool setHeaderFromDataSet( + Header* header, + const char* name, + size_t length, + HeaderID headerId, + uint32_t capacity, + Exception* exception) { + if (setHeader(header, name, length, capacity, exception) == false) { return false; } - header->isDataSet = false; - header->uniqueId = 0; + header->isDataSet = true; + header->headerId = headerId; return true; } @@ -224,13 +214,9 @@ static bool setHeaderFromSegment(Header* header, HeaderSegment* segment) { */ static int getHeaderIndex(Headers *headers, const char *name, size_t length) { Header item; - uint32_t i; - if (name == NULL) { - return false; - } - for (i = 0; i < headers->count; i++) { + for (uint32_t i = 0; i < headers->count; i++) { item = headers->items[i]; - if (item.nameLength == length && + if (item.length == length && StringCompareLength(name, item.name, length) == 0) { return (int)i; } @@ -242,9 +228,13 @@ static int getHeaderIndex(Headers *headers, const char *name, size_t length) { * Returns a pointer to the header if it exits, or NULL if it doesn't. */ static Header* getHeader(Headers* headers, const char* name, size_t length) { - int i = getHeaderIndex(headers, name, length); - if (i >= 0) { - return &headers->items[i]; + Header* item; + for (uint32_t i = 0; i < headers->count; i++) { + item = &headers->items[i]; + if (item->length == length && + StringCompareLength(name, item->name, length) == 0) { + return item; + } } return NULL; } @@ -256,85 +246,256 @@ static Header* getHeader(Headers* headers, const char* name, size_t length) { static bool addHeadersFromDataSet( void* state, HeadersGetMethod get, - HeaderArray* headers) { + Headers* headers, + Exception* exception) { Item item; - long uniqueId; - uint32_t index = 0; + long headerId; const char* name; - size_t nameLength; DataReset(&item.data); - // Get the first name item from the data set. - while ((uniqueId = get(state, index, &item)) >= 0) { - // Only include the header if it is not zero length, has at least one - // segment, and does not already exist in the collection. - name = STRING(item.data.ptr); - nameLength = strlen(name); - if (nameLength > 0 && - countHeaderSegments(name) > 0 && - getHeaderIndex(headers, name, nameLength) < 0) { - - // Set the next header from the data set name item. - if (setHeaderFromDataSet( - &headers->items[headers->count], - name, - nameLength, - (HeaderID)uniqueId) == false) { - return false; - } + // Loop through all the available headers in the data set. + while ((headerId = get(state, headers->count, &item)) >= 0) { - // Release the item and update the counters. - headers->count++; + // Set the next header from the data set name item aborting if there + // was a problem. + name = STRING(item.data.ptr); + if (setHeaderFromDataSet( + &headers->items[headers->count], + name, + strlen(name), + (HeaderID)headerId, + headers->capacity, + exception) == false) { + return false; } - // Release the item from the caller. + // Move to the next available header. + headers->count++; + + // Release the header name item before moving to the next one. COLLECTION_RELEASE(item.collection, &item); + } + return true; +} - // Get the next name item from the data set. - index++; +/** + * If a header with the provided name does not exist then add a new one to the + * array of headers. Returns the header. + */ +static Header* getOrAddHeader( + Headers* headers, + const char* name, + size_t length, + Exception* exception) { + Header* header = getHeader(headers, name, length); + if (header == NULL) { + header = &headers->items[headers->count]; + if (setHeader( + header, + name, + length, + headers->capacity, + exception) == false) { + return NULL; + } + headers->count++; + } + return header; +} + +/** + * Copies the source header into a new header in the headers array returning + * the copied header. + */ +static Header* copyHeader( + Header* source, + Headers* headers, + Exception* exception) { + Header* copied = &headers->items[headers->count++]; + copied->headerId = source->headerId; + copied->isDataSet = source->isDataSet; + copied->index = source->index; + if (setHeaderName( + copied, + source->name, + source->length, + exception) == false) { + return NULL; + } + FIFTYONE_DEGREES_ARRAY_CREATE( + fiftyoneDegreesHeaderPtr, + copied->pseudoHeaders, + source->pseudoHeaders->count); + if (copied->pseudoHeaders == NULL) { + EXCEPTION_SET(INSUFFICIENT_MEMORY); + freeHeader(copied); + return NULL; + } + FIFTYONE_DEGREES_ARRAY_CREATE( + fiftyoneDegreesHeaderPtr, + copied->segmentHeaders, + source->segmentHeaders->count); + if (copied->segmentHeaders == NULL) { + EXCEPTION_SET(INSUFFICIENT_MEMORY); + freeHeader(copied); + return NULL; } + return copied; +} + +/** + * Gets or adds a header from the array and then creates the two way + * relationship between the pseudo header and the segment header. + */ +static bool addHeadersFromHeaderSegment( + Headers* headers, + Header* psuedoHeader, + const char* name, + size_t length, + Exception* exception) { + Header* segmentHeader = getOrAddHeader( + headers, + name, + length, + exception); + if (segmentHeader == NULL) { + return false; + } + + // Relate the segment header to the pseudo header. + relateSegmentHeaderToPseudoHeader(segmentHeader, psuedoHeader); + + // Relate the pseudo header to the segment header. + relatePseudoHeaderToSegmentHeader(psuedoHeader, segmentHeader); + return true; } /** - * Loops through the existing headers checking the segments to ensure they are - * also included in the array of headers. + * Extracts segments of pseudo headers ensuring they also exist in the headers + * array. */ -static bool addHeadersFromSegments(HeaderArray* headers) { - Header *header, *existing; - HeaderSegment* segment; - uint32_t i, s; - uint32_t max = headers->count; - for (i = 0; i < max; i++) { - header = &headers->items[i]; - for (s = 0; s < header->segments->count; s++) { - segment = &header->segments->items[s]; - existing = getHeader(headers, segment->segment, segment->length); - if (existing == NULL) { - if (setHeaderFromSegment( - &headers->items[headers->count], - segment) == false) { - return false; - } - headers->count++; +static bool addHeadersFromHeader( + Headers* headers, + Header* psuedoHeader, + Exception* exception) { + uint32_t start = 0; + uint32_t end = 0; + for (;end < psuedoHeader->length; end++) { + + // If a header has been found then either get the existing header with + // this name, or add a new header. + if (psuedoHeader->name[end] == PSEUDO_HEADER_SEP && + end - start > 0) { + if (addHeadersFromHeaderSegment( + headers, + psuedoHeader, + psuedoHeader->name + start, + end - start, + exception) == false) { + return false; } + + // Move to the next segment. + start = end + 1; + } + } + + // If there is a final segment then process this. + if (end - start > 0) { + if (addHeadersFromHeaderSegment( + headers, + psuedoHeader, + psuedoHeader->name + start, + end - start, + exception) == false) { + return false; + } + } + + return true; +} + +static bool addHeadersFromHeaders(Headers* headers, Exception* exception) { + for (uint32_t i = 0; i < headers->count; i++) { + if (addHeadersFromHeader( + headers, + &headers->items[i], + exception) == false) { + return false; } } return true; } /** - * Check if a header is a pseudo header. + * Maintains the relationship between the source and destination headers using + * instances for the related header from the headers array. + */ +static void copyRelationship( + HeaderPtrs* src, + HeaderPtrs* dst, + Headers* headers) { + assert(src->count == dst->capacity); + for (uint32_t i = 0; i < src->count; i++) { + dst->items[dst->count++] = &headers->items[src->items[i]->index]; + } + assert(src->count == dst->count); +} + +/** + * Copies the relationship instances from the source header to the copied + * instance in the destination. + */ +static void copyRelationships(Header* src, Header* dst, Headers* headers) { + copyRelationship(src->pseudoHeaders, dst->pseudoHeaders, headers); + copyRelationship(src->segmentHeaders, dst->segmentHeaders, headers); +} + +/** + * The arrays are initial created with more capacity than they are likely to + * need. To reduce the amount of data held in memory beyond the creation of the + * headers to a minimum and thus enable the most efficient operation a copy of + * the headers structure is created using only the memory required. */ -bool fiftyoneDegreesHeadersIsPseudo(const char *headerName) { - return strchr(headerName, PSEUDO_HEADER_SEP) != NULL; +static Headers* trimHeaders(Headers* source, Exception* exception) { + Headers* trimmed; + FIFTYONE_DEGREES_ARRAY_CREATE( + fiftyoneDegreesHeader, + trimmed, + source->count); + if (trimmed != NULL) { + + // Initialise all the headers. + initHeaders(trimmed); + + // Copy the headers, but not the relationship between segments and + // pseudos. This is done once all instances are created in the trimmed + // array. + for (uint32_t i = 0; i < source->count; i++) { + if (copyHeader(&source->items[i], trimmed, exception) == NULL) { + HeadersFree(trimmed); + return NULL; + } + } + + // Copy the relationships to the trimmed header instances. + for (uint32_t i = 0; i < source->count; i++) { + copyRelationships(&source->items[i], &trimmed->items[i], trimmed); + } + + // Finally free the sources now a trimmed copies has been made. + HeadersFree(source); + } + return trimmed; } fiftyoneDegreesHeaders* fiftyoneDegreesHeadersCreate( bool expectUpperPrefixedHeaders, void *state, - fiftyoneDegreesHeadersGetMethod get) { - Headers *headers; + fiftyoneDegreesHeadersGetMethod get, + fiftyoneDegreesException* exception) { + Headers* headers; // Count the number of headers and create an array with sufficient capacity // to store all of them. @@ -345,24 +506,30 @@ fiftyoneDegreesHeaders* fiftyoneDegreesHeadersCreate( count); if (headers != NULL) { - // Set the prefixed headers flag. - headers->expectUpperPrefixedHeaders = expectUpperPrefixedHeaders; + // Initialise all the headers. + initHeaders(headers); // Add the headers from the data set. - if (addHeadersFromDataSet(state, get, headers) == false) { + if (addHeadersFromDataSet(state, get, headers, exception) == false) { HeadersFree(headers); return NULL; } - // Add the headers from the segments that were found from the data set. - if (addHeadersFromSegments(headers) == false) { + // Add headers from the headers already added where there are pseudo + // headers present. + if (addHeadersFromHeaders(headers, exception) == false) { HeadersFree(headers); return NULL; } - // Count the number of pseudo headers for the purposes of handling - // evidence. - headers->pseudoHeadersCount = countPseudoHeaders(headers); + // Trim the capacity of all the array to reduce operational memory. + headers = trimHeaders(headers, exception); + if (headers == NULL) { + return NULL; + } + + // Set the prefixed headers flag. + headers->expectUpperPrefixedHeaders = expectUpperPrefixedHeaders; } return headers; } @@ -389,7 +556,7 @@ int fiftyoneDegreesHeaderGetIndex( // Perform a case insensitive compare of the remaining characters. for (i = 0; i < headers->count; i++) { header = &headers->items[i]; - if (header->nameLength == length && + if (header->length == length && StringCompareLength( httpHeaderName, header->name, @@ -406,7 +573,7 @@ fiftyoneDegreesHeader* fiftyoneDegreesHeadersGetHeaderFromUniqueId( HeaderID uniqueId) { uint32_t i; for (i = 0; i < headers->count; i++) { - if (headers->items[i].uniqueId == uniqueId) { + if (headers->items[i].headerId == uniqueId) { return &headers->items[i]; } } @@ -414,13 +581,10 @@ fiftyoneDegreesHeader* fiftyoneDegreesHeadersGetHeaderFromUniqueId( } void fiftyoneDegreesHeadersFree(fiftyoneDegreesHeaders *headers) { - Header* header; uint32_t i; if (headers != NULL) { for (i = 0; i < headers->count; i++) { - header = &headers->items[i]; - Free((void*)header->name); - Free((void*)header->segments); + freeHeader(&headers->items[i]); } Free((void*)headers); headers = NULL; @@ -429,11 +593,9 @@ void fiftyoneDegreesHeadersFree(fiftyoneDegreesHeaders *headers) { bool fiftyoneDegreesHeadersIsHttp( void *state, - fiftyoneDegreesEvidenceKeyValuePair *pair) { - return HeaderGetIndex( - (Headers*)state, - pair->field, - pair->fieldLength) >= 0; + const char* field, + size_t length) { + return HeaderGetIndex((Headers*)state, field, length) >= 0; } /** diff --git a/headers.h b/headers.h index 3ac295e8..2d6123b7 100644 --- a/headers.h +++ b/headers.h @@ -66,7 +66,8 @@ * fiftyoneDegreesHeaders *headers = fiftyoneDegreesHeadersCreate( * false, * state, - * getHeaderId); + * getHeaderId, + * exception); * * // Get the index of a header * int index = fiftyoneDegreesHeaderGetIndex( @@ -99,46 +100,61 @@ #define _strnicmp strncasecmp #endif #include "list.h" -#include "evidence.h" #include "array.h" #include "common.h" /** - * Null or PSEUDO_HEADER_SEP terminated string segment within a header. + * The unique id for the header field string in the data set. */ -typedef struct fiftyone_degrees_header_segment_t { - const char* segment; /**< Segment in the name string */ - size_t length; /**< Length of the segment in the name strings without the - terminating character */ -} fiftyoneDegreesHeaderSegment; +typedef uint32_t fiftyoneDegreesHeaderID; + +/** + * Forward declaration of the header structure. + */ +typedef struct fiftyone_degrees_header_t fiftyoneDegreesHeader; + +/** + * Pointer to a header structure. Used in an array of related headers. + */ +typedef fiftyoneDegreesHeader* fiftyoneDegreesHeaderPtr; +/** + * Array of header indexes. + */ FIFTYONE_DEGREES_ARRAY_TYPE( - fiftyoneDegreesHeaderSegment, + fiftyoneDegreesHeaderPtr, ); - -typedef uint32_t fiftyoneDegreesHeaderID; +typedef fiftyoneDegreesHeaderPtrArray fiftyoneDegreesHeaderPtrs; /** - * Header structure to identify a header that either comes directly from the - * data set, or one that forms a pseudo header. + * Structure for a header known to the corresponding data set. */ -typedef struct fiftyone_degrees_header_t { +struct fiftyone_degrees_header_t { + uint32_t index; /**< Index of the header in the array of all headers */ const char* name; /**< Name of the header or pseudo header field as a null terminated string */ - size_t nameLength; /**< Length of the name string excluding the - terminating null */ - fiftyoneDegreesHeaderSegmentArray* segments; /**< Segments within the - name */ - bool isDataSet; /**< True if the header originates from the data set */ - fiftyoneDegreesHeaderID uniqueId; /** < Unique id provided by the data set */ -} fiftyoneDegreesHeader; + size_t length; /**< Length of the name string excluding the terminating + null */ + fiftyoneDegreesHeaderID headerId; /**< Unique id in the data set for this + full header */ + bool isDataSet; /**< True if the header originates from the data set and + the fullHeaderId is valid */ + fiftyoneDegreesHeaderPtrs* pseudoHeaders; /**< Array of indexes to + related pseudo headers */ + fiftyoneDegreesHeaderPtrs* segmentHeaders; /**< Array of indexes to raw + headers that form this pseudo + header */ +}; #define FIFTYONE_DEGREES_HEADERS_MEMBERS \ bool expectUpperPrefixedHeaders; /**< True if the headers structure should expect input header to be prefixed with - 'HTTP_' */ \ -uint32_t pseudoHeadersCount; /**< Count of the number of pseudo headers */ + 'HTTP_' */ +/** + * Array of Headers which should always be ordered in ascending order of + * fullHeaderId. + */ FIFTYONE_DEGREES_ARRAY_TYPE( fiftyoneDegreesHeader, FIFTYONE_DEGREES_HEADERS_MEMBERS); @@ -149,7 +165,8 @@ FIFTYONE_DEGREES_ARRAY_TYPE( typedef fiftyoneDegreesHeaderArray fiftyoneDegreesHeaders; /** - * Gets the unique id and name of the header at the requested index. + * Gets the unique id and name of the header at the requested index. The caller + * must use COLLECTION_RELEASE on nameItem when finished with the result. * @param state pointer to data used by the method * @param index of the header to get * @param nameItem pointer to the collection item to populate with the name of @@ -186,13 +203,15 @@ EXTERNAL bool fiftyoneDegreesHeadersIsPseudo(const char *headerName); * @param useUpperPrefixedHeaders true if HTTP_ prefixes should be checked * @param state pointer used by getHeaderMethod to retrieve the header integer * @param get used to return the HTTP header unique integer + * @param exception * @return a new instance of #fiftyoneDegreesHeaders ready to be used to filter * HTTP headers. */ EXTERNAL fiftyoneDegreesHeaders* fiftyoneDegreesHeadersCreate( bool useUpperPrefixedHeaders, void *state, - fiftyoneDegreesHeadersGetMethod get); + fiftyoneDegreesHeadersGetMethod get, + fiftyoneDegreesException* exception); /** * Provides the integer index of the HTTP header name, or -1 if there is no @@ -229,14 +248,16 @@ EXTERNAL fiftyoneDegreesHeader* fiftyoneDegreesHeadersGetHeaderFromUniqueId( EXTERNAL void fiftyoneDegreesHeadersFree(fiftyoneDegreesHeaders *headers); /** - * Determines if the key of an evidence pair is an HTTP header. + * Determines if the field is an HTTP header. * @param state results instance to check against - * @param pair the evidence pair to be checked + * @param field name from the evidence pair to be checked + * @param length of field string * @return true if the evidence relates to an HTTP header, otherwise false. */ EXTERNAL bool fiftyoneDegreesHeadersIsHttp( void *state, - fiftyoneDegreesEvidenceKeyValuePair *pair); + const char* field, + size_t length); /** * @} diff --git a/pseudoheader.c b/pseudoheader.c deleted file mode 100644 index f4cca8cd..00000000 --- a/pseudoheader.c +++ /dev/null @@ -1,251 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#include "pseudoheader.h" -#include "fiftyone.h" -#include "string.h" -#include "evidence.h" - -/* - * Return the evidence value from input request header. - * - * @param segment the header segment to get evidence for - * @param evidence the evidence collection to search from - * @param prefix the target prefix in the evidence collection - * @return the evidence value or NULL if not found. - */ -static const char* getEvidenceValueForHeader( - HeaderSegment* segment, - const EvidenceKeyValuePairArray *evidence, - EvidencePrefix prefix) { - EvidenceKeyValuePair *pair; - for (uint32_t i = 0; i < evidence->count; i++) { - pair = &evidence->items[i]; - if (pair->prefix == prefix) { - if (pair->fieldLength == segment->length && - StringCompareLength( - pair->field, - segment->segment, - segment->length) == 0) { - return (char*)evidence->items[i].originalValue; - } - } - } - return NULL; -} - -/* - * Construct a pseudo evidence given a pseudo header and the list of evidence - * and return the number of characters added. - * - * @param buffer the buffer to write the evidence to - * @param bufferSize the size of the buffer - * @param header the pseudo header to create evidence for - * @param evidence the list of evidence to get actual evidence from - * @param prefix the target prefix to look for in the evidence list - * @return the number of characters added or the length of first portion of - * the string where it found the allocated buffer was not big enough to hold. - * Return negative value to indicate something has gone wrong. - */ -static int constructPseudoEvidence( - char* buffer, - size_t bufferSize, - Header* header, - const EvidenceKeyValuePairArray* evidence, - EvidencePrefix prefix) { - uint32_t i; - int added; - HeaderSegment* segment; - const char* value; - char* current = buffer; - char* max = buffer + bufferSize; - - // Use the segments from the header to construct the evidence. - for (i = 0; i < header->segments->count; i++) { - - // Get the evidence for the segment. - segment = &header->segments->items[i]; - value = getEvidenceValueForHeader(segment, evidence, prefix); - - // If there is no evidence value then the header can't be constructed - // so return. - if (value == NULL) { - if (bufferSize > 0) { - memset(buffer, '\0', 1); - } - return 0; - } - - // If this is a subsequent segment value then add the separator. - // make sure that we don't cause heap overflow by overwriting the terminating \0 (at position max - 1) - if (i != 0 && current < max - 1) { - *current = PSEUDO_HEADER_SEP; - current++; - } - - // Add the value to the buffer. - added = Snprintf(current, max - current, "%s", value); - if (added < 0) { - memset(buffer, '\0', bufferSize); - return added; - } - else if (added >= max - current) { - // Don't nullify the buffer in this case, just report that - // it is truncated. - return (int)(current - buffer + added); - } - current += added; - } - - return (int)(current - buffer); -} - -/* - * Check if an evidence is present for a uniqueHeader with specific prefix - * @param evidence the evidence collection - * @param header the target unique header to check for - * @param acceptedPrefixes the list of accepted prefixes - * @param numberOfPrefixes number of accepted prefixes - * @return whether the evidence for the target unique header presents in the - * evidence collection. - */ -static bool isEvidencePresentForHeader( - EvidenceKeyValuePairArray* evidence, - Header* header, - const EvidencePrefix* acceptedPrefixes, - size_t numberOfPrefixes) { - bool matchPrefix = false; - EvidenceKeyValuePair* pair; - for (uint32_t i = 0; i < evidence->count; i++) { - pair = &evidence->items[i]; - matchPrefix = false; - - // Check if the prefix matches is in the check list - for (size_t j = 0; j < numberOfPrefixes; j++) { - if (pair->prefix == acceptedPrefixes[j]) { - matchPrefix = true; - break; - } - } - - // Compare the field name to the header name if the prefix matches. - if (matchPrefix) { - if (pair->fieldLength == header->nameLength && - StringCompare(header->name, pair->field) == 0) { - return true; - } - } - } - return false; -} - -void -fiftyoneDegreesPseudoHeadersAddEvidence( - EvidenceKeyValuePairArray* evidence, - Headers* acceptedHeaders, - size_t bufferSize, - const EvidencePrefix* orderOfPrecedence, - size_t precedenceSize, - Exception* exception) { - Header* header; - char* buffer = NULL; - int charAdded; - uint32_t i; - if (evidence != NULL && acceptedHeaders != NULL) { - for (i = 0; - i < acceptedHeaders->count && EXCEPTION_OKAY; - i++) { - header = &acceptedHeaders->items[i]; - // Only add evidence for pseudo header - if (HeadersIsPseudo(header->name)) { - // Prioritise the supplied evidence. If an evidence exists - // for a pseudo header then don't construct the evidence - // for it. - if (isEvidencePresentForHeader( - evidence, - header, - orderOfPrecedence, - precedenceSize) == false) { - buffer = (char*)evidence->pseudoEvidence->items[ - evidence->pseudoEvidence->count].originalValue; - if (buffer != NULL) { - for (size_t j = 0; j < precedenceSize; j++) { - charAdded = constructPseudoEvidence( - buffer, - bufferSize, - header, - evidence, - orderOfPrecedence[j]); - // charAdded == 0 means no evidence is added due to - // valid reasons such as missing evidence for request - // headers that form the pseudo header. - if (charAdded > 0) { - evidence->pseudoEvidence->items[ - evidence->pseudoEvidence->count].field = - header->name; - evidence->pseudoEvidence->items[ - evidence->pseudoEvidence->count].fieldLength = - header->nameLength; - evidence->pseudoEvidence->items[ - evidence->pseudoEvidence->count].prefix = - orderOfPrecedence[j]; - evidence->pseudoEvidence->count++; - // Once a complete pseudo evidence is found - // move on the next pseudo header - break; - } - else if (charAdded < 0) { - PseudoHeadersRemoveEvidence( - evidence, - bufferSize); - // Without fully constructed pseudo evidence, - // Client Hints won't work. Set an exception. - EXCEPTION_SET( - FIFTYONE_DEGREES_STATUS_ENCODING_ERROR); - break; - } - } - } - } - } - } - } - else { - EXCEPTION_SET( - FIFTYONE_DEGREES_STATUS_NULL_POINTER); - } -} - -void fiftyoneDegreesPseudoHeadersRemoveEvidence( - fiftyoneDegreesEvidenceKeyValuePairArray* evidence, - size_t bufferSize) { - if (evidence != NULL && evidence->pseudoEvidence != NULL) { - EvidenceKeyValuePair* pair = NULL; - for (uint32_t i = 0; i < evidence->pseudoEvidence->count; i++) { - pair = &evidence->pseudoEvidence->items[i]; - pair->field = NULL; - pair->fieldLength = 0; - memset((void*)pair->originalValue, '\0', bufferSize); - } - evidence->pseudoEvidence->count = 0; - } -} diff --git a/pseudoheader.h b/pseudoheader.h deleted file mode 100644 index 37a26cac..00000000 --- a/pseudoheader.h +++ /dev/null @@ -1,78 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#ifndef FIFTYONE_DEGREES_PSEUDO_HEADER_H_INCLUDED -#define FIFTYONE_DEGREES_PSEUDO_HEADER_H_INCLUDED - -#include "dataset.h" -#include "evidence.h" -#include "headers.h" -#include "common.h" - -#define FIFTYONE_DEGREES_PSEUDO_HEADER_SEP '\x1F' /** unit separator of headers - and headers' values that - form pseudo header and - its evidence */ - -/** - * Iterate through pseudo-headers passed in supplied parameters, construct - * their coresponding evidence. The new evidence should be prefixed with - * the prefix of the evidence that form it. The pseudo evidence pointed by the - * evidence collection, should have pre-allocated the memory to hold the new - * constructured evidence. No new evidence should be constructed if evidence - * has already been provided in the evidence collection or there is not enough - * values to form one. - * - * @param evidence pointer to the evidence that contains the real headers - * and will be updated with the pseudo-headers. - * @param acceptedHeaders the list of headers accepted by the - * engine - * @param bufferSize the size of the buffer allocated to hold the new evidence - * pointed by the orignalValue in each pre-allocated pseudoEvidence item of - * the evidence collection. - * @param orderOfPrecedence of the accepted prefixes - * @param precedenceSize the number of accepted prefixes - * @param exception pointer to an exception data structure to be used if an - * exception occurs. See exceptions.h. - */ -EXTERNAL void fiftyoneDegreesPseudoHeadersAddEvidence( - fiftyoneDegreesEvidenceKeyValuePairArray* evidence, - fiftyoneDegreesHeaders* acceptedHeaders, - size_t bufferSize, - const fiftyoneDegreesEvidencePrefix* orderOfPrecedence, - size_t precedenceSize, - fiftyoneDegreesException* exception); - -/** - * Iterate through the evidence collection and reset the pseudo-headers - * evidence. Mainly set the field and value pointers to NULL. - * - * @param evidence pointer to the evidence colletection - * @param bufferSize the size of the buffer allocated to hold the new evidence - * pointed by the orignalValue in each pre-allocated pseudoEvidence item of - * the evidence collection. - */ -EXTERNAL void fiftyoneDegreesPseudoHeadersRemoveEvidence( - fiftyoneDegreesEvidenceKeyValuePairArray* evidence, - size_t bufferSize); - -#endif From c4a9987c4f650582b7655fc93afcba8941d20d6f Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 8 May 2024 13:33:44 +0100 Subject: [PATCH 09/73] BUG: Added the pseudo header separator into the headers. --- fiftyone.h | 1 - headers.h | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fiftyone.h b/fiftyone.h index 1bc9d86d..644b1dbe 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -79,7 +79,6 @@ #include "ip.h" #include "float.h" #include "coordinate.h" -#include "pseudoheader.h" #include "snprintf.h" #include "bool.h" #include "process.h" diff --git a/headers.h b/headers.h index 2d6123b7..cdc27412 100644 --- a/headers.h +++ b/headers.h @@ -103,6 +103,11 @@ #include "array.h" #include "common.h" +#define FIFTYONE_DEGREES_PSEUDO_HEADER_SEP '\x1F' /** unit separator of headers + and headers' values that + form pseudo header and + its evidence */ + /** * The unique id for the header field string in the data set. */ From 8a224e62d2b27a24900c3bb4d826141084f8f755 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Thu, 23 May 2024 15:09:50 +0100 Subject: [PATCH 10/73] FEAT: Added a new config flag for the index relating profiles to values. --- ConfigBase.cpp | 8 ++++++++ ConfigBase.hpp | 14 ++++++++++++++ config.h | 8 ++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/ConfigBase.cpp b/ConfigBase.cpp index f37fbade..f7857b14 100644 --- a/ConfigBase.cpp +++ b/ConfigBase.cpp @@ -56,6 +56,10 @@ void ConfigBase::setPropertyValueIndex(bool index) { this->config->propertyValueIndex = index; } +void ConfigBase::setProfileValuesIndex(bool index) { + this->config->profileValuesIndex = index; +} + bool ConfigBase::getUseUpperPrefixHeaders() const { return config->usesUpperPrefixedHeaders; } @@ -76,6 +80,10 @@ bool ConfigBase::getPropertyValueIndex() const { return config->propertyValueIndex; } +bool ConfigBase::getProfileValuesIndex() const { + return config->profileValuesIndex; +} + uint16_t ConfigBase::getConcurrency() const { return 0; } diff --git a/ConfigBase.hpp b/ConfigBase.hpp index 1232599f..5d6625df 100644 --- a/ConfigBase.hpp +++ b/ConfigBase.hpp @@ -120,6 +120,13 @@ namespace FiftyoneDegrees { */ void setPropertyValueIndex(bool index); + /** + * Set whether or not an index to speed up the retrieval of + * required values for profiles is created. + * @param index should create an index + */ + void setProfileValuesIndex(bool index); + /** * @} * @name Getters @@ -163,6 +170,13 @@ namespace FiftyoneDegrees { */ bool getPropertyValueIndex() const; + /** + * Gets a flag indicating if an index of required values for + * profiles is created. + * @return true if an index should be created, or false if not. + */ + bool getProfileValuesIndex() const; + /** * Get the expected number of concurrent accessors of the data set. * @return concurrency diff --git a/config.h b/config.h index cb5b518e..fbc7cd70 100644 --- a/config.h +++ b/config.h @@ -65,6 +65,8 @@ typedef struct fiftyone_degrees_config_base_t { int tempDirCount; /**< Number of directories in the tempDirs array. */ bool propertyValueIndex; /**< Indicates if an index to values for property and profiles should be created. */ + bool profileValuesIndex; /**< Indicates if an index to get profile values + should be created. */ } fiftyoneDegreesConfigBase; /** Default value for the #FIFTYONE_DEGREES_CONFIG_USE_TEMP_FILE macro. */ @@ -102,7 +104,8 @@ FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY_DEFAULT false, /* reuseTempFile */ \ NULL, /* tempDirs */ \ 0, /* tempDirCount */ \ - true /* propertyValueIndex */ + true, /* propertyValueIndex */ \ + true /* profileValuesIndex */ /** * Default value for the #fiftyoneDegreesConfigBase structure without index. @@ -115,7 +118,8 @@ FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY_DEFAULT false, /* reuseTempFile */ \ NULL, /* tempDirs */ \ 0, /* tempDirCount */ \ - false /* propertyValueIndex */ + false, /* propertyValueIndex */ \ + false /* profileValuesIndex */ /** * @} From 88262f6cce4dc6ba40bf648e0433c2dd4cc0fe05 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Thu, 23 May 2024 15:30:58 +0100 Subject: [PATCH 11/73] EXP: Added an additional index to speed up profile value retrieval. --- array.h | 2 +- dataset.c | 7 ++ dataset.h | 4 + fiftyone.h | 10 +++ indexes.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++ indexes.h | 85 +++++++++++++++++++++- overrides.c | 2 +- profile.c | 48 ++++++++++++ profile.h | 32 +++++++- 9 files changed, 392 insertions(+), 4 deletions(-) diff --git a/array.h b/array.h index 5bf794ff..4f1750b3 100644 --- a/array.h +++ b/array.h @@ -51,7 +51,7 @@ typedef struct fiftyone_degrees_array_##t##_t { \ uint32_t count; /**< Number of used items */ \ uint32_t capacity; /**< Number of available items */ \ - t *items; /**< Pointer to the first item */ \ + t* items; /**< Pointer to the first item */ \ m /**< Add any members provided by the caller */ \ } t##Array; diff --git a/dataset.c b/dataset.c index 7bf87650..05bf7a02 100644 --- a/dataset.c +++ b/dataset.c @@ -76,6 +76,12 @@ void fiftyoneDegreesDataSetFree(fiftyoneDegreesDataSetBase *dataSet) { dataSet->indexPropertyProfile = NULL; } + // Free the memory used for the index for profile values. + if (dataSet->indexProfileValues != NULL) { + IndexProfileValuesFree(dataSet->indexProfileValues); + dataSet->indexProfileValues = NULL; + } + // Free the memory used by the unique headers. HeadersFree(dataSet->uniqueHeaders); dataSet->uniqueHeaders = NULL; @@ -116,6 +122,7 @@ void fiftyoneDegreesDataSetReset(fiftyoneDegreesDataSetBase *dataSet) { dataSet->available = NULL; dataSet->overridable = NULL; dataSet->indexPropertyProfile = NULL; + dataSet->indexProfileValues = NULL; dataSet->config = NULL; dataSet->handle = NULL; } diff --git a/dataset.h b/dataset.h index 570467d0..60184aa3 100644 --- a/dataset.h +++ b/dataset.h @@ -128,6 +128,10 @@ typedef struct fiftyone_degrees_dataset_base_t { that can be overridden */ fiftyoneDegreesIndexPropertyProfile* indexPropertyProfile; /**< Index to + look up profile + values by + property */ + fiftyoneDegreesIndexAllProfileValues* indexProfileValues; /**< Index to look up profile values */ const void *config; /**< Pointer to the config used to create the dataset */ diff --git a/fiftyone.h b/fiftyone.h index 644b1dbe..9fc8c40e 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -181,6 +181,11 @@ MAP_TYPE(Coordinate) MAP_TYPE(KeyValuePair) MAP_TYPE(HeaderID) MAP_TYPE(IndexPropertyProfile) +MAP_TYPE(IndexProfileValue) +MAP_TYPE(IndexProfileValueArray) +MAP_TYPE(IndexProfileValuesPtr) +MAP_TYPE(IndexProfileValuesPtrArray) +MAP_TYPE(IndexAllProfileValues) #define ProfileGetOffsetForProfileId fiftyoneDegreesProfileGetOffsetForProfileId /**< Synonym for #fiftyoneDegreesProfileGetOffsetForProfileId function. */ #define OverrideValuesAdd fiftyoneDegreesOverrideValuesAdd /**< Synonym for #fiftyoneDegreesOverrideValuesAdd function. */ @@ -196,6 +201,7 @@ MAP_TYPE(IndexPropertyProfile) #define PropertyGet fiftyoneDegreesPropertyGet /**< Synonym for #fiftyoneDegreesPropertyGet function. */ #define ProfileIterateValuesForProperty fiftyoneDegreesProfileIterateValuesForProperty /**< Synonym for #fiftyoneDegreesProfileIterateValuesForProperty function. */ #define ProfileIterateValuesForPropertyWithIndex fiftyoneDegreesProfileIterateValuesForPropertyWithIndex /**< Synonym for #fiftyoneDegreesProfileIterateValuesForPropertyWithIndex function. */ +#define ProfileIterateValueIndexes fiftyoneDegreesProfileIterateValueIndexes /**< Synonym for #fiftyoneDegreesProfileIterateValueIndexes function. */ #define ProfileIterateProfilesForPropertyAndValue fiftyoneDegreesProfileIterateProfilesForPropertyAndValue /**< Synonym for #fiftyoneDegreesProfileIterateProfilesForPropertyAndValue function. */ #define PropertiesGetPropertyIndexFromName fiftyoneDegreesPropertiesGetPropertyIndexFromName /**< Synonym for #fiftyoneDegreesPropertiesGetPropertyIndexFromName function. */ #define TreeIterate fiftyoneDegreesTreeIterateNodes /**< Synonym for #fiftyoneDegreesTreeIterateNodes function. */ @@ -341,6 +347,10 @@ MAP_TYPE(IndexPropertyProfile) #define IndexPropertyProfileCreate fiftyoneDegreesIndexPropertyProfileCreate /**< Synonym for fiftyoneDegreesIndexPropertyProfileCreate */ #define IndexPropertyProfileFree fiftyoneDegreesIndexPropertyProfileFree /**< Synonym for fiftyoneDegreesIndexPropertyProfileFree */ #define IndexPropertyProfileLookup fiftyoneDegreesIndexPropertyProfileLookup /**< Synonym for fiftyoneDegreesIndexPropertyProfileLookup */ +#define IndexProfileValuesFree fiftyoneDegreesIndexProfileValuesFree /**< Synonym for fiftyoneDegreesIndexProfileValuesFree */ +#define IndexProfileValuesCreate fiftyoneDegreesIndexProfileValuesCreate /**< Synonym for fiftyoneDegreesIndexProfileValuesCreate */ +#define IndexProfileValuesIterate fiftyoneDegreesIndexProfileValuesIterate /**< Synonym for fiftyoneDegreesIndexProfileValuesIterate */ + /* <-- only one asterisk to avoid inclusion in documentation * Shortened macros. diff --git a/indexes.c b/indexes.c index 69e7d69d..11e9d554 100644 --- a/indexes.c +++ b/indexes.c @@ -28,6 +28,14 @@ typedef struct map_t { int16_t propertyIndex; // index in the properties collection } map; + +typedef void(*iterateProfileValuesMethod)( + IndexPropertyProfile* index, // index in use or null if not available + map* propertyIndexes, // property indexes in ascending order + fiftyoneDegreesCollection* values, // collection of values + Profile* profile, + Exception* exception); + // Gets the index of the profile id in the property profile index. static uint32_t getProfileIndex( IndexPropertyProfile* index, @@ -170,6 +178,90 @@ static map* createPropertyIndexes( return index; } + +// Adds the value index to the array of value indexes in state. +static bool profileValuesAddValueCallback(void* state, uint32_t valueIndex) { + IndexProfileValuesPtr index = (IndexProfileValuesPtr)state; + index->items[index->count++] = valueIndex; + assert(index->count <= index->capacity); + return true; +} + +// Adds all the value indexes to the index array for the profile. The index +// will contain sufficient capacity from a prior call to countValues. +static uint32_t profileValuesAddValues( + fiftyoneDegreesProfile* profile, + fiftyoneDegreesPropertiesAvailable* available, + fiftyoneDegreesCollection* values, + IndexProfileValuesPtr profileIndex, + fiftyoneDegreesException* exception) { + return ProfileIterateValueIndexes( + profile, + available, + values, + (void*)profileIndex, + profileValuesAddValueCallback, + exception); +} + +// Does nothing other than count the value indexes. +static bool profileValuesCountCallback(void* state, uint32_t valueIndex) { + *(uint32_t*)state = valueIndex; + return true; +} + +// Counts the value indexes for the profile that relate to the available +// properties. +static uint32_t profileValuesCountValues( + fiftyoneDegreesProfile* profile, + fiftyoneDegreesPropertiesAvailable* available, + fiftyoneDegreesCollection* values, + fiftyoneDegreesException* exception) { + uint32_t lastValueIndex; // Needed to prevent compiler warning. + return ProfileIterateValueIndexes( + profile, + available, + values, + &lastValueIndex, + profileValuesCountCallback, + exception); +} + +// Creates the initial memory needed for the profile values index. Sets the +// entries to null to handle situations where the profile id is not valid. +static IndexAllProfileValues* profileValuesCreateIndex( + fiftyoneDegreesCollection* profileOffsets, + Exception* exception) { + IndexAllProfileValues* index; + + // Get the minimum and maximum profile id. + uint32_t minProfileId = getProfileId(profileOffsets, 0, exception); + uint32_t maxProfileId = getProfileId( + profileOffsets, + CollectionGetCount(profileOffsets) - 1, + exception); + + // Create the index with sufficient capacity for all possible profile ids. + FIFTYONE_DEGREES_ARRAY_CREATE( + IndexProfileValuesPtr, + index, + maxProfileId - minProfileId + 1); + if (index == NULL || EXCEPTION_FAILED) { + return NULL; + } + index->minProfileId = minProfileId; + index->maxProfileId = maxProfileId; + index->profileCount = CollectionGetCount(profileOffsets); + + // Set all the possible items to NULL and zero to ensure invalid profile + // ids can be identified. + for (uint32_t i = 0; i < index->capacity; i++) { + index->items[i] = NULL; + } + + return index; +} + fiftyoneDegreesIndexPropertyProfile* fiftyoneDegreesIndexPropertyProfileCreate( fiftyoneDegreesCollection* profiles, @@ -258,4 +350,118 @@ uint32_t fiftyoneDegreesIndexPropertyProfileLookup( availablePropertyIndex; assert(valueIndex < index->size); return index->valueIndexes[valueIndex]; +} + +fiftyoneDegreesIndexAllProfileValues* fiftyoneDegreesIndexProfileValuesCreate( + fiftyoneDegreesCollection* profiles, + fiftyoneDegreesCollection* profileOffsets, + fiftyoneDegreesPropertiesAvailable* available, + fiftyoneDegreesCollection* values, + fiftyoneDegreesException* exception) { + Item profileItem; + Profile* profile; + uint32_t count; + int profileIndex; + DataReset(&profileItem.data); + + // Create the array that forms the index for all profiles. + IndexAllProfileValues* index = profileValuesCreateIndex( + profileOffsets, + exception); + if (index == NULL || EXCEPTION_FAILED) { + return NULL; + } + + // For each of the profile ids in the profile offsets. + for (uint32_t i = 0; i < index->profileCount; i++) { + + // Get the next profile based on the index. + profile = (Profile*)ProfileGetByIndex( + profileOffsets, + profiles, + i, + &profileItem, + exception); + if (profile == NULL || EXCEPTION_FAILED) { + IndexProfileValuesFree(index); + return NULL; + } + + // Count the number of required property values that are going to be + // needed for this profile. + count = profileValuesCountValues( + profile, + available, + values, + exception); + if (EXCEPTION_FAILED) { + IndexProfileValuesFree(index); + return NULL; + } + + // Allocate an array of sufficient size to store all the value indexes. + profileIndex = profile->profileId - index->minProfileId; + FIFTYONE_DEGREES_ARRAY_CREATE( + IndexProfileValue, + index->items[profileIndex], + count); + if (index->items[profileIndex] == NULL || EXCEPTION_FAILED) { + IndexProfileValuesFree(index); + return NULL; + } + assert(index->items[profileIndex]->capacity == count); + + // Add the value indexes for each of the available properties for the + // profile. + profileValuesAddValues( + profile, + available, + values, + index->items[profileIndex], + exception); + assert(index->items[profileIndex]->capacity == + index->items[profileIndex]->count); + index->count++; + + // Release the profile. + COLLECTION_RELEASE(profiles, &profileItem); + } + + return index; +} + +void fiftyoneDegreesIndexProfileValuesFree( + fiftyoneDegreesIndexAllProfileValues* index) { + for (uint32_t i = 0; i < index->capacity; i++) { + if (index->items[i] != NULL) { + Free(index->items[i]); + } + } + Free(index); +} + +uint32_t fiftyoneDegreesIndexProfileValuesIterate( + fiftyoneDegreesIndexAllProfileValues* index, + uint32_t profileId, + void* state, + fiftyoneDegreesIndexProfileValuesMethod callback) { + IndexProfileValuesPtr profileIndex; + bool cont = true; + uint32_t count = 0; + + // Check that the profile id is valid considering the min and max ids. + if (profileId >= index->minProfileId && profileId <= index->maxProfileId) { + + // Get the profile values index and check it is valid. + profileIndex = index->items[profileId - index->minProfileId]; + if (profileIndex != NULL) { + + // Callback with all the available value indexes for the profile. + for (uint32_t i = 0; cont && i < profileIndex->count; i++) { + cont = callback(profileIndex->items[i], state); + count++; + } + } + } + return count; } \ No newline at end of file diff --git a/indexes.h b/indexes.h index 84890260..2fb71e3f 100644 --- a/indexes.h +++ b/indexes.h @@ -42,6 +42,7 @@ #pragma warning (default: 5105) #pragma warning (pop) #endif +#include "array.h" #include "data.h" #include "exceptions.h" #include "collection.h" @@ -54,7 +55,7 @@ * the profile for the property. Is an array of uint32_t with entries equal to * the number of properties multiplied by the number of profiles. */ -typedef struct fiftyoneDegrees_index_property_profile{ +typedef struct fiftyone_degrees_index_property_profile{ uint32_t* valueIndexes; // array of value indexes uint32_t availablePropertyCount; // number of available properties uint32_t minProfileId; // minimum profile id @@ -64,6 +65,49 @@ typedef struct fiftyoneDegrees_index_property_profile{ uint32_t filled; // number of elements with values } fiftyoneDegreesIndexPropertyProfile; +/** + * The offset to a value + */ +typedef uint32_t fiftyoneDegreesIndexProfileValue; + +/** + * An array of profile value offsets. + */ +FIFTYONE_DEGREES_ARRAY_TYPE( + fiftyoneDegreesIndexProfileValue, + ) + +/** + * Shorter type for an array of profile values. + */ +typedef fiftyoneDegreesIndexProfileValueArray* + fiftyoneDegreesIndexProfileValuesPtr; + +/** + * Array where the index is the profile id and the value an array of required + * values for the profile id to be returned. + */ +FIFTYONE_DEGREES_ARRAY_TYPE( + fiftyoneDegreesIndexProfileValuesPtr, + uint32_t profileCount; /* total number of profiles */ \ + uint32_t minProfileId; /* minimum profile id */ \ + uint32_t maxProfileId; /* maximum profile id */ +) + +/** + * Shorter type for the array of all profiles and values. + */ +typedef fiftyoneDegreesIndexProfileValuesPtrArray + fiftyoneDegreesIndexAllProfileValues; + +/** + * Callback used with the iterate method to return value offsets for the + * profile. + */ +typedef bool(*fiftyoneDegreesIndexProfileValuesMethod)( + uint32_t valueOffset, + void* state); + /** * Create an index for the profiles, available properties, and values provided * such that given the index to a property and profile the index of the first @@ -109,6 +153,45 @@ EXTERNAL uint32_t fiftyoneDegreesIndexPropertyProfileLookup( uint32_t profileId, uint32_t propertyIndex); +/** + * Create an index for the required property values associated with a profile. + * @param profiles collection of variable sized profiles to be indexed + * @param profileOffsets collection of fixed offsets to profiles to be indexed + * @param available properties provided by the caller + * @param values collection to be indexed + * @param exception pointer to an exception data structure to be used if an + * exception occurs. See exceptions.h + * @return pointer to the index memory structure + */ +EXTERNAL fiftyoneDegreesIndexAllProfileValues* +fiftyoneDegreesIndexProfileValuesCreate( + fiftyoneDegreesCollection* profiles, + fiftyoneDegreesCollection* profileOffsets, + fiftyoneDegreesPropertiesAvailable* available, + fiftyoneDegreesCollection* values, + fiftyoneDegreesException* exception); + +/** + * Frees an index previously created by + * fiftyoneDegreesIndexProfileValuesCreate. + * @param index to be freed + */ +EXTERNAL void fiftyoneDegreesIndexProfileValuesFree( + fiftyoneDegreesIndexAllProfileValues* index); + +/** + * For each of the value indexes associated with the profile id calls the + * callback method with the value and state. + * @param index to use for the values + * @param profileId the values relate to + * @return the number of values + */ +EXTERNAL uint32_t fiftyoneDegreesIndexProfileValuesIterate( + fiftyoneDegreesIndexAllProfileValues* index, + uint32_t profileId, + void* state, + fiftyoneDegreesIndexProfileValuesMethod callback); + /** * @} */ diff --git a/overrides.c b/overrides.c index 00ee7cbd..20185aef 100644 --- a/overrides.c +++ b/overrides.c @@ -262,7 +262,7 @@ uint32_t fiftyoneDegreesOverridesExtractFromEvidence( state.properties = properties; count = EvidenceIterate( evidence, - FIFTYONE_DEGREES_EVIDENCE_COOKIE | + FIFTYONE_DEGREES_EVIDENCE_COOKIE | FIFTYONE_DEGREES_EVIDENCE_QUERY, &state, addOverrideToResults); diff --git a/profile.c b/profile.c index 049c16b1..a373cf49 100644 --- a/profile.c +++ b/profile.c @@ -140,6 +140,17 @@ static uint32_t iterateValues( return count; } +static bool isAvailableProperty( + PropertiesAvailable* available, + uint32_t propertyIndex) { + for (uint32_t i = 0; i < available->count; i++) { + if (available->items[i].propertyIndex == propertyIndex) { + return true; + } + } + return false; +} + uint32_t* fiftyoneDegreesProfileGetOffsetForProfileId( fiftyoneDegreesCollection *profileOffsets, const uint32_t profileId, @@ -362,4 +373,41 @@ uint32_t fiftyoneDegreesProfileIterateProfilesForPropertyAndValue( COLLECTION_RELEASE(properties, &propertyItem); } return count; +} + +uint32_t fiftyoneDegreesProfileIterateValueIndexes( + fiftyoneDegreesProfile* profile, + fiftyoneDegreesPropertiesAvailable* available, + fiftyoneDegreesCollection* values, + void* state, + fiftyoneDegreesProfileIterateValueIndexesMethod callback, + fiftyoneDegreesException* exception) { + Item valueItem; + Value* value; + bool cont = true; + uint32_t count = 0; + const uint32_t* valueIndexes = (const uint32_t*)(profile + 1); + uint32_t valueIndex; + DataReset(&valueItem.data); + + // For all the possible values associated with the profile. + for (uint32_t i = 0; cont && i < profile->valueCount; i++) { + + // Get the value to check if it relates to a required property. + valueIndex = *(valueIndexes + i); + value = values->get(values, valueIndex, &valueItem, exception); + if (value == NULL || EXCEPTION_FAILED) { + return count; + } + + // If the value does relate to an available property then call the + // callback. + if (isAvailableProperty(available, (uint32_t)value->propertyIndex)) { + cont = callback(state, valueIndex); + count++; + } + + COLLECTION_RELEASE(values, &valueItem); + } + return count; } \ No newline at end of file diff --git a/profile.h b/profile.h index 4462ca24..8ea0400a 100644 --- a/profile.h +++ b/profile.h @@ -118,6 +118,17 @@ typedef bool(*fiftyoneDegreesProfileIterateMethod)( void *state, fiftyoneDegreesCollectionItem *item); +/** + * Definition of a callback function which is passed the next values index for + * the profile. + * @param state pointer to data needed by the method + * @param valueIndex for the next value + * @return true if the iteration should continue, otherwise false to stop + */ +typedef bool(*fiftyoneDegreesProfileIterateValueIndexesMethod)( + void* state, + uint32_t valueIndex); + /** * Gets the profile associated with the profileId or NULL if there is no * corresponding profile. @@ -261,12 +272,31 @@ EXTERNAL uint32_t fiftyoneDegreesProfileIterateProfilesForPropertyAndValue( * exception occurs. See exceptions.h * @return pointer to the profile offset or NULL */ -uint32_t* fiftyoneDegreesProfileGetOffsetForProfileId( +EXTERNAL uint32_t* fiftyoneDegreesProfileGetOffsetForProfileId( fiftyoneDegreesCollection *profileOffsets, const uint32_t profileId, uint32_t *profileOffset, fiftyoneDegreesException *exception); +/** + * Calls the callback for every value index available for the profile. + * @param profile to return value indexes for + * @param available required properties + * @param values collection containing all values + * @param state pointer to data needed by the callback method + * @param callback method to be called for each value index + * @param exception pointer to an exception data structure to be used if an + * exception occurs. See exceptions.h + * @return number of times the callback was called. + */ +EXTERNAL uint32_t fiftyoneDegreesProfileIterateValueIndexes( + fiftyoneDegreesProfile* profile, + fiftyoneDegreesPropertiesAvailable* available, + fiftyoneDegreesCollection* values, + void* state, + fiftyoneDegreesProfileIterateValueIndexesMethod callback, + fiftyoneDegreesException* exception); + /** * @} */ From 7a1f494bf02b738c97fa6113f6bb14c7bb3f1785 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 24 May 2024 08:05:07 +0100 Subject: [PATCH 12/73] FEAT: Init JSON output features. --- json.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ json.h | 76 +++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 json.c create mode 100644 json.h diff --git a/json.c b/json.c new file mode 100644 index 00000000..0bf141f5 --- /dev/null +++ b/json.c @@ -0,0 +1,139 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#include "json.h" + +static void addChar(fiftyoneDegreesJson* s, char a) { + if (s->bufferLength > 0) { + *s->buffer = a; + s->buffer++; + s->bufferLength--; + } + s->charsAdded++; +} + +static void addTwo(fiftyoneDegreesJson* s, char a, char b) { + addChar(s, a); + addChar(s, b); +} + +static void addStringEscape( + fiftyoneDegreesJson* s, + const char* value, + size_t valueLength) { + for (int i = 0; i < valueLength; i++) { + switch (value[i]) { + case '\"': + addTwo(s, '\\', '\"'); + break; + case '\b': + addTwo(s, '\\', 'b'); + break; + case '\f': + addTwo(s, '\\', 'f'); + break; + case '\n': + addTwo(s, '\\', 'n'); + break; + case '\r': + addTwo(s, '\\', 'r'); + break; + case '\t': + addTwo(s, '\\', 't'); + break; + default: + addChar(s, value[i]); + break; + } + } +} + +static void addString( + fiftyoneDegreesJson* s, + const char* value, + size_t length) { + addChar(s, '\"'); + addStringEscape(s, value, length); + addChar(s, '\"'); +} + +static void addSeparator(fiftyoneDegreesJson* s) { + addChar(s, ','); +} + +void fiftyoneDegreesJsonDocumentStart(fiftyoneDegreesJson* s) { + addChar(s, '{'); +} + +void fiftyoneDegreesJsonDocumentEnd(fiftyoneDegreesJson* s) { + addChar(s, '}'); +} + +void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* s) { + addSeparator(s); +} + +void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s) { + String* name; + Item stringItem; + Exception* exception = s->exception; + + // Get the property name. + DataReset(&stringItem.data); + name = StringGet( + s->strings, + s->property->nameOffset, + &stringItem, + exception); + if (name != NULL && EXCEPTION_OKAY) { + + // Add the property name to the JSON buffer considering whether + // it's a list or single value property. + addString(s, STRING(name), strlen(STRING(name))); + addChar(s, ':'); + if (s->property->isList) { + addChar(s, '['); + } + + // Release the string. + COLLECTION_RELEASE(s->strings, &stringItem); + } +} + +void fiftyoneDegreesJsonPropertyEnd(fiftyoneDegreesJson* s) { + if (s->property->isList) { + addChar(s, ']'); + } +} + +void fiftyoneDegreesJsonPropertyValues(fiftyoneDegreesJson* s) { + const char* value; + for (uint32_t i = 0; i < s->values->count; i++) { + if (i > 0) { + addSeparator(s); + } + value = STRING((String*)s->values->items[i].data.ptr); + if (value != NULL) { + addString(s, value, strlen(value)); + } + } +} \ No newline at end of file diff --git a/json.h b/json.h new file mode 100644 index 00000000..c031393e --- /dev/null +++ b/json.h @@ -0,0 +1,76 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#ifndef FIFTYONE_DEGREES_JSON_H_INCLUDED +#define FIFTYONE_DEGREES_JSON_H_INCLUDED + + /** + * @ingroup FiftyOneDegreesCommon + * @defgroup FiftyOneDegreesJson JSON + * + * JSON methods + * + * ## Introduction + * + * @{ + */ + +#include +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 5105) +#include +#pragma warning (default: 5105) +#pragma warning (pop) +#endif +#include "property.h" +#include "string.h" +#include "exceptions.h" +#include "fiftyone.h" + +typedef struct fiftyone_degrees_json { + char* buffer; // Buffer to add characters to + size_t bufferLength; // Length of the buffer + size_t charsAdded; // Total characters added over all iterations + fiftyoneDegreesCollection* strings; // Collection of strings + fiftyoneDegreesProperty* property; // The property being processed + fiftyoneDegreesList* values; // The values to be added + fiftyoneDegreesException* exception; +} fiftyoneDegreesJson; + +EXTERNAL void fiftyoneDegreesJsonDocumentStart(fiftyoneDegreesJson* s); + +EXTERNAL void fiftyoneDegreesJsonDocumentEnd(fiftyoneDegreesJson* s); + +EXTERNAL void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s); + +EXTERNAL void fiftyoneDegreesJsonPropertyEnd(fiftyoneDegreesJson* s); + +EXTERNAL void fiftyoneDegreesJsonPropertyValues(fiftyoneDegreesJson* s); + +EXTERNAL void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* s); + +/** + * @} + */ + +#endif \ No newline at end of file From 5eab5052941bb382be8273194316a75f97421066 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 24 May 2024 08:05:37 +0100 Subject: [PATCH 13/73] REFACT: Removed the index for profiles to values as provides little performance improvement in practice. --- config.h | 8 +-- dataset.c | 7 -- dataset.h | 3 - fiftyone.h | 16 ++--- indexes.c | 205 ----------------------------------------------------- indexes.h | 82 --------------------- 6 files changed, 9 insertions(+), 312 deletions(-) diff --git a/config.h b/config.h index fbc7cd70..cb5b518e 100644 --- a/config.h +++ b/config.h @@ -65,8 +65,6 @@ typedef struct fiftyone_degrees_config_base_t { int tempDirCount; /**< Number of directories in the tempDirs array. */ bool propertyValueIndex; /**< Indicates if an index to values for property and profiles should be created. */ - bool profileValuesIndex; /**< Indicates if an index to get profile values - should be created. */ } fiftyoneDegreesConfigBase; /** Default value for the #FIFTYONE_DEGREES_CONFIG_USE_TEMP_FILE macro. */ @@ -104,8 +102,7 @@ FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY_DEFAULT false, /* reuseTempFile */ \ NULL, /* tempDirs */ \ 0, /* tempDirCount */ \ - true, /* propertyValueIndex */ \ - true /* profileValuesIndex */ + true /* propertyValueIndex */ /** * Default value for the #fiftyoneDegreesConfigBase structure without index. @@ -118,8 +115,7 @@ FIFTYONE_DEGREES_CONFIG_ALL_IN_MEMORY_DEFAULT false, /* reuseTempFile */ \ NULL, /* tempDirs */ \ 0, /* tempDirCount */ \ - false, /* propertyValueIndex */ \ - false /* profileValuesIndex */ + false /* propertyValueIndex */ /** * @} diff --git a/dataset.c b/dataset.c index 05bf7a02..7bf87650 100644 --- a/dataset.c +++ b/dataset.c @@ -76,12 +76,6 @@ void fiftyoneDegreesDataSetFree(fiftyoneDegreesDataSetBase *dataSet) { dataSet->indexPropertyProfile = NULL; } - // Free the memory used for the index for profile values. - if (dataSet->indexProfileValues != NULL) { - IndexProfileValuesFree(dataSet->indexProfileValues); - dataSet->indexProfileValues = NULL; - } - // Free the memory used by the unique headers. HeadersFree(dataSet->uniqueHeaders); dataSet->uniqueHeaders = NULL; @@ -122,7 +116,6 @@ void fiftyoneDegreesDataSetReset(fiftyoneDegreesDataSetBase *dataSet) { dataSet->available = NULL; dataSet->overridable = NULL; dataSet->indexPropertyProfile = NULL; - dataSet->indexProfileValues = NULL; dataSet->config = NULL; dataSet->handle = NULL; } diff --git a/dataset.h b/dataset.h index 60184aa3..7209dc71 100644 --- a/dataset.h +++ b/dataset.h @@ -131,9 +131,6 @@ typedef struct fiftyone_degrees_dataset_base_t { look up profile values by property */ - fiftyoneDegreesIndexAllProfileValues* indexProfileValues; /**< Index to - look up profile - values */ const void *config; /**< Pointer to the config used to create the dataset */ } fiftyoneDegreesDataSetBase; diff --git a/fiftyone.h b/fiftyone.h index 9fc8c40e..b05d42d3 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -85,6 +85,7 @@ #include "pair.h" #include "yamlfile.h" #include "indexes.h" +#include "json.h" /** * Macro used to support synonym implementation. Creates a typedef which @@ -181,11 +182,6 @@ MAP_TYPE(Coordinate) MAP_TYPE(KeyValuePair) MAP_TYPE(HeaderID) MAP_TYPE(IndexPropertyProfile) -MAP_TYPE(IndexProfileValue) -MAP_TYPE(IndexProfileValueArray) -MAP_TYPE(IndexProfileValuesPtr) -MAP_TYPE(IndexProfileValuesPtrArray) -MAP_TYPE(IndexAllProfileValues) #define ProfileGetOffsetForProfileId fiftyoneDegreesProfileGetOffsetForProfileId /**< Synonym for #fiftyoneDegreesProfileGetOffsetForProfileId function. */ #define OverrideValuesAdd fiftyoneDegreesOverrideValuesAdd /**< Synonym for #fiftyoneDegreesOverrideValuesAdd function. */ @@ -347,10 +343,12 @@ MAP_TYPE(IndexAllProfileValues) #define IndexPropertyProfileCreate fiftyoneDegreesIndexPropertyProfileCreate /**< Synonym for fiftyoneDegreesIndexPropertyProfileCreate */ #define IndexPropertyProfileFree fiftyoneDegreesIndexPropertyProfileFree /**< Synonym for fiftyoneDegreesIndexPropertyProfileFree */ #define IndexPropertyProfileLookup fiftyoneDegreesIndexPropertyProfileLookup /**< Synonym for fiftyoneDegreesIndexPropertyProfileLookup */ -#define IndexProfileValuesFree fiftyoneDegreesIndexProfileValuesFree /**< Synonym for fiftyoneDegreesIndexProfileValuesFree */ -#define IndexProfileValuesCreate fiftyoneDegreesIndexProfileValuesCreate /**< Synonym for fiftyoneDegreesIndexProfileValuesCreate */ -#define IndexProfileValuesIterate fiftyoneDegreesIndexProfileValuesIterate /**< Synonym for fiftyoneDegreesIndexProfileValuesIterate */ - +#define JsonDocumentStart fiftyoneDegreesJsonDocumentStart /**< Synonym for fiftyoneDegreesJsonDocumentStart */ +#define JsonDocumentEnd fiftyoneDegreesJsonDocumentEnd /**< Synonym for fiftyoneDegreesJsonDocumentEnd */ +#define JsonPropertyStart fiftyoneDegreesJsonPropertyStart /**< Synonym for fiftyoneDegreesJsonPropertyStart */ +#define JsonPropertyEnd fiftyoneDegreesJsonPropertyEnd /**< Synonym for fiftyoneDegreesJsonPropertyEnd */ +#define JsonPropertyValues fiftyoneDegreesJsonPropertyValues /**< Synonym for fiftyoneDegreesJsonPropertyValues */ +#define JsonPropertySeparator fiftyoneDegreesJsonPropertySeparator /**< Synonym for fiftyoneDegreesJsonPropertySeparator */ /* <-- only one asterisk to avoid inclusion in documentation * Shortened macros. diff --git a/indexes.c b/indexes.c index 11e9d554..b6b7954d 100644 --- a/indexes.c +++ b/indexes.c @@ -29,13 +29,6 @@ typedef struct map_t { } map; -typedef void(*iterateProfileValuesMethod)( - IndexPropertyProfile* index, // index in use or null if not available - map* propertyIndexes, // property indexes in ascending order - fiftyoneDegreesCollection* values, // collection of values - Profile* profile, - Exception* exception); - // Gets the index of the profile id in the property profile index. static uint32_t getProfileIndex( IndexPropertyProfile* index, @@ -178,90 +171,6 @@ static map* createPropertyIndexes( return index; } - -// Adds the value index to the array of value indexes in state. -static bool profileValuesAddValueCallback(void* state, uint32_t valueIndex) { - IndexProfileValuesPtr index = (IndexProfileValuesPtr)state; - index->items[index->count++] = valueIndex; - assert(index->count <= index->capacity); - return true; -} - -// Adds all the value indexes to the index array for the profile. The index -// will contain sufficient capacity from a prior call to countValues. -static uint32_t profileValuesAddValues( - fiftyoneDegreesProfile* profile, - fiftyoneDegreesPropertiesAvailable* available, - fiftyoneDegreesCollection* values, - IndexProfileValuesPtr profileIndex, - fiftyoneDegreesException* exception) { - return ProfileIterateValueIndexes( - profile, - available, - values, - (void*)profileIndex, - profileValuesAddValueCallback, - exception); -} - -// Does nothing other than count the value indexes. -static bool profileValuesCountCallback(void* state, uint32_t valueIndex) { - *(uint32_t*)state = valueIndex; - return true; -} - -// Counts the value indexes for the profile that relate to the available -// properties. -static uint32_t profileValuesCountValues( - fiftyoneDegreesProfile* profile, - fiftyoneDegreesPropertiesAvailable* available, - fiftyoneDegreesCollection* values, - fiftyoneDegreesException* exception) { - uint32_t lastValueIndex; // Needed to prevent compiler warning. - return ProfileIterateValueIndexes( - profile, - available, - values, - &lastValueIndex, - profileValuesCountCallback, - exception); -} - -// Creates the initial memory needed for the profile values index. Sets the -// entries to null to handle situations where the profile id is not valid. -static IndexAllProfileValues* profileValuesCreateIndex( - fiftyoneDegreesCollection* profileOffsets, - Exception* exception) { - IndexAllProfileValues* index; - - // Get the minimum and maximum profile id. - uint32_t minProfileId = getProfileId(profileOffsets, 0, exception); - uint32_t maxProfileId = getProfileId( - profileOffsets, - CollectionGetCount(profileOffsets) - 1, - exception); - - // Create the index with sufficient capacity for all possible profile ids. - FIFTYONE_DEGREES_ARRAY_CREATE( - IndexProfileValuesPtr, - index, - maxProfileId - minProfileId + 1); - if (index == NULL || EXCEPTION_FAILED) { - return NULL; - } - index->minProfileId = minProfileId; - index->maxProfileId = maxProfileId; - index->profileCount = CollectionGetCount(profileOffsets); - - // Set all the possible items to NULL and zero to ensure invalid profile - // ids can be identified. - for (uint32_t i = 0; i < index->capacity; i++) { - index->items[i] = NULL; - } - - return index; -} - fiftyoneDegreesIndexPropertyProfile* fiftyoneDegreesIndexPropertyProfileCreate( fiftyoneDegreesCollection* profiles, @@ -350,118 +259,4 @@ uint32_t fiftyoneDegreesIndexPropertyProfileLookup( availablePropertyIndex; assert(valueIndex < index->size); return index->valueIndexes[valueIndex]; -} - -fiftyoneDegreesIndexAllProfileValues* fiftyoneDegreesIndexProfileValuesCreate( - fiftyoneDegreesCollection* profiles, - fiftyoneDegreesCollection* profileOffsets, - fiftyoneDegreesPropertiesAvailable* available, - fiftyoneDegreesCollection* values, - fiftyoneDegreesException* exception) { - Item profileItem; - Profile* profile; - uint32_t count; - int profileIndex; - DataReset(&profileItem.data); - - // Create the array that forms the index for all profiles. - IndexAllProfileValues* index = profileValuesCreateIndex( - profileOffsets, - exception); - if (index == NULL || EXCEPTION_FAILED) { - return NULL; - } - - // For each of the profile ids in the profile offsets. - for (uint32_t i = 0; i < index->profileCount; i++) { - - // Get the next profile based on the index. - profile = (Profile*)ProfileGetByIndex( - profileOffsets, - profiles, - i, - &profileItem, - exception); - if (profile == NULL || EXCEPTION_FAILED) { - IndexProfileValuesFree(index); - return NULL; - } - - // Count the number of required property values that are going to be - // needed for this profile. - count = profileValuesCountValues( - profile, - available, - values, - exception); - if (EXCEPTION_FAILED) { - IndexProfileValuesFree(index); - return NULL; - } - - // Allocate an array of sufficient size to store all the value indexes. - profileIndex = profile->profileId - index->minProfileId; - FIFTYONE_DEGREES_ARRAY_CREATE( - IndexProfileValue, - index->items[profileIndex], - count); - if (index->items[profileIndex] == NULL || EXCEPTION_FAILED) { - IndexProfileValuesFree(index); - return NULL; - } - assert(index->items[profileIndex]->capacity == count); - - // Add the value indexes for each of the available properties for the - // profile. - profileValuesAddValues( - profile, - available, - values, - index->items[profileIndex], - exception); - assert(index->items[profileIndex]->capacity == - index->items[profileIndex]->count); - index->count++; - - // Release the profile. - COLLECTION_RELEASE(profiles, &profileItem); - } - - return index; -} - -void fiftyoneDegreesIndexProfileValuesFree( - fiftyoneDegreesIndexAllProfileValues* index) { - for (uint32_t i = 0; i < index->capacity; i++) { - if (index->items[i] != NULL) { - Free(index->items[i]); - } - } - Free(index); -} - -uint32_t fiftyoneDegreesIndexProfileValuesIterate( - fiftyoneDegreesIndexAllProfileValues* index, - uint32_t profileId, - void* state, - fiftyoneDegreesIndexProfileValuesMethod callback) { - IndexProfileValuesPtr profileIndex; - bool cont = true; - uint32_t count = 0; - - // Check that the profile id is valid considering the min and max ids. - if (profileId >= index->minProfileId && profileId <= index->maxProfileId) { - - // Get the profile values index and check it is valid. - profileIndex = index->items[profileId - index->minProfileId]; - if (profileIndex != NULL) { - - // Callback with all the available value indexes for the profile. - for (uint32_t i = 0; cont && i < profileIndex->count; i++) { - cont = callback(profileIndex->items[i], state); - count++; - } - } - } - return count; } \ No newline at end of file diff --git a/indexes.h b/indexes.h index 2fb71e3f..f06c8b76 100644 --- a/indexes.h +++ b/indexes.h @@ -65,49 +65,6 @@ typedef struct fiftyone_degrees_index_property_profile{ uint32_t filled; // number of elements with values } fiftyoneDegreesIndexPropertyProfile; -/** - * The offset to a value - */ -typedef uint32_t fiftyoneDegreesIndexProfileValue; - -/** - * An array of profile value offsets. - */ -FIFTYONE_DEGREES_ARRAY_TYPE( - fiftyoneDegreesIndexProfileValue, - ) - -/** - * Shorter type for an array of profile values. - */ -typedef fiftyoneDegreesIndexProfileValueArray* - fiftyoneDegreesIndexProfileValuesPtr; - -/** - * Array where the index is the profile id and the value an array of required - * values for the profile id to be returned. - */ -FIFTYONE_DEGREES_ARRAY_TYPE( - fiftyoneDegreesIndexProfileValuesPtr, - uint32_t profileCount; /* total number of profiles */ \ - uint32_t minProfileId; /* minimum profile id */ \ - uint32_t maxProfileId; /* maximum profile id */ -) - -/** - * Shorter type for the array of all profiles and values. - */ -typedef fiftyoneDegreesIndexProfileValuesPtrArray - fiftyoneDegreesIndexAllProfileValues; - -/** - * Callback used with the iterate method to return value offsets for the - * profile. - */ -typedef bool(*fiftyoneDegreesIndexProfileValuesMethod)( - uint32_t valueOffset, - void* state); - /** * Create an index for the profiles, available properties, and values provided * such that given the index to a property and profile the index of the first @@ -153,45 +110,6 @@ EXTERNAL uint32_t fiftyoneDegreesIndexPropertyProfileLookup( uint32_t profileId, uint32_t propertyIndex); -/** - * Create an index for the required property values associated with a profile. - * @param profiles collection of variable sized profiles to be indexed - * @param profileOffsets collection of fixed offsets to profiles to be indexed - * @param available properties provided by the caller - * @param values collection to be indexed - * @param exception pointer to an exception data structure to be used if an - * exception occurs. See exceptions.h - * @return pointer to the index memory structure - */ -EXTERNAL fiftyoneDegreesIndexAllProfileValues* -fiftyoneDegreesIndexProfileValuesCreate( - fiftyoneDegreesCollection* profiles, - fiftyoneDegreesCollection* profileOffsets, - fiftyoneDegreesPropertiesAvailable* available, - fiftyoneDegreesCollection* values, - fiftyoneDegreesException* exception); - -/** - * Frees an index previously created by - * fiftyoneDegreesIndexProfileValuesCreate. - * @param index to be freed - */ -EXTERNAL void fiftyoneDegreesIndexProfileValuesFree( - fiftyoneDegreesIndexAllProfileValues* index); - -/** - * For each of the value indexes associated with the profile id calls the - * callback method with the value and state. - * @param index to use for the values - * @param profileId the values relate to - * @return the number of values - */ -EXTERNAL uint32_t fiftyoneDegreesIndexProfileValuesIterate( - fiftyoneDegreesIndexAllProfileValues* index, - uint32_t profileId, - void* state, - fiftyoneDegreesIndexProfileValuesMethod callback); - /** * @} */ From dc0db9f6deac9eeffe8fb537f31c3c5fbe73a4c0 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 24 May 2024 08:06:29 +0100 Subject: [PATCH 14/73] FEAT: Added JSON to the Visual Studio project. --- VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj index 3cc5186c..efbe6964 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj @@ -19,6 +19,7 @@ + @@ -53,6 +54,7 @@ + From f30aab1234a790922b5a5cc5bfb1c533dc9bab33 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 7 Jun 2024 10:25:03 +0100 Subject: [PATCH 15/73] REFACT/DOC: Added documentation and refactored to avoid using fiftyone.h within the json.c. --- fiftyone.h | 3 ++- json.c | 45 ++++++++++++++++++++++++-------- json.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/fiftyone.h b/fiftyone.h index b05d42d3..0dc03d5b 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -182,6 +182,7 @@ MAP_TYPE(Coordinate) MAP_TYPE(KeyValuePair) MAP_TYPE(HeaderID) MAP_TYPE(IndexPropertyProfile) +MAP_TYPE(Json) #define ProfileGetOffsetForProfileId fiftyoneDegreesProfileGetOffsetForProfileId /**< Synonym for #fiftyoneDegreesProfileGetOffsetForProfileId function. */ #define OverrideValuesAdd fiftyoneDegreesOverrideValuesAdd /**< Synonym for #fiftyoneDegreesOverrideValuesAdd function. */ @@ -429,4 +430,4 @@ MAP_TYPE(IndexPropertyProfile) * @} */ -#endif +#endif \ No newline at end of file diff --git a/json.c b/json.c index 0bf141f5..5d5b2da7 100644 --- a/json.c +++ b/json.c @@ -22,6 +22,8 @@ #include "json.h" +// Add one character to the buffer if space is available. Always advance +// charsAdded. static void addChar(fiftyoneDegreesJson* s, char a) { if (s->bufferLength > 0) { *s->buffer = a; @@ -31,11 +33,13 @@ static void addChar(fiftyoneDegreesJson* s, char a) { s->charsAdded++; } +// Add two characters to the buffer if space is available. static void addTwo(fiftyoneDegreesJson* s, char a, char b) { addChar(s, a); addChar(s, b); } +// Adds a string of characters escaping special characters. static void addStringEscape( fiftyoneDegreesJson* s, const char* value, @@ -67,6 +71,8 @@ static void addStringEscape( } } +// Adds a string including surrounding double quotes and escaping special +// characters. static void addString( fiftyoneDegreesJson* s, const char* value, @@ -76,6 +82,7 @@ static void addString( addChar(s, '\"'); } +// Adds a comma separator. static void addSeparator(fiftyoneDegreesJson* s) { addChar(s, ','); } @@ -93,29 +100,37 @@ void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* s) { } void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s) { - String* name; - Item stringItem; - Exception* exception = s->exception; + fiftyoneDegreesString* name; + fiftyoneDegreesCollectionItem stringItem; + fiftyoneDegreesException* exception = s->exception; - // Get the property name. - DataReset(&stringItem.data); - name = StringGet( + // Check that the property is populated. + if (s->property == NULL) { + FIFTYONE_DEGREES_EXCEPTION_SET( + FIFTYONE_DEGREES_STATUS_NULL_POINTER) + return; + } + + // Get the property name as a string. + fiftyoneDegreesDataReset(&stringItem.data); + name = fiftyoneDegreesStringGet( s->strings, s->property->nameOffset, &stringItem, exception); - if (name != NULL && EXCEPTION_OKAY) { + if (name != NULL && FIFTYONE_DEGREES_EXCEPTION_OKAY) { // Add the property name to the JSON buffer considering whether // it's a list or single value property. - addString(s, STRING(name), strlen(STRING(name))); + const char* value = FIFTYONE_DEGREES_STRING(name); + addString(s, value, strlen(value)); addChar(s, ':'); if (s->property->isList) { addChar(s, '['); } // Release the string. - COLLECTION_RELEASE(s->strings, &stringItem); + FIFTYONE_DEGREES_COLLECTION_RELEASE(s->strings, &stringItem); } } @@ -127,11 +142,21 @@ void fiftyoneDegreesJsonPropertyEnd(fiftyoneDegreesJson* s) { void fiftyoneDegreesJsonPropertyValues(fiftyoneDegreesJson* s) { const char* value; + fiftyoneDegreesException* exception = s->exception; + + // Check that the values is populated. + if (s->values == NULL) { + FIFTYONE_DEGREES_EXCEPTION_SET( + FIFTYONE_DEGREES_STATUS_NULL_POINTER) + return; + } + for (uint32_t i = 0; i < s->values->count; i++) { if (i > 0) { addSeparator(s); } - value = STRING((String*)s->values->items[i].data.ptr); + value = FIFTYONE_DEGREES_STRING( + (fiftyoneDegreesString*)s->values->items[i].data.ptr); if (value != NULL) { addString(s, value, strlen(value)); } diff --git a/json.h b/json.h index c031393e..455008e7 100644 --- a/json.h +++ b/json.h @@ -30,7 +30,25 @@ * JSON methods * * ## Introduction - * + * + * Contains common methods to create JSON documents, add properties, add + * either single or list values to properties. String values are escaped to + * comply with the JSON specification. + * + * ## Data Structures + * + * A single data structure is used with members for a) the output buffer, and + * b) the reference data. + * + * The output buffer is represented as a pointer and a length. An additional + * member is used to record the number of characters that would be needed to + * complete the creation of a valid JSON response. This can be used by the + * caller to increase the buffer size if not big enough and call the related + * methods a subsequent time. + * + * Reference data for the property being added, the values being added, and + * a collection of strings is also provided. + * * @{ */ @@ -44,30 +62,65 @@ #endif #include "property.h" #include "string.h" +#include "list.h" +#include "data.h" +#include "collection.h" +#include "common.h" #include "exceptions.h" -#include "fiftyone.h" +/** + * Structure used to populated a JSON string for all required properties and + * values. The implementation will always check to determine if sufficient + * characters remain in the buffer before adding characters. The charsAdded + * field is always updated to reflect the number of characters that would be + * needed in the buffer if all the values were to be written. This is needed + * to determine when the buffer provided needs to be larger. + */ typedef struct fiftyone_degrees_json { char* buffer; // Buffer to add characters to size_t bufferLength; // Length of the buffer - size_t charsAdded; // Total characters added over all iterations + size_t charsAdded; // Number of characters added or that could be added fiftyoneDegreesCollection* strings; // Collection of strings - fiftyoneDegreesProperty* property; // The property being processed - fiftyoneDegreesList* values; // The values to be added + fiftyoneDegreesProperty* property; // The property being added + fiftyoneDegreesList* values; // The values for the property to be added fiftyoneDegreesException* exception; } fiftyoneDegreesJson; -EXTERNAL void fiftyoneDegreesJsonDocumentStart(fiftyoneDegreesJson* s); +/** + * Writes the start of the JSON document characters to the buffer in json. + * @param json data structure + */ +EXTERNAL void fiftyoneDegreesJsonDocumentStart(fiftyoneDegreesJson* json); -EXTERNAL void fiftyoneDegreesJsonDocumentEnd(fiftyoneDegreesJson* s); +/** + * Writes the end of the JSON document characters to the buffer in json. + * @param json data structure + */ +EXTERNAL void fiftyoneDegreesJsonDocumentEnd(fiftyoneDegreesJson* json); -EXTERNAL void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s); +/** + * Writes the start of the property in json->property to the buffer in json. + * @param json data structure + */ +EXTERNAL void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* json); -EXTERNAL void fiftyoneDegreesJsonPropertyEnd(fiftyoneDegreesJson* s); +/** + * Writes the end of the property in json->property to the buffer in json. + * @param json data structure + */ +EXTERNAL void fiftyoneDegreesJsonPropertyEnd(fiftyoneDegreesJson* json); -EXTERNAL void fiftyoneDegreesJsonPropertyValues(fiftyoneDegreesJson* s); +/** + * Writes the values in the json->values list to the buffer in json. + * @param json data structure + */ +EXTERNAL void fiftyoneDegreesJsonPropertyValues(fiftyoneDegreesJson* json); -EXTERNAL void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* s); +/** + * Writes the a property separator to the buffer in json. + * @param json data structure + */ +EXTERNAL void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* json); /** * @} From d4c6819dd9557eeb8ea6fc8649e2c270aed6be2f Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 17 Jul 2024 14:22:45 +0100 Subject: [PATCH 16/73] BUG: Type should be the same as the length value. --- json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json.c b/json.c index 5d5b2da7..0555ed99 100644 --- a/json.c +++ b/json.c @@ -44,7 +44,7 @@ static void addStringEscape( fiftyoneDegreesJson* s, const char* value, size_t valueLength) { - for (int i = 0; i < valueLength; i++) { + for (size_t i = 0; i < valueLength; i++) { switch (value[i]) { case '\"': addTwo(s, '\\', '\"'); From 1aea193648ba2d4f08495071fcdf1d6672a07e66 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 17 Jul 2024 16:56:48 +0100 Subject: [PATCH 17/73] CLEANUP: Removed methods that are not used and the underlying implementation has been removed. --- ConfigBase.cpp | 8 -------- ConfigBase.hpp | 14 -------------- 2 files changed, 22 deletions(-) diff --git a/ConfigBase.cpp b/ConfigBase.cpp index f7857b14..f37fbade 100644 --- a/ConfigBase.cpp +++ b/ConfigBase.cpp @@ -56,10 +56,6 @@ void ConfigBase::setPropertyValueIndex(bool index) { this->config->propertyValueIndex = index; } -void ConfigBase::setProfileValuesIndex(bool index) { - this->config->profileValuesIndex = index; -} - bool ConfigBase::getUseUpperPrefixHeaders() const { return config->usesUpperPrefixedHeaders; } @@ -80,10 +76,6 @@ bool ConfigBase::getPropertyValueIndex() const { return config->propertyValueIndex; } -bool ConfigBase::getProfileValuesIndex() const { - return config->profileValuesIndex; -} - uint16_t ConfigBase::getConcurrency() const { return 0; } diff --git a/ConfigBase.hpp b/ConfigBase.hpp index 5d6625df..1232599f 100644 --- a/ConfigBase.hpp +++ b/ConfigBase.hpp @@ -120,13 +120,6 @@ namespace FiftyoneDegrees { */ void setPropertyValueIndex(bool index); - /** - * Set whether or not an index to speed up the retrieval of - * required values for profiles is created. - * @param index should create an index - */ - void setProfileValuesIndex(bool index); - /** * @} * @name Getters @@ -170,13 +163,6 @@ namespace FiftyoneDegrees { */ bool getPropertyValueIndex() const; - /** - * Gets a flag indicating if an index of required values for - * profiles is created. - * @return true if an index should be created, or false if not. - */ - bool getProfileValuesIndex() const; - /** * Get the expected number of concurrent accessors of the data set. * @return concurrency From 35e868182e23f0081df3d8ed45c321993311d706 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Thu, 18 Jul 2024 15:46:24 +0100 Subject: [PATCH 18/73] BUG: Added checks for case insensitive header name uniqueness. --- headers.c | 82 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/headers.c b/headers.c index 7226948e..fce71392 100644 --- a/headers.c +++ b/headers.c @@ -27,6 +27,26 @@ /* HTTP header prefix used when processing collections of parameters. */ #define HTTP_PREFIX_UPPER "HTTP_" +/** + * True if the value is not null and not zero length. + */ +static bool isHeaderValid(const char* value) { + return value != NULL && *value != '\0'; +} + +/** + * True if the value does not exist in the headers collection already, + * otherwise false. + */ +static bool isUnique(Headers* headers, const char* value) { + for (uint32_t i = 0; i < headers->count; i++) { + if (StringCompare(headers->items[i].name, value) == 0) { + return false; + } + } + return true; +} + /** * Free the members of the header. */ @@ -91,20 +111,24 @@ static int countHeaderSegments(const char* name) { */ static int countAllSegments(void* state, HeadersGetMethod get) { uint32_t count = 0, index = 0, segments; - Item name; - DataReset(&name.data); - while (get(state, index, &name) >= 0) { + const char* name; + Item item; + DataReset(&item.data); + while (get(state, index, &item) >= 0) { + name = STRING(item.data.ptr); + if (isHeaderValid(name)) { - // Count the number of segments. - segments = countHeaderSegments(STRING(name.data.ptr)); - count += segments; + // Count the number of segments. + segments = countHeaderSegments(STRING(item.data.ptr)); + count += segments; - // If there are more than one segment then this is a pseudo header and - // the count should also include the full header. - if (segments > 1) { - count++; + // If there are more than one segment then this is a pseudo header + // and the count should also include the full header. + if (segments > 1) { + count++; + } } - COLLECTION_RELEASE(name.collection, &name); + COLLECTION_RELEASE(item.collection, &item); index++; } return count; @@ -251,26 +275,30 @@ static bool addHeadersFromDataSet( Item item; long headerId; const char* name; + uint32_t index = 0; DataReset(&item.data); - // Loop through all the available headers in the data set. - while ((headerId = get(state, headers->count, &item)) >= 0) { - - // Set the next header from the data set name item aborting if there - // was a problem. + // Loop through all the available headers in the data set adding those that + // are valid and unique to the headers collection. + while ((headerId = get(state, index++, &item)) >= 0) { name = STRING(item.data.ptr); - if (setHeaderFromDataSet( - &headers->items[headers->count], - name, - strlen(name), - (HeaderID)headerId, - headers->capacity, - exception) == false) { - return false; - } + if (isHeaderValid(name) && isUnique(headers, name)) { + + // Set the next header from the data set name item aborting if + // there was a problem. + if (setHeaderFromDataSet( + &headers->items[headers->count], + name, + strlen(name), + (HeaderID)headerId, + headers->capacity, + exception) == false) { + return false; + } - // Move to the next available header. - headers->count++; + // Move to the next available header to be populated. + headers->count++; + } // Release the header name item before moving to the next one. COLLECTION_RELEASE(item.collection, &item); From 9bd8b03ab4e45a1d214527cd99f1eb7c0abfc03f Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Thu, 18 Jul 2024 15:48:38 +0100 Subject: [PATCH 19/73] TEST/REFACT: Removed tests for pseudo headers that are now handlded differently. New tests to cover the modified implementation need to be added. --- .../Fiftyone.Common.Tests.vcxproj | 2 - .../Fiftyone.Common.Tests.vcxproj.filters | 6 - tests/EvidenceTests.cpp | 31 - ...idenceWithHeadersTests_MultipleHeaders.cpp | 6 +- tests/EvidenceWithHeadersTests_NoHeaders.cpp | 5 +- .../EvidenceWithHeadersTests_SingleHeader.cpp | 5 +- tests/HeadersTests.cpp | 366 ++++---- tests/PseudoHeaderTests.cpp | 803 ------------------ tests/PseudoHeaderTests.hpp | 58 -- 9 files changed, 193 insertions(+), 1089 deletions(-) delete mode 100644 tests/PseudoHeaderTests.cpp delete mode 100644 tests/PseudoHeaderTests.hpp diff --git a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj index 2210f781..cb45d4ce 100644 --- a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj +++ b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj @@ -41,7 +41,6 @@ - @@ -59,7 +58,6 @@ - diff --git a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj.filters b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj.filters index 63f85ceb..f5114936 100644 --- a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj.filters +++ b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj.filters @@ -99,9 +99,6 @@ Source Files - - Source Files - Source Files @@ -140,8 +137,5 @@ Header Files - - Header Files - \ No newline at end of file diff --git a/tests/EvidenceTests.cpp b/tests/EvidenceTests.cpp index ce5c91bb..95a4370d 100644 --- a/tests/EvidenceTests.cpp +++ b/tests/EvidenceTests.cpp @@ -200,35 +200,4 @@ TEST_F(Evidence, Iterate_String_without_pseudo_evidence) { EXPECT_EQ(1, count) << "Number of evidence should be 1\n"; -} - -/* - * Check that the iteration API also iterate through the pseudo evidence list - */ -TEST_F(Evidence, Iterate_String_with_pseudo_evidence) { - CreateEvidence(1); - fiftyoneDegreesEvidenceAddString( - evidence, - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, - "header1", - "value1"); - - evidence->pseudoEvidence = fiftyoneDegreesEvidenceCreate(2); - fiftyoneDegreesEvidenceAddString( - evidence->pseudoEvidence, - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, - "pseudo_header1", - "pseudo_value1"); - - int count = 0; - fiftyoneDegreesEvidenceIterate( - evidence, - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, - &count, - countEvidence); - - EXPECT_EQ(2, count) << - "Number of evidence should be 2\n"; - // The pseudo evidence list should be freed separately - fiftyoneDegreesEvidenceFree(evidence->pseudoEvidence); } \ No newline at end of file diff --git a/tests/EvidenceWithHeadersTests_MultipleHeaders.cpp b/tests/EvidenceWithHeadersTests_MultipleHeaders.cpp index e51846a8..77233a9b 100644 --- a/tests/EvidenceWithHeadersTests_MultipleHeaders.cpp +++ b/tests/EvidenceWithHeadersTests_MultipleHeaders.cpp @@ -24,6 +24,7 @@ #include "EvidenceTests.hpp" #include "../evidence.h" #include "../headers.h" +#include "../exceptions.h" // Header names const char* testEvidenceHeaders_Multiple[] = { @@ -46,13 +47,16 @@ class EvidenceWithHeadersTest_MultipleHeaders : public Evidence fiftyoneDegreesHeaders *headers; void SetUp() { + FIFTYONE_DEGREES_EXCEPTION_CREATE Evidence::SetUp(); count = sizeof(testEvidenceHeaders_Multiple) / sizeof(const char*); strings = new StringCollection(testEvidenceHeaders_Multiple, count); headers = fiftyoneDegreesHeadersCreate( false, strings->getState(), - getHeaderUniqueId); + getHeaderUniqueId, + exception); + FIFTYONE_DEGREES_EXCEPTION_THROW } void TearDown() { fiftyoneDegreesHeadersFree(headers); diff --git a/tests/EvidenceWithHeadersTests_NoHeaders.cpp b/tests/EvidenceWithHeadersTests_NoHeaders.cpp index bb9bf74a..3f8e16bc 100644 --- a/tests/EvidenceWithHeadersTests_NoHeaders.cpp +++ b/tests/EvidenceWithHeadersTests_NoHeaders.cpp @@ -38,13 +38,16 @@ class EvidenceWithHeadersTest_NoHeader : public Evidence fiftyoneDegreesHeaders *headers; void SetUp() { + FIFTYONE_DEGREES_EXCEPTION_CREATE Evidence::SetUp(); count = 0; strings = new StringCollection(testEvidenceHeaders_None, count); headers = fiftyoneDegreesHeadersCreate( false, strings->getState(), - getHeaderUniqueId); + getHeaderUniqueId, + exception); + FIFTYONE_DEGREES_EXCEPTION_THROW } void TearDown() { fiftyoneDegreesHeadersFree(headers); diff --git a/tests/EvidenceWithHeadersTests_SingleHeader.cpp b/tests/EvidenceWithHeadersTests_SingleHeader.cpp index 5b5e2242..f34376ae 100644 --- a/tests/EvidenceWithHeadersTests_SingleHeader.cpp +++ b/tests/EvidenceWithHeadersTests_SingleHeader.cpp @@ -40,13 +40,16 @@ class EvidenceWithHeadersTest_SingleHeader : public Evidence fiftyoneDegreesHeaders *headers; void SetUp() { + FIFTYONE_DEGREES_EXCEPTION_CREATE Evidence::SetUp(); count = sizeof(testEvidenceHeaders_Single) / sizeof(const char*); strings = new StringCollection(testEvidenceHeaders_Single, count); headers = fiftyoneDegreesHeadersCreate( false, strings->getState(), - getHeaderUniqueId); + getHeaderUniqueId, + exception); + FIFTYONE_DEGREES_EXCEPTION_THROW } void TearDown() { diff --git a/tests/HeadersTests.cpp b/tests/HeadersTests.cpp index 41b31b7c..9e8130ca 100644 --- a/tests/HeadersTests.cpp +++ b/tests/HeadersTests.cpp @@ -74,12 +74,14 @@ class HeadersTests : public Base { const char** headersList, int headersCount, bool expectUpperPrefixedHeaders) { + EXCEPTION_CREATE count = headersCount; strings = new StringCollection(headersList, count); headers = fiftyoneDegreesHeadersCreate( expectUpperPrefixedHeaders, strings->getState(), - getHeaderUniqueId); + getHeaderUniqueId, + exception); } }; @@ -97,7 +99,6 @@ TEST_F(HeadersTests, Single) { sizeof(testHeaders_Single) / sizeof(const char*), false); ASSERT_EQ(1, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); for (uint32_t i = 0; i < headers->count; i++) { EXPECT_EQ(true, headers->items[i].isDataSet); } @@ -124,7 +125,6 @@ TEST_F(HeadersTests, Multiple) { false); ASSERT_EQ(4, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); for (uint32_t i = 0; i < headers->count; i++) { EXPECT_EQ(true, headers->items[i].isDataSet); } @@ -151,7 +151,6 @@ TEST_F(HeadersTests, SingleDuplicate) { false); ASSERT_EQ(1, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); for (uint32_t i = 0; i < headers->count; i++) { EXPECT_EQ(true, headers->items[i].isDataSet); } @@ -179,7 +178,6 @@ TEST_F(HeadersTests, MultipleDuplicate) { false); ASSERT_EQ(3, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); for (uint32_t i = 0; i < headers->count; i++) { EXPECT_EQ(true, headers->items[i].isDataSet); } @@ -206,7 +204,6 @@ TEST_F(HeadersTests, EmptyString) { false); ASSERT_EQ(2, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); for (uint32_t i = 0; i < headers->count; i++) { EXPECT_EQ(true, headers->items[i].isDataSet); } @@ -232,7 +229,6 @@ TEST_F(HeadersTests, NullString) { false); ASSERT_EQ(2, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); for (uint32_t i = 0; i < headers->count; i++) { EXPECT_EQ(true, headers->items[i].isDataSet); } @@ -257,7 +253,6 @@ TEST_F(HeadersTests, CheckCase) { sizeof(testHeaders_Case) / sizeof(const char*), false); ASSERT_EQ(2, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); for (uint32_t i = 0; i < headers->count; i++) { EXPECT_EQ(true, headers->items[i].isDataSet); } @@ -305,7 +300,6 @@ TEST_F(HeadersTests, None) { 0, false); ASSERT_EQ(0, headers->count); - EXPECT_EQ(0, headers->pseudoHeadersCount); } // ---------------------------------------------------------------------- @@ -321,180 +315,180 @@ const char* testHeaders_PseudoHeaders[] = { "header1\x1Fheader2\x1Fheader3" }; -TEST_F(HeadersTests, PseudoHeadersPositive) { - CreateHeaders( - testHeaders_PseudoHeaders, - sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), - false); - EXPECT_EQ(6, headers->count); - EXPECT_EQ(3, headers->pseudoHeadersCount); - - for (uint32_t i = 0; i < headers->count; i++) { - EXPECT_EQ(true, headers->items[i].isDataSet); - } - - EXPECT_STREQ("header1", headers->items[0].name); - EXPECT_EQ(1, headers->items[0].segments->count); - - EXPECT_STREQ("header2", headers->items[1].name); - EXPECT_EQ(1, headers->items[1].segments->count); - - EXPECT_STREQ("header3", headers->items[2].name); - EXPECT_EQ(1, headers->items[2].segments->count); - - EXPECT_STREQ("header1\x1Fheader2", headers->items[3].name); - EXPECT_EQ(2, headers->items[3].segments->count); - EXPECT_EQ(7, headers->items[3].segments->items[0].length); - EXPECT_EQ(7, headers->items[3].segments->items[1].length); - EXPECT_EQ(0, StringCompareLength( - "header1", - headers->items[3].segments->items[0].segment, - 7)); - EXPECT_EQ(0, StringCompareLength( - "header2", - headers->items[3].segments->items[1].segment, - 7)); - - EXPECT_STREQ("header2\x1Fheader3", headers->items[4].name); - EXPECT_EQ(2, headers->items[4].segments->count); - EXPECT_EQ(7, headers->items[4].segments->items[0].length); - EXPECT_EQ(7, headers->items[4].segments->items[1].length); - EXPECT_EQ(0, StringCompareLength( - "header2", - headers->items[4].segments->items[0].segment, - 7)); - EXPECT_EQ(0, StringCompareLength( - "header3", - headers->items[4].segments->items[1].segment, - 7)); - - EXPECT_STREQ("header1\x1Fheader2\x1Fheader3", headers->items[5].name); - EXPECT_EQ(3, headers->items[5].segments->count); - EXPECT_EQ(7, headers->items[5].segments->items[0].length); - EXPECT_EQ(7, headers->items[5].segments->items[1].length); - EXPECT_EQ(7, headers->items[5].segments->items[2].length); - EXPECT_EQ(0, StringCompareLength( - "header1", - headers->items[5].segments->items[0].segment, - 7)); - EXPECT_EQ(0, StringCompareLength( - "header2", - headers->items[5].segments->items[1].segment, - 7)); - EXPECT_EQ(0, StringCompareLength( - "header3", - headers->items[5].segments->items[2].segment, - 7)); -} - -// ---------------------------------------------------------------------- -// Check that header collection creation adds headers contained in a -// pseudo header if it is not already present. -// ---------------------------------------------------------------------- -const char* testHeaders_PseudoHeadersMissing[] = { - "header1\x1Fheader2" -}; - -TEST_F(HeadersTests, PseudoHeadersMissing) { - CreateHeaders( - testHeaders_PseudoHeadersMissing, - sizeof(testHeaders_PseudoHeadersMissing) / sizeof(const char*), - false); - EXPECT_EQ(3, headers->count); - EXPECT_EQ(1, headers->pseudoHeadersCount); - - EXPECT_STREQ("header1\x1Fheader2", headers->items[0].name); - EXPECT_EQ(true, headers->items[0].isDataSet); - EXPECT_EQ(2, headers->items[0].segments->count); - EXPECT_EQ(7, headers->items[0].segments->items[0].length); - EXPECT_EQ(7, headers->items[0].segments->items[1].length); - EXPECT_EQ(0, StringCompareLength( - "header1", - headers->items[0].segments->items[0].segment, - 7)); - EXPECT_EQ(0, StringCompareLength( - "header2", - headers->items[0].segments->items[1].segment, - 7)); - - EXPECT_STREQ("header1", headers->items[1].name); - EXPECT_EQ(false, headers->items[1].isDataSet); - EXPECT_EQ(1, headers->items[1].segments->count); - EXPECT_EQ(7, headers->items[1].segments->items[0].length); - EXPECT_EQ(0, StringCompareLength( - "header1", - headers->items[1].segments->items[0].segment, - 7)); - - EXPECT_STREQ("header2", headers->items[2].name); - EXPECT_EQ(false, headers->items[1].isDataSet); - EXPECT_EQ(1, headers->items[2].segments->count); - EXPECT_EQ(7, headers->items[2].segments->items[0].length); - EXPECT_EQ(0, StringCompareLength( - "header2", - headers->items[2].segments->items[0].segment, - 7)); -} - -// ---------------------------------------------------------------------- -// Check that header collection creation construct pseudo headers -// correctly when pseudo header contains special cases. -// These special cases are very unlikely to happen but are valid. Thus -// added to test the robustness of the code. -// ---------------------------------------------------------------------- - -const char* testHeaders_PseudoHeadersSpecialCases[] = { - "header1", - "header2", - "\x1Fheader1", - "header1\x1F", - "\x1F\x1F\x1F", - "header1\x1F\x1Fheader2" -}; - -TEST_F(HeadersTests, PseudoHeadersSpecialCases) { - CreateHeaders( - testHeaders_PseudoHeadersSpecialCases, - sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), - false); - EXPECT_EQ(5, headers->count); - EXPECT_EQ(1, headers->pseudoHeadersCount); - for (uint32_t i = 0; i < headers->count; i++) { - EXPECT_EQ(true, headers->items[i].isDataSet); - } - - EXPECT_STREQ("header1", headers->items[0].name); - EXPECT_EQ(1, headers->items[0].segments->count); - - EXPECT_STREQ("header2", headers->items[1].name); - EXPECT_EQ(1, headers->items[1].segments->count); - - EXPECT_STREQ("\x1Fheader1", headers->items[2].name); - EXPECT_EQ(1, headers->items[2].segments->count); - EXPECT_EQ(7, headers->items[2].segments->items[0].length); - EXPECT_EQ(0, StringCompareLength( - "header1", - headers->items[2].segments->items[0].segment, - 7)); - - EXPECT_STREQ("header1\x1F", headers->items[3].name); - EXPECT_EQ(1, headers->items[3].segments->count); - EXPECT_EQ(7, headers->items[3].segments->items[0].length); - EXPECT_EQ(0, StringCompareLength( - "header1", - headers->items[3].segments->items[0].segment, - 7)); - - EXPECT_STREQ("header1\x1F\x1Fheader2", headers->items[4].name); - EXPECT_EQ(2, headers->items[4].segments->count); - EXPECT_EQ(7, headers->items[4].segments->items[0].length); - EXPECT_EQ(7, headers->items[4].segments->items[1].length); - EXPECT_EQ(0, StringCompareLength( - "header1", - headers->items[4].segments->items[0].segment, - 7)); - EXPECT_EQ(0, StringCompareLength( - "header2", - headers->items[4].segments->items[1].segment, - 7)); -} \ No newline at end of file +// TODO - Review as part of test refactor +//TEST_F(HeadersTests, PseudoHeadersPositive) { +// CreateHeaders( +// testHeaders_PseudoHeaders, +// sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), +// false); +// EXPECT_EQ(6, headers->count); +// +// for (uint32_t i = 0; i < headers->count; i++) { +// EXPECT_EQ(true, headers->items[i].isDataSet); +// } +// +// EXPECT_STREQ("header1", headers->items[0].name); +// EXPECT_EQ(1, headers->items[0].segments->count); +// +// EXPECT_STREQ("header2", headers->items[1].name); +// EXPECT_EQ(1, headers->items[1].segments->count); +// +// EXPECT_STREQ("header3", headers->items[2].name); +// EXPECT_EQ(1, headers->items[2].segments->count); +// +// EXPECT_STREQ("header1\x1Fheader2", headers->items[3].name); +// EXPECT_EQ(2, headers->items[3].segments->count); +// EXPECT_EQ(7, headers->items[3].segments->items[0].length); +// EXPECT_EQ(7, headers->items[3].segments->items[1].length); +// EXPECT_EQ(0, StringCompareLength( +// "header1", +// headers->items[3].segments->items[0].segment, +// 7)); +// EXPECT_EQ(0, StringCompareLength( +// "header2", +// headers->items[3].segments->items[1].segment, +// 7)); +// +// EXPECT_STREQ("header2\x1Fheader3", headers->items[4].name); +// EXPECT_EQ(2, headers->items[4].segments->count); +// EXPECT_EQ(7, headers->items[4].segments->items[0].length); +// EXPECT_EQ(7, headers->items[4].segments->items[1].length); +// EXPECT_EQ(0, StringCompareLength( +// "header2", +// headers->items[4].segments->items[0].segment, +// 7)); +// EXPECT_EQ(0, StringCompareLength( +// "header3", +// headers->items[4].segments->items[1].segment, +// 7)); +// +// EXPECT_STREQ("header1\x1Fheader2\x1Fheader3", headers->items[5].name); +// EXPECT_EQ(3, headers->items[5].segments->count); +// EXPECT_EQ(7, headers->items[5].segments->items[0].length); +// EXPECT_EQ(7, headers->items[5].segments->items[1].length); +// EXPECT_EQ(7, headers->items[5].segments->items[2].length); +// EXPECT_EQ(0, StringCompareLength( +// "header1", +// headers->items[5].segments->items[0].segment, +// 7)); +// EXPECT_EQ(0, StringCompareLength( +// "header2", +// headers->items[5].segments->items[1].segment, +// 7)); +// EXPECT_EQ(0, StringCompareLength( +// "header3", +// headers->items[5].segments->items[2].segment, +// 7)); +//} +// +//// ---------------------------------------------------------------------- +//// Check that header collection creation adds headers contained in a +//// pseudo header if it is not already present. +//// ---------------------------------------------------------------------- +//const char* testHeaders_PseudoHeadersMissing[] = { +// "header1\x1Fheader2" +//}; +// +//TEST_F(HeadersTests, PseudoHeadersMissing) { +// CreateHeaders( +// testHeaders_PseudoHeadersMissing, +// sizeof(testHeaders_PseudoHeadersMissing) / sizeof(const char*), +// false); +// EXPECT_EQ(3, headers->count); +// EXPECT_EQ(1, headers->pseudoHeadersCount); +// +// EXPECT_STREQ("header1\x1Fheader2", headers->items[0].name); +// EXPECT_EQ(true, headers->items[0].isDataSet); +// EXPECT_EQ(2, headers->items[0].segments->count); +// EXPECT_EQ(7, headers->items[0].segments->items[0].length); +// EXPECT_EQ(7, headers->items[0].segments->items[1].length); +// EXPECT_EQ(0, StringCompareLength( +// "header1", +// headers->items[0].segments->items[0].segment, +// 7)); +// EXPECT_EQ(0, StringCompareLength( +// "header2", +// headers->items[0].segments->items[1].segment, +// 7)); +// +// EXPECT_STREQ("header1", headers->items[1].name); +// EXPECT_EQ(false, headers->items[1].isDataSet); +// EXPECT_EQ(1, headers->items[1].segments->count); +// EXPECT_EQ(7, headers->items[1].segments->items[0].length); +// EXPECT_EQ(0, StringCompareLength( +// "header1", +// headers->items[1].segments->items[0].segment, +// 7)); +// +// EXPECT_STREQ("header2", headers->items[2].name); +// EXPECT_EQ(false, headers->items[1].isDataSet); +// EXPECT_EQ(1, headers->items[2].segments->count); +// EXPECT_EQ(7, headers->items[2].segments->items[0].length); +// EXPECT_EQ(0, StringCompareLength( +// "header2", +// headers->items[2].segments->items[0].segment, +// 7)); +//} +// +//// ---------------------------------------------------------------------- +//// Check that header collection creation construct pseudo headers +//// correctly when pseudo header contains special cases. +//// These special cases are very unlikely to happen but are valid. Thus +//// added to test the robustness of the code. +//// ---------------------------------------------------------------------- +// +//const char* testHeaders_PseudoHeadersSpecialCases[] = { +// "header1", +// "header2", +// "\x1Fheader1", +// "header1\x1F", +// "\x1F\x1F\x1F", +// "header1\x1F\x1Fheader2" +//}; +// +//TEST_F(HeadersTests, PseudoHeadersSpecialCases) { +// CreateHeaders( +// testHeaders_PseudoHeadersSpecialCases, +// sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), +// false); +// EXPECT_EQ(5, headers->count); +// EXPECT_EQ(1, headers->pseudoHeadersCount); +// for (uint32_t i = 0; i < headers->count; i++) { +// EXPECT_EQ(true, headers->items[i].isDataSet); +// } +// +// EXPECT_STREQ("header1", headers->items[0].name); +// EXPECT_EQ(1, headers->items[0].segments->count); +// +// EXPECT_STREQ("header2", headers->items[1].name); +// EXPECT_EQ(1, headers->items[1].segments->count); +// +// EXPECT_STREQ("\x1Fheader1", headers->items[2].name); +// EXPECT_EQ(1, headers->items[2].segments->count); +// EXPECT_EQ(7, headers->items[2].segments->items[0].length); +// EXPECT_EQ(0, StringCompareLength( +// "header1", +// headers->items[2].segments->items[0].segment, +// 7)); +// +// EXPECT_STREQ("header1\x1F", headers->items[3].name); +// EXPECT_EQ(1, headers->items[3].segments->count); +// EXPECT_EQ(7, headers->items[3].segments->items[0].length); +// EXPECT_EQ(0, StringCompareLength( +// "header1", +// headers->items[3].segments->items[0].segment, +// 7)); +// +// EXPECT_STREQ("header1\x1F\x1Fheader2", headers->items[4].name); +// EXPECT_EQ(2, headers->items[4].segments->count); +// EXPECT_EQ(7, headers->items[4].segments->items[0].length); +// EXPECT_EQ(7, headers->items[4].segments->items[1].length); +// EXPECT_EQ(0, StringCompareLength( +// "header1", +// headers->items[4].segments->items[0].segment, +// 7)); +// EXPECT_EQ(0, StringCompareLength( +// "header2", +// headers->items[4].segments->items[1].segment, +// 7)); +//} \ No newline at end of file diff --git a/tests/PseudoHeaderTests.cpp b/tests/PseudoHeaderTests.cpp deleted file mode 100644 index 13c55b3a..00000000 --- a/tests/PseudoHeaderTests.cpp +++ /dev/null @@ -1,803 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#include "PseudoHeaderTests.hpp" -#include "../pseudoheader.h" -#include "../fiftyone.h" - -#define PSEUDO_HEADERS_SIZE 3 -#define HEADERS_SIZE 8 -#define HEADERS_DUPLICATE_SIZE 13 -#define HEADERS_NO_PSEUDO_SIZE 2 -// This is the number of actual unique headers plus the headers which are -// constructed from the segments of each pseudo header. -#define HEADERS_CONSTRUCTED_SIZE 20 -#define PREFIXES_SIZE 2 - -// Order of precedence for accepted prefixes -static const EvidencePrefix orderOfPrecedence[PREFIXES_SIZE] = - { - FIFTYONE_DEGREES_EVIDENCE_QUERY, - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }; - -// Fixed set of unique headers -static const char *uniqueHeaders[HEADERS_SIZE] = { - "header1", - "header2", - "header3", - "header4", - "header5", - "header1\x1Fheader2", - "header2\x1Fheader3", - "header1\x1Fheader2\x1Fheader3" -}; - -static const char* nonUniqueHeaders[HEADERS_DUPLICATE_SIZE] = { - "header1\x1Fheader2", - "header2\x1Fheader3", - "header1", - "header2", - "header1", - "header2", - "header5", - "header3", - "header4", - "header1\x1Fheader2\x1Fheader3", - "header3", - "header4", - "header5" -}; - -static const char *uniqueHeadersNoPseudoHeader[HEADERS_NO_PSEUDO_SIZE] = { - "header1", - "header2" -}; - -void PseudoHeaderTests::SetUp() { - Base::SetUp(); - evidence = EvidenceCreate(HEADERS_SIZE); - evidence->pseudoEvidence = EvidenceCreate(PSEUDO_HEADERS_SIZE); - for (int i = 0; i < PSEUDO_HEADERS_SIZE; i++) { - evidence->pseudoEvidence->items[i].originalValue = Malloc(PSEUDO_BUFFER_SIZE); - EXPECT_TRUE(evidence->pseudoEvidence->items[i].originalValue != NULL); - memset( - (void*)evidence->pseudoEvidence->items[i].originalValue, - '\0', - PSEUDO_BUFFER_SIZE); - } -} -void PseudoHeaderTests::TearDown() { - for (int i = 0; i < PSEUDO_HEADERS_SIZE; i++) { - fiftyoneDegreesFree( - (void *)evidence->pseudoEvidence->items[i].originalValue); - } - HeadersFree(acceptedHeaders); - EvidenceFree(evidence->pseudoEvidence); - EvidenceFree(evidence); - if (strings != nullptr) { - delete strings; - } - Base::TearDown(); -} - -/* - * Create an headers structure with the specified capacity and a string - * collection constructed by the StringCollection class from the list of - * header names provided, using the create method in headers.c. The - * expected memory allocation is calculated, and the actual memory - * allocation is tracked. The structure is freed automatically after each - * test, at which point the expected and actual memory allocation is - * checked for equality. - */ -void PseudoHeaderTests::createHeaders( - const char** headersList, - int headersCount, - bool expectUpperPrefixedHeaders) { - int count = headersCount; - strings = new StringCollection(headersList, count); - acceptedHeaders = HeadersCreate( - expectUpperPrefixedHeaders, - strings->getState(), - getHeaderUniqueId); -} - -void PseudoHeaderTests::addEvidence( - testKeyValuePair* evidenceList, - int size, - EvidencePrefix prefix) { - for (int i = 0; i < size; i++) { - EvidenceAddString( - evidence, - prefix, - evidenceList[i].key, - evidenceList[i].value - ); - } -} - -void PseudoHeaderTests::checkResults( - const testExpectedResult *expectedEvidence, - int size) { - EXPECT_EQ(size, evidence->pseudoEvidence->count) << - "Incorrect number of pseudo evidence constructed, here it should be " << - size << "\n"; - for (int i = 0; i < size; i++) { - EXPECT_EQ( - expectedEvidence[i].prefix, - evidence->pseudoEvidence->items[i].prefix) << - "Prefix created should be " << expectedEvidence[i].prefix << "\n"; - EXPECT_EQ(0, strcmp( - expectedEvidence[i].result, - (const char*)evidence->pseudoEvidence->items[i].originalValue)) << - "i="<< i << " Pseudo Evidence " << evidence->pseudoEvidence->items[i].field << " is " << (const char *)evidence->pseudoEvidence->items[i].originalValue <<" is not the same where it should be " << - expectedEvidence[i].result << "\n"; - } -} - -void PseudoHeaderTests::removePseudoEvidence(size_t bufferSize) { - // Test if free work correctly - PseudoHeadersRemoveEvidence(evidence, bufferSize); - EXPECT_EQ(0, evidence->pseudoEvidence->count = 0); - for (uint32_t i = 0; i < evidence->pseudoEvidence->capacity; i++) { - EXPECT_EQ(NULL, evidence->pseudoEvidence->items[i].field) << - "Field should be set to NULL\n"; - EXPECT_EQ('\0', - ((const char*) - evidence->pseudoEvidence->items[i].originalValue)[0]) << - "Memory should be reset to all NULL\n"; - } -} - -int PseudoHeaderTests::getNextPseudoIndex(const char* headers[], int size, int index) { - do { - index++; - } while (index < size && strstr(headers[index], "\x1f") == nullptr); - return index; -} - -/** - * Check that when there are duplicate headers and pseudo headers, - * the unique headers will be constructed correctly. This also tests - * a case where an exception is thrown when a pseudo - * header follows a series of duplicates. - */ -TEST_F(PseudoHeaderTests, CreateWithNonUnique) { - // Create headers - createHeaders(nonUniqueHeaders, HEADERS_DUPLICATE_SIZE, false); - - bool foundPastIndex = false; - int index = -1; - do { - index = getNextPseudoIndex(nonUniqueHeaders, HEADERS_DUPLICATE_SIZE, index); - foundPastIndex = foundPastIndex || - (index < HEADERS_DUPLICATE_SIZE && index > (int)acceptedHeaders->count); - } while (index < HEADERS_DUPLICATE_SIZE); - EXPECT_TRUE(foundPastIndex) << - L"The non unique set of headers is not set up properly to prevent" << - L" regression."; - EXPECT_EQ(acceptedHeaders->capacity, HEADERS_CONSTRUCTED_SIZE); - EXPECT_EQ(acceptedHeaders->count, HEADERS_SIZE); -} - -/* - * Check that pseudo evidence are created correctly if their corresponding - * request evidence are present in the evidence collection. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationPositiveValidInput) { - // Expected value - const testExpectedResult expectedEvidence[3] = - { - { - "value1\x1Fvalue2", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - "value2\x1Fvalue3", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - "value1\x1Fvalue2\x1Fvalue3", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[3] = - { {"header3", "value3"}, {"header1", "value1"}, {"header2", "value2"} }; - addEvidence(evidenceList, 3, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 3); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that pseudo evidence won't be created if only part of the request - * evidence present in the evidence collection. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationPositivePartialInput) { - // Expected value - const testExpectedResult expectedEvidence[1] = - { - { - "value2\x1Fvalue3", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[2] = - { {"header3", "value3"}, {"header2", "value2"}}; - addEvidence(evidenceList, 2, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 1); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that if the request evidence with other prefixes than 'header' or - * 'query' will not be considered when constructing the pseudo evidence. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationPositiveCookieInput) { - // Expected value - const testExpectedResult expectedEvidence[2] = - { - { - "value1\x1Fvalue2", - FIFTYONE_DEGREES_EVIDENCE_QUERY - }, - { - "value2\x1Fvalue3", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair headerEvidenceList[2] = - { {"header3", "value3"}, {"header2", "value2"} }; - addEvidence( - headerEvidenceList, - 2, - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - testKeyValuePair queryEvidenceList[2] = - { {"header1", "value1"}, {"header2", "value2"} }; - addEvidence( - queryEvidenceList, - 2, - FIFTYONE_DEGREES_EVIDENCE_QUERY); - - testKeyValuePair queryEvidence = { "header1", "value1" }; - addEvidence(&queryEvidence, 1, FIFTYONE_DEGREES_EVIDENCE_COOKIE); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 2); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that if the request evidence contains a two complete set of evidence - * for the same pseudo header, the set with higher order of precedence should - * be picked. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationOrderOfPrecedenceDuplicate) { - // Expected value - const testExpectedResult expectedEvidence[1] = - { - { - "value1\x1Fvalue2", - FIFTYONE_DEGREES_EVIDENCE_QUERY - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair headerEvidenceList[2] = - { {"header1", "value1"}, {"header2", "value2"} }; - addEvidence( - headerEvidenceList, - 2, - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - testKeyValuePair queryEvidenceList[2] = - { {"header1", "value1"}, {"header2", "value2"} }; - addEvidence( - queryEvidenceList, - 2, - FIFTYONE_DEGREES_EVIDENCE_QUERY); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 1); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that if the request evidence contains a two set of evidence for the - * same pseudo header, only the complete set will be picked. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationOrderOfPrecedenceNoDuplicate) { - // Expected value - const testExpectedResult expectedEvidence[1] = - { - { - "value1\x1Fvalue2", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair headerEvidenceList[2] = - { {"header1", "value1"}, {"header2", "value2"} }; - addEvidence( - headerEvidenceList, - 2, - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - testKeyValuePair queryEvidenceList[1] = - { {"header2", "value2"} }; - addEvidence( - queryEvidenceList, - 1, - FIFTYONE_DEGREES_EVIDENCE_QUERY); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 1); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that if no pseudo header are present, no pseudo evidence will be created - */ -TEST_F(PseudoHeaderTests, EvidenceCreationNoPseudoHeaders) { - // Create headers - createHeaders(uniqueHeadersNoPseudoHeader, HEADERS_NO_PSEUDO_SIZE, false); - - testKeyValuePair evidenceList[3] = - { {"header1", "value1"}, {"header2", "value2"}, {"header3", "value3"} }; - addEvidence(evidenceList, 3, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - EXPECT_EQ(0, evidence->pseudoEvidence->count) << - "No pseudo evidence should has been added\n"; - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that if no request evidence are present, no pseudo evidence will be - * created. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationNoRequestHeadersForPseudoHeaders) { - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[2] = - { {"header4", "value4"}, {"header5", "value5"} }; - addEvidence(evidenceList, 2, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - EXPECT_EQ(0, evidence->pseudoEvidence->count) << - "No pseudo evidence should has been added\n"; - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that if the request evidence are present with blank values, pseudo - * evidence will only be created if at least one request evidence is non-blank. - * If all request evidence are blank, the pseudo evidence won't be constructed. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationBlankRequestHeadersForPseudoHeaders) { - // Expected value - const testExpectedResult expectedEvidence[3] = - { - { - "\x1F", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - "\x1Fvalue3", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - "\x1F\x1Fvalue3", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[3] = - { - {"header1", ""}, - {"header2", ""}, - {"header3", "value3"} - }; - addEvidence(evidenceList, 3, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 3); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that the PseudoHeadersAddEvidence APIs handle NULL pointer input - * correctly. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationNullPointerInput) { - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - // Check if exception is set correctly for NULL evidence pointer - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - NULL, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXPECT_EQ(FIFTYONE_DEGREES_STATUS_NULL_POINTER, exception->status) << - "Status code should be NULL_POINTER where evidence pointer is null\n"; - - // Check if exception is set correctly for NULL headers pointer - PseudoHeadersAddEvidence( - evidence, - NULL, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXPECT_EQ(FIFTYONE_DEGREES_STATUS_NULL_POINTER, exception->status) << - "Status code should be NULL_POINTER where headers pointer is null\n"; -} - -/* - * Check that pseudo evidence that has already been provided in the evidence - * collection will not be created again. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationPseudoEvidenceAlreadyIncluded) { - // Expected value - const testExpectedResult expectedEvidence[2] = - { - { - "value1\x1Fvalue2", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - "value1\x1Fvalue2\x1Fvalue3", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[3] = - { - {"header1", "value1"}, - {"header2", "value2"}, - {"header3", "value3"} - }; - addEvidence(evidenceList, 3, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - testKeyValuePair queryEvidence = - { "header2\x1Fheader3", "value2\x1Fvalue3" }; - addEvidence(&queryEvidence, 1, FIFTYONE_DEGREES_EVIDENCE_QUERY); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 2); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that no pseudo evidence is created if all already been provied in the - * evidence collection. - */ -TEST_F(PseudoHeaderTests, EvidenceCreationPseudoEvidenceAllAlreadyIncluded) { - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[3] = - { - {"header1", "value1"}, - {"header2", "value2"}, - {"header3", "value3"} - }; - addEvidence(evidenceList, 3, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - testKeyValuePair evidenceList2[3] = - { - {"header1\x1Fheader2", "value1\x1Fvalue2"}, - { "header2\x1Fheader3", "value2\x1Fvalue3" }, - { "header1\x1Fheader2\x1Fheader3", "value1\x1Fvalue2\x1Fvalue3" } - }; - addEvidence(evidenceList2, 3, FIFTYONE_DEGREES_EVIDENCE_QUERY); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - EXPECT_EQ(0, evidence->pseudoEvidence->count) << - "No new evidence should have been added\n"; - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -/* - * Check that PseudoHeadersRemoveEvidence API deal with NULL pointer input - * correctly. - */ -TEST_F(PseudoHeaderTests, EvidenceRemoveNullPointerInput) { - // Should not crash if evidence input is NULL - PseudoHeadersRemoveEvidence(NULL, 0); - - // Should not crash if evidence how a NULL pointer for pseudo evidence - EvidenceKeyValuePairArray* savePseudoEvidence = - evidence->pseudoEvidence; - evidence->pseudoEvidence = NULL; - PseudoHeadersRemoveEvidence(evidence, PSEUDO_BUFFER_SIZE); - evidence->pseudoEvidence = savePseudoEvidence; -} - -TEST_F(PseudoHeaderTests, EvidenceCreationPseudoEvidenceTooLong) { - // Expected value - const testExpectedResult expectedEvidence[1] = - { - { - "value2\x1FXX", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[2] = - { {"header2", "value2"}, {"header3", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"} }; - addEvidence(evidenceList, 2, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - 10, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 1); - removePseudoEvidence(10); -} - -TEST_F(PseudoHeaderTests, EvidenceCreationPseudoEvidenceTooLongReversed) { - // Expected value - const testExpectedResult expectedEvidence[1] = - { - { - "XXXXXXXXX", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[2] = - { {"header2", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}, {"header3", "value3"} }; - addEvidence(evidenceList, 2, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - 10, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - - checkResults(expectedEvidence, 1); - removePseudoEvidence(10); -} - -TEST_F(PseudoHeaderTests, EvidenceCreationPseudoEvidenceHeapOverflow) { - // Expected value - const testExpectedResult expectedEvidence[3] = - { - { - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\x1F""Android", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - "Android\x1F?1", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - // here value1\x1Fvalue2\x1Fvalue3 are concatenated, the problem is that value1\x1Fvalue2\x1F is exactly the size of the - // buffer (which is 100 chars) and it may cause a heap overflow if separator \x1F overwrites the terminating \0 - // this was a bug detected and fixed - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\x1F""Android", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[3] = - { {"header3", "?1"}, {"header1", "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"}, {"header2", "Android"} }; - addEvidence(evidenceList, 3, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 3); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} - -TEST_F(PseudoHeaderTests, EvidenceCreationPseudoEvidenceLastSeparator) { - // Expected value - const testExpectedResult expectedEvidence[3] = - { - { - "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\x1F""Android", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - "Android\x1F?1", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - }, - { - // here value1\x1Fvalue2\x1Fvalue3 are concatenated, value1\x1Fvalue2\x1F is expected to be just 1 less - // than the size of the buffer (which is 100 chars) we check that it does not overflow and terminating \0 - // is added properly - "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\x1F""Android\x1F", - FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING - } - }; - - // Create headers - createHeaders(uniqueHeaders, HEADERS_SIZE, false); - - testKeyValuePair evidenceList[3] = - { {"header3", "?1"}, {"header1", "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"}, {"header2", "Android"} }; - addEvidence(evidenceList, 3, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); - - EXCEPTION_CREATE; - PseudoHeadersAddEvidence( - evidence, - acceptedHeaders, - PSEUDO_BUFFER_SIZE, - orderOfPrecedence, - PREFIXES_SIZE, - exception); - EXCEPTION_THROW; - - checkResults(expectedEvidence, 3); - removePseudoEvidence(PSEUDO_BUFFER_SIZE); -} diff --git a/tests/PseudoHeaderTests.hpp b/tests/PseudoHeaderTests.hpp deleted file mode 100644 index b1519a95..00000000 --- a/tests/PseudoHeaderTests.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#include "Base.hpp" -#include "../headers.h" -#include "../evidence.h" -#include "StringCollection.hpp" - -#define PSEUDO_BUFFER_SIZE 100 -typedef struct test_key_value_pair_t { - char key[50]; - char value[PSEUDO_BUFFER_SIZE]; -} testKeyValuePair; - -typedef struct test_expected_result_t { - char result[PSEUDO_BUFFER_SIZE]; - fiftyoneDegreesEvidencePrefix prefix; -} testExpectedResult; - -class PseudoHeaderTests : public Base { -public: - void SetUp(); - void TearDown(); - void createHeaders( - const char** headersList, - int headersCount, - bool expectUpperPrefixedHeaders); - void addEvidence( - testKeyValuePair* evidenceList, - int size, - fiftyoneDegreesEvidencePrefix prefix); - void checkResults(const testExpectedResult *expectedEvidence, int size); - void removePseudoEvidence(size_t bufferSize); - int getNextPseudoIndex(const char* headers[], int size, int index); -protected: - StringCollection* strings = NULL; - fiftyoneDegreesHeaders* acceptedHeaders = NULL; - fiftyoneDegreesEvidenceKeyValuePairArray* evidence = NULL; -}; \ No newline at end of file From 34e3f9593af0828fffe18b8d6ea514bdb94e5287 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sat, 20 Jul 2024 15:46:13 +0100 Subject: [PATCH 20/73] CLEANUP: Fixed documentation to refer to the correct field name. --- headers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headers.h b/headers.h index cdc27412..22330dc5 100644 --- a/headers.h +++ b/headers.h @@ -143,7 +143,7 @@ struct fiftyone_degrees_header_t { fiftyoneDegreesHeaderID headerId; /**< Unique id in the data set for this full header */ bool isDataSet; /**< True if the header originates from the data set and - the fullHeaderId is valid */ + the headerId is valid */ fiftyoneDegreesHeaderPtrs* pseudoHeaders; /**< Array of indexes to related pseudo headers */ fiftyoneDegreesHeaderPtrs* segmentHeaders; /**< Array of indexes to raw From 3db8bd418cf6ac7e56a5a5ed99f35a02f9bb232f Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sat, 20 Jul 2024 15:52:31 +0100 Subject: [PATCH 21/73] BUG: Fixed to correctly return the point to the next byte in the buffer. --- evidence.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/evidence.c b/evidence.c index c3334236..3daafd17 100644 --- a/evidence.c +++ b/evidence.c @@ -137,12 +137,14 @@ static char* addPairValueToBuffer( char* buffer, size_t length, EvidenceKeyValuePair* pair) { - if (length < pair->parsedLength) { - return NULL; - } - else { - return memcpy(buffer, (char*)pair->parsedValue, pair->parsedLength); + if (length >= pair->parsedLength && + memcpy( + buffer, + (char*)pair->parsedValue, + pair->parsedLength) == buffer) { + return buffer + pair->parsedLength; } + return NULL; } // For the header finds the corresponding evidence in the array of evidence. If From 72b3c62bda1a5860894f1eb01a931b0fb98f9c68 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sat, 20 Jul 2024 17:30:09 +0100 Subject: [PATCH 22/73] BUG: Missed evidence as the index was incremented twice. --- evidence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evidence.c b/evidence.c index 3daafd17..296b5884 100644 --- a/evidence.c +++ b/evidence.c @@ -104,7 +104,7 @@ static EvidenceKeyValuePair* findHeaderEvidence( // For each of the evidence pairs available. for (uint32_t i = 0; i < evidence->count; i++) { - pair = &evidence->items[i++]; + pair = &evidence->items[i]; // Check that the prefix is one that is being considered. if ((pair->prefix & prefixes) == pair->prefix) { From c1ca615eed4a578dff4b070029f6a1aa887f6cec Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sun, 21 Jul 2024 15:20:21 +0100 Subject: [PATCH 23/73] CLEANUP: Removed empty lines. --- cache.c | 1 - component.c | 1 - data.c | 1 - evidence.c | 1 - file.c | 1 - float.c | 1 - headers.c | 1 - ip.c | 1 - list.c | 1 - memory.c | 1 - pool.c | 1 - properties.c | 1 - resource.c | 1 - results.c | 1 - status.c | 1 - string.c | 1 - textfile.c | 1 - threading.c | 1 - 18 files changed, 18 deletions(-) diff --git a/cache.c b/cache.c index 6996777e..1ea8c7c6 100644 --- a/cache.c +++ b/cache.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "cache.h" - #include "fiftyone.h" /** diff --git a/component.c b/component.c index 3626aae9..c411d9e0 100644 --- a/component.c +++ b/component.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "component.h" - #include "fiftyone.h" static uint32_t getFinalComponentSize(void *initial) { diff --git a/data.c b/data.c index 1ebab6c5..7e13a0d2 100644 --- a/data.c +++ b/data.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "data.h" - #include "fiftyone.h" void fiftyoneDegreesDataReset(fiftyoneDegreesData *data) { diff --git a/evidence.c b/evidence.c index 296b5884..4c6f74a9 100644 --- a/evidence.c +++ b/evidence.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "evidence.h" - #include "fiftyone.h" typedef struct evidence_iterate_state_t { diff --git a/file.c b/file.c index 7e0429e2..84622c83 100644 --- a/file.c +++ b/file.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "file.h" - #include "fiftyone.h" #include diff --git a/float.c b/float.c index 50f61aef..962dfc6b 100644 --- a/float.c +++ b/float.c @@ -22,7 +22,6 @@ #include #include "fiftyone.h" -#include "float.h" float fiftyoneDegreesFloatToNative(fiftyoneDegreesFloatInternal f) { fiftyoneDegreesFloatU floatU; diff --git a/headers.c b/headers.c index fce71392..6d8b4b81 100644 --- a/headers.c +++ b/headers.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "headers.h" - #include "fiftyone.h" /* HTTP header prefix used when processing collections of parameters. */ diff --git a/ip.c b/ip.c index 1c1d54ef..a1f61dc6 100644 --- a/ip.c +++ b/ip.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "ip.h" - #include "fiftyone.h" typedef void(*parseIterator)( diff --git a/list.c b/list.c index c572fd3d..d5347831 100644 --- a/list.c +++ b/list.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "list.h" - #include "fiftyone.h" fiftyoneDegreesList* fiftyoneDegreesListInit( diff --git a/memory.c b/memory.c index facf26c5..b557a1b0 100644 --- a/memory.c +++ b/memory.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "memory.h" - #include "fiftyone.h" #ifndef FIFTYONE_DEGREES_MEMORY_TRACKER_SHARDS diff --git a/pool.c b/pool.c index 3e56de6c..773d6eac 100644 --- a/pool.c +++ b/pool.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "pool.h" - #include "fiftyone.h" fiftyoneDegreesPool* fiftyoneDegreesPoolInit( diff --git a/properties.c b/properties.c index 68811284..7e6664ef 100644 --- a/properties.c +++ b/properties.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "properties.h" - #include "fiftyone.h" /* Array of valid property name separators */ diff --git a/resource.c b/resource.c index ec1402ba..cebb3262 100644 --- a/resource.c +++ b/resource.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "resource.h" - #include "fiftyone.h" /** diff --git a/results.c b/results.c index 4f3b6928..286c4bf5 100644 --- a/results.c +++ b/results.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "results.h" - #include "fiftyone.h" fiftyoneDegreesResultsBase* fiftyoneDegreesResultsInit( diff --git a/status.c b/status.c index 3a4d87aa..cf27a3d4 100644 --- a/status.c +++ b/status.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "status.h" - #include "fiftyone.h" typedef struct fiftyone_degrees_status_message { diff --git a/string.c b/string.c index bd37bc9c..de440ee5 100644 --- a/string.c +++ b/string.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "string.h" - #include "fiftyone.h" static uint32_t getFinalStringSize(void *initial) { diff --git a/textfile.c b/textfile.c index ded50fbb..f7a6f465 100644 --- a/textfile.c +++ b/textfile.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "textfile.h" - #include "fiftyone.h" static char* returnNextLine( diff --git a/threading.c b/threading.c index 6042c5dc..0edc67cc 100644 --- a/threading.c +++ b/threading.c @@ -21,7 +21,6 @@ * ********************************************************************* */ #include "threading.h" - #include "fiftyone.h" #ifdef _MSC_VER From 87d8ac28933e8ded82dbacd55470565017413de9 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sun, 21 Jul 2024 15:21:19 +0100 Subject: [PATCH 24/73] FEAT: Added methods to string.h/c to handle adding characters to a buffer tracking for remaining space and reporting the number of characters that were or would be added. Needed to consolidate string building into a single set of methods across the projects. --- string.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ string.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/string.c b/string.c index de440ee5..66910970 100644 --- a/string.c +++ b/string.c @@ -107,3 +107,55 @@ char *fiftyoneDegreesStringSubString(const char *a, const char *b) { } return NULL; } + +fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderInit( + fiftyoneDegreesStringBuilder* builder) { + builder->current = builder->ptr; + builder->remaining = builder->length; + builder->added = 0; + builder->full = false; + return builder; +} + +fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChar( + fiftyoneDegreesStringBuilder* builder, + char const value) { + if (builder->remaining > 1) { + *builder->current = value; + builder->current++; + builder->remaining--; + } + else { + builder->full = true; + } + builder->added++; + return builder; +} + +fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddInteger( + fiftyoneDegreesStringBuilder* builder, + int const value) { + char temp[10]; + if (snprintf(temp, sizeof(temp), "%d", value) > 0) { + StringBuilderAddChars( + builder, + temp, + strlen(temp)); + } + return builder; +} + +fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChars( + fiftyoneDegreesStringBuilder* builder, + char* const value, + size_t const length) { + for (size_t i = 0; i < length; i++) { + StringBuilderAddChar(builder, value[i]); + } + return builder; +} + +fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderComplete( + fiftyoneDegreesStringBuilder* builder) { + return StringBuilderAddChar(builder, '\0'); +} \ No newline at end of file diff --git a/string.h b/string.h index 2b8c2042..ac9cf078 100644 --- a/string.h +++ b/string.h @@ -119,6 +119,17 @@ typedef struct fiftyone_degrees_string_t { } fiftyoneDegreesString; #pragma pack(pop) +/** String buffer for building strings with memory checks */ +typedef struct fiftyone_degrees_string_buffer_t { + char* const ptr; /**< Pointer to the memory used by the buffer */ + size_t const length; /**< Length of buffer */ + char* current; /** Date: Sun, 21 Jul 2024 15:22:29 +0100 Subject: [PATCH 25/73] BUG: Checks the exception as well as the index returned from the search. --- profile.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/profile.c b/profile.c index a373cf49..c2e716f2 100644 --- a/profile.c +++ b/profile.c @@ -156,25 +156,41 @@ uint32_t* fiftyoneDegreesProfileGetOffsetForProfileId( const uint32_t profileId, uint32_t *profileOffset, fiftyoneDegreesException *exception) { + long index; Item profileOffsetItem; DataReset(&profileOffsetItem.data); if (profileId == 0) { EXCEPTION_SET(PROFILE_EMPTY); } - else if (CollectionBinarySearch( - profileOffsets, - &profileOffsetItem, - 0, - CollectionGetCount(profileOffsets) - 1, - (void*)&profileId, - compareProfileId, - exception) >= 0) { - *profileOffset = ((ProfileOffset*)profileOffsetItem.data.ptr)->offset; + else { + + // Get the index in the collection of profile offsets for the required + // profile id. + index = CollectionBinarySearch( + profileOffsets, + &profileOffsetItem, + 0, + CollectionGetCount(profileOffsets) - 1, + (void*)&profileId, + compareProfileId, + exception); + + // If the profile id is present then return the offset for it otherwise + // set the offset to NULL. + if (index >= 0 && EXCEPTION_OKAY) { + *profileOffset = + ((ProfileOffset*)profileOffsetItem.data.ptr)->offset; + } + else { + profileOffset = NULL; + } + + // Release the item that contains the list profile offset found. COLLECTION_RELEASE(profileOffsets, &profileOffsetItem); - return profileOffset; } - return NULL; + + return profileOffset; } fiftyoneDegreesProfile* fiftyoneDegreesProfileGetByProfileId( From 10fa4f787cb65cf8e496d0d07c03bd3093e4ab92 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sun, 21 Jul 2024 15:23:05 +0100 Subject: [PATCH 26/73] REFACT: Simplified the initialization of the callback structure. --- overrides.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/overrides.c b/overrides.c index 20185aef..b036aa17 100644 --- a/overrides.c +++ b/overrides.c @@ -437,9 +437,7 @@ void fiftyoneDegreesOverrideProfileIds( fiftyoneDegreesEvidenceKeyValuePairArray *evidence, void *state, fiftyoneDegreesOverrideProfileIdMethod override) { - overrideProfileIds callback; - callback.state = state; - callback.override = override; + overrideProfileIds callback = { override, state }; EvidenceIterate( evidence, FIFTYONE_DEGREES_EVIDENCE_COOKIE | From 0f556fa6cb91c9648730e3fa190725c93e041779 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sun, 21 Jul 2024 15:23:30 +0100 Subject: [PATCH 27/73] REFACT: Modified the json handling to use the string builder. --- json.c | 36 ++++++++++++++---------------------- json.h | 12 +++++------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/json.c b/json.c index 0555ed99..4fa006a9 100644 --- a/json.c +++ b/json.c @@ -21,22 +21,12 @@ * ********************************************************************* */ #include "json.h" - -// Add one character to the buffer if space is available. Always advance -// charsAdded. -static void addChar(fiftyoneDegreesJson* s, char a) { - if (s->bufferLength > 0) { - *s->buffer = a; - s->buffer++; - s->bufferLength--; - } - s->charsAdded++; -} +#include "fiftyone.h" // Add two characters to the buffer if space is available. static void addTwo(fiftyoneDegreesJson* s, char a, char b) { - addChar(s, a); - addChar(s, b); + StringBuilderAddChar(&s->buffer, a); + StringBuilderAddChar(&s->buffer, b); } // Adds a string of characters escaping special characters. @@ -65,7 +55,7 @@ static void addStringEscape( addTwo(s, '\\', 't'); break; default: - addChar(s, value[i]); + StringBuilderAddChar(&s->buffer, value[i]); break; } } @@ -77,22 +67,24 @@ static void addString( fiftyoneDegreesJson* s, const char* value, size_t length) { - addChar(s, '\"'); + StringBuilderAddChar(&s->buffer, '\"'); addStringEscape(s, value, length); - addChar(s, '\"'); + StringBuilderAddChar(&s->buffer, '\"'); } // Adds a comma separator. static void addSeparator(fiftyoneDegreesJson* s) { - addChar(s, ','); + StringBuilderAddChar(&s->buffer, ','); } void fiftyoneDegreesJsonDocumentStart(fiftyoneDegreesJson* s) { - addChar(s, '{'); + StringBuilderInit(&s->buffer); + StringBuilderAddChar(&s->buffer, '{'); } void fiftyoneDegreesJsonDocumentEnd(fiftyoneDegreesJson* s) { - addChar(s, '}'); + StringBuilderAddChar(&s->buffer, '}'); + StringBuilderComplete(&s->buffer); } void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* s) { @@ -124,9 +116,9 @@ void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s) { // it's a list or single value property. const char* value = FIFTYONE_DEGREES_STRING(name); addString(s, value, strlen(value)); - addChar(s, ':'); + StringBuilderAddChar(&s->buffer, ':'); if (s->property->isList) { - addChar(s, '['); + StringBuilderAddChar(&s->buffer, '['); } // Release the string. @@ -136,7 +128,7 @@ void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s) { void fiftyoneDegreesJsonPropertyEnd(fiftyoneDegreesJson* s) { if (s->property->isList) { - addChar(s, ']'); + StringBuilderAddChar(&s->buffer, ']'); } } diff --git a/json.h b/json.h index 455008e7..01f25181 100644 --- a/json.h +++ b/json.h @@ -77,13 +77,11 @@ * to determine when the buffer provided needs to be larger. */ typedef struct fiftyone_degrees_json { - char* buffer; // Buffer to add characters to - size_t bufferLength; // Length of the buffer - size_t charsAdded; // Number of characters added or that could be added - fiftyoneDegreesCollection* strings; // Collection of strings - fiftyoneDegreesProperty* property; // The property being added - fiftyoneDegreesList* values; // The values for the property to be added - fiftyoneDegreesException* exception; + fiftyoneDegreesStringBuilder buffer; /**< Output buffer */ + fiftyoneDegreesCollection* strings; /**< Collection of strings */ + fiftyoneDegreesProperty* property; /**< The property being added */ + fiftyoneDegreesList* values; /**< The values for the property */ + fiftyoneDegreesException* exception; /**< Exception */ } fiftyoneDegreesJson; /** From 8cd2828fbd3085c7e2d603a297e00cc1355a43e5 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sun, 21 Jul 2024 15:23:54 +0100 Subject: [PATCH 28/73] FEAT: Added string builder aliases to the fiftyone headers. --- fiftyone.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fiftyone.h b/fiftyone.h index 0dc03d5b..f528e88a 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -182,6 +182,7 @@ MAP_TYPE(Coordinate) MAP_TYPE(KeyValuePair) MAP_TYPE(HeaderID) MAP_TYPE(IndexPropertyProfile) +MAP_TYPE(StringBuilder) MAP_TYPE(Json) #define ProfileGetOffsetForProfileId fiftyoneDegreesProfileGetOffsetForProfileId /**< Synonym for #fiftyoneDegreesProfileGetOffsetForProfileId function. */ @@ -350,6 +351,11 @@ MAP_TYPE(Json) #define JsonPropertyEnd fiftyoneDegreesJsonPropertyEnd /**< Synonym for fiftyoneDegreesJsonPropertyEnd */ #define JsonPropertyValues fiftyoneDegreesJsonPropertyValues /**< Synonym for fiftyoneDegreesJsonPropertyValues */ #define JsonPropertySeparator fiftyoneDegreesJsonPropertySeparator /**< Synonym for fiftyoneDegreesJsonPropertySeparator */ +#define StringBuilderInit fiftyoneDegreesStringBuilderInit /**< Synonym for fiftyoneDegreesStringBuilderInit */ +#define StringBuilderAddChar fiftyoneDegreesStringBuilderAddChar /**< Synonym for fiftyoneDegreesStringBuilderAddChar */ +#define StringBuilderAddInteger fiftyoneDegreesStringBuilderAddInteger /**< Synonym for fiftyoneDegreesStringBuilderAddInteger */ +#define StringBuilderAddChars fiftyoneDegreesStringBuilderAddChars /**< Synonym for fiftyoneDegreesStringBuilderAddChars */ +#define StringBuilderComplete fiftyoneDegreesStringBuilderComplete /**< Synonym for fiftyoneDegreesStringBuilderComplete */ /* <-- only one asterisk to avoid inclusion in documentation * Shortened macros. From f058ca9c62c7599f3a22f5f3e5cf5395c8ee3837 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sun, 21 Jul 2024 15:24:16 +0100 Subject: [PATCH 29/73] CLEANUP: formatting. --- headers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headers.h b/headers.h index 22330dc5..b38406d7 100644 --- a/headers.h +++ b/headers.h @@ -139,13 +139,13 @@ struct fiftyone_degrees_header_t { const char* name; /**< Name of the header or pseudo header field as a null terminated string */ size_t length; /**< Length of the name string excluding the terminating - null */ + null */ fiftyoneDegreesHeaderID headerId; /**< Unique id in the data set for this full header */ bool isDataSet; /**< True if the header originates from the data set and the headerId is valid */ fiftyoneDegreesHeaderPtrs* pseudoHeaders; /**< Array of indexes to - related pseudo headers */ + related pseudo headers */ fiftyoneDegreesHeaderPtrs* segmentHeaders; /**< Array of indexes to raw headers that form this pseudo header */ From bdbaec5e01153f90fcd16475e786ca7738b4667a Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sun, 21 Jul 2024 15:24:53 +0100 Subject: [PATCH 30/73] REFACT: Modified evidence manipulation for pseudo headers to use the string builder. --- evidence.c | 87 +++++++++++++++++++++--------------------------------- evidence.h | 4 +-- 2 files changed, 36 insertions(+), 55 deletions(-) diff --git a/evidence.c b/evidence.c index 4c6f74a9..185d57d6 100644 --- a/evidence.c +++ b/evidence.c @@ -132,18 +132,13 @@ static EvidenceKeyValuePair* findHeaderEvidence( // sufficient bytes remaining in the buffer for the parsed value. Returns NULL // if there is insufficient bytes remaining in the buffer, or the new pointer // in buffer for the next character. -static char* addPairValueToBuffer( - char* buffer, - size_t length, +static StringBuilder* addPairValueToBuffer( + StringBuilder* builder, EvidenceKeyValuePair* pair) { - if (length >= pair->parsedLength && - memcpy( - buffer, - (char*)pair->parsedValue, - pair->parsedLength) == buffer) { - return buffer + pair->parsedLength; - } - return NULL; + return StringBuilderAddChars( + builder, + (char*)pair->parsedValue, + pair->parsedLength); } // For the header finds the corresponding evidence in the array of evidence. If @@ -151,13 +146,11 @@ static char* addPairValueToBuffer( // length available. Returns the next character in the buffer to add more // characters, or NULL if either not found or insufficient buffer length // remaining. -static char* addHeaderValueToBuffer( +static StringBuilder* addHeaderValueToBuilder( fiftyoneDegreesEvidenceKeyValuePairArray* evidence, int prefixes, fiftyoneDegreesHeader* header, - char* buffer, - size_t length) { - char* current = buffer; + StringBuilder* builder) { // Get the evidence that corresponds to the header. If it doesn't exist // then there is no evidence for the header and a call back will not be @@ -172,26 +165,15 @@ static char* addHeaderValueToBuffer( // Copy the value of the evidence pair in to the buffer advancing the // current character in the buffer. - current = addPairValueToBuffer(buffer, length, pair); + addPairValueToBuffer(builder, pair); // If the buffer is not sufficiently large to hold the pair value then // return. - if (current == NULL) { + if (builder->remaining <= 0) { return NULL; } - return current; -} - -// Adds the character value checking sufficient capacity in the buffer is -// available. Returns the next character in the buffer to add more characters, -// or NULL if either not found or insufficient buffer length remaining. -static char* addCharacter(char* buffer, size_t length, char value) { - if (length == 0) { - return NULL; - } - *buffer = value; - return buffer + 1; + return builder; } // Assembles a pseudo header in the buffer. If this can not be achieved returns @@ -202,52 +184,47 @@ static bool processPseudoHeader( EvidenceKeyValuePairArray* evidence, int prefixes, Header* header, - char* buffer, - size_t length, + StringBuilder* builder, void* state, fiftyoneDegreesEvidenceIterateForHeadersMethod callback) { - char* current = buffer; - size_t remaining = length; // For each of the headers that form the pseudo header. for (uint32_t i = 0; i < header->segmentHeaders->count; i++) { // Add the header evidence that forms the segment if available updating // the current buffer position if available. - current = addHeaderValueToBuffer( + addHeaderValueToBuilder( evidence, prefixes, header->segmentHeaders->items[i], - current, - remaining); + builder); // If the pseudo header wasn't found, or insufficient space was // available to copy it, then return. - if (current == NULL) { + if (builder->full) { return true; } - // Update the remaining capacity of the buffer. - remaining = length - (current - buffer); - // Add the pseudo header separator. - current = addCharacter(current, remaining, PSEUDO_HEADER_SEP); + StringBuilderAddChar(builder, PSEUDO_HEADER_SEP); // If there was insufficient space for the separator then return. - if (current == NULL) { + if (builder->full) { return true; } - - // Update the remaining capacity of the buffer. - remaining = length - (current - buffer); } // Switch the last pseudo header separator to a null terminating character. - *(current - 1) = '\0'; + StringBuilderComplete(builder); + + // If there was insufficient space for the terminating character then return. + if (builder->full) { + return true; + } // A full header has been formed so call the callback with the buffer and // the number of characters populated. - return callback(state, header, (const char*)buffer, length - remaining); + return callback(state, header, builder->ptr, builder->added); } // Finds the header in the evidence, and if available calls the callback. @@ -365,11 +342,12 @@ bool fiftyoneDegreesEvidenceIterateForHeaders( fiftyoneDegreesEvidenceKeyValuePairArray* evidence, int prefixes, fiftyoneDegreesHeaderPtrs* headers, - char* buffer, - size_t length, + char* const buffer, + size_t const length, void* state, fiftyoneDegreesEvidenceIterateForHeadersMethod callback) { Header* header; + StringBuilder builder = { buffer, length }; // For each of the headers process as either a standard header, or a pseudo // header. @@ -387,15 +365,18 @@ bool fiftyoneDegreesEvidenceIterateForHeaders( } // If the header is a pseudo header then attempt to assemble a complete - // value from the evidence and process it. + // value from the evidence and process it. Note: if there is only one + // segment then that will be the header that was already processed in + // processHeader therefore there is no point processing the same value + // a second time as a pseudo header. if (header->segmentHeaders != NULL && - header->segmentHeaders->count > 0) { + header->segmentHeaders->count > 1) { + StringBuilderInit(&builder); if (processPseudoHeader( evidence, prefixes, header, - buffer, - length, + &builder, state, callback) == false) { return true; diff --git a/evidence.h b/evidence.h index 27943c02..2a5e0c21 100644 --- a/evidence.h +++ b/evidence.h @@ -281,8 +281,8 @@ EXTERNAL bool fiftyoneDegreesEvidenceIterateForHeaders( fiftyoneDegreesEvidenceKeyValuePairArray* evidence, int prefixes, fiftyoneDegreesHeaderPtrs* headers, - char* buffer, - size_t length, + char* const buffer, + size_t const length, void* state, fiftyoneDegreesEvidenceIterateForHeadersMethod callback); From 4d6e8fb8409be4d2b6b786352cd41c4d4763ae67 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Mon, 22 Jul 2024 09:39:17 +0100 Subject: [PATCH 31/73] REFACT: Changed the name of the buffer to builder to align with the common name. Updated comments. --- string.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/string.h b/string.h index ac9cf078..6c9dc10b 100644 --- a/string.h +++ b/string.h @@ -104,7 +104,10 @@ typedef enum fiftyone_degrees_string_format { NULL : \ &((fiftyoneDegreesString*)s)->trail.secondValue) -/** String structure containing its value and size. */ +/** + * String structure containing its value and size which maps to the string + * byte format used in data files. + */ #pragma pack(push, 1) typedef struct fiftyone_degrees_string_t { int16_t size; /**< Size of the string in memory */ @@ -120,7 +123,7 @@ typedef struct fiftyone_degrees_string_t { #pragma pack(pop) /** String buffer for building strings with memory checks */ -typedef struct fiftyone_degrees_string_buffer_t { +typedef struct fiftyone_degrees_string_builder_t { char* const ptr; /**< Pointer to the memory used by the buffer */ size_t const length; /**< Length of buffer */ char* current; /** Date: Mon, 22 Jul 2024 10:05:37 +0100 Subject: [PATCH 32/73] OPTIM: Uses memcpy to add multiple characters to the builder. If the buffer is full with printable characters overwrites the last character to avoid creating strings that are not null terminated and risking memory faults. --- string.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/string.c b/string.c index 66910970..9da0ba04 100644 --- a/string.c +++ b/string.c @@ -149,13 +149,33 @@ fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChars( fiftyoneDegreesStringBuilder* builder, char* const value, size_t const length) { - for (size_t i = 0; i < length; i++) { - StringBuilderAddChar(builder, value[i]); + assert(strlen(value) == length); + if (length < builder->remaining && + memcpy(builder->current, value, length) == builder->current) { + builder->remaining -= length; + builder->current += length; } + else { + builder->full = true; + } + builder->added += length; return builder; } fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderComplete( fiftyoneDegreesStringBuilder* builder) { - return StringBuilderAddChar(builder, '\0'); + + // Always ensures that the string is null terminated even if that means + // overwriting the last character to turn it into a null. + if (builder->remaining >= 1) { + *builder->current = '\0'; + builder->current++; + builder->remaining--; + builder->added++; + } + else { + *(builder->ptr + builder->length - 1) = '\0'; + builder->full = true; + } + return builder; } \ No newline at end of file From 616712c0b06df3ff44079cecc7353f18a188dc99 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Mon, 5 Aug 2024 15:20:28 +0200 Subject: [PATCH 33/73] TEST: (pseudo)headers, string, evidence, profile, indices extract HeadersContainer from HeadersTests to reuse in EvidenceTests resurrect tests dedicated to pseudoheaders add pseudo header graph construction correct pseudo header construction cover string builder and other string routines with tests rename indexes -> indices cover indices and profile --- .../FiftyOne.Common.C.vcxproj | 4 +- dataset.h | 4 +- evidence.c | 86 ++- fiftyone.h | 4 +- headers.c | 83 ++- headers.h | 20 +- indexes.c => indices.c | 9 +- indexes.h => indices.h | 0 profile.c | 8 +- profile.h | 4 +- string.c | 7 +- string.h | 16 +- tests/EvidenceTests.cpp | 219 +++++++- tests/EvidenceTests.hpp | 14 +- tests/FixedSizeCollection.hpp | 98 ++++ tests/HeadersContainer.cpp | 45 ++ tests/HeadersContainer.hpp | 42 ++ tests/HeadersTests.cpp | 450 ++++++++------- tests/PoolTests.cpp | 23 +- tests/ProfileTests.cpp | 514 ++++++++++++++++++ tests/PropertyTests.cpp | 137 +++++ tests/ResourceManagerTests.cpp | 5 +- tests/StringsTests.cpp | 504 +++++++++++++++++ tests/VariableSizeCollection.hpp | 98 ++++ 24 files changed, 2038 insertions(+), 356 deletions(-) rename indexes.c => indices.c (97%) rename indexes.h => indices.h (100%) create mode 100644 tests/FixedSizeCollection.hpp create mode 100644 tests/HeadersContainer.cpp create mode 100644 tests/HeadersContainer.hpp create mode 100644 tests/ProfileTests.cpp create mode 100644 tests/PropertyTests.cpp create mode 100644 tests/StringsTests.cpp create mode 100644 tests/VariableSizeCollection.hpp diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj index efbe6964..de228a36 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj @@ -21,7 +21,7 @@ - + @@ -56,7 +56,7 @@ - + diff --git a/dataset.h b/dataset.h index 7209dc71..29e9c163 100644 --- a/dataset.h +++ b/dataset.h @@ -95,7 +95,7 @@ #include "config.h" #include "overrides.h" #include "common.h" -#include "indexes.h" +#include "indices.h" /** * Base data set structure which contains the 'must have's for all data sets. @@ -440,4 +440,4 @@ fiftyoneDegreesException *exception) { \ * @} */ -#endif \ No newline at end of file +#endif diff --git a/evidence.c b/evidence.c index 185d57d6..9ff50bbb 100644 --- a/evidence.c +++ b/evidence.c @@ -128,29 +128,22 @@ static EvidenceKeyValuePair* findHeaderEvidence( return NULL; } -// Copies the pair parsed value to the buffer checking that there are -// sufficient bytes remaining in the buffer for the parsed value. Returns NULL -// if there is insufficient bytes remaining in the buffer, or the new pointer -// in buffer for the next character. -static StringBuilder* addPairValueToBuffer( - StringBuilder* builder, - EvidenceKeyValuePair* pair) { - return StringBuilderAddChars( - builder, - (char*)pair->parsedValue, - pair->parsedLength); +// Safe-copies the pair parsed value to the buffer checking that there are +// sufficient bytes remaining in the buffer for the parsed value. +static void addPairValueToBuffer(StringBuilder* builder, EvidenceKeyValuePair* pair) { + StringBuilderAddChars(builder, (char*)pair->parsedValue, pair->parsedLength); } // For the header finds the corresponding evidence in the array of evidence. If // found then copies the parsed value into the buffer considering the remaining -// length available. Returns the next character in the buffer to add more -// characters, or NULL if either not found or insufficient buffer length -// remaining. -static StringBuilder* addHeaderValueToBuilder( +// length available. Returns true if successful + +static bool addHeaderValueToBuilder( fiftyoneDegreesEvidenceKeyValuePairArray* evidence, int prefixes, fiftyoneDegreesHeader* header, - StringBuilder* builder) { + StringBuilder* builder, + bool prependSeparator) { // Get the evidence that corresponds to the header. If it doesn't exist // then there is no evidence for the header and a call back will not be @@ -160,20 +153,20 @@ static StringBuilder* addHeaderValueToBuilder( prefixes, header); if (pair == NULL) { - return NULL; + return false; } - // Copy the value of the evidence pair in to the buffer advancing the + // Add the pseudo header separator. + if (prependSeparator) { + StringBuilderAddChar(builder, PSEUDO_HEADER_SEP); + } + + // Copy the value of the evidence pair in to the buffer advancing the // current character in the buffer. addPairValueToBuffer(builder, pair); - - // If the buffer is not sufficiently large to hold the pair value then - // return. - if (builder->remaining <= 0) { - return NULL; - } - - return builder; + + // return false if we have overfilled the buffer + return !builder->full; } // Assembles a pseudo header in the buffer. If this can not be achieved returns @@ -190,38 +183,23 @@ static bool processPseudoHeader( // For each of the headers that form the pseudo header. for (uint32_t i = 0; i < header->segmentHeaders->count; i++) { + //if this is a subsequent segment - we prepend the separator + bool prependSeparator = i > 0; // Add the header evidence that forms the segment if available updating // the current buffer position if available. - addHeaderValueToBuilder( - evidence, - prefixes, - header->segmentHeaders->items[i], - builder); + bool success = addHeaderValueToBuilder(evidence, prefixes, header->segmentHeaders->items[i], builder, prependSeparator); // If the pseudo header wasn't found, or insufficient space was // available to copy it, then return. - if (builder->full) { - return true; - } - - // Add the pseudo header separator. - StringBuilderAddChar(builder, PSEUDO_HEADER_SEP); - - // If there was insufficient space for the separator then return. - if (builder->full) { - return true; + if (!success) { + return true; // which means continue iteration } } - // Switch the last pseudo header separator to a null terminating character. + // Append (or overwrite if it is the last character) a null terminating character. StringBuilderComplete(builder); - // If there was insufficient space for the terminating character then return. - if (builder->full) { - return true; - } - // A full header has been formed so call the callback with the buffer and // the number of characters populated. return callback(state, header, builder->ptr, builder->added); @@ -315,27 +293,31 @@ fiftyoneDegreesEvidencePrefixMap* fiftyoneDegreesEvidenceMapPrefix( uint32_t i; size_t length = strlen(key); EvidencePrefixMap *map; + EvidencePrefixMap *result = NULL; for (i = 0; i < sizeof(_map) / sizeof(EvidencePrefixMap); i++) { map = &_map[i]; if (map->prefixLength < length && strncmp(map->prefix, key, map->prefixLength) == 0) { - return map; + result = map; + break; } } - return NULL; + return result; } EXTERNAL const char* fiftyoneDegreesEvidencePrefixString( fiftyoneDegreesEvidencePrefix prefix) { uint32_t i; EvidencePrefixMap* map; + const char *result = NULL; for (i = 0; i < sizeof(_map) / sizeof(EvidencePrefixMap); i++) { map = &_map[i]; if (map->prefixEnum == prefix) { - return map->prefix; + result = map->prefix; + break; } } - return NULL; + return result ; } bool fiftyoneDegreesEvidenceIterateForHeaders( @@ -385,4 +367,4 @@ bool fiftyoneDegreesEvidenceIterateForHeaders( } return false; -} \ No newline at end of file +} diff --git a/fiftyone.h b/fiftyone.h index f528e88a..e971ac1d 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -84,7 +84,7 @@ #include "process.h" #include "pair.h" #include "yamlfile.h" -#include "indexes.h" +#include "indices.h" #include "json.h" /** @@ -436,4 +436,4 @@ MAP_TYPE(Json) * @} */ -#endif \ No newline at end of file +#endif diff --git a/headers.c b/headers.c index 6d8b4b81..a02bfe70 100644 --- a/headers.c +++ b/headers.c @@ -27,10 +27,17 @@ #define HTTP_PREFIX_UPPER "HTTP_" /** - * True if the value is not null and not zero length. + * True if the value is not null, not zero length + * and contains at least something meaningful besides pseudoheader separator characters */ static bool isHeaderValid(const char* value) { - return value != NULL && *value != '\0'; + bool valid = false; + + while (value && *value != '\0' && !valid) { + valid = *value != PSEUDO_HEADER_SEP; + value++; + } + return valid; } /** @@ -232,21 +239,6 @@ static bool setHeaderFromDataSet( return true; } -/** - * Returns the index to the header if it exits, or -1 if it doesn't. - */ -static int getHeaderIndex(Headers *headers, const char *name, size_t length) { - Header item; - for (uint32_t i = 0; i < headers->count; i++) { - item = headers->items[i]; - if (item.length == length && - StringCompareLength(name, item.name, length) == 0) { - return (int)i; - } - } - return -1; -} - /** * Returns a pointer to the header if it exits, or NULL if it doesn't. */ @@ -376,7 +368,7 @@ static Header* copyHeader( */ static bool addHeadersFromHeaderSegment( Headers* headers, - Header* psuedoHeader, + Header* pseudoHeader, const char* name, size_t length, Exception* exception) { @@ -390,10 +382,10 @@ static bool addHeadersFromHeaderSegment( } // Relate the segment header to the pseudo header. - relateSegmentHeaderToPseudoHeader(segmentHeader, psuedoHeader); + relateSegmentHeaderToPseudoHeader(segmentHeader, pseudoHeader); // Relate the pseudo header to the segment header. - relatePseudoHeaderToSegmentHeader(psuedoHeader, segmentHeader); + relatePseudoHeaderToSegmentHeader(pseudoHeader, segmentHeader); return true; } @@ -404,36 +396,40 @@ static bool addHeadersFromHeaderSegment( */ static bool addHeadersFromHeader( Headers* headers, - Header* psuedoHeader, + Header* pseudoHeader, Exception* exception) { uint32_t start = 0; uint32_t end = 0; - for (;end < psuedoHeader->length; end++) { + bool separatorEncountered = false; + for (;end < pseudoHeader->length; end++) { // If a header has been found then either get the existing header with // this name, or add a new header. - if (psuedoHeader->name[end] == PSEUDO_HEADER_SEP && - end - start > 0) { - if (addHeadersFromHeaderSegment( - headers, - psuedoHeader, - psuedoHeader->name + start, - end - start, - exception) == false) { - return false; - } + if (pseudoHeader->name[end] == PSEUDO_HEADER_SEP) { + separatorEncountered = true; + if (end - start > 0) { + if (addHeadersFromHeaderSegment( + headers, + pseudoHeader, + pseudoHeader->name + start, + end - start, + exception) == false) { + return false; + } + } // Move to the next segment. start = end + 1; } } - // If there is a final segment then process this. - if (end - start > 0) { + // If there is a final segment then process this, but only if it is a pseudoheader + // (i.e. separator was encountered) - do not do this for ordinary headers + if (separatorEncountered && end - start > 0) { if (addHeadersFromHeaderSegment( headers, - psuedoHeader, - psuedoHeader->name + start, + pseudoHeader, + pseudoHeader->name + start, end - start, exception) == false) { return false; @@ -444,7 +440,11 @@ static bool addHeadersFromHeader( } static bool addHeadersFromHeaders(Headers* headers, Exception* exception) { - for (uint32_t i = 0; i < headers->count; i++) { + // cache count here, for it may change if headers are added + // and thus loop may process additional headers which were not intended + uint32_t count = headers->count; + + for (uint32_t i = 0; i < count; i++) { if (addHeadersFromHeader( headers, &headers->items[i], @@ -624,12 +624,3 @@ bool fiftyoneDegreesHeadersIsHttp( size_t length) { return HeaderGetIndex((Headers*)state, field, length) >= 0; } - -/** - * SIZE CALCULATION METHODS - */ - -size_t fiftyoneDegreesHeadersSize(int count) { - return sizeof(Headers) + // Headers structure - (count * sizeof(Header)); // Header names -} \ No newline at end of file diff --git a/headers.h b/headers.h index b38406d7..fe8bacd3 100644 --- a/headers.h +++ b/headers.h @@ -103,9 +103,9 @@ #include "array.h" #include "common.h" -#define FIFTYONE_DEGREES_PSEUDO_HEADER_SEP '\x1F' /** unit separator of headers - and headers' values that - form pseudo header and +#define FIFTYONE_DEGREES_PSEUDO_HEADER_SEP '\x1F' /** unit separator of headers + and headers' values that + form pseudo header and its evidence */ /** @@ -144,9 +144,9 @@ struct fiftyone_degrees_header_t { full header */ bool isDataSet; /**< True if the header originates from the data set and the headerId is valid */ - fiftyoneDegreesHeaderPtrs* pseudoHeaders; /**< Array of indexes to + fiftyoneDegreesHeaderPtrs* pseudoHeaders; /**< Array of pointers to related pseudo headers */ - fiftyoneDegreesHeaderPtrs* segmentHeaders; /**< Array of indexes to raw + fiftyoneDegreesHeaderPtrs* segmentHeaders; /**< Array of pointers to raw headers that form this pseudo header */ }; @@ -183,14 +183,6 @@ typedef long(*fiftyoneDegreesHeadersGetMethod)( uint32_t index, fiftyoneDegreesCollectionItem *nameItem); -/** - * Returns the number of bytes that will be allocated for a headers structure - * created using the #fiftyoneDegreesHeadersCreate method. - * @param count number of headers in the structure - * @return number of bytes needed - */ -EXTERNAL size_t fiftyoneDegreesHeadersSize(int count); - /** * Check if a header is a pseudo header. * @param headerName name of the header @@ -268,4 +260,4 @@ EXTERNAL bool fiftyoneDegreesHeadersIsHttp( * @} */ -#endif \ No newline at end of file +#endif diff --git a/indexes.c b/indices.c similarity index 97% rename from indexes.c rename to indices.c index b6b7954d..c31d9806 100644 --- a/indexes.c +++ b/indices.c @@ -20,7 +20,7 @@ * such notice(s) shall fulfill the requirements of that article. * ********************************************************************* */ -#include "indexes.h" +#include "indices.h" #include "fiftyone.h" typedef struct map_t { @@ -67,8 +67,9 @@ static void addProfileValuesMethod( // If the value doesn't relate to the next property index then // move to the next property index. - while (propertyIndexes[p].propertyIndex < value->propertyIndex && - p < index->availablePropertyCount) { + while (p < index->availablePropertyCount && //first check validity of the subscript and then use it + propertyIndexes[p].propertyIndex < value->propertyIndex + ) { p++; } @@ -259,4 +260,4 @@ uint32_t fiftyoneDegreesIndexPropertyProfileLookup( availablePropertyIndex; assert(valueIndex < index->size); return index->valueIndexes[valueIndex]; -} \ No newline at end of file +} diff --git a/indexes.h b/indices.h similarity index 100% rename from indexes.h rename to indices.h diff --git a/profile.c b/profile.c index c2e716f2..e1d99d7d 100644 --- a/profile.c +++ b/profile.c @@ -120,8 +120,10 @@ static uint32_t iterateValues( // Loop through until the last value for the property has been returned // or the callback doesn't need to continue. while (cont == true && - *valueIndex <= property->lastValueIndex && - valueIndex < maxValueIndex && + //first check the address validity, before dereferencing to prevent potential segfault on deref + valueIndex < maxValueIndex && + *valueIndex <= property->lastValueIndex && + EXCEPTION_OKAY) { // Reset the items as they should never share the same memory. @@ -426,4 +428,4 @@ uint32_t fiftyoneDegreesProfileIterateValueIndexes( COLLECTION_RELEASE(values, &valueItem); } return count; -} \ No newline at end of file +} diff --git a/profile.h b/profile.h index 8ea0400a..66666c3f 100644 --- a/profile.h +++ b/profile.h @@ -79,7 +79,7 @@ #include "property.h" #include "value.h" #include "common.h" -#include "indexes.h" +#include "indices.h" /** * Encapsulates a profile stored within a data set. A profile pertains to a @@ -301,4 +301,4 @@ EXTERNAL uint32_t fiftyoneDegreesProfileIterateValueIndexes( * @} */ -#endif \ No newline at end of file +#endif diff --git a/string.c b/string.c index 9da0ba04..25a1d6c9 100644 --- a/string.c +++ b/string.c @@ -100,7 +100,7 @@ char *fiftyoneDegreesStringSubString(const char *a, const char *b) { break; } } - if (d == 0) { + if (d == 0 && *b1 == '\0') { return (char *)a; } } @@ -135,7 +135,8 @@ fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChar( fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddInteger( fiftyoneDegreesStringBuilder* builder, int const value) { - char temp[10]; + // 64-bit INT_MIN is -9,223,372,036,854,775,807 => 21 characters + char temp[22]; if (snprintf(temp, sizeof(temp), "%d", value) > 0) { StringBuilderAddChars( builder, @@ -178,4 +179,4 @@ fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderComplete( builder->full = true; } return builder; -} \ No newline at end of file +} diff --git a/string.h b/string.h index 6c9dc10b..c84bf57a 100644 --- a/string.h +++ b/string.h @@ -199,17 +199,17 @@ EXTERNAL char *fiftyoneDegreesStringSubString(const char *a, const char *b); /** * Initializes the buffer. - * @param buffer to initialize - * @return pointer to the buffer passed + * @param builder to initialize + * @return pointer to the builder passed */ EXTERNAL fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderInit( fiftyoneDegreesStringBuilder* builder); /** * Adds the character to the buffer. - * @param buffer to add the character to + * @param builder to add the character to * @param value character to add - * @return pointer to the buffer passed + * @return pointer to the builder passed */ EXTERNAL fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChar( fiftyoneDegreesStringBuilder* builder, @@ -217,7 +217,7 @@ EXTERNAL fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChar( /** * Adds the integer to the buffer. - * @param buffer to add the character to + * @param builder to add the character to * @param value integer to add * @return pointer to the buffer passed */ @@ -227,7 +227,7 @@ EXTERNAL fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddInteger( /** * Adds the string to the buffer. - * @param buffer to add the character to + * @param builder to add the character to * @param value of chars to add * @param length of chars to add * @return pointer to the buffer passed @@ -239,7 +239,7 @@ EXTERNAL fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChars( /** * Adds a null terminating character to the buffer. - * @param buffer to terminate + * @param builder to terminate * @return pointer to the buffer passed */ EXTERNAL fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderComplete( @@ -249,4 +249,4 @@ EXTERNAL fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderComplete( * @} */ -#endif \ No newline at end of file +#endif diff --git a/tests/EvidenceTests.cpp b/tests/EvidenceTests.cpp index 95a4370d..4c3a3574 100644 --- a/tests/EvidenceTests.cpp +++ b/tests/EvidenceTests.cpp @@ -22,6 +22,7 @@ #include "pch.h" #include "EvidenceTests.hpp" +#include "memory.h" void assertStringHeaderAdded( fiftyoneDegreesEvidenceKeyValuePair *pair, @@ -200,4 +201,220 @@ TEST_F(Evidence, Iterate_String_without_pseudo_evidence) { EXPECT_EQ(1, count) << "Number of evidence should be 1\n"; -} \ No newline at end of file +} + +bool callback1(void* state, fiftyoneDegreesHeader*, const char* value, size_t) { + std::vector *results = (std::vector *) state; + results->push_back(value); + return true; +} + +TEST_F(Evidence, MapPrefix) { + auto map = fiftyoneDegreesEvidenceMapPrefix("server.param1"); + EXPECT_EQ(map->prefixEnum, FIFTYONE_DEGREES_EVIDENCE_SERVER); + map = fiftyoneDegreesEvidenceMapPrefix("query.PARAM"); + EXPECT_EQ(map->prefixEnum, FIFTYONE_DEGREES_EVIDENCE_QUERY); + map = fiftyoneDegreesEvidenceMapPrefix("cookie.some_value"); + EXPECT_EQ(map->prefixEnum, FIFTYONE_DEGREES_EVIDENCE_COOKIE); + map = fiftyoneDegreesEvidenceMapPrefix("header.HTTP_STRING"); + EXPECT_EQ(map->prefixEnum, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING); + map = fiftyoneDegreesEvidenceMapPrefix("header."); // a string without a param does not have a prefix NULL + EXPECT_EQ(map, nullptr); + map = fiftyoneDegreesEvidenceMapPrefix("nonsense"); + EXPECT_EQ(map, nullptr); + map = fiftyoneDegreesEvidenceMapPrefix("HEADER.something"); // case-sensitivity of the prefixes + EXPECT_EQ(map, nullptr); +} + +TEST_F(Evidence, IterateForHeaders_Order) { + const char *headers[] = { + (char *)"Material", + (char *)"Size", + (char *)"Color" + }; + headersContainer.CreateHeaders(headers, 3, false); + CreateEvidence(3); + + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Size", "Big"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Material", "Apple"); + + std::vector results; + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buffer, bufferSize, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results[0], "Apple"); + EXPECT_EQ(results[1], "Big"); + EXPECT_EQ(results[2], "Green"); +} + +TEST_F(Evidence, IterateForHeaders_NoMatchingPrefix) { + const char *headers[] = { + (char *)"Material", + (char *)"Size", + (char *)"Color" + }; + headersContainer.CreateHeaders(headers, 3, false); + CreateEvidence(3); + + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_QUERY, "Size", "Big"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_QUERY, "Material", "Apple"); + + std::vector results; + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buffer, bufferSize, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results[0], "Green"); // only Color is the header string + EXPECT_EQ(results.size(), 1); +} + +TEST_F(Evidence, IterateForHeaders_PrefixPrecedence) { + const char *headers[] = { + (char *)"Material", + (char *)"Size", + (char *)"Color" + }; + headersContainer.CreateHeaders(headers, 3, false); + CreateEvidence(7); + + // at this level no prefix precedence is enforced - + // the first evidence matching the prefix mask will be used + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Size", "BigHeader"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_COOKIE, "Size", "BigCookie"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_QUERY, "Size", "BigQuery"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_QUERY, "Material", "AppleQuery"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_COOKIE, "Material", "AppleCookie"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Material", "AppleHeader"); + + + std::vector results; + uint32_t prefixMask = FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING | FIFTYONE_DEGREES_EVIDENCE_QUERY | FIFTYONE_DEGREES_EVIDENCE_COOKIE; + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, prefixMask, headersContainer.headerPointers, buffer, bufferSize, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results.size(), 3); + EXPECT_EQ(results[0], "AppleQuery"); // only Color is the header string + EXPECT_EQ(results[1], "BigHeader"); // only Color is the header string + EXPECT_EQ(results[2], "Green"); // only Color is the header string +} + +TEST_F(Evidence, IterateForHeaders_MissingOneEvidence) { + const char *headers[] = { + (char *)"Material", + (char *)"Size", + (char *)"Color" + }; + headersContainer.CreateHeaders(headers, 3, false); + CreateEvidence(3); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Material", "Apple"); + + + std::vector results; + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buffer, bufferSize, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results[0], "Apple"); + EXPECT_EQ(results[1], "Green"); +} + +TEST_F(Evidence, IterateForHeaders_ConstructPseudoHeader) { + const char *headers[] = { + "Material", + "Size\x1FTaste", //Taste is a missing evidence, this pseudoheader should not be constructed + "Size\x1F""Color", + "Size\x1F""Color\x1F""Material", + + }; + headersContainer.CreateHeaders(headers, 4, false); + CreateEvidence(3); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Size", "Big"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Material", "Apple"); + + + std::vector results; + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buffer, bufferSize, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results.size(), 3); + EXPECT_EQ(results[0], "Apple"); + EXPECT_EQ(results[1], "Big\x1FGreen"); + EXPECT_EQ(results[2], "Big\x1FGreen\x1F""Apple"); +} + +bool callback2(void* state, fiftyoneDegreesHeader* , const char* value, size_t ) { + std::vector *results = (std::vector *) state; + results->push_back(value); + return results->size() <= 1; // on the second header we signal early exit by returning false +} + +TEST_F(Evidence, IterateForHeaders_CallbackSignalsEarlyExit) { + const char *headers[] = { + "Material", + "Size\x1FTaste", //Taste is a missing evidence, this pseudoheader should not be constructed + "Size\x1F""Color", + "Size\x1F""Color\x1F""Material", + }; + headersContainer.CreateHeaders(headers, 4, false); + CreateEvidence(3); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Size", "Big"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Material", "Apple"); + + std::vector results; + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buffer, bufferSize, &results, callback2); + EXPECT_TRUE(res); + EXPECT_EQ(results.size(), 2); + EXPECT_EQ(results[0], "Apple"); + EXPECT_EQ(results[1], "Big\x1FGreen"); +} + +bool callback_false(void* , fiftyoneDegreesHeader* , const char* , size_t ) { + return false; +} + +TEST_F(Evidence, IterateForHeaders_CallbackSignalsEarlyExit_OrdinaryHeader) { + const char *headers[] = { + "Material" + }; + headersContainer.CreateHeaders(headers, 1, false); + CreateEvidence(1); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Material", "Apple"); + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buffer, bufferSize, NULL, callback_false); + EXPECT_TRUE(res); +} + +TEST_F(Evidence, IterateForHeaders_SmallBuffer) { + const char *headers[] = { + "Size\x1F""Color", + "Size\x1F""Color\x1F""Material", + }; + headersContainer.CreateHeaders(headers, 2, false); + CreateEvidence(3); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Size", "Big"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Material", "Apple"); + + std::vector results; + + // Big\x1FGreen\x1FApple => 15 characters, buffer size must be 16, + // let's make it exactly 15, and the last result should not be formed + + size_t length = 15; + char *buf = (char *) fiftyoneDegreesMalloc(length); + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buf, length, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results.size(), 1); + EXPECT_EQ(results[0], "Big\x1F""Green"); + fiftyoneDegreesFree(buf); + + // now let's make it 16 and it should be formed + results.clear(); + length = 16; + buf = (char *) fiftyoneDegreesMalloc(length); + res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buf, length, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results.size(), 2); + EXPECT_EQ(results[1], "Big\x1F""Green\x1F""Apple"); + fiftyoneDegreesFree(buf); +} + + diff --git a/tests/EvidenceTests.hpp b/tests/EvidenceTests.hpp index b6c00966..710f5ead 100644 --- a/tests/EvidenceTests.hpp +++ b/tests/EvidenceTests.hpp @@ -27,6 +27,7 @@ #include "Base.hpp" #include "StringCollection.hpp" #include "../evidence.h" +#include "HeadersContainer.hpp" /** * Evidence test class used to test the functionality in evidence.c. @@ -34,6 +35,9 @@ class Evidence : public Base { protected: fiftyoneDegreesEvidenceKeyValuePairArray *evidence = nullptr; + HeadersContainer headersContainer; + size_t bufferSize = 512; + char *buffer = nullptr; /** * Calls the base setup method to enable memory leak checking and memory @@ -41,6 +45,7 @@ class Evidence : public Base { */ void SetUp() { Base::SetUp(); + buffer = (char *) fiftyoneDegreesMalloc(bufferSize); } /** @@ -52,7 +57,12 @@ class Evidence : public Base { if (evidence != nullptr) { fiftyoneDegreesEvidenceFree(evidence); } - Base::TearDown(); + headersContainer.Dealloc(); + if (buffer != nullptr) { + fiftyoneDegreesFree(buffer); + buffer = nullptr; + } + Base::TearDown(); } /** @@ -67,4 +77,4 @@ class Evidence : public Base { } }; -#endif \ No newline at end of file +#endif diff --git a/tests/FixedSizeCollection.hpp b/tests/FixedSizeCollection.hpp new file mode 100644 index 00000000..106dd66a --- /dev/null +++ b/tests/FixedSizeCollection.hpp @@ -0,0 +1,98 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#ifndef FixedSizeCollection_hpp +#define FixedSizeCollection_hpp + +#include "../collection.h" +#include "../string.h" +#include + +typedef struct fixed_size_collection_state_t { + fiftyoneDegreesCollection *collection; + uint32_t *offsets; + void *data; + uint32_t count; +} fixedSizeCollectionState; + +/** + * Fixed size object collection helper class, used by test classes to fetch. + * T must be a fixed size value type (s.a. struct) the colleciton will hold instance of size sizeof(T) + */ +template +class FixedSizeCollection { +public: + FixedSizeCollection(const std::vector& values); + ~FixedSizeCollection(); + fixedSizeCollectionState* getState(); + +private: + fixedSizeCollectionState state; +}; + +template +FixedSizeCollection::~FixedSizeCollection() { + fiftyoneDegreesFree(state.offsets); + fiftyoneDegreesFree(state.data); + state.collection->freeCollection(state.collection); +} + +template +FixedSizeCollection::FixedSizeCollection(const std::vector &values) { + uint32_t currentOffsetIndex = 0; + fiftyoneDegreesMemoryReader reader; + size_t dataLength = values.size() * sizeof(T); + state.count = (uint32_t) values.size(); + + reader.length = (long)(dataLength + sizeof(uint32_t)); + state.data = fiftyoneDegreesMalloc(reader.length); + *(int32_t*)state.data = (int32_t)dataLength; + state.offsets = (uint32_t*)fiftyoneDegreesMalloc(values.size() * sizeof(uint32_t)); + reader.startByte = ((byte*)state.data); + reader.lastByte = reader.startByte + reader.length; + reader.current = reader.startByte + sizeof(uint32_t); + + for (size_t i = 0; i < values.size(); i++) { + T *element = (T*)reader.current; + memcpy((void *) element, (void *) &values[i], sizeof(T)); + state.offsets[currentOffsetIndex] = + (uint32_t)(reader.current - (reader.startByte + sizeof(uint32_t))); + reader.current += sizeof(T); + currentOffsetIndex++; + } + assert(currentOffsetIndex == state.count); + assert(reader.lastByte == reader.current); + assert((byte*)state.data + sizeof(int32_t) + dataLength == reader.lastByte); + assert((byte*)state.data + reader.length == reader.lastByte); + reader.current = reader.startByte; + state.collection = fiftyoneDegreesCollectionCreateFromMemory( + &reader, + fiftyoneDegreesCollectionHeaderFromMemory(&reader, sizeof(T), false)); + assert(state.collection->size == dataLength); +} + +template +fixedSizeCollectionState* FixedSizeCollection::getState() { + return &state; +} + +#endif /* FixedSizeCollection_hpp */ diff --git a/tests/HeadersContainer.cpp b/tests/HeadersContainer.cpp new file mode 100644 index 00000000..81564594 --- /dev/null +++ b/tests/HeadersContainer.cpp @@ -0,0 +1,45 @@ +// +// HeadersContainer.cpp +// CommonTests +// +// Created by Eugene Dorfman on 8/6/24. +// + +#include "HeadersContainer.hpp" + +void HeadersContainer::CreateHeaders( + const char** headersList, + int headersCount, + bool expectUpperPrefixedHeaders) { + EXCEPTION_CREATE + count = headersCount; + strings = new StringCollection(headersList, count); + headers = fiftyoneDegreesHeadersCreate( + expectUpperPrefixedHeaders, + strings->getState(), + getHeaderUniqueId, + exception); + + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesHeaderPtr, headerPointers, headersCount); + if (headerPointers) { + for (int i=0;iitems[i] = &headers->items[i]; + headerPointers->count++; + } + } +} + +void HeadersContainer::Dealloc() { + if (headers != nullptr) { + fiftyoneDegreesHeadersFree(headers); + headers = nullptr; + } + if (strings != nullptr) { + delete strings; + strings = nullptr; + } + if (headerPointers != nullptr) { + fiftyoneDegreesFree(headerPointers); + headerPointers = nullptr; + } +} diff --git a/tests/HeadersContainer.hpp b/tests/HeadersContainer.hpp new file mode 100644 index 00000000..3e672772 --- /dev/null +++ b/tests/HeadersContainer.hpp @@ -0,0 +1,42 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#ifndef HeadersContainer_hpp +#define HeadersContainer_hpp + +#include "StringCollection.hpp" +#include "../headers.h" +#include "../exceptions.h" +#include "../fiftyone.h" + +class HeadersContainer { +public: + fiftyoneDegreesHeaders *headers = nullptr; + fiftyoneDegreesHeaderPtrs *headerPointers = nullptr; + StringCollection *strings = nullptr; + int count = 0; + + void Dealloc(); + void CreateHeaders(const char** headersList, int headersCount, bool expectUpperPrefixedHeaders); +}; + +#endif /* HeadersContainer_hpp */ diff --git a/tests/HeadersTests.cpp b/tests/HeadersTests.cpp index 9e8130ca..41644c85 100644 --- a/tests/HeadersTests.cpp +++ b/tests/HeadersTests.cpp @@ -23,6 +23,8 @@ #include "pch.h" #include "Base.hpp" #include "StringCollection.hpp" +#include "HeadersContainer.hpp" + extern "C" { #include "../headers.h" #include "../fiftyone.h" @@ -34,10 +36,8 @@ extern "C" { */ class HeadersTests : public Base { protected: - StringCollection *strings = nullptr; - int count = 0; - fiftyoneDegreesHeaders *headers = nullptr; - + HeadersContainer container; + Headers *headers = nullptr; /** * Calls the base setup method to enable memory leak checking and memory * allocation checking. @@ -52,12 +52,8 @@ class HeadersTests : public Base { * and compare expected and actual memory allocations. */ void TearDown() { - if (headers != nullptr) { - fiftyoneDegreesHeadersFree(headers); - } - if (strings != nullptr) { - delete strings; - } + container.Dealloc(); + headers = nullptr; Base::TearDown(); } @@ -74,14 +70,8 @@ class HeadersTests : public Base { const char** headersList, int headersCount, bool expectUpperPrefixedHeaders) { - EXCEPTION_CREATE - count = headersCount; - strings = new StringCollection(headersList, count); - headers = fiftyoneDegreesHeadersCreate( - expectUpperPrefixedHeaders, - strings->getState(), - getHeaderUniqueId, - exception); + container.CreateHeaders(headersList, headersCount, expectUpperPrefixedHeaders); + headers = container.headers; } }; @@ -278,16 +268,50 @@ TEST_F(HeadersTests, HttpPrefix) { ASSERT_EQ(0, fiftyoneDegreesHeaderGetIndex( - headers, + container.headers, "HTTP_Red", strlen("HTTP_Red"))); ASSERT_EQ(1, fiftyoneDegreesHeaderGetIndex( - headers, + container.headers, "HTTP_Black", strlen("HTTP_Black"))); } +TEST_F(HeadersTests, IsHttp) { + CreateHeaders( + testHeaders_HttpPrefix, + sizeof(testHeaders_HttpPrefix) / sizeof(const char*), + true); + EXPECT_TRUE(fiftyoneDegreesHeadersIsHttp(container.headers, "HTTP_Red", strlen("HTTP_Red"))); + EXPECT_TRUE(fiftyoneDegreesHeadersIsHttp(container.headers, "Red", strlen("Red"))); +} + +TEST_F(HeadersTests, IsHttpNoPrefix) { + CreateHeaders( + testHeaders_HttpPrefix, + sizeof(testHeaders_HttpPrefix) / sizeof(const char*), + false); + EXPECT_TRUE(fiftyoneDegreesHeadersIsHttp(container.headers, "Red", strlen("Red"))); + EXPECT_FALSE(fiftyoneDegreesHeadersIsHttp(container.headers, "Yellow", strlen("Yellow"))); +} + +TEST_F(HeadersTests, FromUniqueId) { + CreateHeaders( + testHeaders_HttpPrefix, + sizeof(testHeaders_HttpPrefix) / sizeof(const char*), + true); + Header *header = &container.headers->items[0]; + HeaderID uniqueId = header->headerId; + Header *actual = fiftyoneDegreesHeadersGetHeaderFromUniqueId(container.headers, uniqueId); + EXPECT_EQ(actual, header); + EXPECT_NE(actual, nullptr); + + Header *shouldBeNull = fiftyoneDegreesHeadersGetHeaderFromUniqueId(container.headers, 123456789); + EXPECT_EQ(shouldBeNull,nullptr); +} + + // ---------------------------------------------------------------------- // Check that header collection creation works correctly when a // collection with no headers is passed @@ -315,180 +339,212 @@ const char* testHeaders_PseudoHeaders[] = { "header1\x1Fheader2\x1Fheader3" }; -// TODO - Review as part of test refactor -//TEST_F(HeadersTests, PseudoHeadersPositive) { -// CreateHeaders( -// testHeaders_PseudoHeaders, -// sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), -// false); -// EXPECT_EQ(6, headers->count); -// -// for (uint32_t i = 0; i < headers->count; i++) { -// EXPECT_EQ(true, headers->items[i].isDataSet); -// } -// -// EXPECT_STREQ("header1", headers->items[0].name); -// EXPECT_EQ(1, headers->items[0].segments->count); -// -// EXPECT_STREQ("header2", headers->items[1].name); -// EXPECT_EQ(1, headers->items[1].segments->count); -// -// EXPECT_STREQ("header3", headers->items[2].name); -// EXPECT_EQ(1, headers->items[2].segments->count); -// -// EXPECT_STREQ("header1\x1Fheader2", headers->items[3].name); -// EXPECT_EQ(2, headers->items[3].segments->count); -// EXPECT_EQ(7, headers->items[3].segments->items[0].length); -// EXPECT_EQ(7, headers->items[3].segments->items[1].length); -// EXPECT_EQ(0, StringCompareLength( -// "header1", -// headers->items[3].segments->items[0].segment, -// 7)); -// EXPECT_EQ(0, StringCompareLength( -// "header2", -// headers->items[3].segments->items[1].segment, -// 7)); -// -// EXPECT_STREQ("header2\x1Fheader3", headers->items[4].name); -// EXPECT_EQ(2, headers->items[4].segments->count); -// EXPECT_EQ(7, headers->items[4].segments->items[0].length); -// EXPECT_EQ(7, headers->items[4].segments->items[1].length); -// EXPECT_EQ(0, StringCompareLength( -// "header2", -// headers->items[4].segments->items[0].segment, -// 7)); -// EXPECT_EQ(0, StringCompareLength( -// "header3", -// headers->items[4].segments->items[1].segment, -// 7)); -// -// EXPECT_STREQ("header1\x1Fheader2\x1Fheader3", headers->items[5].name); -// EXPECT_EQ(3, headers->items[5].segments->count); -// EXPECT_EQ(7, headers->items[5].segments->items[0].length); -// EXPECT_EQ(7, headers->items[5].segments->items[1].length); -// EXPECT_EQ(7, headers->items[5].segments->items[2].length); -// EXPECT_EQ(0, StringCompareLength( -// "header1", -// headers->items[5].segments->items[0].segment, -// 7)); -// EXPECT_EQ(0, StringCompareLength( -// "header2", -// headers->items[5].segments->items[1].segment, -// 7)); -// EXPECT_EQ(0, StringCompareLength( -// "header3", -// headers->items[5].segments->items[2].segment, -// 7)); -//} -// -//// ---------------------------------------------------------------------- -//// Check that header collection creation adds headers contained in a -//// pseudo header if it is not already present. -//// ---------------------------------------------------------------------- -//const char* testHeaders_PseudoHeadersMissing[] = { -// "header1\x1Fheader2" -//}; -// -//TEST_F(HeadersTests, PseudoHeadersMissing) { -// CreateHeaders( -// testHeaders_PseudoHeadersMissing, -// sizeof(testHeaders_PseudoHeadersMissing) / sizeof(const char*), -// false); -// EXPECT_EQ(3, headers->count); -// EXPECT_EQ(1, headers->pseudoHeadersCount); -// -// EXPECT_STREQ("header1\x1Fheader2", headers->items[0].name); -// EXPECT_EQ(true, headers->items[0].isDataSet); -// EXPECT_EQ(2, headers->items[0].segments->count); -// EXPECT_EQ(7, headers->items[0].segments->items[0].length); -// EXPECT_EQ(7, headers->items[0].segments->items[1].length); -// EXPECT_EQ(0, StringCompareLength( -// "header1", -// headers->items[0].segments->items[0].segment, -// 7)); -// EXPECT_EQ(0, StringCompareLength( -// "header2", -// headers->items[0].segments->items[1].segment, -// 7)); -// -// EXPECT_STREQ("header1", headers->items[1].name); -// EXPECT_EQ(false, headers->items[1].isDataSet); -// EXPECT_EQ(1, headers->items[1].segments->count); -// EXPECT_EQ(7, headers->items[1].segments->items[0].length); -// EXPECT_EQ(0, StringCompareLength( -// "header1", -// headers->items[1].segments->items[0].segment, -// 7)); -// -// EXPECT_STREQ("header2", headers->items[2].name); -// EXPECT_EQ(false, headers->items[1].isDataSet); -// EXPECT_EQ(1, headers->items[2].segments->count); -// EXPECT_EQ(7, headers->items[2].segments->items[0].length); -// EXPECT_EQ(0, StringCompareLength( -// "header2", -// headers->items[2].segments->items[0].segment, -// 7)); -//} -// -//// ---------------------------------------------------------------------- -//// Check that header collection creation construct pseudo headers -//// correctly when pseudo header contains special cases. -//// These special cases are very unlikely to happen but are valid. Thus -//// added to test the robustness of the code. -//// ---------------------------------------------------------------------- -// -//const char* testHeaders_PseudoHeadersSpecialCases[] = { -// "header1", -// "header2", -// "\x1Fheader1", -// "header1\x1F", -// "\x1F\x1F\x1F", -// "header1\x1F\x1Fheader2" -//}; -// -//TEST_F(HeadersTests, PseudoHeadersSpecialCases) { -// CreateHeaders( -// testHeaders_PseudoHeadersSpecialCases, -// sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), -// false); -// EXPECT_EQ(5, headers->count); -// EXPECT_EQ(1, headers->pseudoHeadersCount); -// for (uint32_t i = 0; i < headers->count; i++) { -// EXPECT_EQ(true, headers->items[i].isDataSet); -// } -// -// EXPECT_STREQ("header1", headers->items[0].name); -// EXPECT_EQ(1, headers->items[0].segments->count); -// -// EXPECT_STREQ("header2", headers->items[1].name); -// EXPECT_EQ(1, headers->items[1].segments->count); -// -// EXPECT_STREQ("\x1Fheader1", headers->items[2].name); -// EXPECT_EQ(1, headers->items[2].segments->count); -// EXPECT_EQ(7, headers->items[2].segments->items[0].length); -// EXPECT_EQ(0, StringCompareLength( -// "header1", -// headers->items[2].segments->items[0].segment, -// 7)); -// -// EXPECT_STREQ("header1\x1F", headers->items[3].name); -// EXPECT_EQ(1, headers->items[3].segments->count); -// EXPECT_EQ(7, headers->items[3].segments->items[0].length); -// EXPECT_EQ(0, StringCompareLength( -// "header1", -// headers->items[3].segments->items[0].segment, -// 7)); -// -// EXPECT_STREQ("header1\x1F\x1Fheader2", headers->items[4].name); -// EXPECT_EQ(2, headers->items[4].segments->count); -// EXPECT_EQ(7, headers->items[4].segments->items[0].length); -// EXPECT_EQ(7, headers->items[4].segments->items[1].length); -// EXPECT_EQ(0, StringCompareLength( -// "header1", -// headers->items[4].segments->items[0].segment, -// 7)); -// EXPECT_EQ(0, StringCompareLength( -// "header2", -// headers->items[4].segments->items[1].segment, -// 7)); -//} \ No newline at end of file +TEST_F(HeadersTests, PseudoHeadersPositive) { + CreateHeaders( + testHeaders_PseudoHeaders, + sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), + false); + EXPECT_EQ(6, headers->count); + + for (uint32_t i = 0; i < headers->count; i++) { + EXPECT_EQ(true, headers->items[i].isDataSet); + } + + EXPECT_STREQ("header1", headers->items[0].name); + EXPECT_EQ(0, headers->items[0].segmentHeaders->count); // a simple header does not have segments + EXPECT_EQ(2, headers->items[0].pseudoHeaders->count); // mentioned in 2 pseudoheaders + EXPECT_EQ(&headers->items[3], headers->items[0].pseudoHeaders->items[0]); + EXPECT_EQ(&headers->items[5], headers->items[0].pseudoHeaders->items[1]); + + EXPECT_STREQ("header2", headers->items[1].name); + EXPECT_EQ(0, headers->items[1].segmentHeaders->count); // a simple header does not have segments + EXPECT_EQ(3, headers->items[1].pseudoHeaders->count); //mentioned in 3 pseudoheaders + EXPECT_EQ(&headers->items[3], headers->items[1].pseudoHeaders->items[0]); + EXPECT_EQ(&headers->items[4], headers->items[1].pseudoHeaders->items[1]); + EXPECT_EQ(&headers->items[5], headers->items[1].pseudoHeaders->items[2]); + + EXPECT_STREQ("header3", headers->items[2].name); + EXPECT_EQ(0, headers->items[2].segmentHeaders->count); // a simple header does not have segments + EXPECT_EQ(2, headers->items[2].pseudoHeaders->count); // mentioned in 2 pseudoheaders + EXPECT_EQ(&headers->items[4], headers->items[2].pseudoHeaders->items[0]); + EXPECT_EQ(&headers->items[5], headers->items[2].pseudoHeaders->items[1]); + + EXPECT_STREQ("header1\x1Fheader2", headers->items[3].name); + EXPECT_EQ(2, headers->items[3].segmentHeaders->count); + EXPECT_EQ(7, headers->items[3].segmentHeaders->items[0]->length); + EXPECT_EQ(7, headers->items[3].segmentHeaders->items[1]->length); + EXPECT_EQ(0, StringCompareLength( + "header1", + headers->items[3].segmentHeaders->items[0]->name, + 7)); + EXPECT_EQ(&headers->items[0], headers->items[3].segmentHeaders->items[0]); + + EXPECT_EQ(0, StringCompareLength( + "header2", + headers->items[3].segmentHeaders->items[1]->name, + 7)); + EXPECT_EQ(&headers->items[1], headers->items[3].segmentHeaders->items[1]); + + EXPECT_STREQ("header2\x1Fheader3", headers->items[4].name); + EXPECT_EQ(2, headers->items[4].segmentHeaders->count); + EXPECT_EQ(7, headers->items[4].segmentHeaders->items[0]->length); + EXPECT_EQ(7, headers->items[4].segmentHeaders->items[1]->length); + EXPECT_EQ(0, StringCompareLength( + "header2", + headers->items[4].segmentHeaders->items[0]->name, + 7)); + EXPECT_EQ(&headers->items[1], headers->items[4].segmentHeaders->items[0]); + EXPECT_EQ(0, StringCompareLength( + "header3", + headers->items[4].segmentHeaders->items[1]->name, + 7)); + EXPECT_EQ(&headers->items[2], headers->items[4].segmentHeaders->items[1]); + + + EXPECT_STREQ("header1\x1Fheader2\x1Fheader3", headers->items[5].name); + EXPECT_EQ(3, headers->items[5].segmentHeaders->count); + EXPECT_EQ(7, headers->items[5].segmentHeaders->items[0]->length); + EXPECT_EQ(7, headers->items[5].segmentHeaders->items[1]->length); + EXPECT_EQ(7, headers->items[5].segmentHeaders->items[2]->length); + + EXPECT_EQ(0, StringCompareLength( + "header1", + headers->items[5].segmentHeaders->items[0]->name, + 7)); + EXPECT_EQ(&headers->items[0], headers->items[5].segmentHeaders->items[0]); + + EXPECT_EQ(0, StringCompareLength( + "header2", + headers->items[5].segmentHeaders->items[1]->name, + 7)); + EXPECT_EQ(&headers->items[1], headers->items[5].segmentHeaders->items[1]); + + EXPECT_EQ(0, StringCompareLength( + "header3", + headers->items[5].segmentHeaders->items[2]->name, + 7)); + EXPECT_EQ(&headers->items[2], headers->items[5].segmentHeaders->items[2]); +} + +// ---------------------------------------------------------------------- +// Check that header collection creation adds headers contained in a +// pseudo header if it is not already present. +// ---------------------------------------------------------------------- + +const char* testHeaders_PseudoHeadersMissing[] = { + "header1\x1Fheader2" +}; + +TEST_F(HeadersTests, PseudoHeadersMissing) { + CreateHeaders( + testHeaders_PseudoHeadersMissing, + sizeof(testHeaders_PseudoHeadersMissing) / sizeof(const char*), + false); + EXPECT_EQ(3, headers->count); + + EXPECT_STREQ("header1\x1Fheader2", headers->items[0].name); + EXPECT_EQ(true, headers->items[0].isDataSet); + EXPECT_EQ(2, headers->items[0].segmentHeaders->count); + EXPECT_EQ(7, headers->items[0].segmentHeaders->items[0]->length); + EXPECT_EQ(1, headers->items[0].segmentHeaders->items[0]->pseudoHeaders->count); + EXPECT_EQ(&headers->items[0], headers->items[0].segmentHeaders->items[0]->pseudoHeaders->items[0]); + + EXPECT_EQ(7, headers->items[0].segmentHeaders->items[1]->length); + EXPECT_EQ(1, headers->items[0].segmentHeaders->items[1]->pseudoHeaders->count); + EXPECT_EQ(&headers->items[0], headers->items[0].segmentHeaders->items[1]->pseudoHeaders->items[0]); + + EXPECT_EQ(0, StringCompareLength( + "header1", + headers->items[0].segmentHeaders->items[0]->name, + 7)); + EXPECT_EQ(0, StringCompareLength( + "header2", + headers->items[0].segmentHeaders->items[1]->name, + 7)); + + EXPECT_STREQ("header1", headers->items[1].name); + EXPECT_EQ(false, headers->items[1].isDataSet); + EXPECT_EQ(1, headers->items[1].pseudoHeaders->count); + EXPECT_EQ(headers->items[0].segmentHeaders->items[0], &headers->items[1]); + + + EXPECT_STREQ("header2", headers->items[2].name); + EXPECT_EQ(false, headers->items[2].isDataSet); + EXPECT_EQ(1, headers->items[2].pseudoHeaders->count); + EXPECT_EQ(headers->items[0].segmentHeaders->items[1], &headers->items[2]); +} + +// ---------------------------------------------------------------------- +// Check that header collection creation construct pseudo headers +// correctly when pseudo header contains special cases. +// These special cases are very unlikely to happen but are valid. Thus +// added to test the robustness of the code. +// ---------------------------------------------------------------------- + +const char* testHeaders_PseudoHeadersSpecialCases[] = { + "header1", + "header2", + "\x1Fheader1", + "header1\x1F", + "\x1F\x1F\x1F", + "header1\x1F\x1Fheader2" +}; + +TEST_F(HeadersTests, PseudoHeadersSpecialCases) { + CreateHeaders( + testHeaders_PseudoHeadersSpecialCases, + sizeof(testHeaders_PseudoHeaders) / sizeof(const char*), + false); + EXPECT_EQ(5, headers->count); + + for (uint32_t i = 0; i < headers->count; i++) { + EXPECT_EQ(true, headers->items[i].isDataSet); + } + + EXPECT_STREQ("header1", headers->items[0].name); + EXPECT_EQ(0, headers->items[0].segmentHeaders->count); // simple header + EXPECT_EQ(3, headers->items[0].pseudoHeaders->count); // mentioned in 3 pseudoheaders + + EXPECT_STREQ("header2", headers->items[1].name); + EXPECT_EQ(0, headers->items[1].segmentHeaders->count); // simple header + + EXPECT_STREQ("\x1Fheader1", headers->items[2].name); + EXPECT_EQ(1, headers->items[2].segmentHeaders->count); + EXPECT_EQ(7, headers->items[2].segmentHeaders->items[0]->length); + EXPECT_EQ(0, StringCompareLength( + "header1", + headers->items[2].segmentHeaders->items[0]->name, + 7)); + + //child parent links: + EXPECT_EQ(&headers->items[0], headers->items[2].segmentHeaders->items[0]); + EXPECT_EQ(headers->items[0].pseudoHeaders->items[0], &headers->items[2]); + + EXPECT_STREQ("header1\x1F", headers->items[3].name); + EXPECT_EQ(1, headers->items[3].segmentHeaders->count); + EXPECT_EQ(7, headers->items[3].segmentHeaders->items[0]->length); + EXPECT_EQ(0, StringCompareLength( + "header1", + headers->items[3].segmentHeaders->items[0]->name, + 7)); + //child parent links: + EXPECT_EQ(&headers->items[0], headers->items[3].segmentHeaders->items[0]); + EXPECT_EQ(headers->items[0].pseudoHeaders->items[1], &headers->items[3]); + + + EXPECT_STREQ("header1\x1F\x1Fheader2", headers->items[4].name); + EXPECT_EQ(2, headers->items[4].segmentHeaders->count); + EXPECT_EQ(7, headers->items[4].segmentHeaders->items[0]->length); + EXPECT_EQ(7, headers->items[4].segmentHeaders->items[1]->length); + EXPECT_EQ(0, StringCompareLength( + "header1", + headers->items[4].segmentHeaders->items[0]->name, + 7)); + EXPECT_EQ(0, StringCompareLength( + "header2", + headers->items[4].segmentHeaders->items[1]->name, + 7)); + //child parent links: + EXPECT_EQ(&headers->items[0], headers->items[4].segmentHeaders->items[0]); + EXPECT_EQ(headers->items[0].pseudoHeaders->items[2], &headers->items[4]); +} diff --git a/tests/PoolTests.cpp b/tests/PoolTests.cpp index 40fb84b6..c08e810a 100644 --- a/tests/PoolTests.cpp +++ b/tests/PoolTests.cpp @@ -140,14 +140,13 @@ TEST_F(Pool, PoolInit) { */ TEST_F(Pool, PoolResourcesCreated) { // Arrange - void* result; testResourceCounter counter; counter.count = 0; counter.resources = resources; FIFTYONE_DEGREES_EXCEPTION_CREATE; // Act - result = fiftyoneDegreesPoolInit( + fiftyoneDegreesPoolInit( &pool, maxConcurrency, &counter, @@ -168,12 +167,11 @@ TEST_F(Pool, PoolResourcesCreated) { */ TEST_F(Pool, PoolResourcesFreed) { // Arrange - void* result; testResourceCounter counter; counter.count = 0; counter.resources = resources; FIFTYONE_DEGREES_EXCEPTION_CREATE; - result = fiftyoneDegreesPoolInit( + fiftyoneDegreesPoolInit( &pool, maxConcurrency, &counter, @@ -197,12 +195,11 @@ TEST_F(Pool, PoolResourcesFreed) { */ TEST_F(Pool, PoolGet) { // Arrange - void* result; testResourceCounter counter; counter.count = 0; counter.resources = resources; FIFTYONE_DEGREES_EXCEPTION_CREATE; - result = fiftyoneDegreesPoolInit( + fiftyoneDegreesPoolInit( &pool, 1, &counter, @@ -230,12 +227,11 @@ TEST_F(Pool, PoolGet) { */ TEST_F(Pool, PoolGetMultiple) { // Arrange - void* result; testResourceCounter counter; counter.count = 0; counter.resources = resources; FIFTYONE_DEGREES_EXCEPTION_CREATE; - result = fiftyoneDegreesPoolInit( + fiftyoneDegreesPoolInit( &pool, 2, &counter, @@ -268,12 +264,11 @@ TEST_F(Pool, PoolGetMultiple) { */ TEST_F(Pool, PoolGetInsufficientHandles) { // Arrange - void* result; testResourceCounter counter; counter.count = 0; counter.resources = resources; FIFTYONE_DEGREES_EXCEPTION_CREATE; - result = fiftyoneDegreesPoolInit( + fiftyoneDegreesPoolInit( &pool, 1, &counter, @@ -305,12 +300,11 @@ TEST_F(Pool, PoolGetInsufficientHandles) { */ TEST_F(Pool, PoolGetInsufficientHandlesMultiple) { // Arrange - void* result; testResourceCounter counter; counter.count = 0; counter.resources = resources; FIFTYONE_DEGREES_EXCEPTION_CREATE; - result = fiftyoneDegreesPoolInit( + fiftyoneDegreesPoolInit( &pool, 2, &counter, @@ -347,12 +341,11 @@ TEST_F(Pool, PoolGetInsufficientHandlesMultiple) { */ TEST_F(Pool, PoolGetHandlesReturned) { // Arrange - void* result; testResourceCounter counter; counter.count = 0; counter.resources = resources; FIFTYONE_DEGREES_EXCEPTION_CREATE; - result = fiftyoneDegreesPoolInit( + fiftyoneDegreesPoolInit( &pool, 1, &counter, @@ -385,4 +378,4 @@ TEST_F(Pool, PoolGetHandlesReturned) { // Cleanup fiftyoneDegreesPoolItemRelease(item); fiftyoneDegreesPoolItemRelease(item2); -} \ No newline at end of file +} diff --git a/tests/ProfileTests.cpp b/tests/ProfileTests.cpp new file mode 100644 index 00000000..d3c24063 --- /dev/null +++ b/tests/ProfileTests.cpp @@ -0,0 +1,514 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#include "../fiftyone.h" +#include "../string.h" +#include "../profile.h" +#include "../value.h" + +#include "Base.hpp" +#include "limits.h" +#include "StringCollection.hpp" +#include "FixedSizeCollection.hpp" +#include "VariableSizeCollection.hpp" +#include + +constexpr int N_PROPERTIES = 15; + +//in fact because each our profile has a fixed size and has a fixed number of properties - we can construct +//a fixed width element collection - in the generic case this would not be possible, for some profiles +//may miss values - so the arrays of valueIndices would be of variadic length +#pragma pack(push, 1) +typedef struct { + fiftyoneDegreesProfile profile; + uint32_t valueIndices[N_PROPERTIES]; +} ProfileContainer; +#pragma pack(pop) + +class ProfileTests : public Base { +public: + ProfileTests(); + virtual ~ProfileTests(); + virtual void SetUp(); + virtual void TearDown(); + void CreateObjects(); + void assessProperty(fiftyoneDegreesProperty *property, int i); + void assessValue(fiftyoneDegreesValue *value, int i); + + int valueNameStringIndexFromValueIndex(int i); + uint32_t profileIdFromProfileIndex(int i); + uint32_t profileIndexFromProfileId(int id); + fiftyoneDegreesPropertyAvailableArray *createAvailableProperties(std::vector &propertyNames); + int propertyIndexFromPropertyName(string &propertyName); + void indicesLookup(std::vector &propertyNames); + void iterateValueIndicesForAvailableProperties(std::vector &propertyNames); + void iterateValueIndicesForEachAvailableProperty(std::vector &propertyNames); + + fiftyoneDegreesCollectionItem item; + + StringCollection *stringsCollectionHelper; + FixedSizeCollection *propertiesCollectionHelper; + VariableSizeCollection *profilesCollectionHelper; + FixedSizeCollection *profileOffsetsCollectionHelper; + FixedSizeCollection *valuesCollectionHelper; + + fiftyoneDegreesCollection *stringsCollection; + fiftyoneDegreesCollection *propertiesCollection; + fiftyoneDegreesCollection *profilesCollection; + fiftyoneDegreesCollection *valuesCollection; + fiftyoneDegreesCollection *profileOffsetsCollection; + + + static constexpr int N_PER_PROPERTY = 6; + static constexpr int N_PROFILES = 5; + static const char *strings[N_PROPERTIES * (N_PER_PROPERTY + 1)]; + static const char *profileValueNames[][N_PROPERTIES]; +}; + +//NOTE: each property name is in the beginning of the line, followed by values +//NOTE: both properties and values within a property must be alphabetically sorted +// for the binary search is able to search within the values collection between the property first and last values + +const char *ProfileTests::strings[] = { + "Brightness", "Bright", "Dim", "Dull", "Glowing", "Shiny", //0 + "Color", "Black", "Blue", "Green", "Red", "Yellow", //6 + "Condition", "Broken", "New", "Old", "Pristine", "Worn", //12 + "Flexibility", "Bendable", "Flexible", "Pliable", "Rigid", "Stiff", //18 + "Material", "Fabric", "Glass", "Metal", "Plastic", "Wood", //24 + "Opacity", "Opaque", "Semi-opaque", "Semi-transparent", "Translucent", "Transparent", //30 + "Pattern", "Checkered", "Dotted", "Floral", "Plain", "Striped", //36 + "Position", "Diagonal", "Horizontal", "Tilted", "Upside-down", "Vertical", //42 + "Shape", "Oval", "Rectangular", "Round", "Square", "Triangular", //48 + "Size", "Enormous", "Large", "Medium", "Small", "Tiny", //54 + "Taste", "Bitter", "Salty", "Savory", "Sour", "Sweet", //60 + "Temperature", "Cold", "Cool", "Freezing", "Hot", "Warm", //66 + "Texture", "Hard", "Rough", "Smooth", "Soft", "Sticky", //72 + "Volume", "Loud", "Moderate", "Mute", "Quiet", "Silent", //78 + "Weight", "Dense", "Heavy", "Light", "Lightweight", "Moderate" //84 +}; + +//each profile is actually a set of indices of the property values in the valuesCollection array, +//but to make it more human readable - we compute those indices from value names +//for each profile value indices need to be +//strictly increasing for the binary search to work, this is achieved, by the fact that +//properties are sorted alphabetically and index of each value increases in the valuesCollection + +const char *ProfileTests::profileValueNames[][N_PROPERTIES] = { + { + "Bright", "Black", "Broken", "Bendable", "Fabric", "Opaque", + "Checkered", "Diagonal", "Oval", "Enormous", "Bitter", + "Cold", "Hard", "Loud", "Dense" + }, + { + "Dim", "Blue", "New", "Flexible", "Glass", "Semi-opaque", + "Dotted", "Horizontal", "Rectangular", "Large", "Salty", + "Cool", "Rough", "Moderate", "Heavy" + }, + { + "Dull", "Green", "Old", "Pliable", "Metal", "Semi-transparent", + "Floral", "Tilted", "Round", "Medium", "Savory", + "Freezing", "Smooth", "Mute", "Light" + }, + { + "Glowing", "Red", "Pristine", "Rigid", "Plastic", "Translucent", + "Plain", "Upside-down", "Square", "Small", "Sour", + "Hot", "Soft", "Quiet", "Lightweight" + }, + { + "Shiny", "Yellow", "Worn", "Stiff", "Wood", "Transparent", + "Striped", "Vertical", "Triangular", "Tiny", "Sweet", + "Warm", "Sticky", "Silent", "Moderate" + } +}; + +ProfileTests::ProfileTests() { + CreateObjects(); +} + +ProfileTests::~ProfileTests() { + delete propertiesCollectionHelper; + delete valuesCollectionHelper; + delete stringsCollectionHelper; + delete profilesCollectionHelper; + delete profileOffsetsCollectionHelper; +} + +void ProfileTests::CreateObjects() { + stringsCollectionHelper = new StringCollection(strings, sizeof(strings)/sizeof(strings[0])); + stringsCollection = stringsCollectionHelper->getState()->collection; + uint32_t *offsets = stringsCollectionHelper->getState()->offsets; + + std::vector values; + for (int i=0;i(values); + valuesCollection = valuesCollectionHelper->getState()->collection; + + std::vector tempProperties; + for (byte i=0;i(tempProperties); + propertiesCollection = propertiesCollectionHelper->getState()->collection; + + std::vector tempProfiles; + for (int i=0; i(tempProfiles); + profilesCollection = profilesCollectionHelper->getState()->collection; + uint32_t *profileOffsets = profilesCollectionHelper->getState()->offsets; + + std::vector tempProfileOffsets; + for (int i=0;i(tempProfileOffsets); + profileOffsetsCollection = profileOffsetsCollectionHelper->getState()->collection; +} + +void ProfileTests::SetUp() { + Base::SetUp(); +} + +void ProfileTests::TearDown() { + Base::TearDown(); +} + +// projects value collection index onto the strings array index +int ProfileTests::valueNameStringIndexFromValueIndex(int i) { + int propertyIdx = i / (N_PER_PROPERTY - 1); + int propertyNameIdx = propertyIdx * N_PER_PROPERTY; + int valueNameIdx = propertyNameIdx + i % (N_PER_PROPERTY - 1) + 1; + return valueNameIdx; +} + +// compute unique profile Id from profile index in the collection +uint32_t ProfileTests::profileIdFromProfileIndex(int i) { + return 101 * i + 1; +} + +uint32_t ProfileTests::profileIndexFromProfileId(int id) { + return (id - 1) / 101; +} + +void ProfileTests::assessValue(fiftyoneDegreesValue *value, int i) { + EXCEPTION_CREATE + int valueNameIdx = valueNameStringIndexFromValueIndex(i); + + EXPECT_STREQ(&fiftyoneDegreesValueGetName(stringsCollection, value, &item, exception)->value, strings[valueNameIdx]); + + EXPECT_STREQ(&fiftyoneDegreesValueGetDescription(stringsCollection, value, &item, exception)->value, strings[valueNameIdx]); + + EXPECT_STREQ(&fiftyoneDegreesValueGetUrl(stringsCollection, value, &item, exception)->value, strings[valueNameIdx]); + + +} + +TEST_F(ProfileTests, Values) { + for (int i=0;i<(N_PER_PROPERTY-1) * N_PROPERTIES;i++) { + EXCEPTION_CREATE + fiftyoneDegreesValue *value = fiftyoneDegreesValueGet(valuesCollection, i, &item, exception); + assessValue(value, i); + + int propertyIdx = i / (N_PER_PROPERTY - 1); + int valueNameIdx = valueNameStringIndexFromValueIndex(i); + fiftyoneDegreesProperty *property = fiftyoneDegreesPropertyGet(propertiesCollection, propertyIdx, &item, exception); + value = fiftyoneDegreesValueGetByName(valuesCollection, stringsCollection, property, strings[valueNameIdx], &item, exception); + assessValue(value, i); + + EXPECT_EQ(i, fiftyoneDegreesValueGetIndexByName(valuesCollection, stringsCollection, property, strings[valueNameIdx], exception)); + EXPECT_EQ(-1, fiftyoneDegreesValueGetIndexByName(valuesCollection, stringsCollection, property, "NonExistantName", exception)); + } +} + +TEST_F(ProfileTests, ProfileGetBy) { + EXCEPTION_CREATE + for (int i=0; iprofileId, id); + + profile = fiftyoneDegreesProfileGetByIndex(profileOffsetsCollection, profilesCollection, i, &item, exception); + EXPECT_EQ(profile->profileId, id); + } +} + +bool iterateValuesCallback(void *state, fiftyoneDegreesCollectionItem *item) { + std::vector *values = (std::vector *)state; + values->push_back((fiftyoneDegreesValue *)item->data.ptr); + return true; +} + +bool iterateValueIndices(void *state, uint32_t valueIndex) { + std::vector *indices = (std::vector *)state; + indices->push_back(valueIndex); + return true; +} + +TEST_F(ProfileTests, ProfileIterateValues) { + EXCEPTION_CREATE + for (int i=0;i values; + fiftyoneDegreesProfileIterateValuesForProperty(valuesCollection, profile, property, &values, iterateValuesCallback, exception); + EXPECT_GT(values.size(), 0); + for(auto v: values) { + EXPECT_EQ(v->propertyIndex, j); + } + } + } + EXPECT_TRUE(EXCEPTION_OKAY); +} + +int ProfileTests::propertyIndexFromPropertyName(std::string &propertyName) { + EXCEPTION_CREATE + for (int i=0;ivalue)) { + return i; + } + } + return -1; +} + +fiftyoneDegreesPropertyAvailableArray *ProfileTests::createAvailableProperties(std::vector &propertyNames) { + //random set of 5 available properties + fiftyoneDegreesPropertyAvailableArray * FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesPropertyAvailable, propertiesAvailable, N_PROPERTIES); + EXCEPTION_CREATE + for (size_t j=0;jgetState(), N_PER_PROPERTY * propIdx, &item); + propertiesAvailable->items[j].propertyIndex = propIdx; + propertiesAvailable->items[j].name = item; + propertiesAvailable->items[j].evidenceProperties = NULL; + propertiesAvailable->items[j].delayExecution = false; + propertiesAvailable->count++; + } + return propertiesAvailable; +} + +void ProfileTests::iterateValueIndicesForAvailableProperties(std::vector &propertyNames) { + fiftyoneDegreesPropertiesAvailable *propertiesAvailable = createAvailableProperties(propertyNames); + + for (int i=0;i valueIndices; + EXCEPTION_CLEAR + fiftyoneDegreesProfileIterateValueIndexes(profile, propertiesAvailable, valuesCollection, &valueIndices, iterateValueIndices, exception); + EXPECT_EQ(valueIndices.size(), propertiesAvailable->count); + for (auto index: valueIndices) { + fiftyoneDegreesValue *value = fiftyoneDegreesValueGet(valuesCollection, index, &item, exception); + fiftyoneDegreesString *s = fiftyoneDegreesStringGet(stringsCollection, value->nameOffset, &item, exception); + const char **found = std::find_if(&profileValueNames[i][0], &profileValueNames[i][0] + N_PROPERTIES, [s](const char *p){ return 0 == strcmp(&s->value, p); }); + EXPECT_NE(&profileValueNames[i][0] + N_PROPERTIES, found); //belongs to current profile + } + EXPECT_TRUE(EXCEPTION_OKAY) << exception->status; + } + + fiftyoneDegreesFree(propertiesAvailable); +} + +TEST_F(ProfileTests, ProfileIterateValueIndices) { + std::vector propertyNamesNonRepetitive {"Material","Position","Opacity","Shape","Weight"}; + std::vector propertyNamesRepetitive {"Material","Material","Opacity","Shape","Weight"}; + iterateValueIndicesForAvailableProperties(propertyNamesNonRepetitive); + //repetitive are not supported: + //iterateValueIndicesForAvailableProperties(propertyNamesRepetitive); +} + +bool iterateProfiles(void *state, fiftyoneDegreesCollectionItem *item) { + std::vector *profiles = (std::vector *)state; + profiles->push_back((fiftyoneDegreesProfile *) item->data.ptr); + return true; +} + +TEST_F(ProfileTests, profileIterateForPropertyAndValue) { + std::vector profiles; + EXCEPTION_CREATE + fiftyoneDegreesProfileIterateProfilesForPropertyAndValue(stringsCollection, propertiesCollection, valuesCollection, profilesCollection, profileOffsetsCollection, "Size", "Enormous", &profiles, iterateProfiles, exception); + EXPECT_EQ(profileIndexFromProfileId(profiles[0]->profileId), 0); + EXPECT_TRUE(EXCEPTION_OKAY); + + profiles.clear(); + fiftyoneDegreesProfileIterateProfilesForPropertyAndValue(stringsCollection, propertiesCollection, valuesCollection, profilesCollection, profileOffsetsCollection, "Position", "Horizontal", &profiles, iterateProfiles, exception); + EXPECT_EQ(profileIndexFromProfileId(profiles[0]->profileId), 1); + + profiles.clear(); + fiftyoneDegreesProfileIterateProfilesForPropertyAndValue(stringsCollection, propertiesCollection, valuesCollection, profilesCollection, profileOffsetsCollection, "Material", "Metal", &profiles, iterateProfiles, exception); + EXPECT_EQ(profileIndexFromProfileId(profiles[0]->profileId), 2); + + profiles.clear(); + fiftyoneDegreesProfileIterateProfilesForPropertyAndValue(stringsCollection, propertiesCollection, valuesCollection, profilesCollection, profileOffsetsCollection, "Brightness", "Glowing", &profiles, iterateProfiles, exception); + EXPECT_EQ(profileIndexFromProfileId(profiles[0]->profileId), 3); + + profiles.clear(); + fiftyoneDegreesProfileIterateProfilesForPropertyAndValue(stringsCollection, propertiesCollection, valuesCollection, profilesCollection, profileOffsetsCollection, "Weight", "Moderate", &profiles, iterateProfiles, exception); + EXPECT_EQ(profileIndexFromProfileId(profiles[0]->profileId), 4); +} + +void ProfileTests::indicesLookup(std::vector &propertyNames) { + EXCEPTION_CREATE + fiftyoneDegreesPropertiesAvailable *availableProperties = createAvailableProperties(propertyNames); + fiftyoneDegreesIndexPropertyProfile *index = fiftyoneDegreesIndexPropertyProfileCreate(profilesCollection, profileOffsetsCollection, availableProperties, valuesCollection, exception); + + for (int i=0;icount;j++) { + fiftyoneDegreesPropertyAvailable property = availableProperties->items[j]; + //lookup property value Index within the profile with profileId for the available property indexed at j in the array of availableProperties + uint32_t valueIdxIdxWithinProfile = fiftyoneDegreesIndexPropertyProfileLookup(index, profileId, j); + //convert valueIdxIdxWithinProfile to the valueIndex in the actual values collection by looking it up with the profile collection + uint32_t *first = (uint32_t*)(profile + 1); + uint32_t valueIdx = *(first + valueIdxIdxWithinProfile); + //now retrieve value and check that it belongs to the property above + fiftyoneDegreesValue *value = fiftyoneDegreesValueGet(valuesCollection, valueIdx, &item, exception); + EXPECT_EQ(value->propertyIndex, property.propertyIndex); + } + } + + fiftyoneDegreesIndexPropertyProfileFree(index); + fiftyoneDegreesFree(availableProperties); +} + +TEST_F(ProfileTests, indicesLookup) { + std::vector propertyNamesNonRepetitive {"Volume","Position","Texture","Flexibility","Weight", "Brightness"}; + std::vector propertyNamesRepetitive {"Material","Position","Shape","Shape","Weight"}; + + indicesLookup(propertyNamesNonRepetitive); + //repetitive are not supported: + //indicesLookup(propertyNamesRepetitive); +} + +bool collectValues(void *state, fiftyoneDegreesCollectionItem *item) { + std::vector *values = (std::vector *)state; + values->push_back((fiftyoneDegreesValue *)item->data.ptr); + return true; +} + +void ProfileTests::iterateValueIndicesForEachAvailableProperty(std::vector &propertyNames) { + EXCEPTION_CREATE + fiftyoneDegreesPropertiesAvailable *availableProperties = createAvailableProperties(propertyNames); + fiftyoneDegreesIndexPropertyProfile *index = fiftyoneDegreesIndexPropertyProfileCreate(profilesCollection, profileOffsetsCollection, availableProperties, valuesCollection, exception); + + for (int i=0;i values; + fiftyoneDegreesProfileIterateValuesForPropertyWithIndex(valuesCollection, index, (uint32_t) j, profile, property, &values, collectValues, exception); + EXPECT_EQ(values.size(), 1); + fiftyoneDegreesValue *expectedValue = NULL; + uint32_t *first = (uint32_t *) (profile + 1); + for (uint32_t k=0;kvalueCount;++k) { + uint32_t valueIdx = *(first + k); + fiftyoneDegreesValue *candidate = fiftyoneDegreesValueGet(valuesCollection, valueIdx, &item, exception); + if ((uint32_t) candidate->propertyIndex == availableProperties->items[j].propertyIndex) { + expectedValue = candidate; + break; + } + } + EXPECT_NE(expectedValue, (fiftyoneDegreesValue *) NULL); + EXPECT_EQ(values[0], expectedValue); + EXPECT_EQ(values[0]->propertyIndex, expectedValue->propertyIndex); + EXPECT_TRUE(EXCEPTION_OKAY); + } + } + + fiftyoneDegreesIndexPropertyProfileFree(index); + fiftyoneDegreesFree(availableProperties); +} + +TEST_F(ProfileTests, iterateValueForPropertyWithIndex) { + std::vector propertyNamesNonRepetitive {"Taste","Position","Material","Size","Color", "Temperature"}; + iterateValueIndicesForEachAvailableProperty(propertyNamesNonRepetitive); +} + +TEST_F(ProfileTests, unhappyPaths) { + //NULL profileId + uint32_t profileOffset; + EXCEPTION_CREATE + uint32_t *profileOffsetPtr = fiftyoneDegreesProfileGetOffsetForProfileId(profileOffsetsCollection, 0, &profileOffset, exception); + EXPECT_FALSE(EXCEPTION_OKAY); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_PROFILE_EMPTY); + + // non-existent profileId + EXCEPTION_CLEAR + profileOffsetPtr = fiftyoneDegreesProfileGetOffsetForProfileId(profileOffsetsCollection, 20003, &profileOffset, exception); + EXPECT_EQ(profileOffsetPtr, (uint32_t *) NULL); +} diff --git a/tests/PropertyTests.cpp b/tests/PropertyTests.cpp new file mode 100644 index 00000000..f5b12f2b --- /dev/null +++ b/tests/PropertyTests.cpp @@ -0,0 +1,137 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#include "../fiftyone.h" +#include "../string.h" +#include "Base.hpp" +#include "limits.h" +#include "StringCollection.hpp" +#include "FixedSizeCollection.hpp" + +class PropertyTests : public Base { +public: + virtual void SetUp(); + virtual void TearDown(); + void CreateObjects(); + void assessProperty(fiftyoneDegreesProperty *property, int i); + + fiftyoneDegreesProperty *property; + StringCollection *stringsCollectionHelper; + fiftyoneDegreesCollection *stringsCollection; + + fiftyoneDegreesCollectionItem item; + + FixedSizeCollection *propertiesCollectionHelper; + fiftyoneDegreesCollection *propertiesCollection; + + static constexpr int N_PROPERTIES = 3; + static constexpr int N_PER_PROPERTY = 5; + static const char *strings[N_PROPERTIES * N_PER_PROPERTY]; + static const fiftyoneDegreesPropertyValueType types[N_PROPERTIES]; + +}; + +const fiftyoneDegreesPropertyValueType PropertyTests::types[N_PROPERTIES] = { + FIFTYONE_DEGREES_PROPERTY_VALUE_TYPE_JAVASCRIPT, + FIFTYONE_DEGREES_PROPERTY_VALUE_TYPE_STRING, + FIFTYONE_DEGREES_PROPERTY_VALUE_TYPE_DOUBLE, +}; + +const char *PropertyTests::strings[] = { + "prop1", "name", "descr", "cat", "url", + "prop2", "name2", "descr2", "cat2", "url2", + "prop3", "name3", "descr3", "cat3", "url3" +}; + +void PropertyTests::CreateObjects() { + stringsCollectionHelper = new StringCollection(strings, sizeof(strings)/sizeof(strings[0])); + stringsCollection = stringsCollectionHelper->getState()->collection; + uint32_t *offsets = stringsCollectionHelper->getState()->offsets; + std::vector tempProperties; + for (byte i=0;i(tempProperties); + propertiesCollection = propertiesCollectionHelper->getState()->collection; +} + +void PropertyTests::SetUp() { + CreateObjects(); + Base::SetUp(); +} + +void PropertyTests::TearDown() { + Base::TearDown(); + delete stringsCollectionHelper; + delete propertiesCollection; +} + +void PropertyTests::assessProperty(fiftyoneDegreesProperty *p, int i) { + EXCEPTION_CREATE + + String *name = fiftyoneDegreesPropertyGetName(stringsCollection, p, &item, exception); + EXPECT_STREQ(strings[i * N_PER_PROPERTY + 1], &name->value); + + String *descr = fiftyoneDegreesPropertyGetDescription(stringsCollection, p, &item, exception); + EXPECT_STREQ(strings[i * N_PER_PROPERTY + 2], &descr->value); + + String *cat = fiftyoneDegreesPropertyGetCategory(stringsCollection, p, &item, exception); + EXPECT_STREQ(strings[i * N_PER_PROPERTY + 3], &cat->value); + + String *url = fiftyoneDegreesPropertyGetUrl(stringsCollection, p, &item, exception); + EXPECT_STREQ(strings[i*N_PER_PROPERTY + 4], &url->value); +} + +TEST_F(PropertyTests, RetrievePropertyFromCollection) { + + for (int i=0;iptr); + fiftyoneDegreesFree(builder); +} + +TEST_F(Strings, StringBuilderAddChar) { + fiftyoneDegreesStringBuilderInit(builder); + fiftyoneDegreesStringBuilderAddChar(builder, 'a'); + ASSERT_EQ(builder->added, 1); + ASSERT_EQ(builder->remaining, bufferSize - 1); + + fiftyoneDegreesStringBuilderAddChar(builder, 'b'); + ASSERT_EQ(builder->added, 2); + ASSERT_EQ(builder->remaining, bufferSize - 2); + ASSERT_FALSE(builder->full); + + fiftyoneDegreesStringBuilderComplete(builder); + ASSERT_STREQ(builder->ptr, "ab"); + ASSERT_EQ(builder->added, 3); + ASSERT_EQ(builder->remaining, bufferSize - 3); + ASSERT_EQ(builder->length, bufferSize); + ASSERT_FALSE(builder->full); + + //adding a char after completion does not influence the result for it is null-terminated + //but adds to added field + char *current = builder->current; + fiftyoneDegreesStringBuilderAddChar(builder, 'c'); + fiftyoneDegreesStringBuilderAddChar(builder, 'd'); + ASSERT_STREQ(builder->ptr, "ab"); + ASSERT_EQ(builder->added, 5); + ASSERT_EQ(builder->remaining, bufferSize - 5); + StringBuilderComplete(builder); + ASSERT_EQ(builder->added, 6); + ASSERT_EQ(builder->remaining, bufferSize - 6); + ASSERT_STREQ(builder->ptr, "ab"); + ASSERT_STREQ(current, "cd"); + ASSERT_EQ(builder->length, bufferSize); + + ASSERT_FALSE(builder->full); + +} + +TEST_F(Strings, StringBuilderAddCharPastTheEnd) { + fiftyoneDegreesStringBuilderInit(builder); + for (size_t i=0; i < bufferSize + 3; ++i) { + fiftyoneDegreesStringBuilderAddChar(builder, 'a'); + } + + ASSERT_EQ(builder->added, bufferSize + 3); + ASSERT_EQ(builder->length, bufferSize); + ASSERT_EQ(builder->remaining, 1); + fiftyoneDegreesStringBuilderComplete(builder); + ASSERT_EQ(strlen(builder->ptr), bufferSize - 1); + ASSERT_TRUE(builder->full); +} + +TEST_F(Strings, StringBuilderAddChars) { + fiftyoneDegreesStringBuilderInit(builder); + char *s = (char *) "abcdef"; + size_t len = strlen(s); + fiftyoneDegreesStringBuilderAddChars(builder, s, len); + + ASSERT_EQ(builder->added, len); + ASSERT_EQ(builder->length, bufferSize); + ASSERT_EQ(builder->remaining, bufferSize - len); + fiftyoneDegreesStringBuilderComplete(builder); + ASSERT_STREQ(builder->ptr, s); + ASSERT_EQ(strlen(builder->ptr), len); + ASSERT_FALSE(builder->full); +} + +TEST_F(Strings, StringBuilderAddCharsPastEnd) { + fiftyoneDegreesStringBuilderInit(builder); + char *s = (char *) "abcdef"; + size_t len = strlen(s); + for (size_t i = 0; i < bufferSize / len + 1; i++) { + fiftyoneDegreesStringBuilderAddChars(builder, s, len); + } + // 512 = 85*6 + 2 => 512 % 6 = 2 + // 512 + (6 - 2) = 516 + ASSERT_EQ(builder->added, bufferSize + (len - bufferSize % len)); + ASSERT_EQ(builder->length, bufferSize); + + // 512 % 6 = 2 + ASSERT_EQ(builder->remaining, bufferSize % len); + + // is marked as full when could not add stuff... which might be slightly incorrect, + // since it still can fit several characters (precisely 2) + ASSERT_TRUE(builder->full); + + fiftyoneDegreesStringBuilderComplete(builder); + ASSERT_EQ(strlen(builder->ptr), bufferSize - bufferSize % len); +} + +TEST_F(Strings, StringBuilderAddInteger) { + fiftyoneDegreesStringBuilderInit(builder); + int x = 42; + size_t len = 2; + + fiftyoneDegreesStringBuilderAddInteger(builder, x); + ASSERT_EQ(builder->added, len); + ASSERT_EQ(builder->length, bufferSize); + ASSERT_EQ(builder->remaining, bufferSize - len); + + fiftyoneDegreesStringBuilderComplete(builder); + ASSERT_STREQ(builder->ptr, "42"); + ASSERT_EQ(builder->added, len + 1); + ASSERT_EQ(strlen(builder->ptr), len); + ASSERT_FALSE(builder->full); +} + +TEST_F(Strings, StringBuilderAddMaxInt) { + fiftyoneDegreesStringBuilderInit(builder); + int x = INT_MIN; + char buf[bufferSize]; + snprintf(buf, bufferSize, "%d", x); + size_t len = strlen(buf); + + fiftyoneDegreesStringBuilderAddInteger(builder, x); + ASSERT_EQ(builder->added, len); + ASSERT_EQ(builder->length, bufferSize); + ASSERT_EQ(builder->remaining, bufferSize - len); + + fiftyoneDegreesStringBuilderComplete(builder); + ASSERT_STREQ(builder->ptr, buf); + ASSERT_EQ(builder->added, len + 1); + ASSERT_EQ(strlen(builder->ptr), len); + ASSERT_FALSE(builder->full); +} + +//Some tests below were generated using OpenAI ChatGPT +TEST_F(Strings, SubString) { + // Basic Match Test + { + const char *haystack = "hello world"; + EXPECT_EQ(haystack + 6, fiftyoneDegreesStringSubString(haystack, "WORLD")); + } + + // No Match Test + { + const char *haystack = "hello world"; + EXPECT_EQ(nullptr, fiftyoneDegreesStringSubString(haystack, "planet")); + } + + // Exact Match Test + { + const char *haystack = "OpenAI"; + EXPECT_EQ(haystack + 0, fiftyoneDegreesStringSubString(haystack, "OpenAI")); + } + + // Different Cases Test + { + const char *haystack = "TeStInG"; + EXPECT_EQ(haystack + 0, fiftyoneDegreesStringSubString(haystack, "testing")); + } + + // Empty Substring Test + { + const char *haystack = "something"; + EXPECT_EQ(nullptr, fiftyoneDegreesStringSubString(haystack, "")); + } + + // Substring at Start Test + { + const char *haystack = "Python Programming"; + EXPECT_EQ(haystack + 0, fiftyoneDegreesStringSubString(haystack, "PYTHON")); + } + + // Substring at End Test + { + const char *haystack = "CaseInsensitive"; + EXPECT_EQ(haystack + 9, fiftyoneDegreesStringSubString(haystack, "SITIVE")); + } + + // Single Character Match Test + { + const char *haystack = "A"; + EXPECT_EQ(haystack + 0, fiftyoneDegreesStringSubString(haystack, "a")); + } + + // No Match With Similar Characters Test + { + const char *haystack = "abcdefgh"; + EXPECT_EQ(nullptr, fiftyoneDegreesStringSubString(haystack, "ABCXYZ")); + } + + // Substring Longer Than String Test + { + const char *haystack = "short"; + EXPECT_EQ(nullptr, fiftyoneDegreesStringSubString(haystack, "much longer")); + } + + // Unicode and Special Characters Test - currently not supported :( +// { +// const char *haystack = "café"; +// EXPECT_EQ(haystack + 0, fiftyoneDegreesStringSubString(haystack, "CAFÉ")); +// } + + // Only Spaces Test + { + const char *haystack = " "; + EXPECT_EQ(haystack + 0, fiftyoneDegreesStringSubString(haystack, " ")); + } + + // Substring with Mixed Alphanumeric Characters + { + const char *haystack = "A1B2C3"; + EXPECT_EQ(haystack + 0, fiftyoneDegreesStringSubString(haystack, "a1b2c3")); + } + + // String Containing Symbols Test + { + const char *haystack = "Hello, World!"; + EXPECT_EQ(haystack + 7, fiftyoneDegreesStringSubString(haystack, "WORLD!")); + } + + // Substring Not Present in String + { + const char *haystack = "OpenAI GPT"; + EXPECT_EQ(nullptr, fiftyoneDegreesStringSubString(haystack, "ChatGPT")); + } + +} + +TEST_F(Strings, StringCompare) { + // Identical Strings Test + { + const char *str1 = "OpenAI"; + const char *str2 = "OpenAI"; + EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Different Case Identical Strings Test + { + const char *str1 = "OpenAI"; + const char *str2 = "openai"; + EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Completely Different Strings Test + { + const char *str1 = "Hello"; + const char *str2 = "World"; + EXPECT_NE(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Prefix Match Test + { + const char *str1 = "HelloWorld"; + const char *str2 = "hello"; + EXPECT_NE(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // String with Special Characters Test, no unicode support for now :( +// { +// const char *str1 = "café"; +// const char *str2 = "CAFÉ"; +// EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); +// } + + // Different Lengths Same Prefix Test + { + const char *str1 = "Hello"; + const char *str2 = "hello world"; + EXPECT_NE(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Empty Strings Test + { + const char *str1 = ""; + const char *str2 = ""; + EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // One Empty String Test + { + const char *str1 = "NonEmpty"; + const char *str2 = ""; + EXPECT_NE(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Numbers in Strings Test + { + const char *str1 = "123abc"; + const char *str2 = "123ABC"; + EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Special Symbols and Punctuation Test + { + const char *str1 = "!@#abc"; + const char *str2 = "!@#ABC"; + EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Case Difference Test + { + const char *str1 = "aBcDeF"; + const char *str2 = "AbCdEf"; + EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); + } + +// // Non-ASCII Characters Test - no unicode support for now :( +// { +// const char *str1 = "über"; +// const char *str2 = "ÜBER"; +// EXPECT_EQ(0, fiftyoneDegreesStringCompare(str1, str2)); +// } + + // Substring Comparison Test + { + const char *str1 = "Hello"; + const char *str2 = "Hel"; + EXPECT_NE(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Longer vs Shorter String Test + { + const char *str1 = "abcd"; + const char *str2 = "abcdef"; + EXPECT_NE(0, fiftyoneDegreesStringCompare(str1, str2)); + } + + // Leading Space Difference Test + { + const char *str1 = " Hello"; + const char *str2 = "hello"; + EXPECT_NE(0, fiftyoneDegreesStringCompare(str1, str2)); + } +} + +TEST_F(Strings, StringCompareLength) { + // Identical Strings, Full Length Test + { + const char *str1 = "OpenAI"; + const char *str2 = "OpenAI"; + size_t n = 6; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Identical Strings, Partial Length Test + { + const char *str1 = "OpenAI"; + const char *str2 = "OpenAI"; + size_t n = 3; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Different Case, Full Length Test + { + const char *str1 = "OpenAI"; + const char *str2 = "openai"; + size_t n = 6; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Different Case, Partial Length Test + { + const char *str1 = "OpenAI"; + const char *str2 = "openai"; + size_t n = 4; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Completely Different Strings Test + { + const char *str1 = "Hello"; + const char *str2 = "World"; + size_t n = 3; + EXPECT_NE(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Prefix Match, Exceeding Length Test + { + const char *str1 = "HelloWorld"; + const char *str2 = "hello"; + size_t n = 8; + EXPECT_NE(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Prefix Match, Exact Length Test + { + const char *str1 = "HelloWorld"; + const char *str2 = "hello"; + size_t n = 5; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // String with Special Characters Test + { + const char *str1 = "café"; + const char *str2 = "CAFÉ"; + size_t n = 4; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Different Lengths, Limit Exceeds First String Test + { + const char *str1 = "Hello"; + const char *str2 = "hello world"; + size_t n = 10; + EXPECT_NE(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Empty Strings Test + { + const char *str1 = ""; + const char *str2 = ""; + size_t n = 0; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // One Empty String Test + { + const char *str1 = "NonEmpty"; + const char *str2 = ""; + size_t n = 3; + EXPECT_NE(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Numbers in Strings Test + { + const char *str1 = "123abc"; + const char *str2 = "123ABC"; + size_t n = 6; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Special Symbols and Punctuation Test + { + const char *str1 = "!@#abc"; + const char *str2 = "!@#ABC"; + size_t n = 6; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Case Difference, Short Length Test + { + const char *str1 = "aBcDeF"; + const char *str2 = "AbCdEf"; + size_t n = 3; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + +// // Non-ASCII Characters Test, unicode unsupported for now +// { +// const char *str1 = "über"; +// const char *str2 = "ÜBER"; +// size_t n = 4; +// EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); +// } + + // Substring Comparison Test + { + const char *str1 = "Hello"; + const char *str2 = "Hel"; + size_t n = 3; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Longer vs Shorter String Test, Limit within First String + { + const char *str1 = "abcdef"; + const char *str2 = "abcd"; + size_t n = 4; + EXPECT_EQ(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } + + // Leading Space Difference Test + { + const char *str1 = " Hello"; + const char *str2 = "hello"; + size_t n = 6; + EXPECT_NE(0, fiftyoneDegreesStringCompareLength(str1, str2, n)); + } +} diff --git a/tests/VariableSizeCollection.hpp b/tests/VariableSizeCollection.hpp new file mode 100644 index 00000000..3b5ee914 --- /dev/null +++ b/tests/VariableSizeCollection.hpp @@ -0,0 +1,98 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#ifndef VariableSizeCollection_hpp +#define VariableSizeCollection_hpp + +#include "../collection.h" +#include "../string.h" +#include + +typedef struct variable_size_collection_state_t { + fiftyoneDegreesCollection *collection; + uint32_t *offsets; + void *data; + uint32_t count; +} variableSizeCollectionState; + +/** + * Fixed size object collection helper class, used by test classes to fetch. + * T must be a fixed size value type (s.a. struct) the colleciton will hold instance of size sizeof(T) + */ +template +class VariableSizeCollection { +public: + VariableSizeCollection(const std::vector& values); + ~VariableSizeCollection(); + variableSizeCollectionState* getState(); + +private: + variableSizeCollectionState state; +}; + +template +VariableSizeCollection::~VariableSizeCollection() { + fiftyoneDegreesFree(state.offsets); + fiftyoneDegreesFree(state.data); + state.collection->freeCollection(state.collection); +} + +template +VariableSizeCollection::VariableSizeCollection(const std::vector &values) { + uint32_t currentOffsetIndex = 0; + fiftyoneDegreesMemoryReader reader; + size_t dataLength = values.size() * sizeof(T); + state.count = (uint32_t) values.size(); + + reader.length = (long)(dataLength + sizeof(uint32_t)); + state.data = fiftyoneDegreesMalloc(reader.length); + *(int32_t*)state.data = (int32_t)dataLength; + state.offsets = (uint32_t*)fiftyoneDegreesMalloc(values.size() * sizeof(uint32_t)); + reader.startByte = ((byte*)state.data); + reader.lastByte = reader.startByte + reader.length; + reader.current = reader.startByte + sizeof(uint32_t); + + for (size_t i = 0; i < values.size(); i++) { + T *element = (T*)reader.current; + memcpy((void *) element, (void *)&values[i], sizeof(T)); + state.offsets[currentOffsetIndex] = + (uint32_t)(reader.current - (reader.startByte + sizeof(uint32_t))); + reader.current += sizeof(T); + currentOffsetIndex++; + } + assert(currentOffsetIndex == state.count); + assert(reader.lastByte == reader.current); + assert((byte*)state.data + sizeof(int32_t) + dataLength == reader.lastByte); + assert((byte*)state.data + reader.length == reader.lastByte); + reader.current = reader.startByte; + state.collection = fiftyoneDegreesCollectionCreateFromMemory( + &reader, + fiftyoneDegreesCollectionHeaderFromMemory(&reader, 0, false)); + assert(state.collection->size == dataLength); +} + +template +variableSizeCollectionState* VariableSizeCollection::getState() { + return &state; +} + +#endif /* VariableSizeCollection_hpp */ From 0e3fb4bda922bb6054902ab930455c314fdb5926 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Fri, 21 Jun 2024 10:59:04 +0200 Subject: [PATCH 34/73] FEAT design convert GHEV and SUA to HTTP headers --- evidence.h | 20 ++- tests/TransformTests.cpp | 294 +++++++++++++++++++++++++++++++++++++++ transform.c | 71 ++++++++++ transform.h | 236 +++++++++++++++++++++++++++++++ 4 files changed, 618 insertions(+), 3 deletions(-) create mode 100644 tests/TransformTests.cpp create mode 100644 transform.c create mode 100644 transform.h diff --git a/evidence.h b/evidence.h index 2a5e0c21..cc3f4d82 100644 --- a/evidence.h +++ b/evidence.h @@ -24,8 +24,7 @@ #define FIFTYONE_DEGREES_EVIDENCE_H_INCLUDED /** - * @ingroup FiftyOneDegreesCommon - * @defgroup FiftyOneDegreesEvidence Evidence + * @ingroup FiftyOneDegreesCommon * @defgroup FiftyOneDegreesEvidence Evidence * * Contains key value pairs as evidence to be processed. * @@ -261,6 +260,21 @@ EXTERNAL uint32_t fiftyoneDegreesEvidenceIterate( void *state, fiftyoneDegreesEvidenceIterateMethod callback); + +/** + * First checks if provided prefix+field are not already part of the + * evidence array with the same parameters. + * If not calls fiftyoneDegreesEvidenceAddString. + * @param evidence pointer to the evidence array to add the entry to + * @param prefix enum indicating the category the entry belongs to + * @param field used as the key for the entry within its prefix + * @param originalValue the value to be parsed + */ +EXTERNAL fiftyoneDegreesEvidenceKeyValuePair* fiftyoneDegreesEvidenceAddStringUnique( + fiftyoneDegreesEvidenceKeyValuePairArray *evidence, + fiftyoneDegreesEvidencePrefix prefix, + const char *field, + const char *originalValue); /** * Iterates over the headers assembling the evidence values, considering the * prefixes, in the buffer if available. The call back method is called for @@ -290,4 +304,4 @@ EXTERNAL bool fiftyoneDegreesEvidenceIterateForHeaders( * @} */ -#endif \ No newline at end of file +#endif diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp new file mode 100644 index 00000000..36a36db7 --- /dev/null +++ b/tests/TransformTests.cpp @@ -0,0 +1,294 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#include "EvidenceTests.hpp" +#include "../memory.h" +#include "../transform.h" + +class Transform: public Base { +public: + virtual void SetUp(); + + static bool found; + static fiftyoneDegreesKeyValuePairArray *results; + static fiftyoneDegreesException exception; + static const char *expectedFieldName; + static const char *expectedFieldValue; + void checkFieldValue(const char *field, const char *value); + void checkFieldAbsent(const char *field); +}; + +bool Transform::found = false; +const char *Transform::expectedFieldName = NULL; +const char *Transform::expectedFieldValue = NULL; +fiftyoneDegreesKeyValuePairArray *Transform::results = NULL; +fiftyoneDegreesException Transform::exception; + + +void Transform::checkFieldValue(const char *field, const char *value) { + found = false; + expectedFieldName = field; + expectedFieldValue = value; + for (int i = 0; i < results->count; ++i) { + fiftyoneDegreesKeyValuePair *pair = &results->items[i]; + if (strcmp((const char*)pair->key, expectedFieldName) == 0) { + EXPECT_TRUE(strcmp((const char*)pair->value, expectedFieldValue) == 0) << + L"Expected value to be '" << expectedFieldValue << "' not '" << + (const char*)pair->value << "'"; + found = true; + break; + } + } + ASSERT_TRUE(found) << "Field " << field << " was not found should be " << value; +} + +void Transform::checkFieldAbsent(const char *field) { + found = false; + expectedFieldName = field; + expectedFieldValue = ""; + for (int i = 0; i < results->count; ++i) { + fiftyoneDegreesKeyValuePair *pair = &results->items[i]; + if (strcmp((const char*)pair->key, expectedFieldName) == 0) { + found = true; + break; + } + } + ASSERT_FALSE(found) << "Field " << field << " should be absent"; +} + +bool fillResultsCallback(fiftyoneDegreesKeyValuePair pair) { + fiftyoneDegreesKeyValuePairArray *results = Transform::results; + if (results->count < results->capacity) { + results->items[results->count++] = pair; + return true; + } + return false; +} + +void Transform::SetUp() { + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, results, 8) +} + +TEST_F(Transform, GHEVHappyPath) { + const char *ghev = "{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback); + + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + checkFieldAbsent("sec-ch-ua-model"); + checkFieldValue("sec-ch-ua-arch", "x86"); + checkFieldValue("sec-ch-ua-full-version-list", "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", \"Google Chrome\";v=\"126.0.6478.61\""); +} + +TEST_F(Transform, GHEVPartial) { + const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":null}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(count, 3); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile + // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list + // we check that either empty value (model), null-value (platform) + // or entire absence of key (platformVersion) result in no header in the output + // the policy is - we don't output empty data - no value == no evidence field + + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldAbsent("sec-ch-ua-platform"); + checkFieldAbsent("sec-ch-ua-model"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldValue("sec-ch-ua-full-version-list", "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", \"Google Chrome\";v=\"126.0.6478.61\""); +} + +TEST_F(Transform, GHEVCorruptInput) { + const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\""; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); +} + +TEST_F(Transform, GHEVBufferTooSmall) { + const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\"}"; + + size_t bufferLength = 20; + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); +} + +TEST_F(Transform, GHEVEvidenceLowCapacity) { + const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\"}"; + + size_t bufferLength = 20; + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); +} + +TEST_F(Transform, SUAHappyPath) { + const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + + ASSERT_EQ(count, 7); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + + // In device.sua representation there is no distinction between + // sec-ch-ua and sec-ch-ua-full-version-list + checkFieldValue("sec-ch-ua", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-full-version-list", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"12\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-arch", "\"arm\""); + checkFieldAbsent("sec-ch-ua-bitness"); //we ignore bitness + checkFieldValue("sec-ch-ua-model", "\"Pixel 6\""); +} + +TEST_F(Transform, SUAPartial1) { + const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\"]},\"mobile\": 1,\"model\": \"\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(count, 4); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-full-version-list", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldAbsent("sec-ch-ua-platform-version"); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua-model"); +} + +TEST_F(Transform, SUAPartial2) { + const char *sua = "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(count, 2); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + + checkFieldAbsent("sec-ch-ua"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldAbsent("sec-ch-ua-platform-version"); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua-model"); +} + +TEST_F(Transform, SUACorrupt1) { + const char *sua = "{\"source\": 2,,\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); +} + +TEST_F(Transform, SUACorrupt2) { + const char *sua = "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": [12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); +} + +TEST_F(Transform, SUACorrupt3) { + const char *sua = "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": \"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); +} + +TEST_F(Transform, SUABufferTooSmall) { + const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + + size_t bufferLength = 15; + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); +} + +TEST_F(Transform, SUAEvidenceLowCapacity) { + const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + + size_t bufferLength = 15; + char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); +} diff --git a/transform.c b/transform.c new file mode 100644 index 00000000..cb6fe000 --- /dev/null +++ b/transform.c @@ -0,0 +1,71 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#include "transform.h" + +size_t fiftyoneDegreesTransformIterateGhevFromJson(const char *json, + char * const buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesTransformCallback callback) { + return 0; +} + +size_t fiftyoneDegreesTransformIterateGhevFromBase64(const char *base64, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesTransformCallback callback) { + return 0; +} + +size_t fiftyoneDegreesTransformIterateSua(const char *json, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesTransformCallback callback) { + return 0; +} + +size_t fiftyoneDegreesTransformGhevFromJson(const char *json, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesKeyValuePairArray * const headers) { + return 0; +} + +size_t fiftyoneDegreesTransformGhevFromBase64(const char *base64, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesKeyValuePairArray * const headers){ + return 0; +} + +size_t fiftyoneDegreesTransformSua(const char *json, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesKeyValuePairArray * const headers) { + return 0; +} diff --git a/transform.h b/transform.h new file mode 100644 index 00000000..e1e80273 --- /dev/null +++ b/transform.h @@ -0,0 +1,236 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#ifndef FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED +#define FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED + +#include "exceptions.h" +#include "pair.h" +#include "array.h" +#include "fiftyone.h" +/** + * User-Agent Client Hints Representation Conversion Routines + * + * 3 common ways to represent UACH are: + * - [HTTP header map](https://wicg.github.io/ua-client-hints/) + * - [getHighEntropyValues()](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues) JavaScript API result as a JSON string + * - [OpenRTB](https://iabtechlab.com/wp-content/uploads/2022/04/OpenRTB-2-6_FINAL.pdf) [device.sua](https://51degrees.com/blog/openrtb-structured-user-agent-and-user-agent-client-hints) field as a JSON string + * + * 51degrees uses HTTP header map to represent UACH and expects the evidence to be provided as + * HTTP headers (or same name query parameters). The header names in question are: + * - Sec-CH-UA + * - Sec-CH-UA-Platform + * - Sec-CH-UA-Mobile + * - Sec-CH-UA-Model + * - Sec-CH-UA-Full-Version-List + * - Sec-CH-UA-Platform-Version + * - Sec-CH-UA-Arch + * - Sec-CH-UA-Bitness + * + * The conversion routines are provided in 2 styles: iterative (for potentially lazy consumption) and eager. The former uses callback + * to iteratively provide header name-value pairs to the caller, the latter provides the whole header map as output. + * In addition 2 variants of GHEV routine is provided: one that accepts a raw JSON string + * and one that accepts a base64 encoded JSON string as input parameter. + * + * Both styles use an externally preallocated memory buffer to write the formed http header values to. The output callback or headermap + * will have pointers to the null-terminated strings stored in that buffer. Thus the buffer should outlive the last output evidence use. + */ + + +/** + * A callback function type definition that is called every time a header name-value pair is formed and + * allows the caller to decide how to handle the output. The callback function must be provided as a param to the Iterate-style + * conversion routines. + * @param name the name of the discovered HTTP header + * @param value the value of the discoverd HTTP header, may be NULL if buffer had insufficient space to store the value, + * the valueLength will still be passed and will indicate the necessary length of the buffer to store the value + * @param nameLength the length of the name string + * @param valueLength the lengh of the value string written or that should have been written in case buffer was of insufficietn capacity + * @return the implementer returns true to continue the iteration or false to stop + */ +EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)(fiftyoneDegreesKeyValuePair header); + +/** + * Iteratively convert getHighEntropyValue() API result JSON string to HTTP header representation. + * @param json a JSON string with the getHighEntropyValue() API result + * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values + * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer + * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be + * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, + * or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, + * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the + * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called + * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended + * @param callback a function that is called whenever a header is extracted with header name and value passed as params + * if the function returns true, iteration continues, otherwise halts + * @return the number of iterations (callback calls) made + */ +EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson(const char *json, + char * const buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesTransformCallback callback); + +/** + * Iteratively convert getHighEntropyValue() API result base64 encoded JSON string to HTTP header representation. + * @param base64 a base64 encoded JSON string with the getHighEntropyValue() API result + * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values + * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer + * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be + * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, + * or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, + * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the + * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called + * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended + * @param callback a function that is called whenever a header is extracted with header name and value passed as params + * if the function returns true, iteration continues, otherwise halts + * @return the number of iterations (callback calls) made + */ +EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64(const char *base64, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesTransformCallback callback); + +/** + * Iteratively convert device.sua JSON string to HTTP header representation. + * @param json a JSON string with the device.sua raw representation + * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values + * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer + * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be + * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, + * or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, + * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the + * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called + * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended + * @param callback a function that is called whenever a header is extracted with header name and value passed as params + * if the function returns true, iteration continues, otherwise halts + * @return the number of iterations (callback calls) made + */ +EXTERNAL size_t fiftyoneDegreesTransformIterateSua(const char *json, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesTransformCallback callback); +/** + * A preallocated array of key-value pairs intended to be an array of HTTP headers + */ +#define NONE +FIFTYONE_DEGREES_ARRAY_TYPE(fiftyoneDegreesKeyValuePair, NONE); + +/** + * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header representation. + * @param json a JSON string with the getHighEntropyValue() API result + * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values + * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer + * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be + * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, + * or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, + * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the + * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called + * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended + * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array of capacity enough to hold up to 8 UACH headers; + * upon function return will contain the output http header names and value const char * pointers either to the DATA segment allocated (names) + * string constants or preallocated buffer on the heap. must not be NULL and has to be memory managed outside of the function, its lifetime + * should be long enough to survive the last use of the returned headers + * @return the number of headers that was written or should have been written to the array in case this number is higher than headers->capacity + * in the latter case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status and the returned capacity will signal + * the array size that needs to be allocated + */ +EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson(const char *json, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesKeyValuePairArray * const headers); + +/** + * Eagerly convert getHighEntropyValue() API result from base64-encoded JSON string to HTTP header representation. + * @param base64 a base64-encoded JSON string with the getHighEntropyValue() API result + * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values + * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer + * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be + * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, + * or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, + * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the + * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called + * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended + * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array of capacity enough to hold up to 8 UACH headers; + * upon function return will contain the output http header names and value const char * pointers either to the DATA segment allocated (names) + * string constants or preallocated buffer on the heap. must not be NULL and has to be memory managed outside of the function, its lifetime + * should be long enough to survive the last use of the returned headers + * @return the number of headers that was written or should have been written to the array in case this number is higher than headers->capacity + * in the latter case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status and the returned capacity will signal + * the array size that needs to be allocated + */ +EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64(const char *base64, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesKeyValuePairArray * const headers); + +/** + * Eagerly convert device.sua JSON string to HTTP header representation. + * @param json a raw JSON string with device.sua field contents + * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values + * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer + * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be + * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, + * or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, + * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the + * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called + * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended + * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array of capacity enough to hold up to 8 UACH headers; + * upon function return will contain the output http header names and value const char * pointers either to the DATA segment allocated (names) + * string constants or preallocated buffer on the heap. must not be NULL and has to be memory managed outside of the function, its lifetime + * should be long enough to survive the last use of the returned headers + * @return the number of headers that was written or should have been written to the array in case this number is higher than headers->capacity + * in the latter case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status and the returned capacity will signal + * the array size that needs to be allocated + */ +EXTERNAL size_t fiftyoneDegreesTransformSua(const char *json, + char *buffer, + size_t length, + fiftyoneDegreesException * const exception, + fiftyoneDegreesKeyValuePairArray * const headers); + +// TODO: TDD for iterative style functions +// TODO: C++ wrappers for the non-iterative style functions: hpp + cpp and corresponding tests that are testing CPP wrappers + +#endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ From e314211d9d45afe34c1c97caacf176b88675bc96 Mon Sep 17 00:00:00 2001 From: loskamo Date: Thu, 25 Jul 2024 20:47:45 +0300 Subject: [PATCH 35/73] FEAT: implement transform routines transform.c Transform.cpp test coverage Signed-off-by: loskamo --- Transform.cpp | 52 ++ Transform.hpp | 44 ++ status.h | 1 + tests/TransformTests.cpp | 1536 ++++++++++++++++++++++++++++++++------ transform.c | 1285 ++++++++++++++++++++++++++++++- transform.h | 391 ++++++---- 6 files changed, 2908 insertions(+), 401 deletions(-) create mode 100644 Transform.cpp create mode 100644 Transform.hpp diff --git a/Transform.cpp b/Transform.cpp new file mode 100644 index 00000000..eda27ea7 --- /dev/null +++ b/Transform.cpp @@ -0,0 +1,52 @@ +#include "Transform.hpp" + +using namespace FiftyoneDegrees::Common; + +Transform::Transform(size_t capacity) : buffer(capacity) {} + +Transform::Headers Transform::apiInvoker(CTransformAPI func, + const std::string &json) { + Transform::Headers res; + EXCEPTION_CREATE; + + fiftyoneDegreesKeyValuePairArray *headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); + + while (1) { + func(json.c_str(), buffer.data(), buffer.size(), exception, headers); + + switch (exception->status) { + case FIFTYONE_DEGREES_STATUS_CORRUPT_DATA: { + fiftyoneDegreesFree(headers); + EXCEPTION_THROW; + } break; + + case FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY: { + headers->count = 0; + exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; + buffer.resize(buffer.size() * 2); + continue; + } break; + + case FIFTYONE_DEGREES_STATUS_SUCCESS: { + for (int i = 0; i < headers->count; ++i) { + fiftyoneDegreesKeyValuePair &pair = headers->items[i]; + + res.emplace(std::string{pair.key, pair.keyLength}, + std::string{pair.value, pair.valueLength}); + } + + fiftyoneDegreesFree(headers); + + return res; + } break; + + default: + break; + } + + break; + } + + return res; +} diff --git a/Transform.hpp b/Transform.hpp new file mode 100644 index 00000000..0e61764f --- /dev/null +++ b/Transform.hpp @@ -0,0 +1,44 @@ +#ifndef FIFTYONE_DEGREES_TRANSFORM_HPP +#define FIFTYONE_DEGREES_TRANSFORM_HPP + +#include +#include +#include + +#include "transform.h" + +namespace FiftyoneDegrees { +namespace Common { + +class Transform { + using Headers = std::map; + + using CTransformAPI = + size_t (*)(const char* base64, char* buffer, size_t length, + fiftyoneDegreesException* const exception, + fiftyoneDegreesKeyValuePairArray* const headers); + + Headers apiInvoker(CTransformAPI func, const std::string& json); + + public: + Transform(size_t capacity = 1024); + + Headers fromJsonGHEV(const std::string& json) { + return apiInvoker(fiftyoneDegreesTransformGhevFromJson, json); + } + + Headers fromBase64GHEV(const std::string& json) { + return apiInvoker(fiftyoneDegreesTransformGhevFromBase64, json); + } + + Headers fromSUA(const std::string& json) { + return apiInvoker(fiftyoneDegreesTransformSua, json); + } + + private: + std::vector buffer; +}; +} // namespace Common +} // namespace FiftyoneDegrees + +#endif // FIFTYONE_DEGREES_TRANSFORM_HPP diff --git a/status.h b/status.h index 116d0ebe..edb8bdd8 100644 --- a/status.h +++ b/status.h @@ -138,6 +138,7 @@ typedef enum e_fiftyone_degrees_status_code { FIFTYONE_DEGREES_STATUS_INCORRECT_IP_ADDRESS_FORMAT, /**< IP address format is incorrect */ FIFTYONE_DEGREES_STATUS_TEMP_FILE_ERROR, /**< Error creating temp file */ + FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY, } fiftyoneDegreesStatusCode; /** diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 36a36db7..18e6b1b5 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -20,21 +20,22 @@ * such notice(s) shall fulfill the requirements of that article. * ********************************************************************* */ -#include "EvidenceTests.hpp" #include "../memory.h" -#include "../transform.h" - -class Transform: public Base { -public: - virtual void SetUp(); - - static bool found; - static fiftyoneDegreesKeyValuePairArray *results; - static fiftyoneDegreesException exception; - static const char *expectedFieldName; - static const char *expectedFieldValue; - void checkFieldValue(const char *field, const char *value); - void checkFieldAbsent(const char *field); +// #include "../transform.h" +#include "../Transform.hpp" +#include "Base.hpp" + +class Transform : public Base { + public: + virtual void SetUp(); + virtual void TearDown(); + static bool found; + static fiftyoneDegreesKeyValuePairArray *results; + static fiftyoneDegreesException exception; + static const char *expectedFieldName; + static const char *expectedFieldValue; + void checkFieldValue(const char *field, const char *value); + void checkFieldAbsent(const char *field); }; bool Transform::found = false; @@ -43,252 +44,1359 @@ const char *Transform::expectedFieldValue = NULL; fiftyoneDegreesKeyValuePairArray *Transform::results = NULL; fiftyoneDegreesException Transform::exception; - void Transform::checkFieldValue(const char *field, const char *value) { - found = false; - expectedFieldName = field; - expectedFieldValue = value; - for (int i = 0; i < results->count; ++i) { - fiftyoneDegreesKeyValuePair *pair = &results->items[i]; - if (strcmp((const char*)pair->key, expectedFieldName) == 0) { - EXPECT_TRUE(strcmp((const char*)pair->value, expectedFieldValue) == 0) << - L"Expected value to be '" << expectedFieldValue << "' not '" << - (const char*)pair->value << "'"; - found = true; - break; + found = false; + expectedFieldName = field; + expectedFieldValue = value; + size_t expectedFieldName_len = strlen(expectedFieldName); + size_t expectedFieldValue_len = strlen(expectedFieldValue); + + for (size_t i = 0; i < results->count; ++i) { + fiftyoneDegreesKeyValuePair *pair = &results->items[i]; + + if (expectedFieldName_len == pair->keyLength) { + found = true; + + for (size_t j = 0; j < pair->keyLength; ++j) { + if (pair->key[j] != expectedFieldName[j]) { + found = false; + break; } + } + + if (found) { + EXPECT_TRUE(expectedFieldValue_len == pair->valueLength) + << L"Expected value len to be '" << expectedFieldValue_len + << "' not '" << pair->valueLength << "'"; + + if (expectedFieldValue_len == pair->valueLength) { + bool value_compare = true; + + for (size_t j = 0; j < pair->valueLength; ++j) { + if (pair->value[j] != expectedFieldValue[j]) { + value_compare = false; + break; + } + } + + EXPECT_TRUE(value_compare) + << L"Expected value to be '" << expectedFieldValue << "' not '" + << (const char *)pair->value << "'"; + + break; + } + } } - ASSERT_TRUE(found) << "Field " << field << " was not found should be " << value; + } + + ASSERT_TRUE(found) << "Field " << field << " was not found should be " + << value; } void Transform::checkFieldAbsent(const char *field) { - found = false; - expectedFieldName = field; - expectedFieldValue = ""; - for (int i = 0; i < results->count; ++i) { - fiftyoneDegreesKeyValuePair *pair = &results->items[i]; - if (strcmp((const char*)pair->key, expectedFieldName) == 0) { - found = true; - break; + found = false; + expectedFieldName = field; + size_t expectedFieldName_len = strlen(expectedFieldName); + + for (size_t i = 0; i < results->count; ++i) { + fiftyoneDegreesKeyValuePair *pair = &results->items[i]; + + if (expectedFieldName_len == pair->keyLength) { + found = true; + + for (size_t j = 0; j < pair->keyLength; ++j) { + if (pair->key[j] != expectedFieldName[j]) { + found = false; + break; } + } + + if (found) { + break; + } } - ASSERT_FALSE(found) << "Field " << field << " should be absent"; + } + + ASSERT_FALSE(found) << "Field " << field << " should be absent"; } -bool fillResultsCallback(fiftyoneDegreesKeyValuePair pair) { - fiftyoneDegreesKeyValuePairArray *results = Transform::results; - if (results->count < results->capacity) { - results->items[results->count++] = pair; - return true; - } - return false; +bool fillResultsCallback(void *ctx, fiftyoneDegreesKeyValuePair pair, + fiftyoneDegreesException *const) { + fiftyoneDegreesKeyValuePairArray *results = + (fiftyoneDegreesKeyValuePairArray *)ctx; + + if (results->count < results->capacity) { + results->items[results->count++] = pair; + return true; + } + + return false; } void Transform::SetUp() { - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, results, 8) -} - -TEST_F(Transform, GHEVHappyPath) { - const char *ghev = "{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback); - - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, count); - - checkFieldValue("sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - checkFieldAbsent("sec-ch-ua-model"); - checkFieldValue("sec-ch-ua-arch", "x86"); - checkFieldValue("sec-ch-ua-full-version-list", "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", \"Google Chrome\";v=\"126.0.6478.61\""); + Base::SetUp(); + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, results, 8) } -TEST_F(Transform, GHEVPartial) { - const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":null}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(count, 3); +void Transform::TearDown() { + fiftyoneDegreesFree(results); + Base::TearDown(); +} +// Tests +// ------------------------------------------------------------------------------------------ + +TEST_F(Transform, GHEVIterativeJSON) { + const char *ghev = + "{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 7); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " + "\"Google Chrome\";v=\"126.0.6478.61\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, IncompleteJSON) { + size_t bufferLength = 4096; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + std::vector correct{ + "{ \"key_without_value\" }", + + "{ \"key_without_value\": ", + + "{\"architecture\":\"x86\"," + " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", + + "{ \"incomplete_string\": \" \n", + "{ \"complete_string\": \" \" \n", + + "{\"incomplete_unknown_object\": { \"other_nested_object", + + "{\"incomplete_unknown_array\": [ \"other_nested_string", + + "{\"incomplete_unknown_array\": [", + + "{\"incomplete_unknown_array\": [[", + + "{\"incomplete_unknown_array\": [[],\"", + "{\"incomplete_unknown_array\": [[],\"\"", + "{\"complete_unknown_array\": [],", + + "{ \"incomplete_bool\": false", + + "{ \"\": \"empty_key\" }", + + "{\"bool\": true}", + + "{\"more\": true}", + + "{\"platformer\": 0}", + + }; + + std::vector corrupted{ + "{ \"model\": n", + "{ \"model\": nu", + "{ \"model\": \"he", + "{ \"model\": \"he\\", + "", + "{ \"", + "{ \"mobile\":", + "{ \"mo", + "{\"a", + "{\"brands\":[{\"brand\": \"one\", \"version\":null}}", + }; + + for (const std::string &j : correct) { + fiftyoneDegreesTransformIterateGhevFromJson( + j.c_str(), buffer, bufferLength, &Transform::exception, + fillResultsCallback, Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); - - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile - // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list - // we check that either empty value (model), null-value (platform) - // or entire absence of key (platformVersion) result in no header in the output - // the policy is - we don't output empty data - no value == no evidence field - - ASSERT_EQ(results->count, count); - - checkFieldValue("sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldAbsent("sec-ch-ua-platform"); - checkFieldAbsent("sec-ch-ua-model"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldValue("sec-ch-ua-full-version-list", "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", \"Google Chrome\";v=\"126.0.6478.61\""); + } + + for (const std::string &j : corrupted) { + fiftyoneDegreesTransformIterateGhevFromJson( + j.c_str(), buffer, bufferLength, &Transform::exception, + fillResultsCallback, Transform::results); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + } + fiftyoneDegreesFree(buffer); } -TEST_F(Transform, GHEVCorruptInput) { - const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\""; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); +TEST_F(Transform, IncompleteSUA) { + size_t bufferLength = 4096; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + std::vector correct{ + "{ \"key_without_value\" }", + + "{ \"key_without_value\": ", + + "{ \"skip\": { \"key\": \"\\\"\\n\\\\\"value\\\"\" } }", + + "{\"architecture\":\"x86\\\"\"," + " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", + + "{ \"incomplete_string\": \" \n", + "{ \"complete_string\": \" \" \n", + + "{\"incomplete_unknown_object\": { \"other_nested_object", + + "{\"incomplete_unknown_array\": [ \"other_nested_string", + + "{\"incomplete_unknown_array\": [", + + "{\"incomplete_unknown_array\": [[", + + "{\"incomplete_unknown_array\": [[],\"", + "{\"incomplete_unknown_array\": [[],\"\"", + "{\"complete_unknown_array\": [],", + + "{ \"incomplete_bool\": false", + + "{ \"\": \"empty_key\" }", + + "{\"bool\": true}", + + "{\"more\": true}", + "{\"browsers\":[{\"brand\": null}]}", + }; + + std::vector corrupted{ + "", + "{ \"", + "{\"a", + "{ \"mo", + "{ \"mobile\":", + "{\"platformer\": 0}", + "{\"browsers\":[{\"brand\": null}}}", + }; + + for (const std::string &j : correct) { + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, + &Transform::exception, + fillResultsCallback, Transform::results); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + } + + for (const std::string &j : corrupted) { + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, + &Transform::exception, + fillResultsCallback, Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + } + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVIncorrectBool) { + const char *ghev = + "{\"architecture\":\"x86\"," + "\"brands\":[{\"brand\": null, \"version\":\"8\"}," + "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," + "\"fullVersionList\":[{\"brand\": null}]," + "\"mobile\": 0,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateGhevFromJson( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, SUAIncorrectBool) { + const char *json = "{\"mobile\": false,\"model\":\"\"}"; + + size_t bufferLength = 512; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, + &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVIterativeNULLBrandJSON) { + const char *ghev = + "{\"architecture\":\"x86\"," + "\"brands\":[{\"brand\": null, \"version\":\"8\"}," + "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," + "\"fullVersionList\":[{\"brand\": null}]," + "\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 6); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", "\"Google Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { + const char *ghev = + "{\"architecture\":\"x86\"," + "\"brands\":[{\"brand\": \"one\", \"version\":null}," + "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," + "\"fullVersionList\":[{\"brand\": null}]," + "\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 6); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", "\"one\";v=null, \"Google Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVIterativeBase64) { + const char *ghev = + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 6); + ASSERT_EQ(results->count, count); + + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVBase64CorruptedLen) { + const char *ghev = + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>" + "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateGhevFromBase64( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVBase64CorruptedSymbol) { + const char *ghev = + "====cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" + "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateGhevFromBase64( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVBase64CorruptedSymbol2) { + const char *ghev = + "&&&&cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" + "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateGhevFromBase64( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVIterativeSua) { + const char *sua = + "{\n \"source\": 2,\n \"platform\": {\n " + "\"brand\": \"macOS\",\n \"version\": [\"14\", \"5\", " + "\"0\"]\n },\n \"browsers\": [{\n " + "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " + "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " + "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " + "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " + " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " + " }],\n \"mobile\": 0,\n \"model\": \"\",\n " + "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 7); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldAbsent("sec-ch-ua"); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, SuaWeirdPlatformVersion) { + const char *sua = + "{\n \"source\": 2,\n \"platform\": {\n " + "\"brand\": \"macOS\",\n \"version\": [\"\\\"x\\\"\", " + "\"\\\"y\\\"\", " + "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " + "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " + "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " + "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " + "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " + " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " + " }],\n \"mobile\": 0,\n \"model\": \"\",\n " + "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 7); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldAbsent("sec-ch-ua"); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", + "\"\\\"x\\\".\\\"y\\\".\\\"z\\\"\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, SuaNullBrandPlatform) { + const char *sua = + "{\n \"source\": 2,\n \"platform\": {\n " + "\"brand\": null,\n \"version\": [\"\\\"x\\\"\", " + "\"\\\"y\\\"\", " + "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " + "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " + "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " + "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " + "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " + " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " + " }],\n \"mobile\": 0,\n \"model\": \"\",\n " + "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 5); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldAbsent("sec-ch-ua"); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldAbsent("sec-ch-ua-platform"); + checkFieldAbsent("sec-ch-ua-platform-version"); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVArrayJSON) { + const char *ghev = + "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" + "\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " + " : false, \"model\" : \"MacBook\" , \"platform\" : " + "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformGhevFromJson( + ghev, buffer, bufferLength, &Transform::exception, results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 7); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"MacBook\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVArrayInsufficientCapacity) { + const char *ghev = + "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" + "\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " + " : false, \"model\" : \"MacBook\" , \"platform\" : " + "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesKeyValuePairArray *headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 2); + + fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + + size_t count = fiftyoneDegreesTransformGhevFromJson( + ghev, buffer, bufferLength, &Transform::exception, headers); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 2); + ASSERT_EQ(headers->count, count); + + // --- + + count = fiftyoneDegreesTransformGhevFromJson( + ghev, buffer, bufferLength, &Transform::exception, empty_headers); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_EQ(count, 1); + + fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(headers); + fiftyoneDegreesFree(empty_headers); +} + +TEST_F(Transform, GHEVBase64) { + const char *ghev = + "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" + "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" + "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" + "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" + "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" + "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" + "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; + + size_t bufferLength = strlen(ghev) * 2; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformGhevFromBase64( + ghev, buffer, bufferLength, &Transform::exception, results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 6); + ASSERT_EQ(results->count, count); + + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldAbsent("sec-ch-ua-platform-version"); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVBase64NotEnoughMemory) { + const char *ghev = + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( + ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, + Transform::results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVArraySua) { + const char *sua = + "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," + "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, + &Transform::exception, results); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 6); + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua-model", "\"MacBook\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/" + "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " + "Chrome\";v=\"126.0.6478.127\""); + + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua"); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVPartial) { + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":null}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + ghev, buffer, bufferLength, &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(count, 4); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile + // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list + // we check that either empty value (model), null-value (platform) + // or entire absence of key (platformVersion) result in no header in the + // output the policy is - we don't output empty data - no value == no evidence + // field + + ASSERT_EQ(results->count, count); + + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " + "\"Google Chrome\";v=\"126.0.6478.61\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + + checkFieldAbsent("sec-ch-ua-platform"); + checkFieldAbsent("sec-ch-ua-platform-version"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVIgnoreUnused) { + const char *ghev = + "{ \"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{ " + "\"brand\" : " + "\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\" , " + "\"version\" : \"126.0.6478.127\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.127\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":" + "false}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + ghev, buffer, bufferLength, &exception, fillResultsCallback, + Transform::results); + + ASSERT_EQ(count, 8); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile + // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list + // we check that either empty value (model), null-value (platform) + // or entire absence of key (platformVersion) result in no header in the + // output the policy is - we don't output empty data - no value == no evidence + // field + + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, GHEVCorruptInput) { + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\""; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVBufferTooSmall) { - const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\"}"; - - size_t bufferLength = 20; - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\"}"; + + size_t bufferLength = 20; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVEvidenceLowCapacity) { - const char *ghev = "{\"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":\"126.0.6478.61\"},{\"brand\":\"Google Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\"}"; - - size_t bufferLength = 20; - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\"}"; + + size_t bufferLength = 20; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAHappyPath) { - const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - - ASSERT_EQ(count, 7); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, count); - - // In device.sua representation there is no distinction between - // sec-ch-ua and sec-ch-ua-full-version-list - checkFieldValue("sec-ch-ua", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-full-version-list", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"12\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-arch", "\"arm\""); - checkFieldAbsent("sec-ch-ua-bitness"); //we ignore bitness - checkFieldValue("sec-ch-ua-model", "\"Pixel 6\""); + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &exception, fillResultsCallback, + Transform::results); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 7); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + + // In device.sua representation there is no distinction between + // sec-ch-ua and sec-ch-ua-full-version-list + // checkFieldValue( + // "sec-ch-ua", + // "\"Not A;Brand\";v=\"99.0.0.0\",\"Chromium\";v=\"99.0.4844.88\"," + // "\"Google Chrome\";v=\"99.0.4844.88\"\n"); + + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " + "\"Google Chrome\";v=\"99.0.4844.88\""); + + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"12\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-arch", "\"arm\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue("sec-ch-ua-model", "\"Pixel6\""); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, SUAPlatformExt) { + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\", \"version\": [\"13\"], " + "\"ext\": { \"some_random_key\" : [ \"some\", \"random\", \"\\n\", " + "\" \\\" values \" ], " + "\"another_random_key\": null}}," + "\"mobile\": 1,\"model\": \"\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(count, 5); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + checkFieldAbsent("sec-ch-ua"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " + "\"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"13\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue("sec-ch-ua-model", "\"\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAPartial1) { - const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\"]},\"mobile\": 1,\"model\": \"\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(count, 4); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, count); - - checkFieldValue("sec-ch-ua", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-full-version-list", "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", \"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldAbsent("sec-ch-ua-platform-version"); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua-model"); + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\"},\"mobile\": 1,\"model\": \"\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(count, 4); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + checkFieldAbsent("sec-ch-ua"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " + "\"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldAbsent("sec-ch-ua-platform-version"); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue("sec-ch-ua-model", "\"\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAPartial2) { - const char *sua = "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(count, 2); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, count); - - checkFieldAbsent("sec-ch-ua"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldAbsent("sec-ch-ua-platform-version"); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua-model"); -} - -TEST_F(Transform, SUACorrupt1) { - const char *sua = "{\"source\": 2,,\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + const char *sua = + "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " + "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(count, 5); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + ASSERT_EQ(results->count, count); + + checkFieldAbsent("sec-ch-ua"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"12\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-arch", "\"arm\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldAbsent("sec-ch-ua-model"); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, SUATolerableCorrupt) { + const char *sua = + "{\"source\": 2,,\"platform\": {\"brand\": \"Android\",\"version\": " + "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + size_t count = fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, &exception, fillResultsCallback, + Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + + ASSERT_EQ(count, 5); + ASSERT_EQ(results->count, count); + + checkFieldAbsent("sec-ch-ua"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + + checkFieldValue("sec-ch-ua-arch", "\"arm\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-model"); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"12\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUACorrupt2) { - const char *sua = "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": [12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + const char *sua = + "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " + "[12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, + fillResultsCallback, Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUACorrupt3) { - const char *sua = "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": \"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": null}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + const char *sua = + "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " + "\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, + fillResultsCallback, Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUABufferTooSmall) { - const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + + size_t bufferLength = 15; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - size_t bufferLength = 15; - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, + fillResultsCallback, Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAEvidenceLowCapacity) { - const char *sua = "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": [\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": {\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + + size_t bufferLength = 15; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, + fillResultsCallback, Transform::results); + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, CPPWrapperGHEV) { + FiftyoneDegrees::Common::Transform t; + + auto h = t.fromJsonGHEV( + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\"," + "\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":null}"); + + ASSERT_EQ(h.size(), 4); + + EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + + ASSERT_EQ(h["sec-ch-ua"], + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + ASSERT_EQ(h["sec-ch-ua-full-version-list"], + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " + "\"Google Chrome\";v=\"126.0.6478.61\""); + + ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); + ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); + + EXPECT_FALSE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-platform-version") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); +} + +TEST_F(Transform, CPPWrapperBase64) { + FiftyoneDegrees::Common::Transform t; + + auto h = t.fromBase64GHEV( + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); + + ASSERT_EQ(h.size(), 6); + + EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); + + ASSERT_EQ(h["sec-ch-ua"], + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + ASSERT_EQ(h["sec-ch-ua-full-version-list"], + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + + ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); + ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); + ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); + ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); + + + EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); +} + +TEST_F(Transform, CPPWrapperBase64InsufficientMemory) { + FiftyoneDegrees::Common::Transform t(128); + + auto h = t.fromBase64GHEV( + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); + + ASSERT_EQ(h.size(), 6); + + EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); + + ASSERT_EQ(h["sec-ch-ua"], + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + ASSERT_EQ(h["sec-ch-ua-full-version-list"], + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + + ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); + ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); + ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); + ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); + + + EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); +} + +TEST_F(Transform, CPPWrapperSUA) { + FiftyoneDegrees::Common::Transform t; + + auto h = t.fromSUA( + "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," + "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"); + + ASSERT_EQ(h.size(), 6); + + EXPECT_TRUE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + + + ASSERT_EQ(h["sec-ch-ua-arch"], "\"x86\""); + ASSERT_EQ(h["sec-ch-ua-model"], "\"MacBook\""); + ASSERT_EQ(h["sec-ch-ua-mobile"], "?1"); + ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); + ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); + ASSERT_EQ(h["sec-ch-ua-full-version-list"], + "\"Not/" + "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " + "Chrome\";v=\"126.0.6478.127\""); - size_t bufferLength = 15; - char *buffer = (char *) fiftyoneDegreesMalloc(bufferLength); - - size_t count = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, fillResultsCallback); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + EXPECT_FALSE(h.find("sec-ch-ua") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } diff --git a/transform.c b/transform.c index cb6fe000..f094adbf 100644 --- a/transform.c +++ b/transform.c @@ -22,50 +22,1267 @@ #include "transform.h" -size_t fiftyoneDegreesTransformIterateGhevFromJson(const char *json, - char * const buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesTransformCallback callback) { - return 0; +#define initStaticKey(x) {x, sizeof(x) - 1} + +#define NotExpectSymbol(json, ch, exit) \ + if (*json == ch) { \ + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; \ + exit; \ + } + +#define ExpectSymbol(json, ch, exit) \ + if (*json != ch) { \ + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; \ + exit; \ + } + +#define ValuePtr \ + (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY ? NULL \ + : begin) + +#define GET_SEXTET(str, i) \ + (str[i] == '=' ? 0 & i++ : base64_char_to_value(str[i++], exception)) + +typedef enum { + ARCHITECTURE, // sec-ch-ua-arch + BRANDS, // sec-ch-ua + BITNESS, // sec-ch-ua-bitness + FULLVERSIONLIST, // sec-ch-ua-full-version-list + MOBILE, // sec-ch-ua-mobile + MODEL, // sec-ch-ua-model + PLATFORM, // sec-ch-ua-platform + PLATFORMVERSION, // sec-ch-ua-platform-version + KEY_UNDEFINED, // +} Key; + +typedef Key (*readKeyCallback)(const char**, fiftyoneDegreesException* const); +typedef char* (*readValueCallback)(const char** json, char* begin, + const char* const end, + fiftyoneDegreesKeyValuePair* cache, Key key, + fiftyoneDegreesException* const exception); + +static struct { + const char* key; + size_t len; +} key_map[] = { + initStaticKey("sec-ch-ua-arch"), + initStaticKey("sec-ch-ua"), + initStaticKey("sec-ch-ua-bitness"), + initStaticKey("sec-ch-ua-full-version-list"), + initStaticKey("sec-ch-ua-mobile"), + initStaticKey("sec-ch-ua-model"), + initStaticKey("sec-ch-ua-platform"), + initStaticKey("sec-ch-ua-platform-version"), +}; + +// ---- + +static inline char* safe_write_to_buffer( + char* begin, const char* const end, char symbol, + fiftyoneDegreesException* const exception) { + if (begin < end) { + *begin = symbol; + } else { + exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY; + } + + return ++begin; } -size_t fiftyoneDegreesTransformIterateGhevFromBase64(const char *base64, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesTransformCallback callback) { - return 0; +static inline uint32_t base64_char_to_value( + char c, fiftyoneDegreesException* const exception) { + static const uint32_t base64_lookup_table[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255}; + + if (base64_lookup_table[(uint8_t)c] == 255) { + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + } + + return base64_lookup_table[(uint8_t)c]; } -size_t fiftyoneDegreesTransformIterateSua(const char *json, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesTransformCallback callback) { - return 0; +static size_t base64_decode(const char* base64_input, char* const buffer, + size_t length, + fiftyoneDegreesException* const exception) { + size_t input_length = strlen(base64_input); + if (input_length % 4 != 0) { + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + return 0; // Invalid base64 input length + } + + char* begin = buffer; + char* end = buffer + length; + + for (size_t i = 0; i < input_length;) { + uint32_t sextet_a = GET_SEXTET(base64_input, i); + uint32_t sextet_b = GET_SEXTET(base64_input, i); + uint32_t sextet_c = GET_SEXTET(base64_input, i); + uint32_t sextet_d = GET_SEXTET(base64_input, i); + + if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + return 0; + } + + uint32_t triple = + (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; + + begin = safe_write_to_buffer(begin, end, (triple >> 16) & 0xFF, exception); + begin = safe_write_to_buffer(begin, end, (triple >> 8) & 0xFF, exception); + begin = safe_write_to_buffer(begin, end, triple & 0xFF, exception); + } + + begin = safe_write_to_buffer(begin, end, '\0', exception); + + return begin - buffer; } -size_t fiftyoneDegreesTransformGhevFromJson(const char *json, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesKeyValuePairArray * const headers) { - return 0; +static inline const char* skip_whitespaces(const char* json) { + for (;; ++json) { + switch (*json) { + case ' ': + case '\t': + case '\n': + case '\v': + case '\r': + case '\f': + break; + + default: + return json; + } + } +} + +static inline const char* skip_to_next_char(const char* json, + const char target) { + for (; *json != target; ++json) { + switch (*json) { + case '\0': { + return json; + } break; + + case '\\': { + if (json[1] == target || json[1] == '\\') { + ++json; + } + } break; + } + } + + return json; +} + +static const char* skip_value(const char* json) { + json = skip_to_next_char(json, ':'); + if (*json == '\0') { + return json; + } + + json = skip_whitespaces(json + 1); + + switch (*json) { + case '\0': { + return json; + } break; + + case '{': { // skip nested object + ++json; + + for (int nesting_level = 1; nesting_level > 0; ++json) { + switch (*json) { + case '\0': { + return json; + } break; + + case '{': { + ++nesting_level; + } break; + + case '}': { + --nesting_level; + } break; + + case '"': { + json = skip_to_next_char(json + 1, '"'); + if (*json == '\0') { + return json; + } + } break; + } + } + } break; + + case '[': { + ++json; + + for (int nesting_level = 1; nesting_level > 0; ++json) { + switch (*json) { + case '\0': { + return json; + } break; + + case '[': { + ++nesting_level; + } break; + + case ']': { + --nesting_level; + } break; + + case '"': { + json = skip_to_next_char(json + 1, '"'); + if (*json == '\0') { + return json; + } + } break; + } + } + } break; + + case '"': { + json = skip_to_next_char(json + 1, '"'); + } break; + + default: { + for (int flag = 1; flag;) { + switch (*json) { + case '\0': { + return json; + } break; + + case ',': + case '}': + case ']': { + flag = 0; + } break; + + default: { + ++json; + } break; + } + } + } break; + } + + if (*json == '\0') { + return json; + } + + return skip_to_next_char(json + 1, '"'); +} + +static inline char* init_keys(char* const begin, const char* const end, + fiftyoneDegreesKeyValuePair* cache, + fiftyoneDegreesException* const exception) { + char* ptr = begin; + + for (size_t k = 0; k < KEY_UNDEFINED; ++k) { + cache[k].key = ptr; + cache[k].keyLength = key_map[k].len; + + for (size_t i = 0; i < key_map[k].len; ++i) { + ptr = safe_write_to_buffer(ptr, end, key_map[k].key[i], exception); + } + } + + return ptr; +} + +static const char* init_parsing(const char* json, char** begin, + const char* const end, + fiftyoneDegreesKeyValuePair* cache, + fiftyoneDegreesException* const exception) { + exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; + + *begin = init_keys(*begin, end, cache, exception); + + json = skip_whitespaces(json); + ExpectSymbol(json, '{', return json); + return skip_to_next_char(json, '"'); } -size_t fiftyoneDegreesTransformGhevFromBase64(const char *base64, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesKeyValuePairArray * const headers){ +static Key read_ghev_key(const char** json, + fiftyoneDegreesException* const exception) { + enum ReadKeyState { + READ_KEY_INIT, + ARCH, + BRANDS_OR_BITNESS, + FULL_VERSION_LIST, + MOBILE_OR_MODEL, + PLATFORM_OR_VERSION, + READ_KEY_BRANDS, + READ_KEY_BITNESS, + READ_KEY_MOBILE, + READ_KEY_MODEL, + READ_KEY_PLATFORMVERSION, + }; + + ++*json; + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { + switch (state) { + case READ_KEY_INIT: { + switch (**json) { + case 'a': { + state = ARCH; + } break; + + case 'f': { + state = FULL_VERSION_LIST; + } break; + + case 'b': { + state = BRANDS_OR_BITNESS; + } break; + + case 'm': { + state = MOBILE_OR_MODEL; + } break; + + case 'p': { + state = PLATFORM_OR_VERSION; + } break; + + default: { + *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED; + } break; + + case '"': { + return KEY_UNDEFINED; + } break; + } + } break; + + case ARCH: { + for (const char* i = "rchitecture"; *i != '\0'; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return ARCHITECTURE; + } break; + + case FULL_VERSION_LIST: { + for (const char* i = "ullVersionList"; *i != '\0'; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return FULLVERSIONLIST; + } break; + + case BRANDS_OR_BITNESS: { + switch (**json) { + case 'r': { + state = READ_KEY_BRANDS; + } break; + + case 'i': { + state = READ_KEY_BITNESS; + } break; + + default: { + *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case MOBILE_OR_MODEL: { + ExpectSymbol(*json, 'o', *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + + ++(*json); + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + switch (**json) { + case 'b': { + state = READ_KEY_MOBILE; + } break; + + case 'd': { + state = READ_KEY_MODEL; + } break; + + default: { + *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case PLATFORM_OR_VERSION: { + for (const char* i = "latform"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + switch (**json) { + case '"': { + return PLATFORM; + } break; + + case 'V': { + state = READ_KEY_PLATFORMVERSION; + } break; + + default: { + *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case READ_KEY_BRANDS: { + for (const char* i = "ands"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return BRANDS; + } break; + + case READ_KEY_BITNESS: { + for (const char* i = "tness"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return BITNESS; + } break; + + case READ_KEY_MOBILE: { + for (const char* i = "ile"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return MOBILE; + } break; + + case READ_KEY_MODEL: { + for (const char* i = "el"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return MODEL; + } break; + + case READ_KEY_PLATFORMVERSION: { + for (const char* i = "ersion"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return PLATFORMVERSION; + } break; + } + } + + return KEY_UNDEFINED; +} + +static Key read_sua_key(const char** json, + fiftyoneDegreesException* const exception) { + enum ReadKeyState { + READ_KEY_INIT, + BROWSERS_OR_BITNESS, + MOBILE_OR_MODEL, + READ_KEY_BITNESS, + ARCH, + READ_KEY_MOBILE, + READ_KEY_MODEL, + READ_KEY_PLATFORM, + READ_KEY_BROWSERS, + }; + + ++*json; + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { + switch (state) { + case READ_KEY_INIT: { + switch (**json) { + case 'a': { + state = ARCH; + } break; + + case 'b': { + state = BROWSERS_OR_BITNESS; + } break; + + case 'm': { + state = MOBILE_OR_MODEL; + } break; + + case 'p': { + state = READ_KEY_PLATFORM; + } break; + + default: { + *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED; + } break; + + case '"': { + return KEY_UNDEFINED; + } break; + } + } break; + + case ARCH: { + for (const char* i = "rchitecture"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return ARCHITECTURE; + } break; + + case BROWSERS_OR_BITNESS: { + switch (**json) { + case 'r': { + state = READ_KEY_BROWSERS; + } break; + + case 'i': { + state = READ_KEY_BITNESS; + } break; + + default: { + *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case MOBILE_OR_MODEL: { + ExpectSymbol(*json, 'o', *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + + ++(*json); + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + switch (**json) { + case 'b': { + state = READ_KEY_MOBILE; + } break; + + case 'd': { + state = READ_KEY_MODEL; + } break; + + default: { + *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case READ_KEY_PLATFORM: { + for (const char* i = "latform"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, ':'); + return KEY_UNDEFINED); + } + + return **json == '\"' ? PLATFORM : KEY_UNDEFINED; + } break; + + case READ_KEY_BROWSERS: { + for (const char* i = "owsers"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return FULLVERSIONLIST; + } break; + + case READ_KEY_BITNESS: { + for (const char* i = "tness"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return BITNESS; + } break; + + case READ_KEY_MOBILE: { + for (const char* i = "ile"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return MOBILE; + } break; + + case READ_KEY_MODEL: { + for (const char* i = "el"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); + return KEY_UNDEFINED); + } + + return MODEL; + } break; + } + } + + return KEY_UNDEFINED; +} + +static char* read_string_value(const char** json, char* begin, + const char* const end, + fiftyoneDegreesException* const exception) { + *json = skip_whitespaces(*json); + if (**json == 'n') { + ++(*json); + NotExpectSymbol(*json, '\0', return begin); + + for (const char* i = "ull"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, return begin); + } + + return NULL; + } + + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (begin = safe_write_to_buffer(begin, end, '"', exception);; ++(*json)) { + NotExpectSymbol(*json, '\0', return begin); + + begin = safe_write_to_buffer(begin, end, **json, exception); + + switch (**json) { + case '\"': { + ++(*json); + return begin; + } + + case '\\': { + if ((*json)[1] == '\\' || (*json)[1] == '"') { + ++(*json); + + NotExpectSymbol(*json, '\0', return begin); + begin = safe_write_to_buffer(begin, end, **json, exception); + } + } break; + } + } +} + +static char* read_bool_ghev_value(const char** json, char* begin, + const char* const end, + fiftyoneDegreesKeyValuePair* cache, Key key, + fiftyoneDegreesException* const exception) { + char* ptr = begin; + + switch (**json) { + case 't': { + ++(*json); + for (const char* i = "rue"; *i != '\0'; ++(*json), ++i) { + ExpectSymbol(*json, *i, return begin); + } + + ptr = safe_write_to_buffer(ptr, end, '?', exception); + ptr = safe_write_to_buffer(ptr, end, '1', exception); + } break; + + case 'f': { + ++(*json); + for (const char* i = "alse"; *i != '\0'; ++(*json), ++i) { + ExpectSymbol(*json, *i, return begin); + } + + ptr = safe_write_to_buffer(ptr, end, '?', exception); + ptr = safe_write_to_buffer(ptr, end, '0', exception); + } break; + + default: { + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + return begin; + } break; + } + + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + + return ptr; +} + +static char* read_bool_sua_value(const char** json, char* begin, + const char* const end, + fiftyoneDegreesKeyValuePair* cache, Key key, + fiftyoneDegreesException* const exception) { + switch (**json) { + case '0': + case '1': { + } break; + + default: { + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + return begin; + } break; + } + + char* ptr = safe_write_to_buffer(begin, end, '?', exception); + ptr = safe_write_to_buffer(ptr, end, **json, exception); + + ++(*json); + + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + + return ptr; +} + +static char* read_version_sua(const char** json, char* begin, + const char* const end, + fiftyoneDegreesException* const exception) { + enum version_state { + version_read, + version_skip, + version_exit, + } state = version_skip; + + for (char* ptr = begin;; ++(*json)) { + NotExpectSymbol(*json, '\0', return begin); + + switch (state) { + case version_read: { + switch (**json) { + case '\"': { + state = version_skip; + } break; + + case '\\': { + ptr = safe_write_to_buffer(ptr, end, **json, exception); + + if ((*json)[1] == '\\' || (*json)[1] == '"') { + ++(*json); + NotExpectSymbol(*json, '\0', return begin); + + ptr = safe_write_to_buffer(ptr, end, **json, exception); + } + } break; + + default: { + ptr = safe_write_to_buffer(ptr, end, **json, exception); + } break; + } + } break; + + case version_skip: { + switch (**json) { + case '"': { + state = version_read; + } break; + + case ',': { + ptr = safe_write_to_buffer(ptr, end, '.', exception); + } break; + + case ']': { + state = version_exit; + } break; + } + } break; + + case version_exit: { + ptr = safe_write_to_buffer(ptr, end, '"', exception); + return ptr; + } break; + } + } +} + +static char* read_brands_ghev_value(const char** json, char* begin, + const char* const end, + fiftyoneDegreesKeyValuePair* cache, Key key, + fiftyoneDegreesException* const exception) { + *json = skip_to_next_char(*json, '['); + ExpectSymbol(*json, '[', return begin); + + ++*json; + + for (char* ptr = begin;; ++*json) { + NotExpectSymbol(*json, '\0', return begin); + + *json = skip_to_next_char(*json, '{'); + ExpectSymbol(*json, '{', return begin); + + *json = skip_to_next_char(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skip_to_next_char(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + ++*json; + NotExpectSymbol(*json, '\0', return begin); + + char* ptr2 = read_string_value(json, ptr, end, exception); + if (ptr2 != NULL) { + ptr = safe_write_to_buffer(ptr2, end, ';', exception); + ptr = safe_write_to_buffer(ptr, end, 'v', exception); + ptr = safe_write_to_buffer(ptr, end, '=', exception); + + *json = skip_to_next_char(*json, ','); + ExpectSymbol(*json, ',', return begin); + + *json = skip_to_next_char(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skip_to_next_char(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + ++*json; + + ptr2 = read_string_value(json, ptr, end, exception); + if (ptr2 == NULL) { + ptr2 = ptr; + + ptr = safe_write_to_buffer(ptr, end, 'n', exception); + ptr = safe_write_to_buffer(ptr, end, 'u', exception); + ptr = safe_write_to_buffer(ptr, end, 'l', exception); + ptr = safe_write_to_buffer(ptr, end, 'l', exception); + } else { + ptr = ptr2; + } + } + + *json = skip_to_next_char(*json, '}'); + ExpectSymbol(*json, '}', return begin); + + *json = skip_whitespaces(*json + 1); + NotExpectSymbol(*json, '\0', return begin); + + switch (**json) { + case ']': { + if (ptr != begin) { + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + return ptr; + } else { + return NULL; + } + + } break; + + case ',': { + if (ptr2 != NULL) { + ptr = safe_write_to_buffer(ptr, end, ',', exception); + ptr = safe_write_to_buffer(ptr, end, ' ', exception); + } + } break; + + default: { + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + return begin; + } break; + } + } +} + +static char* read_brands_sua_value(const char** json, char* begin, + const char* const end, + fiftyoneDegreesKeyValuePair* cache, Key key, + fiftyoneDegreesException* const exception) { + *json = skip_to_next_char(*json, '['); + ExpectSymbol(*json, '[', return begin); + + for (char* ptr = begin;; ++*json) { + NotExpectSymbol(*json, '\0', return begin); + + *json = skip_to_next_char(*json, '{'); + ExpectSymbol(*json, '{', return begin); + + *json = skip_to_next_char(*json, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skip_to_next_char(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + ++*json; + + char* ptr2 = read_string_value(json, ptr, end, exception); + if (ptr2 != NULL) { + ptr = safe_write_to_buffer(ptr2, end, ';', exception); + ptr = safe_write_to_buffer(ptr, end, 'v', exception); + ptr = safe_write_to_buffer(ptr, end, '=', exception); + + *json = skip_to_next_char(*json, ','); + ExpectSymbol(*json, ',', return begin); + + *json = skip_to_next_char(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skip_to_next_char(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + *json = skip_to_next_char(*json, '['); + ExpectSymbol(*json, '[', return begin); + + ++*json; + + ptr = safe_write_to_buffer(ptr, end, '"', exception); + ptr = read_version_sua(json, ptr, end, exception); + } + + *json = skip_to_next_char(*json, '}'); + ExpectSymbol(*json, '}', return begin); + + *json = skip_whitespaces(*json + 1); + NotExpectSymbol(*json, '\0', return begin); + + switch (**json) { + case ']': { + if (ptr != begin) { + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + return ptr; + } else { + return NULL; + } + } break; + + case ',': { + if (ptr != begin) { + ptr = safe_write_to_buffer(ptr, end, ',', exception); + ptr = safe_write_to_buffer(ptr, end, ' ', exception); + } + } break; + + default: { + exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + return begin; + } break; + } + } +} + +static char* read_pure_string_value(const char** json, char* begin, + const char* const end, + fiftyoneDegreesKeyValuePair* cache, Key key, + fiftyoneDegreesException* const exception) { + char* ptr = read_string_value(json, begin, end, exception); + + if (ptr != NULL) { + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + } + + return ptr; +} + +static char* read_platform_sua_value( + const char** json, char* begin, const char* const end, + fiftyoneDegreesKeyValuePair* cache, Key key, + fiftyoneDegreesException* const exception) { + *json = skip_to_next_char(*json, '{'); + ExpectSymbol(*json, '{', return begin); + + *json = skip_to_next_char(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skip_to_next_char(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + ++*json; + + char* ptr = read_string_value(json, begin, end, exception); + if (ptr == NULL) { + return NULL; + } + + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + + cache[PLATFORMVERSION].value = NULL; + cache[PLATFORMVERSION].valueLength = 0; + + begin = ptr; + + *json = skip_whitespaces(*json); + + if (**json == '}') { + return begin; + } + + ExpectSymbol(*json, ',', return begin); + + *json = skip_to_next_char(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skip_to_next_char(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + *json = skip_to_next_char(*json + 1, '['); + ExpectSymbol(*json, '[', return begin); + + ++*json; + NotExpectSymbol(*json, '\0', return begin); + + ptr = safe_write_to_buffer(ptr, end, '"', exception); + ptr = read_version_sua(json, ptr, end, exception); + + cache[PLATFORMVERSION].value = ValuePtr; + cache[PLATFORMVERSION].valueLength = ptr - begin; + + return ptr; +} + +static inline readValueCallback read_value_switch(Key key, int isSua) { + readValueCallback res = NULL; + + switch (key) { + case ARCHITECTURE: { + res = read_pure_string_value; + } break; + + case BITNESS: { + res = read_pure_string_value; + } break; + + case BRANDS: { + res = isSua ? NULL : read_brands_ghev_value; + } break; + + case FULLVERSIONLIST: { + res = isSua ? read_brands_sua_value : read_brands_ghev_value; + } break; + + case MOBILE: { + res = isSua ? read_bool_sua_value : read_bool_ghev_value; + } break; + + case MODEL: { + res = read_pure_string_value; + } break; + + case PLATFORM: { + res = isSua ? read_platform_sua_value : read_pure_string_value; + } break; + + case PLATFORMVERSION: { + res = isSua ? NULL : read_pure_string_value; + } break; + + case KEY_UNDEFINED: { + res = NULL; + } break; + } + + return res; +} + +static bool pushToHeaders(void* ctx, fiftyoneDegreesKeyValuePair header, + fiftyoneDegreesException* const exception) { + fiftyoneDegreesKeyValuePairArray* const headers = + (fiftyoneDegreesKeyValuePairArray* const)ctx; + + if (headers->count < headers->capacity) { + fiftyoneDegreesKeyValuePair* pair = headers->items + headers->count++; + + pair->key = header.key; + pair->keyLength = header.keyLength; + pair->value = header.value; + pair->valueLength = header.valueLength; + } + + return (headers->count < headers->capacity); +} +// ------------------------------------------------------------------------------------------------ +static size_t main_parsing_body(const char* json, char* const buffer, + size_t length, + fiftyoneDegreesException* const exception, + int isSua, + fiftyoneDegreesTransformCallback callback, + void* ctx) { + fiftyoneDegreesKeyValuePair cache[KEY_UNDEFINED]; + + // define buffer range + char* begin = buffer; + const char* const end = buffer + length; + + // write keys to buffer, init cache and skip to the first key + json = init_parsing(json, &begin, end, cache, exception); + if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA || + exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { return 0; + } + + size_t iterations = 0; // total number of parsed key-value pairs + + // main reading loop + readKeyCallback read_key = isSua ? read_sua_key : read_ghev_key; + while (*json != '\0') { + Key key = read_key(&json, exception); + ExpectSymbol(json, '"', break); + + readValueCallback read_value = read_value_switch(key, isSua); + if (key == KEY_UNDEFINED || read_value == NULL) { + json = skip_value(json + 1); + continue; + } + + json = skip_to_next_char(json, ':'); + ExpectSymbol(json, ':', break); + + json = skip_whitespaces(json + 1); + NotExpectSymbol(json, '\0', break); + + char* ptr = read_value(&json, begin, end, cache, key, exception); + if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + break; + } + + if (ptr != NULL) { + begin = ptr; + + ++iterations; + if (!callback(ctx, cache[key], exception)) { + return iterations; + } + + if (key == PLATFORM && isSua && cache[PLATFORMVERSION].valueLength != 0) { + ++iterations; + if (!callback(ctx, cache[PLATFORMVERSION], exception)) { + return iterations; + } + } + } + + json = skip_to_next_char(json, '"'); + } + + return iterations; } -size_t fiftyoneDegreesTransformSua(const char *json, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesKeyValuePairArray * const headers) { +// ------------------------------------------------------------------------------------------------ +size_t fiftyoneDegreesTransformIterateGhevFromJson( + const char* json, char* const buffer, size_t length, + fiftyoneDegreesException* const exception, + fiftyoneDegreesTransformCallback callback, void* ctx) { + return main_parsing_body(json, buffer, length, exception, 0, callback, ctx); +} + +size_t fiftyoneDegreesTransformIterateGhevFromBase64( + const char* base64, char* buffer, size_t length, + fiftyoneDegreesException* const exception, + fiftyoneDegreesTransformCallback callback, void* ctx) { + size_t offset = base64_decode(base64, buffer, length, exception); + + if (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY || + exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { return 0; + } + + return fiftyoneDegreesTransformIterateGhevFromJson( + buffer, buffer + offset, length > offset ? length - offset : 0, exception, + callback, ctx); +} + +size_t fiftyoneDegreesTransformIterateSua( + const char* json, char* const buffer, size_t length, + fiftyoneDegreesException* const exception, + fiftyoneDegreesTransformCallback callback, void* ctx) { + return main_parsing_body(json, buffer, length, exception, 1, callback, ctx); +} + +size_t fiftyoneDegreesTransformGhevFromJson( + const char* json, char* buffer, size_t length, + fiftyoneDegreesException* const exception, + fiftyoneDegreesKeyValuePairArray* const headers) { + size_t calls = fiftyoneDegreesTransformIterateGhevFromJson( + json, buffer, length, exception, pushToHeaders, headers); + + if (calls != headers->count) { + exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; + } + + return calls; +} + +size_t fiftyoneDegreesTransformGhevFromBase64( + const char* base64, char* buffer, size_t length, + fiftyoneDegreesException* const exception, + fiftyoneDegreesKeyValuePairArray* const headers) { + size_t calls = fiftyoneDegreesTransformIterateGhevFromBase64( + base64, buffer, length, exception, pushToHeaders, headers); + + if (calls != headers->count) { + exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; + } + + return calls; +} + +size_t fiftyoneDegreesTransformSua( + const char* json, char* buffer, size_t length, + fiftyoneDegreesException* const exception, + fiftyoneDegreesKeyValuePairArray* const headers) { + size_t calls = fiftyoneDegreesTransformIterateSua( + json, buffer, length, exception, pushToHeaders, headers); + + if (calls != headers->count) { + exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; + } + + return calls; } diff --git a/transform.h b/transform.h index e1e80273..5c20a464 100644 --- a/transform.h +++ b/transform.h @@ -23,20 +23,30 @@ #ifndef FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED #define FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED -#include "exceptions.h" -#include "pair.h" #include "array.h" +#include "exceptions.h" #include "fiftyone.h" +#include "pair.h" + /** * User-Agent Client Hints Representation Conversion Routines * * 3 common ways to represent UACH are: * - [HTTP header map](https://wicg.github.io/ua-client-hints/) - * - [getHighEntropyValues()](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues) JavaScript API result as a JSON string - * - [OpenRTB](https://iabtechlab.com/wp-content/uploads/2022/04/OpenRTB-2-6_FINAL.pdf) [device.sua](https://51degrees.com/blog/openrtb-structured-user-agent-and-user-agent-client-hints) field as a JSON string + * - getHighEntropyValues() JS API call result in JSON format + * - Structured User Agent Object from OpenRTB 2.6 * - * 51degrees uses HTTP header map to represent UACH and expects the evidence to be provided as - * HTTP headers (or same name query parameters). The header names in question are: + * Links: + * - + * [getHighEntropyValues()](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues) + * - + * [device.sua](https://51degrees.com/blog/openrtb-structured-user-agent-and-user-agent-client-hints) + * - [OpenRTB 2.6 + * spec](https://iabtechlab.com/wp-content/uploads/2022/04/OpenRTB-2-6_FINAL.pdf) + * + * 51degrees uses HTTP header map to represent UACH and expects the evidence to + * be provided as HTTP headers (or same name query parameters). The header + * names in question are: * - Sec-CH-UA * - Sec-CH-UA-Platform * - Sec-CH-UA-Mobile @@ -46,191 +56,266 @@ * - Sec-CH-UA-Arch * - Sec-CH-UA-Bitness * - * The conversion routines are provided in 2 styles: iterative (for potentially lazy consumption) and eager. The former uses callback - * to iteratively provide header name-value pairs to the caller, the latter provides the whole header map as output. - * In addition 2 variants of GHEV routine is provided: one that accepts a raw JSON string - * and one that accepts a base64 encoded JSON string as input parameter. + * The conversion routines transform the GHEV or SUA input into the HTTP header + * maps Routines are provided in 2 styles: iterative (for potentially lazy + * consumption) and array-results (for eager consumption). The former uses + * callback to iteratively provide header name-value pairs to the caller, the + * latter provides the whole header map array as output. In addition 2 variants + * of GHEV routine is provided: one that accepts a raw JSON string and one that + * accepts a base64 encoded JSON string as input parameter. * - * Both styles use an externally preallocated memory buffer to write the formed http header values to. The output callback or headermap - * will have pointers to the null-terminated strings stored in that buffer. Thus the buffer should outlive the last output evidence use. + * Both styles use an externally preallocated memory buffer to write the formed + * http header values to. The output callback or headermap will have pointers to + * the null-terminated strings stored in that buffer. Thus the buffer should + * outlive the last output evidence use. */ - /** - * A callback function type definition that is called every time a header name-value pair is formed and - * allows the caller to decide how to handle the output. The callback function must be provided as a param to the Iterate-style - * conversion routines. - * @param name the name of the discovered HTTP header - * @param value the value of the discoverd HTTP header, may be NULL if buffer had insufficient space to store the value, - * the valueLength will still be passed and will indicate the necessary length of the buffer to store the value - * @param nameLength the length of the name string - * @param valueLength the lengh of the value string written or that should have been written in case buffer was of insufficietn capacity - * @return the implementer returns true to continue the iteration or false to stop + * A callback function type definition that is called every time a header + * name-value pair is formed and allows the caller to decide how to handle the + * output. The callback function must be provided as a param to the + * Iterate-style conversion routines. + * @param state - arbitrary context object - f.e. external state or a structure + * to accumulate output + * @param header - a header key value pair containing the pointer to the header + * name and value + * @return the implementer returns true to continue the iteration or false to + * stop */ -EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)(fiftyoneDegreesKeyValuePair header); +EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( + void *state, fiftyoneDegreesKeyValuePair header, + Exception *const exception); /** - * Iteratively convert getHighEntropyValue() API result JSON string to HTTP header representation. + * Iteratively convert getHighEntropyValue() API result JSON string to HTTP + * header representation. * @param json a JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values - * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values. The lifetime of this buffer is managed by the + * caller * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be - * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, - * or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, - * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the - * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called - * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended - * @param callback a function that is called whenever a header is extracted with header name and value passed as params - * if the function returns true, iteration continues, otherwise halts - * @return the number of iterations (callback calls) made + * @param exception - a constant pointer to a (preallocated) exception object + * that is filled in case any errors occurred. must be checked by the caller + * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * if the conversion was successful, or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * insufficient size, in that case the callback will still be called, but value + * will be NULL and valueLength will indicate the length necessary to store the + * value in the buffer - this info can be then used by the caller to allocate + * the buffer of sufficient size and execute another call - essentially + * resulting in a dry run before allocation. + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * case callback will likely not be called, or will be called a limited number + * of times until the corruption becomes obvious to the iterator as no lookahead + * logic is implemented + * @param callback a function that is called whenever a header key value is + * extracted header key value pair is passed as a param; if callback returns + * true, iteration continues, otherwise halts + * @param state an external context state to pass to be used by the callback + * function + * @return the number of iterations / header pairs detected (callback calls + * made) */ -EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson(const char *json, - char * const buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesTransformCallback callback); +EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( + const char *json, char *const buffer, size_t length, + fiftyoneDegreesException *const exception, + fiftyoneDegreesTransformCallback callback, void *state); /** - * Iteratively convert getHighEntropyValue() API result base64 encoded JSON string to HTTP header representation. - * @param base64 a base64 encoded JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values - * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * Iteratively convert getHighEntropyValue() API result base64 encoded JSON + * string to HTTP header representation. + * @param base64 a base64 encoded JSON string with the getHighEntropyValue() API + * result + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be - * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, - * or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, - * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the - * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called - * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended - * @param callback a function that is called whenever a header is extracted with header name and value passed as params - * if the function returns true, iteration continues, otherwise halts - * @return the number of iterations (callback calls) made + * @param exception - a constant pointer to a (preallocated) exception object + * that is filled in case any errors occurred. must be checked by the caller + * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * if the conversion was successful, or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * insufficient size, in that case the callback will still be called, but value + * will be NULL and valueLength will indicate the length necessary to store the + * value in the buffer - this info can be then used by the caller to allocate + * the buffer of sufficient size and execute another call - essentially + * resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * case callback will likely not be called, or will be called a limited number + * of times until the corruption becomes obvious to the iterator as no lookahead + * logic is intended + * @param callback a function that is called whenever a header is extracted with + * header name and value passed as params if the function returns true, + * iteration continues, otherwise halts + * @param state an external context state to pass to be used by the callback + * function + * @return the number of iterations / header pairs detected (callback calls + * made) */ -EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64(const char *base64, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesTransformCallback callback); +EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( + const char *base64, char *buffer, size_t length, + fiftyoneDegreesException *const exception, + fiftyoneDegreesTransformCallback callback, void *state); /** * Iteratively convert device.sua JSON string to HTTP header representation. * @param json a JSON string with the device.sua raw representation - * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values - * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be - * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, - * or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, - * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the - * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called - * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended - * @param callback a function that is called whenever a header is extracted with header name and value passed as params - * if the function returns true, iteration continues, otherwise halts - * @return the number of iterations (callback calls) made + * @param exception - a constant pointer to a (preallocated) exception object + * that is filled in case any errors occurred. must be checked by the caller + * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * if the conversion was successful, or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * insufficient size, in that case the callback will still be called, but value + * will be NULL and valueLength will indicate the length necessary to store the + * value in the buffer - this info can be then used by the caller to allocate + * the buffer of sufficient size and execute another call - essentially + * resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * case callback will likely not be called, or will be called a limited number + * of times until the corruption becomes obvious to the iterator as no lookahead + * logic is intended + * @param callback a function that is called whenever a header is extracted with + * header name and value passed as params if the function returns true, + * iteration continues, otherwise halts + * @param state an external context state to pass to be used by the callback + * function + * @return the number of iterations / header pairs detected (callback calls + * made) */ -EXTERNAL size_t fiftyoneDegreesTransformIterateSua(const char *json, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesTransformCallback callback); +EXTERNAL size_t fiftyoneDegreesTransformIterateSua( + const char *json, char *buffer, size_t length, + fiftyoneDegreesException *const exception, + fiftyoneDegreesTransformCallback callback, void *state); + /** - * A preallocated array of key-value pairs intended to be an array of HTTP headers + * A preallocated array of key-value pairs intended to be an array of HTTP + * headers */ #define NONE FIFTYONE_DEGREES_ARRAY_TYPE(fiftyoneDegreesKeyValuePair, NONE); /** - * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header representation. + * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header + * representation. * @param json a JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values - * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be - * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, - * or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, - * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the - * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called - * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended - * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array of capacity enough to hold up to 8 UACH headers; - * upon function return will contain the output http header names and value const char * pointers either to the DATA segment allocated (names) - * string constants or preallocated buffer on the heap. must not be NULL and has to be memory managed outside of the function, its lifetime - * should be long enough to survive the last use of the returned headers - * @return the number of headers that was written or should have been written to the array in case this number is higher than headers->capacity - * in the latter case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status and the returned capacity will signal - * the array size that needs to be allocated + * @param exception - a constant pointer to a (preallocated) exception object + * that is filled in case any errors occurred. must be checked by the caller + * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * if the conversion was successful, or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * insufficient size, in that case the callback will still be called, but value + * will be NULL and valueLength will indicate the length necessary to store the + * value in the buffer - this info can be then used by the caller to allocate + * the buffer of sufficient size and execute another call - essentially + * resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * case callback will likely not be called, or will be called a limited number + * of times until the corruption becomes obvious to the iterator as no lookahead + * logic is intended + * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array + * of capacity enough to hold up to 8 UACH headers; upon function return will + * contain the output http header names and value const char * pointers either + * to the DATA segment allocated (names) string constants or preallocated buffer + * on the heap. must not be NULL and has to be memory managed outside of the + * function, its lifetime should be long enough to survive the last use of the + * returned headers + * @return the number of headers that was written or should have been written to + * the array in case this number is higher than headers->capacity in the latter + * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY + * status and the returned capacity will signal the array size that needs to be + * allocated */ -EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson(const char *json, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesKeyValuePairArray * const headers); +EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( + const char *json, char *buffer, size_t length, + fiftyoneDegreesException *const exception, + fiftyoneDegreesKeyValuePairArray *const headers); /** - * Eagerly convert getHighEntropyValue() API result from base64-encoded JSON string to HTTP header representation. - * @param base64 a base64-encoded JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values - * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * Eagerly convert getHighEntropyValue() API result from base64-encoded JSON + * string to HTTP header representation. + * @param base64 a base64-encoded JSON string with the getHighEntropyValue() API + * result + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be - * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, - * or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, - * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the - * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called - * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended - * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array of capacity enough to hold up to 8 UACH headers; - * upon function return will contain the output http header names and value const char * pointers either to the DATA segment allocated (names) - * string constants or preallocated buffer on the heap. must not be NULL and has to be memory managed outside of the function, its lifetime - * should be long enough to survive the last use of the returned headers - * @return the number of headers that was written or should have been written to the array in case this number is higher than headers->capacity - * in the latter case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status and the returned capacity will signal - * the array size that needs to be allocated + * @param exception - a constant pointer to a (preallocated) exception object + * that is filled in case any errors occurred. must be checked by the caller + * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * if the conversion was successful, or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * insufficient size, in that case the callback will still be called, but value + * will be NULL and valueLength will indicate the length necessary to store the + * value in the buffer - this info can be then used by the caller to allocate + * the buffer of sufficient size and execute another call - essentially + * resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * case callback will likely not be called, or will be called a limited number + * of times until the corruption becomes obvious to the iterator as no lookahead + * logic is intended + * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array + * of capacity enough to hold up to 8 UACH headers; upon function return will + * contain the output http header names and value const char * pointers either + * to the DATA segment allocated (names) string constants or preallocated buffer + * on the heap. must not be NULL and has to be memory managed outside of the + * function, its lifetime should be long enough to survive the last use of the + * returned headers + * @return the number of headers that was written or should have been written to + * the array in case this number is higher than headers->capacity in the latter + * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY + * status and the returned capacity will signal the array size that needs to be + * allocated */ -EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64(const char *base64, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesKeyValuePairArray * const headers); +EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( + const char *base64, char *buffer, size_t length, + fiftyoneDegreesException *const exception, + fiftyoneDegreesKeyValuePairArray *const headers); /** * Eagerly convert device.sua JSON string to HTTP header representation. * @param json a raw JSON string with device.sua field contents - * @param buffer preallocated working memory buffer used to store the converted HTTP header names and values - * with a new line separator (\n) between each header key-value pair. The lifetime of this buffer is managed by the caller + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object that is filled in case any errors occurred. must be - * checked by the caller upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS if the conversion was successful, - * or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of insufficient size, in that case the callback will still be called, - * but value will be NULL and valueLength will indicate the length necessary to store the value in the buffer - this info can be then used by the - * caller to allocate the buffer of sufficient size and execute another call - essentially resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that case callback will likely not be called, or will be called - * a limited number of times until the corruption becomes obvious to the iterator as no lookahead logic is intended - * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array of capacity enough to hold up to 8 UACH headers; - * upon function return will contain the output http header names and value const char * pointers either to the DATA segment allocated (names) - * string constants or preallocated buffer on the heap. must not be NULL and has to be memory managed outside of the function, its lifetime - * should be long enough to survive the last use of the returned headers - * @return the number of headers that was written or should have been written to the array in case this number is higher than headers->capacity - * in the latter case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status and the returned capacity will signal - * the array size that needs to be allocated + * @param exception - a constant pointer to a (preallocated) exception object + * that is filled in case any errors occurred. must be checked by the caller + * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * if the conversion was successful, or will indicate error otherwise, s.a. + * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * insufficient size, in that case the callback will still be called, but value + * will be NULL and valueLength will indicate the length necessary to store the + * value in the buffer - this info can be then used by the caller to allocate + * the buffer of sufficient size and execute another call - essentially + * resulting in a dry run before allocation... + * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * case callback will likely not be called, or will be called a limited number + * of times until the corruption becomes obvious to the iterator as no lookahead + * logic is intended + * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array + * of capacity enough to hold up to 8 UACH headers; upon function return will + * contain the output http header names and value const char * pointers either + * to the DATA segment allocated (names) string constants or preallocated buffer + * on the heap. must not be NULL and has to be memory managed outside of the + * function, its lifetime should be long enough to survive the last use of the + * returned headers + * @return the number of headers that was written or should have been written to + * the array in case this number is higher than headers->capacity in the latter + * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY + * status and the returned capacity will signal the array size that needs to be + * allocated */ -EXTERNAL size_t fiftyoneDegreesTransformSua(const char *json, - char *buffer, - size_t length, - fiftyoneDegreesException * const exception, - fiftyoneDegreesKeyValuePairArray * const headers); - -// TODO: TDD for iterative style functions -// TODO: C++ wrappers for the non-iterative style functions: hpp + cpp and corresponding tests that are testing CPP wrappers +EXTERNAL size_t +fiftyoneDegreesTransformSua(const char *json, char *buffer, size_t length, + fiftyoneDegreesException *const exception, + fiftyoneDegreesKeyValuePairArray *const headers); #endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ From 78264a674b7988fd9651654bde60403ed4e68289 Mon Sep 17 00:00:00 2001 From: Alexander Popel Date: Mon, 12 Aug 2024 10:43:55 +0300 Subject: [PATCH 36/73] ci: add unified nightly pipeline # Conflicts: # .github/workflows/nightly-pipeline.yml --- .github/workflows/nightly-pipeline.yml | 40 ++++++++++++++++++++++---- .github/workflows/scheduler.yml | 17 +++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/scheduler.yml diff --git a/.github/workflows/nightly-pipeline.yml b/.github/workflows/nightly-pipeline.yml index 4d61f56b..063c737b 100644 --- a/.github/workflows/nightly-pipeline.yml +++ b/.github/workflows/nightly-pipeline.yml @@ -1,7 +1,35 @@ -name: Nightly Pipeline (dummy) -on: workflow_dispatch +name: Nightly Pipeline + +on: + workflow_dispatch: + inputs: + dryrun: + type: boolean + default: false + schedule: + - cron: '5 0 * * *' + jobs: - dummy: - runs-on: ubuntu-latest - steps: - - run: echo "This is a dummy action to allow actions with the same filename to run from other branches" + PullRequests: + name: Nightly Pull Requests + uses: postindustria-tech/common-ci/.github/workflows/nightly-pull-requests.yml@nightly-pipeline + with: + common-ci-ref: nightly-pipeline + repo-name: ${{ github.event.repository.name }} + org-name: ${{ github.event.repository.owner.login }} + dryrun: ${{ inputs.dryrun || false }} + secrets: + token: ${{ secrets.ACCESS_TOKEN }} + + Publish: + if: ${{ !cancelled() }} + needs: PullRequests + name: Nightly Publish + uses: postindustria-tech/common-ci/.github/workflows/nightly-publish.yml@nightly-pipeline + with: + common-ci-ref: nightly-pipeline + repo-name: ${{ github.event.repository.name }} + org-name: ${{ github.event.repository.owner.login }} + dryrun: ${{ inputs.dryrun || false }} + secrets: + token: ${{ secrets.ACCESS_TOKEN }} diff --git a/.github/workflows/scheduler.yml b/.github/workflows/scheduler.yml new file mode 100644 index 00000000..7b79fbae --- /dev/null +++ b/.github/workflows/scheduler.yml @@ -0,0 +1,17 @@ +# Schedules nightly pipeline on non-main branches +name: Nightly Pipeline Scheduler + +on: + schedule: + - cron: '5 0 * * *' + +jobs: + schedule: + runs-on: ubuntu-latest + permissions: + actions: write + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Schedule version/4.5 + run: gh workflow run nightly-pipeline.yml --repo '${{ github.repository }}' --ref version/4.5 From 9293a12cabb92d738fb0c68081a0694e5eb9612e Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Sat, 17 Aug 2024 00:03:45 +0200 Subject: [PATCH 37/73] FIX: vcxproj: add in missing files --- VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj | 2 ++ .../FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters | 2 +- VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj | 2 ++ .../Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj | 5 +++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj index de228a36..a21583b9 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj @@ -37,6 +37,7 @@ + @@ -71,6 +72,7 @@ + diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters index e7d1f12e..7127f334 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj.filters @@ -126,7 +126,7 @@ Header Files - + Header Files diff --git a/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj b/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj index 418756f8..30a7aee5 100644 --- a/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj +++ b/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj @@ -15,6 +15,7 @@ + @@ -34,6 +35,7 @@ + diff --git a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj index cb45d4ce..5b61549c 100644 --- a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj +++ b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj @@ -50,6 +50,8 @@ + + @@ -60,6 +62,9 @@ + + + From 080d88bbad93824268f01fd4b131aad38a29d054 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Sat, 17 Aug 2024 00:03:56 +0200 Subject: [PATCH 38/73] FIX: remove exception from callback signature --- Transform.cpp | 2 +- tests/TransformTests.cpp | 5 ++--- transform.c | 8 +++----- transform.h | 3 +-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index eda27ea7..430ae415 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -29,7 +29,7 @@ Transform::Headers Transform::apiInvoker(CTransformAPI func, } break; case FIFTYONE_DEGREES_STATUS_SUCCESS: { - for (int i = 0; i < headers->count; ++i) { + for (uint32_t i = 0; i < headers->count; ++i) { fiftyoneDegreesKeyValuePair &pair = headers->items[i]; res.emplace(std::string{pair.key, pair.keyLength}, diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 18e6b1b5..a1233040 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -120,8 +120,7 @@ void Transform::checkFieldAbsent(const char *field) { ASSERT_FALSE(found) << "Field " << field << " should be absent"; } -bool fillResultsCallback(void *ctx, fiftyoneDegreesKeyValuePair pair, - fiftyoneDegreesException *const) { +bool fillResultsCallback(void *ctx, fiftyoneDegreesKeyValuePair pair) { fiftyoneDegreesKeyValuePairArray *results = (fiftyoneDegreesKeyValuePairArray *)ctx; @@ -787,7 +786,7 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( + fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, Transform::results); diff --git a/transform.c b/transform.c index f094adbf..5f2a7e69 100644 --- a/transform.c +++ b/transform.c @@ -1131,8 +1131,7 @@ static inline readValueCallback read_value_switch(Key key, int isSua) { return res; } -static bool pushToHeaders(void* ctx, fiftyoneDegreesKeyValuePair header, - fiftyoneDegreesException* const exception) { +static bool pushToHeaders(void* ctx, fiftyoneDegreesKeyValuePair header) { fiftyoneDegreesKeyValuePairArray* const headers = (fiftyoneDegreesKeyValuePairArray* const)ctx; @@ -1144,7 +1143,6 @@ static bool pushToHeaders(void* ctx, fiftyoneDegreesKeyValuePair header, pair->value = header.value; pair->valueLength = header.valueLength; } - return (headers->count < headers->capacity); } // ------------------------------------------------------------------------------------------------ @@ -1196,13 +1194,13 @@ static size_t main_parsing_body(const char* json, char* const buffer, begin = ptr; ++iterations; - if (!callback(ctx, cache[key], exception)) { + if (!callback(ctx, cache[key])) { return iterations; } if (key == PLATFORM && isSua && cache[PLATFORMVERSION].valueLength != 0) { ++iterations; - if (!callback(ctx, cache[PLATFORMVERSION], exception)) { + if (!callback(ctx, cache[PLATFORMVERSION])) { return iterations; } } diff --git a/transform.h b/transform.h index 5c20a464..b66ba3e2 100644 --- a/transform.h +++ b/transform.h @@ -83,8 +83,7 @@ * stop */ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( - void *state, fiftyoneDegreesKeyValuePair header, - Exception *const exception); + void *state, fiftyoneDegreesKeyValuePair header); /** * Iteratively convert getHighEntropyValue() API result JSON string to HTTP From 760bb78f31cb8f603d93dea7c39f4f772972d649 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Sun, 18 Aug 2024 13:45:50 +0200 Subject: [PATCH 39/73] FIX: throw cpp exceptions from Transform wrapper --- Exceptions.cpp | 2 +- Transform.cpp | 1 + tests/TransformTests.cpp | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Exceptions.cpp b/Exceptions.cpp index 51f3a4ac..f236bf4b 100644 --- a/Exceptions.cpp +++ b/Exceptions.cpp @@ -58,7 +58,7 @@ fiftyoneDegreesStatusCode StatusCodeException::getCode() const noexcept { } FatalException::FatalException( - fiftyoneDegreesException *exception) : StatusCodeException() { + fiftyoneDegreesException *exception) : StatusCodeException(exception->status) { const char *exceptionMessage = ExceptionGetMessage(exception); if (exceptionMessage != nullptr) { message.append(exceptionMessage); diff --git a/Transform.cpp b/Transform.cpp index 430ae415..0243ca7d 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -1,3 +1,4 @@ +#include "Exceptions.hpp" #include "Transform.hpp" using namespace FiftyoneDegrees::Common; diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index a1233040..c8bca73c 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -21,8 +21,8 @@ * ********************************************************************* */ #include "../memory.h" -// #include "../transform.h" #include "../Transform.hpp" +#include "../Exceptions.hpp" #include "Base.hpp" class Transform : public Base { @@ -1399,3 +1399,18 @@ TEST_F(Transform, CPPWrapperSUA) { EXPECT_FALSE(h.find("sec-ch-ua") != h.end()); EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } + +TEST_F(Transform, emptycases) { + FiftyoneDegrees::Common::Transform t; + auto result = t.fromJsonGHEV("{}"); + EXPECT_EQ(result.size(), 0); + bool thrown = false; + try { + t.fromJsonGHEV(""); + } catch (FiftyoneDegrees::Common::FatalException e) { + EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + thrown = true; + } + + EXPECT_TRUE(thrown); +} From 89e5494e0125856fd90a18b996257a054c5db0b2 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Sun, 18 Aug 2024 14:30:13 +0200 Subject: [PATCH 40/73] FIX: warnings in tests --- tests/FileTests.cpp | 2 +- tests/TransformTests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FileTests.cpp b/tests/FileTests.cpp index da4a3429..263ecad3 100644 --- a/tests/FileTests.cpp +++ b/tests/FileTests.cpp @@ -820,7 +820,7 @@ TEST_F(File, GetPath) { sizeof(foundPath))) << "The file was not found."; - GET_CURRENT_DIR(absolutePath, sizeof(absolutePath)); + EXPECT_NE(GET_CURRENT_DIR(absolutePath, sizeof(absolutePath)), (char *)NULL); int written = Snprintf(absolutePath + strlen(absolutePath), FIFTYONE_DEGREES_FILE_MAX_PATH - strlen(absolutePath), "/%s", relativePath); if (written < 0 || written >= (int)(FIFTYONE_DEGREES_FILE_MAX_PATH - strlen(absolutePath))) { diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index c8bca73c..fab9bf41 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -1407,7 +1407,7 @@ TEST_F(Transform, emptycases) { bool thrown = false; try { t.fromJsonGHEV(""); - } catch (FiftyoneDegrees::Common::FatalException e) { + } catch (const FiftyoneDegrees::Common::FatalException &e) { EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); thrown = true; } From 5d39d2389786e0e18d897e43b03918896a88e17d Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Mon, 19 Aug 2024 16:23:08 +0200 Subject: [PATCH 41/73] TEST: construct ph from empty or missing evidence --- tests/EvidenceTests.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/EvidenceTests.cpp b/tests/EvidenceTests.cpp index 4c3a3574..18b89857 100644 --- a/tests/EvidenceTests.cpp +++ b/tests/EvidenceTests.cpp @@ -340,6 +340,27 @@ TEST_F(Evidence, IterateForHeaders_ConstructPseudoHeader) { EXPECT_EQ(results[2], "Big\x1FGreen\x1F""Apple"); } +TEST_F(Evidence, IterateForHeaders_ConstructPseudoHeader_Missing_or_Empty) { + const char *headers[] = { + "Taste\x1FObject", //Taste is a missing evidence, this pseudoheader should not be constructed + "Size\x1FTaste", //Taste is a missing evidence, this pseudoheader should not be constructed + "Size\x1F""Color", //Color is going to be empty evidence, so this header is to be constructed + "Size\x1F""Color\x1F""Object", + }; + + headersContainer.CreateHeaders(headers, 4, false); + CreateEvidence(3); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Size", ""); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Color", "Green"); + fiftyoneDegreesEvidenceAddString(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, "Object", "Apple"); + std::vector results; + bool res = fiftyoneDegreesEvidenceIterateForHeaders(evidence, FIFTYONE_DEGREES_EVIDENCE_HTTP_HEADER_STRING, headersContainer.headerPointers, buffer, bufferSize, &results, callback1); + EXPECT_FALSE(res); + EXPECT_EQ(results.size(), 2); + EXPECT_EQ(results[0], "\x1FGreen"); + EXPECT_EQ(results[1], "\x1FGreen\x1F""Apple"); +} + bool callback2(void* state, fiftyoneDegreesHeader* , const char* value, size_t ) { std::vector *results = (std::vector *) state; results->push_back(value); From 17841da56a3b1dbc8ace05fcac8ff987abef2632 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Mon, 19 Aug 2024 17:02:54 +0200 Subject: [PATCH 42/73] FIX: exception is the last parameter in the signature of transform functions --- Transform.cpp | 2 +- Transform.hpp | 4 +- tests/TransformTests.cpp | 147 ++++++++++++++++++++------------------- transform.c | 34 ++++----- transform.h | 63 +++++++++++------ 5 files changed, 137 insertions(+), 113 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index 0243ca7d..e11569eb 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -14,7 +14,7 @@ Transform::Headers Transform::apiInvoker(CTransformAPI func, FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); while (1) { - func(json.c_str(), buffer.data(), buffer.size(), exception, headers); + func(json.c_str(), buffer.data(), buffer.size(), headers, exception); switch (exception->status) { case FIFTYONE_DEGREES_STATUS_CORRUPT_DATA: { diff --git a/Transform.hpp b/Transform.hpp index 0e61764f..7e26afa8 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -15,8 +15,8 @@ class Transform { using CTransformAPI = size_t (*)(const char* base64, char* buffer, size_t length, - fiftyoneDegreesException* const exception, - fiftyoneDegreesKeyValuePairArray* const headers); + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception); Headers apiInvoker(CTransformAPI func, const std::string& json); diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index fab9bf41..6d554d45 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -159,8 +159,8 @@ TEST_F(Transform, GHEVIterativeJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -239,16 +239,16 @@ TEST_F(Transform, IncompleteJSON) { for (const std::string &j : correct) { fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, &Transform::exception, - fillResultsCallback, Transform::results); + j.c_str(), buffer, bufferLength, + fillResultsCallback, Transform::results, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); } for (const std::string &j : corrupted) { fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, &Transform::exception, - fillResultsCallback, Transform::results); + j.c_str(), buffer, bufferLength, + fillResultsCallback, Transform::results, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); } @@ -306,16 +306,16 @@ TEST_F(Transform, IncompleteSUA) { for (const std::string &j : correct) { fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, - &Transform::exception, - fillResultsCallback, Transform::results); + fillResultsCallback, Transform::results, + &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); } for (const std::string &j : corrupted) { fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, - &Transform::exception, - fillResultsCallback, Transform::results); + fillResultsCallback, Transform::results, + &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); } @@ -335,8 +335,8 @@ TEST_F(Transform, GHEVIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -351,8 +351,9 @@ TEST_F(Transform, SUAIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, - &Transform::exception, fillResultsCallback, - Transform::results); + fillResultsCallback, + Transform::results, + &Transform::exception); // --- @@ -373,8 +374,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -407,8 +408,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -442,8 +443,8 @@ TEST_F(Transform, GHEVIterativeBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -483,8 +484,8 @@ TEST_F(Transform, GHEVBase64CorruptedLen) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); @@ -505,8 +506,8 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); @@ -527,8 +528,8 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); @@ -551,8 +552,8 @@ TEST_F(Transform, GHEVIterativeSua) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -593,8 +594,8 @@ TEST_F(Transform, SuaWeirdPlatformVersion) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -636,8 +637,8 @@ TEST_F(Transform, SuaNullBrandPlatform) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -673,7 +674,7 @@ TEST_F(Transform, GHEVArrayJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, &Transform::exception, results); + ghev, buffer, bufferLength, results, &Transform::exception); // --- @@ -714,7 +715,7 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, &Transform::exception, headers); + ghev, buffer, bufferLength, headers, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -724,7 +725,7 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { // --- count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, &Transform::exception, empty_headers); + ghev, buffer, bufferLength, empty_headers, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); @@ -748,7 +749,7 @@ TEST_F(Transform, GHEVBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformGhevFromBase64( - ghev, buffer, bufferLength, &Transform::exception, results); + ghev, buffer, bufferLength, results, &Transform::exception); // --- @@ -787,8 +788,8 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, &Transform::exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); // --- @@ -809,8 +810,8 @@ TEST_F(Transform, GHEVArraySua) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - size_t count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, - &Transform::exception, results); + size_t count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, + &Transform::exception); // --- @@ -850,8 +851,8 @@ TEST_F(Transform, GHEVPartial) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, &exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(count, 4); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -900,8 +901,8 @@ TEST_F(Transform, GHEVIgnoreUnused) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, &exception, fillResultsCallback, - Transform::results); + ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(count, 8); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -946,9 +947,9 @@ TEST_F(Transform, GHEVCorruptInput) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - &exception, fillResultsCallback, - Transform::results); + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -967,9 +968,9 @@ TEST_F(Transform, GHEVBufferTooSmall) { size_t bufferLength = 20; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - &exception, fillResultsCallback, - Transform::results); + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -988,9 +989,9 @@ TEST_F(Transform, GHEVEvidenceLowCapacity) { size_t bufferLength = 20; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - &exception, fillResultsCallback, - Transform::results); + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, + Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1008,8 +1009,8 @@ TEST_F(Transform, SUAHappyPath) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -1058,8 +1059,8 @@ TEST_F(Transform, SUAPlatformExt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(count, 5); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform @@ -1095,12 +1096,12 @@ TEST_F(Transform, SUAPartial1) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(count, 4); // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // low entropy: sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-model, sec-ch-ua-arch, // sec-ch-ua-full-version-list ASSERT_EQ(results->count, count); @@ -1130,8 +1131,8 @@ TEST_F(Transform, SUAPartial2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(count, 5); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -1163,8 +1164,8 @@ TEST_F(Transform, SUATolerableCorrupt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, &exception, fillResultsCallback, - Transform::results); + sua, buffer, bufferLength, fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); ASSERT_EQ(count, 5); @@ -1191,8 +1192,8 @@ TEST_F(Transform, SUACorrupt2) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, - fillResultsCallback, Transform::results); + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + fillResultsCallback, Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -1206,8 +1207,8 @@ TEST_F(Transform, SUACorrupt3) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, - fillResultsCallback, Transform::results); + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + fillResultsCallback, Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -1224,8 +1225,8 @@ TEST_F(Transform, SUABufferTooSmall) { size_t bufferLength = 15; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, - fillResultsCallback, Transform::results); + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + fillResultsCallback, Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1242,8 +1243,8 @@ TEST_F(Transform, SUAEvidenceLowCapacity) { size_t bufferLength = 15; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, &exception, - fillResultsCallback, Transform::results); + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + fillResultsCallback, Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } diff --git a/transform.c b/transform.c index 5f2a7e69..a87c5da9 100644 --- a/transform.c +++ b/transform.c @@ -1215,15 +1215,15 @@ static size_t main_parsing_body(const char* json, char* const buffer, // ------------------------------------------------------------------------------------------------ size_t fiftyoneDegreesTransformIterateGhevFromJson( const char* json, char* const buffer, size_t length, - fiftyoneDegreesException* const exception, - fiftyoneDegreesTransformCallback callback, void* ctx) { + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { return main_parsing_body(json, buffer, length, exception, 0, callback, ctx); } size_t fiftyoneDegreesTransformIterateGhevFromBase64( const char* base64, char* buffer, size_t length, - fiftyoneDegreesException* const exception, - fiftyoneDegreesTransformCallback callback, void* ctx) { + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { size_t offset = base64_decode(base64, buffer, length, exception); if (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY || @@ -1232,23 +1232,23 @@ size_t fiftyoneDegreesTransformIterateGhevFromBase64( } return fiftyoneDegreesTransformIterateGhevFromJson( - buffer, buffer + offset, length > offset ? length - offset : 0, exception, - callback, ctx); + buffer, buffer + offset, length > offset ? length - offset : 0, + callback, ctx, exception); } size_t fiftyoneDegreesTransformIterateSua( const char* json, char* const buffer, size_t length, - fiftyoneDegreesException* const exception, - fiftyoneDegreesTransformCallback callback, void* ctx) { + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { return main_parsing_body(json, buffer, length, exception, 1, callback, ctx); } size_t fiftyoneDegreesTransformGhevFromJson( const char* json, char* buffer, size_t length, - fiftyoneDegreesException* const exception, - fiftyoneDegreesKeyValuePairArray* const headers) { + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception) { size_t calls = fiftyoneDegreesTransformIterateGhevFromJson( - json, buffer, length, exception, pushToHeaders, headers); + json, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count) { exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; @@ -1259,10 +1259,10 @@ size_t fiftyoneDegreesTransformGhevFromJson( size_t fiftyoneDegreesTransformGhevFromBase64( const char* base64, char* buffer, size_t length, - fiftyoneDegreesException* const exception, - fiftyoneDegreesKeyValuePairArray* const headers) { + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception) { size_t calls = fiftyoneDegreesTransformIterateGhevFromBase64( - base64, buffer, length, exception, pushToHeaders, headers); + base64, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count) { exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; @@ -1273,10 +1273,10 @@ size_t fiftyoneDegreesTransformGhevFromBase64( size_t fiftyoneDegreesTransformSua( const char* json, char* buffer, size_t length, - fiftyoneDegreesException* const exception, - fiftyoneDegreesKeyValuePairArray* const headers) { + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception) { size_t calls = fiftyoneDegreesTransformIterateSua( - json, buffer, length, exception, pushToHeaders, headers); + json, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count) { exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; diff --git a/transform.h b/transform.h index b66ba3e2..bb246448 100644 --- a/transform.h +++ b/transform.h @@ -115,10 +115,14 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * @return the number of iterations / header pairs detected (callback calls * made) */ -EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( +EXTERNAL +size_t +fiftyoneDegreesTransformIterateGhevFromJson +( const char *json, char *const buffer, size_t length, - fiftyoneDegreesException *const exception, - fiftyoneDegreesTransformCallback callback, void *state); + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception +); /** * Iteratively convert getHighEntropyValue() API result base64 encoded JSON @@ -151,10 +155,14 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( * @return the number of iterations / header pairs detected (callback calls * made) */ -EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( +EXTERNAL +size_t +fiftyoneDegreesTransformIterateGhevFromBase64 +( const char *base64, char *buffer, size_t length, - fiftyoneDegreesException *const exception, - fiftyoneDegreesTransformCallback callback, void *state); + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception +); /** * Iteratively convert device.sua JSON string to HTTP header representation. @@ -185,10 +193,14 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( * @return the number of iterations / header pairs detected (callback calls * made) */ -EXTERNAL size_t fiftyoneDegreesTransformIterateSua( +EXTERNAL +size_t +fiftyoneDegreesTransformIterateSua +( const char *json, char *buffer, size_t length, - fiftyoneDegreesException *const exception, - fiftyoneDegreesTransformCallback callback, void *state); + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception +); /** * A preallocated array of key-value pairs intended to be an array of HTTP @@ -232,10 +244,14 @@ FIFTYONE_DEGREES_ARRAY_TYPE(fiftyoneDegreesKeyValuePair, NONE); * status and the returned capacity will signal the array size that needs to be * allocated */ -EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( - const char *json, char *buffer, size_t length, - fiftyoneDegreesException *const exception, - fiftyoneDegreesKeyValuePairArray *const headers); +EXTERNAL +size_t +fiftyoneDegreesTransformGhevFromJson +( + const char *json, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray *const headers, + fiftyoneDegreesException *const exception +); /** * Eagerly convert getHighEntropyValue() API result from base64-encoded JSON @@ -273,10 +289,14 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( * status and the returned capacity will signal the array size that needs to be * allocated */ -EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( - const char *base64, char *buffer, size_t length, - fiftyoneDegreesException *const exception, - fiftyoneDegreesKeyValuePairArray *const headers); +EXTERNAL +size_t +fiftyoneDegreesTransformGhevFromBase64 +( + const char *base64, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray *const headers, + fiftyoneDegreesException *const exception +); /** * Eagerly convert device.sua JSON string to HTTP header representation. @@ -313,8 +333,11 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( * allocated */ EXTERNAL size_t -fiftyoneDegreesTransformSua(const char *json, char *buffer, size_t length, - fiftyoneDegreesException *const exception, - fiftyoneDegreesKeyValuePairArray *const headers); +fiftyoneDegreesTransformSua +( + const char *json, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray *const headers, + fiftyoneDegreesException *const exception +); #endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ From 00467e8d04d9587006864820aa7d61049e2eae28 Mon Sep 17 00:00:00 2001 From: loskamo Date: Tue, 20 Aug 2024 17:52:54 +0300 Subject: [PATCH 43/73] FEAT: Transform.cpp test coverage api implementation moved to *.cpp file --- Transform.cpp | 63 ++++---- Transform.hpp | 14 +- tests/TransformTests.cpp | 341 ++++++++++++++++++++++++++++++--------- transform.c | 68 ++++---- 4 files changed, 323 insertions(+), 163 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index e11569eb..7d9d3739 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -1,53 +1,54 @@ -#include "Exceptions.hpp" #include "Transform.hpp" +#include "Exceptions.hpp" + using namespace FiftyoneDegrees::Common; Transform::Transform(size_t capacity) : buffer(capacity) {} Transform::Headers Transform::apiInvoker(CTransformAPI func, - const std::string &json) { + const std::string& json) { Transform::Headers res; EXCEPTION_CREATE; - fiftyoneDegreesKeyValuePairArray *headers = NULL; + fiftyoneDegreesKeyValuePairArray* headers = NULL; FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); - while (1) { - func(json.c_str(), buffer.data(), buffer.size(), headers, exception); + func(json.c_str(), buffer.data(), buffer.size(), headers, exception); - switch (exception->status) { - case FIFTYONE_DEGREES_STATUS_CORRUPT_DATA: { - fiftyoneDegreesFree(headers); - EXCEPTION_THROW; - } break; + while (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { + headers->count = 0; + exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; + buffer.resize(buffer.size() * 2); - case FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY: { - headers->count = 0; - exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; - buffer.resize(buffer.size() * 2); - continue; - } break; + func(json.c_str(), buffer.data(), buffer.size(), headers, exception); + } - case FIFTYONE_DEGREES_STATUS_SUCCESS: { - for (uint32_t i = 0; i < headers->count; ++i) { - fiftyoneDegreesKeyValuePair &pair = headers->items[i]; + if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + fiftyoneDegreesFree(headers); + EXCEPTION_THROW; + } - res.emplace(std::string{pair.key, pair.keyLength}, - std::string{pair.value, pair.valueLength}); - } + for (uint32_t i = 0; i < headers->count; ++i) { + fiftyoneDegreesKeyValuePair& pair = headers->items[i]; - fiftyoneDegreesFree(headers); + res.emplace(std::string{pair.key, pair.keyLength}, + std::string{pair.value, pair.valueLength}); + } - return res; - } break; + fiftyoneDegreesFree(headers); - default: - break; - } + return res; +} - break; - } +Transform::Headers Transform::fromJsonGHEV(const std::string& json) { + return apiInvoker(fiftyoneDegreesTransformGhevFromJson, json); +} - return res; +Transform::Headers Transform::fromBase64GHEV(const std::string& json) { + return apiInvoker(fiftyoneDegreesTransformGhevFromBase64, json); +} + +Transform::Headers Transform::fromSUA(const std::string& json) { + return apiInvoker(fiftyoneDegreesTransformSua, json); } diff --git a/Transform.hpp b/Transform.hpp index 7e26afa8..d52c2879 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -23,17 +23,9 @@ class Transform { public: Transform(size_t capacity = 1024); - Headers fromJsonGHEV(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformGhevFromJson, json); - } - - Headers fromBase64GHEV(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformGhevFromBase64, json); - } - - Headers fromSUA(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformSua, json); - } + Headers fromJsonGHEV(const std::string& json); + Headers fromBase64GHEV(const std::string& json); + Headers fromSUA(const std::string& json); private: std::vector buffer; diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 6d554d45..9d628970 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -20,9 +20,9 @@ * such notice(s) shall fulfill the requirements of that article. * ********************************************************************* */ -#include "../memory.h" -#include "../Transform.hpp" #include "../Exceptions.hpp" +#include "../Transform.hpp" +#include "../memory.h" #include "Base.hpp" class Transform : public Base { @@ -159,8 +159,8 @@ TEST_F(Transform, GHEVIterativeJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -214,14 +214,28 @@ TEST_F(Transform, IncompleteJSON) { "{ \"incomplete_bool\": false", + "{ \"arch\": \"x86\" }", + "{ \"full\" : \"\" } ", + "{ \"min\": -1 }", + "{ \"placebo\": \"___\" }", + "{ \"bind\": true }", + "{ \"break\": true }", + "{ \"mode\": \"default\" }", + "{ \"\": \"empty_key\" }", "{\"bool\": true}", "{\"more\": true}", + "{\"moby\": 1}", "{\"platformer\": 0}", + "{\"platformVer\": 0}", + "{ " + "\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]", }; std::vector corrupted{ @@ -232,23 +246,43 @@ TEST_F(Transform, IncompleteJSON) { "", "{ \"", "{ \"mobile\":", + "{ \"mobile\": t", + "{ \"mobile\": f", "{ \"mo", "{\"a", "{\"brands\":[{\"brand\": \"one\", \"version\":null}}", + "{ \"brands\":", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", + "{ \"brands\":{", + "{ \"brands\":[", + "{ \"brands\":[{", + "{ \"brands\":[{\"bra", + "{ \"brands\":[{\"brand\"", + "{ \"brands\":[{\"brand\":", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"ver", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"}", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", }; for (const std::string &j : correct) { fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, - fillResultsCallback, Transform::results, &Transform::exception); + j.c_str(), buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); } for (const std::string &j : corrupted) { fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, - fillResultsCallback, Transform::results, &Transform::exception); + j.c_str(), buffer, bufferLength, fillResultsCallback, + Transform::results, &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); } @@ -260,6 +294,8 @@ TEST_F(Transform, IncompleteSUA) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); std::vector correct{ + "{\"browsers\":[{\"brand\": \"\", \"version\": [\"\\\"123\\\\\"]}]}", + "{ \"key_without_value\" }", "{ \"key_without_value\": ", @@ -290,8 +326,18 @@ TEST_F(Transform, IncompleteSUA) { "{\"bool\": true}", + "{ \"arch\": \"x86\" }", + "{ \"full\" : \"\" } ", + "{ \"min\": -1 }", + "{ \"placebo\": \"___\" }", + "{ \"bind\": true }", + "{ \"break\": true }", + "{ \"mode\": \"default\" }", + "{\"more\": true}", "{\"browsers\":[{\"brand\": null}]}", + "{\"platformer\": 0}", + "{\"platformVer\": 0}", }; std::vector corrupted{ @@ -300,8 +346,37 @@ TEST_F(Transform, IncompleteSUA) { "{\"a", "{ \"mo", "{ \"mobile\":", - "{\"platformer\": 0}", "{\"browsers\":[{\"brand\": null}}}", + + "{\n \"browsers\"", + "{\n \"browsers\":{", + "{\n \"browsers\":[", + "{\n \"browsers\":[{", + "{\n \"browsers\":[{\"", + "{\n \"browsers\":[{\"bra", + "{\n \"browsers\":[{\"brand", + "{\n \"browsers\":[{\"brand\"", + "{\n \"browsers\":[{\"brand\":", + "{\n \"browsers\":[{\"brand\":\"", + "{\n \"browsers\":[{\"brand\":\"google", + "{\n \"browsers\":[{\"brand\":\"google\"", + "{\n \"browsers\":[{\"brand\":\"google\",", + "{\n \"browsers\":[{\"brand\":\"google\",\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"ver", + "{\n \"browsers\":[{\"brand\":\"google\",\"version", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]}", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]},", }; for (const std::string &j : correct) { @@ -335,8 +410,8 @@ TEST_F(Transform, GHEVIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -351,8 +426,7 @@ TEST_F(Transform, SUAIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, - fillResultsCallback, - Transform::results, + fillResultsCallback, Transform::results, &Transform::exception); // --- @@ -374,8 +448,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -408,8 +482,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -443,8 +517,8 @@ TEST_F(Transform, GHEVIterativeBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -484,8 +558,8 @@ TEST_F(Transform, GHEVBase64CorruptedLen) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); @@ -506,8 +580,8 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); @@ -528,8 +602,8 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); @@ -552,8 +626,8 @@ TEST_F(Transform, GHEVIterativeSua) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -594,8 +668,8 @@ TEST_F(Transform, SuaWeirdPlatformVersion) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -637,8 +711,8 @@ TEST_F(Transform, SuaNullBrandPlatform) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -735,6 +809,67 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { fiftyoneDegreesFree(empty_headers); } +TEST_F(Transform, GHEVBase64InsufficientCapacity) { + const char *ghev = + "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" + "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" + "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" + "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" + "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" + "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" + "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; + + size_t bufferLength = strlen(ghev) * 3; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + + size_t count = fiftyoneDegreesTransformGhevFromBase64( + ghev, buffer, bufferLength, empty_headers, &Transform::exception); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_EQ(count, 1); + + fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(empty_headers); +} + +TEST_F(Transform, SUAInsufficientCapacity) { + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + + size_t count = fiftyoneDegreesTransformSua( + sua, buffer, bufferLength, empty_headers, &Transform::exception); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_EQ(count, 1); + + fiftyoneDegreesKeyValuePairArray *headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); + + count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, + &Transform::exception); + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + ASSERT_EQ(count, 3); + + fiftyoneDegreesFree(headers); + fiftyoneDegreesFree(empty_headers); + fiftyoneDegreesFree(buffer); +} + TEST_F(Transform, GHEVBase64) { const char *ghev = "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" @@ -788,8 +923,31 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); +} + +TEST_F(Transform, SUANotEnoughMemory) { + const char *sua = + "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," + "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; + + size_t bufferLength = 144; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformIterateSua( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &Transform::exception); // --- @@ -836,6 +994,27 @@ TEST_F(Transform, GHEVArraySua) { fiftyoneDegreesFree(buffer); } +TEST_F(Transform, SUAArrayNotEnoughMemory) { + const char *sua = + "{\"source\":2,\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; + + size_t bufferLength = 142; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, + &Transform::exception); + + // --- + + ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); +} + TEST_F(Transform, GHEVPartial) { const char *ghev = "{\"brands\":[{\"brand\":\"Not/" @@ -851,8 +1030,8 @@ TEST_F(Transform, GHEVPartial) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(count, 4); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -901,8 +1080,8 @@ TEST_F(Transform, GHEVIgnoreUnused) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(count, 8); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -947,9 +1126,9 @@ TEST_F(Transform, GHEVCorruptInput) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, - &exception); + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -968,9 +1147,9 @@ TEST_F(Transform, GHEVBufferTooSmall) { size_t bufferLength = 20; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, - &exception); + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -989,9 +1168,9 @@ TEST_F(Transform, GHEVEvidenceLowCapacity) { size_t bufferLength = 20; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, - &exception); + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + fillResultsCallback, + Transform::results, &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1009,8 +1188,8 @@ TEST_F(Transform, SUAHappyPath) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -1059,8 +1238,8 @@ TEST_F(Transform, SUAPlatformExt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(count, 5); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform @@ -1096,8 +1275,8 @@ TEST_F(Transform, SUAPartial1) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(count, 4); // we expect to see these headers detected: // low entropy: sec-ch-ua-mobile, sec-ch-ua-platform @@ -1131,8 +1310,8 @@ TEST_F(Transform, SUAPartial2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(count, 5); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); @@ -1164,8 +1343,8 @@ TEST_F(Transform, SUATolerableCorrupt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); ASSERT_EQ(count, 5); @@ -1193,7 +1372,8 @@ TEST_F(Transform, SUACorrupt2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, &exception); + fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -1208,7 +1388,8 @@ TEST_F(Transform, SUACorrupt3) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, &exception); + fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -1226,7 +1407,8 @@ TEST_F(Transform, SUABufferTooSmall) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, &exception); + fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1244,7 +1426,8 @@ TEST_F(Transform, SUAEvidenceLowCapacity) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, &exception); + fillResultsCallback, Transform::results, + &exception); ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1310,18 +1493,17 @@ TEST_F(Transform, CPPWrapperBase64) { ASSERT_EQ(h["sec-ch-ua"], "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); + "Chrome\";v=\"126\""); ASSERT_EQ(h["sec-ch-ua-full-version-list"], "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); + "\"Google Chrome\";v=\"126.0.6478.127\""); ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } @@ -1349,18 +1531,17 @@ TEST_F(Transform, CPPWrapperBase64InsufficientMemory) { ASSERT_EQ(h["sec-ch-ua"], "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); + "Chrome\";v=\"126\""); ASSERT_EQ(h["sec-ch-ua-full-version-list"], "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); + "\"Google Chrome\";v=\"126.0.6478.127\""); ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } @@ -1386,14 +1567,14 @@ TEST_F(Transform, CPPWrapperSUA) { EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - ASSERT_EQ(h["sec-ch-ua-arch"], "\"x86\""); ASSERT_EQ(h["sec-ch-ua-model"], "\"MacBook\""); ASSERT_EQ(h["sec-ch-ua-mobile"], "?1"); ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - ASSERT_EQ(h["sec-ch-ua-full-version-list"], - "\"Not/" + ASSERT_EQ( + h["sec-ch-ua-full-version-list"], + "\"Not/" "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " "Chrome\";v=\"126.0.6478.127\""); @@ -1402,16 +1583,16 @@ TEST_F(Transform, CPPWrapperSUA) { } TEST_F(Transform, emptycases) { - FiftyoneDegrees::Common::Transform t; - auto result = t.fromJsonGHEV("{}"); - EXPECT_EQ(result.size(), 0); - bool thrown = false; - try { - t.fromJsonGHEV(""); - } catch (const FiftyoneDegrees::Common::FatalException &e) { - EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); - thrown = true; - } + FiftyoneDegrees::Common::Transform t; + auto result = t.fromJsonGHEV("{}"); + EXPECT_EQ(result.size(), 0); + bool thrown = false; + try { + t.fromJsonGHEV(""); + } catch (const FiftyoneDegrees::Common::FatalException &e) { + EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + thrown = true; + } - EXPECT_TRUE(thrown); + EXPECT_TRUE(thrown); } diff --git a/transform.c b/transform.c index a87c5da9..1a5c7748 100644 --- a/transform.c +++ b/transform.c @@ -36,6 +36,12 @@ exit; \ } +#define ExpectKeySymbol(json, ch) \ + if (*json != ch) { \ + json = skip_to_next_char(json, '"'); \ + return KEY_UNDEFINED; \ + } + #define ValuePtr \ (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY ? NULL \ : begin) @@ -374,8 +380,7 @@ static Key read_ghev_key(const char** json, case ARCH: { for (const char* i = "rchitecture"; *i != '\0'; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return ARCHITECTURE; @@ -383,8 +388,7 @@ static Key read_ghev_key(const char** json, case FULL_VERSION_LIST: { for (const char* i = "ullVersionList"; *i != '\0'; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return FULLVERSIONLIST; @@ -408,8 +412,7 @@ static Key read_ghev_key(const char** json, } break; case MOBILE_OR_MODEL: { - ExpectSymbol(*json, 'o', *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, 'o'); ++(*json); NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); @@ -432,8 +435,7 @@ static Key read_ghev_key(const char** json, case PLATFORM_OR_VERSION: { for (const char* i = "latform"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } switch (**json) { @@ -454,8 +456,7 @@ static Key read_ghev_key(const char** json, case READ_KEY_BRANDS: { for (const char* i = "ands"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return BRANDS; @@ -463,8 +464,7 @@ static Key read_ghev_key(const char** json, case READ_KEY_BITNESS: { for (const char* i = "tness"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return BITNESS; @@ -472,8 +472,7 @@ static Key read_ghev_key(const char** json, case READ_KEY_MOBILE: { for (const char* i = "ile"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return MOBILE; @@ -481,8 +480,7 @@ static Key read_ghev_key(const char** json, case READ_KEY_MODEL: { for (const char* i = "el"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return MODEL; @@ -490,8 +488,7 @@ static Key read_ghev_key(const char** json, case READ_KEY_PLATFORMVERSION: { for (const char* i = "ersion"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return PLATFORMVERSION; @@ -552,8 +549,7 @@ static Key read_sua_key(const char** json, case ARCH: { for (const char* i = "rchitecture"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return ARCHITECTURE; @@ -577,8 +573,7 @@ static Key read_sua_key(const char** json, } break; case MOBILE_OR_MODEL: { - ExpectSymbol(*json, 'o', *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, 'o'); ++(*json); NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); @@ -601,17 +596,17 @@ static Key read_sua_key(const char** json, case READ_KEY_PLATFORM: { for (const char* i = "latform"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, ':'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } - return **json == '\"' ? PLATFORM : KEY_UNDEFINED; + ExpectKeySymbol(*json, '"'); + + return PLATFORM; } break; case READ_KEY_BROWSERS: { for (const char* i = "owsers"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return FULLVERSIONLIST; @@ -619,8 +614,7 @@ static Key read_sua_key(const char** json, case READ_KEY_BITNESS: { for (const char* i = "tness"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return BITNESS; @@ -628,8 +622,7 @@ static Key read_sua_key(const char** json, case READ_KEY_MOBILE: { for (const char* i = "ile"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return MOBILE; @@ -637,8 +630,7 @@ static Key read_sua_key(const char** json, case READ_KEY_MODEL: { for (const char* i = "el"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, *json = skip_to_next_char(*json, '"'); - return KEY_UNDEFINED); + ExpectKeySymbol(*json, *i); } return MODEL; @@ -683,7 +675,6 @@ static char* read_string_value(const char** json, char* begin, if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - NotExpectSymbol(*json, '\0', return begin); begin = safe_write_to_buffer(begin, end, **json, exception); } } break; @@ -780,8 +771,6 @@ static char* read_version_sua(const char** json, char* begin, if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - NotExpectSymbol(*json, '\0', return begin); - ptr = safe_write_to_buffer(ptr, end, **json, exception); } } break; @@ -826,8 +815,6 @@ static char* read_brands_ghev_value(const char** json, char* begin, ++*json; for (char* ptr = begin;; ++*json) { - NotExpectSymbol(*json, '\0', return begin); - *json = skip_to_next_char(*json, '{'); ExpectSymbol(*json, '{', return begin); @@ -844,7 +831,6 @@ static char* read_brands_ghev_value(const char** json, char* begin, ExpectSymbol(*json, ':', return begin); ++*json; - NotExpectSymbol(*json, '\0', return begin); char* ptr2 = read_string_value(json, ptr, end, exception); if (ptr2 != NULL) { @@ -1232,8 +1218,8 @@ size_t fiftyoneDegreesTransformIterateGhevFromBase64( } return fiftyoneDegreesTransformIterateGhevFromJson( - buffer, buffer + offset, length > offset ? length - offset : 0, - callback, ctx, exception); + buffer, buffer + offset, length - offset, callback, + ctx, exception); } size_t fiftyoneDegreesTransformIterateSua( From e3a0d194d014cdf44a3f095c429737826c5f858d Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Sun, 25 Aug 2024 00:28:53 +0200 Subject: [PATCH 44/73] FEAT: cmake: build tests iff BUILD_TESTING enabled # Conflicts: # CMakeLists.txt --- CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index baa1ecb9..f4a3fa35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,21 +143,21 @@ else () endif() endif() -# Examples - -add_executable(CachePerf ${CMAKE_CURRENT_LIST_DIR}/performance/CachePerf.c) -target_link_libraries(CachePerf fiftyone-common-c) -if (MSVC) - target_compile_options(CachePerf PRIVATE "/D_CRT_SECURE_NO_WARNINGS" "/W4" "/WX") - target_link_options(CachePerf PRIVATE "/WX") -else () - target_compile_options(CachePerf PRIVATE ${COMPILE_OPTION_DEBUG} "-Werror") -endif() -set_target_properties(CachePerf PROPERTIES FOLDER "Examples/Common") - # Tests +# BUILD_TESTING is true by default, but in some cases it is desirable to turn off if(BUILD_TESTING) + # Examples + add_executable(CachePerf ${CMAKE_CURRENT_LIST_DIR}/performance/CachePerf.c) + target_link_libraries(CachePerf fiftyone-common-c) + if (MSVC) + target_compile_options(CachePerf PRIVATE "/D_CRT_SECURE_NO_WARNINGS" "/W4" "/WX") + target_link_options(CachePerf PRIVATE "/WX") + else () + target_compile_options(CachePerf PRIVATE ${COMPILE_OPTION_DEBUG} "-Werror") + endif() + set_target_properties(CachePerf PROPERTIES FOLDER "Examples/Common") + # Download and unpack googletest at configure time configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in googletest-download/CMakeLists.txt) execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . From 8c23e1fae46c56be11718c3c5e7d4b5478f5aef8 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Sun, 25 Aug 2024 11:47:16 +0200 Subject: [PATCH 45/73] FIX: order of headers in Transform.cpp to throw C++ exceptions --- Transform.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index 7d9d3739..566629a6 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -1,6 +1,5 @@ -#include "Transform.hpp" - #include "Exceptions.hpp" +#include "Transform.hpp" using namespace FiftyoneDegrees::Common; From 9bc7a22247dbd77533a4db81b77463b1ede6cedc Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Mon, 26 Aug 2024 00:02:48 +0200 Subject: [PATCH 46/73] FIX: /EHsc flag for MSVC to suppress warning --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4a3fa35..81ccdaa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,7 +125,7 @@ target_link_libraries(fiftyone-common-cxx fiftyone-common-c) set_target_properties(fiftyone-common-c fiftyone-common-cxx PROPERTIES FOLDER "Common") if (MSVC) target_compile_options(fiftyone-common-c PRIVATE "/D_CRT_SECURE_NO_WARNINGS" "/W4" "/WX") - target_compile_options(fiftyone-common-cxx PRIVATE "/D_CRT_SECURE_NO_WARNINGS" "/W4" "/WX") + target_compile_options(fiftyone-common-cxx PRIVATE "/D_CRT_SECURE_NO_WARNINGS" "/W4" "/WX" "/EHsc") else () target_compile_options(fiftyone-common-c PRIVATE ${COMPILE_OPTION_DEBUG} "-Werror" "-Wno-atomic-alignment") target_compile_options(fiftyone-common-cxx PRIVATE ${COMPILE_OPTION_DEBUG} "-Werror") From f4df68379d785e9b337c4903aa5e7f47fedd831f Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Tue, 27 Aug 2024 12:53:29 +0200 Subject: [PATCH 47/73] FEAT: json.c test coverage FIX: JsonTests warnings on windows --- json.c | 34 ++++--- json.h | 4 +- tests/JsonTests.cpp | 214 +++++++++++++++++++++++++++++++++++++++++ tests/StringsTests.cpp | 27 ++++-- 4 files changed, 257 insertions(+), 22 deletions(-) create mode 100644 tests/JsonTests.cpp diff --git a/json.c b/json.c index 4fa006a9..ed6781e1 100644 --- a/json.c +++ b/json.c @@ -25,8 +25,8 @@ // Add two characters to the buffer if space is available. static void addTwo(fiftyoneDegreesJson* s, char a, char b) { - StringBuilderAddChar(&s->buffer, a); - StringBuilderAddChar(&s->buffer, b); + StringBuilderAddChar(&s->builder, a); + StringBuilderAddChar(&s->builder, b); } // Adds a string of characters escaping special characters. @@ -55,7 +55,7 @@ static void addStringEscape( addTwo(s, '\\', 't'); break; default: - StringBuilderAddChar(&s->buffer, value[i]); + StringBuilderAddChar(&s->builder, value[i]); break; } } @@ -67,24 +67,24 @@ static void addString( fiftyoneDegreesJson* s, const char* value, size_t length) { - StringBuilderAddChar(&s->buffer, '\"'); + StringBuilderAddChar(&s->builder, '\"'); addStringEscape(s, value, length); - StringBuilderAddChar(&s->buffer, '\"'); + StringBuilderAddChar(&s->builder, '\"'); } // Adds a comma separator. static void addSeparator(fiftyoneDegreesJson* s) { - StringBuilderAddChar(&s->buffer, ','); + StringBuilderAddChar(&s->builder, ','); } void fiftyoneDegreesJsonDocumentStart(fiftyoneDegreesJson* s) { - StringBuilderInit(&s->buffer); - StringBuilderAddChar(&s->buffer, '{'); + StringBuilderInit(&s->builder); + StringBuilderAddChar(&s->builder, '{'); } void fiftyoneDegreesJsonDocumentEnd(fiftyoneDegreesJson* s) { - StringBuilderAddChar(&s->buffer, '}'); - StringBuilderComplete(&s->buffer); + StringBuilderAddChar(&s->builder, '}'); + StringBuilderComplete(&s->builder); } void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* s) { @@ -116,9 +116,9 @@ void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s) { // it's a list or single value property. const char* value = FIFTYONE_DEGREES_STRING(name); addString(s, value, strlen(value)); - StringBuilderAddChar(&s->buffer, ':'); + StringBuilderAddChar(&s->builder, ':'); if (s->property->isList) { - StringBuilderAddChar(&s->buffer, '['); + StringBuilderAddChar(&s->builder, '['); } // Release the string. @@ -127,8 +127,14 @@ void fiftyoneDegreesJsonPropertyStart(fiftyoneDegreesJson* s) { } void fiftyoneDegreesJsonPropertyEnd(fiftyoneDegreesJson* s) { + if (s->property == NULL) { + fiftyoneDegreesException* exception = s->exception; + FIFTYONE_DEGREES_EXCEPTION_SET( + FIFTYONE_DEGREES_STATUS_NULL_POINTER) + return; + } if (s->property->isList) { - StringBuilderAddChar(&s->buffer, ']'); + StringBuilderAddChar(&s->builder, ']'); } } @@ -153,4 +159,4 @@ void fiftyoneDegreesJsonPropertyValues(fiftyoneDegreesJson* s) { addString(s, value, strlen(value)); } } -} \ No newline at end of file +} diff --git a/json.h b/json.h index 01f25181..9770651d 100644 --- a/json.h +++ b/json.h @@ -77,7 +77,7 @@ * to determine when the buffer provided needs to be larger. */ typedef struct fiftyone_degrees_json { - fiftyoneDegreesStringBuilder buffer; /**< Output buffer */ + fiftyoneDegreesStringBuilder builder; /**< Output buffer */ fiftyoneDegreesCollection* strings; /**< Collection of strings */ fiftyoneDegreesProperty* property; /**< The property being added */ fiftyoneDegreesList* values; /**< The values for the property */ @@ -124,4 +124,4 @@ EXTERNAL void fiftyoneDegreesJsonPropertySeparator(fiftyoneDegreesJson* json); * @} */ -#endif \ No newline at end of file +#endif diff --git a/tests/JsonTests.cpp b/tests/JsonTests.cpp new file mode 100644 index 00000000..c8f57bd8 --- /dev/null +++ b/tests/JsonTests.cpp @@ -0,0 +1,214 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#include "Base.hpp" +#include "StringCollection.hpp" +#include "FixedSizeCollection.hpp" +#include "../fiftyone.h" + +class JsonTests : public Base { +public: + JsonTests(); + virtual ~JsonTests(); + void CreateObjects(); + + StringCollection *stringsCollectionHelper; + FixedSizeCollection *propertiesCollectionHelper; + FixedSizeCollection *valuesCollectionHelper; + fiftyoneDegreesCollection *stringsCollection; + fiftyoneDegreesCollection *propertiesCollection; + fiftyoneDegreesCollection *valuesCollection; + + fiftyoneDegreesCollectionItem item; + + static constexpr size_t BUFFER_SIZE = 512; + static constexpr int N_PROPERTIES = 15; + static constexpr int N_PER_PROPERTY = 6; + static const char *strings[N_PROPERTIES * (N_PER_PROPERTY + 1)]; +}; + +const char *JsonTests::strings[] = { + "Brightness", "Bright", "Dim", "Dull", "Glowing", "Shiny", //0 + "Color", "Black", "Blue", "Green", "Red", "Yellow", //6 + "Condition", "\"Bro\\ken\"", "New", "Old", "Pristine", "Worn", //12 + "Flexibility", "\t\r\f\bBendable\n", "Flexible", "Pliable", "Rigid", "Stiff", //18 - some special characters + "Material", "Fabric", "Glass", "Metal", "Plastic", "Wood", //24 + "Opacity", "Opaque", "Semi-opaque", "Semi-transparent", "Translucent", "Transparent", //30 + "Pattern", "Checkered", "Dotted", "Floral", "Plain", "Striped", //36 + "Position", "Diagonal", "Horizontal", "Tilted", "Upside-down", "Vertical", //42 + "Shape", "Oval", "Rectangular", "Round", "Square", "Triangular", //48 + "Size", "Enormous", "Large", "Medium", "Small", "Tiny", //54 + "Taste", "Bitter", "Salty", "Savory", "Sour", "Sweet", //60 + "Temperature", "Cold", "Cool", "Freezing", "Hot", "Warm", //66 + "Texture", "Hard", "Rough", "Smooth", "Soft", "Sticky", //72 + "Volume", "Loud", "Moderate", "Mute", "Quiet", "Silent", //78 + "Weight", "Dense", "Heavy", "Light", "Lightweight", "Moderate" //84 +}; + +JsonTests::JsonTests() { + CreateObjects(); +} + +JsonTests::~JsonTests() { + delete propertiesCollectionHelper; + delete valuesCollectionHelper; + delete stringsCollectionHelper; +} + +void JsonTests::CreateObjects() { + stringsCollectionHelper = new StringCollection(strings, sizeof(strings)/sizeof(strings[0])); + stringsCollection = stringsCollectionHelper->getState()->collection; + uint32_t *offsets = stringsCollectionHelper->getState()->offsets; + + std::vector values; + for (int i=0;i(values); + valuesCollection = valuesCollectionHelper->getState()->collection; + + std::vector tempProperties; + for (byte i=0;i(tempProperties); + propertiesCollection = propertiesCollectionHelper->getState()->collection; +} + +TEST_F(JsonTests, basicJsonForming) { + FIFTYONE_DEGREES_EXCEPTION_CREATE; + fiftyoneDegreesStringBuilder builder {(char * const)fiftyoneDegreesMalloc(BUFFER_SIZE), BUFFER_SIZE}; + + fiftyoneDegreesJson json { + builder, /**< Output buffer */ + stringsCollection, /**< Collection of strings */ + NULL, /**< The property being added */ + NULL, /**< The values for the property */ + exception /**< Exception */ + }; + + fiftyoneDegreesJsonDocumentStart(&json); + for (uint32_t propIdx = 0; propIdx < 4; ++propIdx) { + fiftyoneDegreesProperty *property = fiftyoneDegreesPropertyGet(propertiesCollection, propIdx, &item, exception); + fiftyoneDegreesList valuesList; + fiftyoneDegreesListInit(&valuesList, 4); + + fiftyoneDegreesCollectionItem valueItem; + fiftyoneDegreesCollectionItem valueItem2; + + fiftyoneDegreesValue *value = fiftyoneDegreesValueGet(valuesCollection, propIdx * (N_PER_PROPERTY - 1), &valueItem, exception); + fiftyoneDegreesValueGetName(stringsCollection, value, &valueItem, exception); + fiftyoneDegreesListAdd(&valuesList, &valueItem); + + if (property->isList) { + fiftyoneDegreesValue *value2 = fiftyoneDegreesValueGet(valuesCollection, propIdx * (N_PER_PROPERTY - 1) + 1, &valueItem2, exception); + fiftyoneDegreesValueGetName(stringsCollection, value2, &valueItem2, exception); + fiftyoneDegreesListAdd(&valuesList, &valueItem2); + } + + json.property = property; + json.values = &valuesList; + + if (propIdx > 0) { + fiftyoneDegreesJsonPropertySeparator(&json); + } + + fiftyoneDegreesJsonPropertyStart(&json); + fiftyoneDegreesJsonPropertyValues(&json); + fiftyoneDegreesJsonPropertyEnd(&json); + fiftyoneDegreesListFree(&valuesList); + } + + fiftyoneDegreesJsonDocumentEnd(&json); + + EXPECT_STREQ(json.builder.ptr, "{\"Brightness\":\"Bright\",\"Color\":[\"Black\",\"Blue\"],\"Condition\":\"\\\"Bro\\ken\\\"\",\"Flexibility\":\"\\t\\r\\f\\bBendable\\n\"}"); + + fiftyoneDegreesFree(builder.ptr); +} + +TEST_F(JsonTests, unhappyPaths) { + FIFTYONE_DEGREES_EXCEPTION_CREATE; + fiftyoneDegreesStringBuilder builder {(char * const)fiftyoneDegreesMalloc(BUFFER_SIZE), BUFFER_SIZE}; + + fiftyoneDegreesJson json { + builder, /**< Output buffer */ + stringsCollection, /**< Collection of strings */ + NULL, /**< The property being added */ + NULL, /**< The values for the property */ + exception /**< Exception */ + }; + { + EXCEPTION_CLEAR; + fiftyoneDegreesJsonDocumentStart(&json); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NOT_SET); + + EXCEPTION_CLEAR; + fiftyoneDegreesJsonPropertyStart(&json); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NULL_POINTER); + + EXCEPTION_CLEAR; + fiftyoneDegreesJsonPropertyValues(&json); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NULL_POINTER); + + EXCEPTION_CLEAR; + fiftyoneDegreesJsonPropertyEnd(&json); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NULL_POINTER); + + EXCEPTION_CLEAR; + fiftyoneDegreesJsonDocumentEnd(&json); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NOT_SET); + + EXPECT_STREQ(builder.ptr, "{}"); + } + + fiftyoneDegreesFree(builder.ptr); +} diff --git a/tests/StringsTests.cpp b/tests/StringsTests.cpp index bdba592b..fd015b6b 100644 --- a/tests/StringsTests.cpp +++ b/tests/StringsTests.cpp @@ -1,9 +1,24 @@ -// -// StringsTests.cpp -// CommonTests -// -// Created by Eugene Dorfman on 8/5/24. -// +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ #include "../fiftyone.h" #include "../string.h" From d4074728586f9f433b7baaf60c04d1b4d1281306 Mon Sep 17 00:00:00 2001 From: loskamo Date: Tue, 20 Aug 2024 14:42:20 +0300 Subject: [PATCH 48/73] FEAT: interop/dotnet: codegen, swig wrappers, example FEAT: interop Transform generate a default ctor DOC: Transform.hpp, interop approach Co-authored-by: Eugene Dorfman --- .gitignore | 12 + README.md | 31 +- Transform.cpp | 22 + Transform.hpp | 103 +- Transform.i | 34 + common.i | 32 + commonCSHARP_wrap.cxx | 1288 +++++++++++++++++ interop/dotnet/CMakeLists.txt | 94 ++ .../dotnet/FiftyoneDegrees.Common/Common.cs | 16 + .../FiftyoneDegrees.Common/CommonPINVOKE.cs | 338 +++++ .../FiftyoneDegrees.Common.csproj | 17 + .../FiftyoneDegrees.Common.sln | 25 + .../MapStringStringSwig.cs | 313 ++++ .../FiftyoneDegrees.Common/Transform.cs | 86 ++ .../VectorStringSwig.cs | 374 +++++ .../example/TransformExample/Program.cs | 45 + .../TransformExample/TransformExample.csproj | 14 + .../TransformExample/TransformExample.sln | 31 + 18 files changed, 2852 insertions(+), 23 deletions(-) create mode 100644 Transform.i create mode 100644 common.i create mode 100644 commonCSHARP_wrap.cxx create mode 100644 interop/dotnet/CMakeLists.txt create mode 100644 interop/dotnet/FiftyoneDegrees.Common/Common.cs create mode 100644 interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs create mode 100644 interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj create mode 100644 interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln create mode 100644 interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs create mode 100644 interop/dotnet/FiftyoneDegrees.Common/Transform.cs create mode 100644 interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs create mode 100644 interop/dotnet/example/TransformExample/Program.cs create mode 100644 interop/dotnet/example/TransformExample/TransformExample.csproj create mode 100644 interop/dotnet/example/TransformExample/TransformExample.sln diff --git a/.gitignore b/.gitignore index a0b7c633..266f1701 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,15 @@ googletest*/ *.CopyComplete *.iobj *.ipdb +**/Debug +**/Release +**/bin +**/CMakeFiles/ +**/CMakeCache.txt +**/*.cmake +**/DartConfiguration.tcl +**/CMakeScripts/ +**/*.ninja* +**/obj +**/*.dll +**/.vs diff --git a/README.md b/README.md index f08db6ab..ed177329 100644 --- a/README.md +++ b/README.md @@ -154,4 +154,33 @@ mkdir -p coverage; gcovr --html-detail -o coverage/coverage.html; open coverage/ To check the summary run: ``` gcovr -r . --print-summary -``` \ No newline at end of file +``` + +# Interoperability + +Under interoperability we understand the ability to use (explicitly) exposed and wrapped `common-cxx` code directly from higher-level languages. For now C# is the primary target. + +## Approach + +- SWIG interface definitions are in the `common.i` (and included files s.a. `Transform.i`). +- There is an `interop/dotnet` directory containing: + - `CMakeLists.txt` file that would update SWIG generated wrapper files if SWIG is installed and build the native shared library exposing SWIG generated native interfaces + - The generated SWIG .cs (C#) wrapper files are output into the `interop/dotnet/FiftyoneDegrees.Common` directory that contains a project that one can reference in their C# project to use the native common-cxx routines + - The usage example is provided as part of the `examples/TransformExample` + +## Integration + +C# integration is as simple as adding `interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj` into your solution - so it becomes a project reference dependency. Then you can refer to the exposed APIs in C# +by first `using FiftyoneDegrees.Common` namespace and then calling APIs s.a. f.e.: + +- `Transform` class +- `MapStringStringSwig` - a simple Dictionary like container + +The `.csproj` includes a call to cmake to pre-build a native DLL and then copies that artifact into the output directory as needed. This integration is demonstrated as parrt of the `interop/dotnet/examples/TransformExample`. + +You can simply navigate into that directoy and run `dotnet run`. + +**Note:** if you are using Visual Studio - sometimes it runs the pre-build targets behind the scenes - so it will invoke +`cmake` and your manual build will invoke it again - and concurrent cmake builds may cause some errors in the +build process. They usually are resolved by closing Visual Studio, running `git clean -fdx` in the `common-cxx` +directory (so all CMake caches are removed), then doing a `dotnet run` once, so cmake creates correct artifacts. diff --git a/Transform.cpp b/Transform.cpp index 566629a6..7b7a8af9 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -1,3 +1,25 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + #include "Exceptions.hpp" #include "Transform.hpp" diff --git a/Transform.hpp b/Transform.hpp index d52c2879..928c0c69 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -1,3 +1,25 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + #ifndef FIFTYONE_DEGREES_TRANSFORM_HPP #define FIFTYONE_DEGREES_TRANSFORM_HPP @@ -7,30 +29,67 @@ #include "transform.h" -namespace FiftyoneDegrees { -namespace Common { - -class Transform { - using Headers = std::map; - - using CTransformAPI = - size_t (*)(const char* base64, char* buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception); - Headers apiInvoker(CTransformAPI func, const std::string& json); +/** + * A C++ wrapper class for transform.h conversion routines. + * Encapsulates a working memory buffer to be reused when invoking conversion functions. The methods of a single instance can be invoked + * however many times synchronously / serially on a single thread. + * The instance is not thread safe, for access to the working buffer memory is not synchronized. + * Thus to be used in a multi-threaded setting - an instance per thread must be created. This is also true for the SWIG-generated + * wrappers of this class. + * + * Note the return values of the functions are copied value-types (map) - thus memory in the internal buffer is safely + * overwritten without harming the previously returned results - which is ideal for the (memory-)managed languages s.a. C#. + */ - public: - Transform(size_t capacity = 1024); - - Headers fromJsonGHEV(const std::string& json); - Headers fromBase64GHEV(const std::string& json); - Headers fromSUA(const std::string& json); - - private: - std::vector buffer; -}; -} // namespace Common +namespace FiftyoneDegrees { + namespace Common { + class Transform { + using Headers = std::map; + + using CTransformAPI = + size_t (*)(const char* base64, char* buffer, size_t length, + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception); + + Headers apiInvoker(CTransformAPI func, const std::string& json); + + public: + /** + * Due to the default param value Transform() ctor is also available in C++ and managed language s.a. C# + * @param capacity the size of the working memory buffer The buffer size will automatically double + * if there is not enough memory to perform the conversion and the conversion will repeat with a bigger buffer. + */ + Transform(size_t capacity = 1024); + + /** + * Convert from getHighEntropyValues() results JSON string into HTTP header key-value pairs, + * see help section to the `fiftyoneDegreesTransformGhevFromJson` routine defined in transform.h + * @param json string representation of the GHEV JSON + * @return the converted HTTP header map + */ + Headers fromJsonGHEV(const std::string& json); + + /** + * Convert from the base64 encoded getHighEntropyValues() results JSON string into HTTP header key-value pairs, + * see help section to the `fiftyoneDegreesTransformGhevFromBase64` routine defined in transform.h + * @param base64 base64-encoded string representation of the GHEV JSON + * @return the converted HTTP header map + */ + Headers fromBase64GHEV(const std::string& base64); + + /** + * Convert from the oRTB 2.6 Structured User-Agent JSON string into HTTP header key-value pairs, + * see help section to the `fiftyoneDegreesTransformSUA` routine defined in transform.h + * @param json string representation of the oRTB SUA JSON + * @return the converted HTTP header map + */ + Headers fromSUA(const std::string& json); + + private: + std::vector buffer; + }; + } // namespace Common } // namespace FiftyoneDegrees #endif // FIFTYONE_DEGREES_TRANSFORM_HPP diff --git a/Transform.i b/Transform.i new file mode 100644 index 00000000..e753fc5f --- /dev/null +++ b/Transform.i @@ -0,0 +1,34 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +%include Types.i + +class Transform { +public: + Transform(); + Transform(size_t capacity); + + std::map fromJsonGHEV(const std::string& json); + std::map fromBase64GHEV(const std::string& json); + std::map fromSUA(const std::string& json); +}; + diff --git a/common.i b/common.i new file mode 100644 index 00000000..4169b06e --- /dev/null +++ b/common.i @@ -0,0 +1,32 @@ +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +%module "Common" + +%{ +#include "Transform.hpp" + +using namespace FiftyoneDegrees::Common; + +%} + +%include "Transform.i" \ No newline at end of file diff --git a/commonCSHARP_wrap.cxx b/commonCSHARP_wrap.cxx new file mode 100644 index 00000000..7f3df2b9 --- /dev/null +++ b/commonCSHARP_wrap.cxx @@ -0,0 +1,1288 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (https://www.swig.org). + * Version 4.2.1 + * + * Do not make changes to this file unless you know what you are doing - modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + + +#define SWIG_VERSION 0x040201 +#define SWIGCSHARP + +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIG_MSC_UNSUPPRESS_4505 +# if defined(_MSC_VER) +# pragma warning(disable : 4505) /* unreferenced local function has been removed */ +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if defined(__GNUC__) +# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + +/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ +#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) +# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 +#endif + +/* Intel's compiler complains if a variable which was never initialised is + * cast to void, which is a common idiom which we use to indicate that we + * are aware a variable isn't used. So we just silence that warning. + * See: https://github.com/swig/swig/issues/192 for more discussion. + */ +#ifdef __INTEL_COMPILER +# pragma warning disable 592 +#endif + +#if defined(__cplusplus) && __cplusplus >=201103L +# define SWIG_NULLPTR nullptr +#else +# define SWIG_NULLPTR NULL +#endif + +/* ----------------------------------------------------------------------------- + * swigcompat.swg + * + * Macros to provide support compatibility with older C and C++ standards. + * ----------------------------------------------------------------------------- */ + +/* C99 and C++11 should provide snprintf, but define SWIG_NO_SNPRINTF + * if you're missing it. + */ +#if ((defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \ + (defined __cplusplus && __cplusplus >= 201103L) || \ + defined SWIG_HAVE_SNPRINTF) && \ + !defined SWIG_NO_SNPRINTF +# define SWIG_snprintf(O,S,F,A) snprintf(O,S,F,A) +# define SWIG_snprintf2(O,S,F,A,B) snprintf(O,S,F,A,B) +#else +/* Fallback versions ignore the buffer size, but most of our uses either have a + * fixed maximum possible size or dynamically allocate a buffer that's large + * enough. + */ +# define SWIG_snprintf(O,S,F,A) sprintf(O,F,A) +# define SWIG_snprintf2(O,S,F,A,B) sprintf(O,F,A,B) +#endif + + +#include +#include +#include + + +/* Support for throwing C# exceptions from C/C++. There are two types: + * Exceptions that take a message and ArgumentExceptions that take a message and a parameter name. */ +typedef enum { + SWIG_CSharpApplicationException, + SWIG_CSharpArithmeticException, + SWIG_CSharpDivideByZeroException, + SWIG_CSharpIndexOutOfRangeException, + SWIG_CSharpInvalidCastException, + SWIG_CSharpInvalidOperationException, + SWIG_CSharpIOException, + SWIG_CSharpNullReferenceException, + SWIG_CSharpOutOfMemoryException, + SWIG_CSharpOverflowException, + SWIG_CSharpSystemException +} SWIG_CSharpExceptionCodes; + +typedef enum { + SWIG_CSharpArgumentException, + SWIG_CSharpArgumentNullException, + SWIG_CSharpArgumentOutOfRangeException +} SWIG_CSharpExceptionArgumentCodes; + +typedef void (SWIGSTDCALL* SWIG_CSharpExceptionCallback_t)(const char *); +typedef void (SWIGSTDCALL* SWIG_CSharpExceptionArgumentCallback_t)(const char *, const char *); + +typedef struct { + SWIG_CSharpExceptionCodes code; + SWIG_CSharpExceptionCallback_t callback; +} SWIG_CSharpException_t; + +typedef struct { + SWIG_CSharpExceptionArgumentCodes code; + SWIG_CSharpExceptionArgumentCallback_t callback; +} SWIG_CSharpExceptionArgument_t; + +static SWIG_CSharpException_t SWIG_csharp_exceptions[] = { + { SWIG_CSharpApplicationException, NULL }, + { SWIG_CSharpArithmeticException, NULL }, + { SWIG_CSharpDivideByZeroException, NULL }, + { SWIG_CSharpIndexOutOfRangeException, NULL }, + { SWIG_CSharpInvalidCastException, NULL }, + { SWIG_CSharpInvalidOperationException, NULL }, + { SWIG_CSharpIOException, NULL }, + { SWIG_CSharpNullReferenceException, NULL }, + { SWIG_CSharpOutOfMemoryException, NULL }, + { SWIG_CSharpOverflowException, NULL }, + { SWIG_CSharpSystemException, NULL } +}; + +static SWIG_CSharpExceptionArgument_t SWIG_csharp_exceptions_argument[] = { + { SWIG_CSharpArgumentException, NULL }, + { SWIG_CSharpArgumentNullException, NULL }, + { SWIG_CSharpArgumentOutOfRangeException, NULL } +}; + +static void SWIGUNUSED SWIG_CSharpSetPendingException(SWIG_CSharpExceptionCodes code, const char *msg) { + SWIG_CSharpExceptionCallback_t callback = SWIG_csharp_exceptions[SWIG_CSharpApplicationException].callback; + if ((size_t)code < sizeof(SWIG_csharp_exceptions)/sizeof(SWIG_CSharpException_t)) { + callback = SWIG_csharp_exceptions[code].callback; + } + callback(msg); +} + +static void SWIGUNUSED SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpExceptionArgumentCodes code, const char *msg, const char *param_name) { + SWIG_CSharpExceptionArgumentCallback_t callback = SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentException].callback; + if ((size_t)code < sizeof(SWIG_csharp_exceptions_argument)/sizeof(SWIG_CSharpExceptionArgument_t)) { + callback = SWIG_csharp_exceptions_argument[code].callback; + } + callback(msg, param_name); +} + + +#ifdef __cplusplus +extern "C" +#endif +SWIGEXPORT void SWIGSTDCALL SWIGRegisterExceptionCallbacks_Common( + SWIG_CSharpExceptionCallback_t applicationCallback, + SWIG_CSharpExceptionCallback_t arithmeticCallback, + SWIG_CSharpExceptionCallback_t divideByZeroCallback, + SWIG_CSharpExceptionCallback_t indexOutOfRangeCallback, + SWIG_CSharpExceptionCallback_t invalidCastCallback, + SWIG_CSharpExceptionCallback_t invalidOperationCallback, + SWIG_CSharpExceptionCallback_t ioCallback, + SWIG_CSharpExceptionCallback_t nullReferenceCallback, + SWIG_CSharpExceptionCallback_t outOfMemoryCallback, + SWIG_CSharpExceptionCallback_t overflowCallback, + SWIG_CSharpExceptionCallback_t systemCallback) { + SWIG_csharp_exceptions[SWIG_CSharpApplicationException].callback = applicationCallback; + SWIG_csharp_exceptions[SWIG_CSharpArithmeticException].callback = arithmeticCallback; + SWIG_csharp_exceptions[SWIG_CSharpDivideByZeroException].callback = divideByZeroCallback; + SWIG_csharp_exceptions[SWIG_CSharpIndexOutOfRangeException].callback = indexOutOfRangeCallback; + SWIG_csharp_exceptions[SWIG_CSharpInvalidCastException].callback = invalidCastCallback; + SWIG_csharp_exceptions[SWIG_CSharpInvalidOperationException].callback = invalidOperationCallback; + SWIG_csharp_exceptions[SWIG_CSharpIOException].callback = ioCallback; + SWIG_csharp_exceptions[SWIG_CSharpNullReferenceException].callback = nullReferenceCallback; + SWIG_csharp_exceptions[SWIG_CSharpOutOfMemoryException].callback = outOfMemoryCallback; + SWIG_csharp_exceptions[SWIG_CSharpOverflowException].callback = overflowCallback; + SWIG_csharp_exceptions[SWIG_CSharpSystemException].callback = systemCallback; +} + +#ifdef __cplusplus +extern "C" +#endif +SWIGEXPORT void SWIGSTDCALL SWIGRegisterExceptionArgumentCallbacks_Common( + SWIG_CSharpExceptionArgumentCallback_t argumentCallback, + SWIG_CSharpExceptionArgumentCallback_t argumentNullCallback, + SWIG_CSharpExceptionArgumentCallback_t argumentOutOfRangeCallback) { + SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentException].callback = argumentCallback; + SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentNullException].callback = argumentNullCallback; + SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentOutOfRangeException].callback = argumentOutOfRangeCallback; +} + + +/* Callback for returning strings to C# without leaking memory */ +typedef char * (SWIGSTDCALL* SWIG_CSharpStringHelperCallback)(const char *); +static SWIG_CSharpStringHelperCallback SWIG_csharp_string_callback = NULL; + + +#ifdef __cplusplus +extern "C" +#endif +SWIGEXPORT void SWIGSTDCALL SWIGRegisterStringCallback_Common(SWIG_CSharpStringHelperCallback callback) { + SWIG_csharp_string_callback = callback; +} + + +/* Contract support */ + +#define SWIG_contract_assert(nullreturn, expr, msg) do { if (!(expr)) {SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, msg, ""); return nullreturn; } } while (0) + + +#ifdef __cplusplus +#include +/* SwigValueWrapper is described in swig.swg */ +template class SwigValueWrapper { + struct SwigSmartPointer { + T *ptr; + SwigSmartPointer(T *p) : ptr(p) { } + ~SwigSmartPointer() { delete ptr; } + SwigSmartPointer& operator=(SwigSmartPointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; } + void reset(T *p) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = p; } + } pointer; + SwigValueWrapper& operator=(const SwigValueWrapper& rhs); + SwigValueWrapper(const SwigValueWrapper& rhs); +public: + SwigValueWrapper() : pointer(0) { } + SwigValueWrapper& operator=(const T& t) { SwigSmartPointer tmp(new T(t)); pointer = tmp; return *this; } +#if __cplusplus >=201103L + SwigValueWrapper& operator=(T&& t) { SwigSmartPointer tmp(new T(std::move(t))); pointer = tmp; return *this; } + operator T&&() const { return std::move(*pointer.ptr); } +#else + operator T&() const { return *pointer.ptr; } +#endif + T *operator&() const { return pointer.ptr; } + static void reset(SwigValueWrapper& t, T *p) { t.pointer.reset(p); } +}; + +/* + * SwigValueInit() is a generic initialisation solution as the following approach: + * + * T c_result = T(); + * + * doesn't compile for all types for example: + * + * unsigned int c_result = unsigned int(); + */ +template T SwigValueInit() { + return T(); +} + +#if __cplusplus >=201103L +# define SWIG_STD_MOVE(OBJ) std::move(OBJ) +#else +# define SWIG_STD_MOVE(OBJ) OBJ +#endif + +#endif + + +#include "Transform.hpp" + +using namespace FiftyoneDegrees::Common; + + + +#include + + +#include +#include + + +#include +#include +#include + + +#include +#include +#include + +SWIGINTERN std::map< std::string,std::string >::mapped_type const &std_map_Sl_std_string_Sc_std_string_Sg__getitem(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key){ + std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); + if (iter != self->end()) + return iter->second; + else + throw std::out_of_range("key not found"); + } +SWIGINTERN void std_map_Sl_std_string_Sc_std_string_Sg__setitem(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key,std::map< std::string,std::string >::mapped_type const &x){ +#ifdef __cpp_lib_map_try_emplace + (*self).insert_or_assign(key, x); +#else + (*self)[key] = x; +#endif + } +SWIGINTERN bool std_map_Sl_std_string_Sc_std_string_Sg__ContainsKey(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key){ + std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); + return iter != self->end(); + } +SWIGINTERN void std_map_Sl_std_string_Sc_std_string_Sg__Add(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key,std::map< std::string,std::string >::mapped_type const &value){ + std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); + if (iter != self->end()) + throw std::out_of_range("key already exists"); + self->insert(std::pair< std::string, std::string >(key, value)); + } +SWIGINTERN bool std_map_Sl_std_string_Sc_std_string_Sg__Remove(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key){ + std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); + if (iter != self->end()) { + self->erase(iter); + return true; + } + return false; + } +SWIGINTERN std::map< std::string,std::string,std::less< std::string > >::iterator *std_map_Sl_std_string_Sc_std_string_Sg__create_iterator_begin(std::map< std::string,std::string > *self){ + return new std::map< std::string, std::string, std::less< std::string > >::iterator(self->begin()); + } +SWIGINTERN std::map< std::string,std::string >::key_type const &std_map_Sl_std_string_Sc_std_string_Sg__get_next_key(std::map< std::string,std::string > *self,std::map< std::string,std::string,std::less< std::string > >::iterator *swigiterator){ + (void)self; + std::map< std::string, std::string, std::less< std::string > >::iterator iter = *swigiterator; + (*swigiterator)++; + return (*iter).first; + } +SWIGINTERN void std_map_Sl_std_string_Sc_std_string_Sg__destroy_iterator(std::map< std::string,std::string > *self,std::map< std::string,std::string,std::less< std::string > >::iterator *swigiterator){ + (void)self; + delete swigiterator; + } +SWIGINTERN std::vector< std::string > *new_std_vector_Sl_std_string_Sg___SWIG_2(int capacity){ + std::vector< std::string >* pv = 0; + if (capacity >= 0) { + pv = new std::vector< std::string >(); + pv->reserve(capacity); + } else { + throw std::out_of_range("capacity"); + } + return pv; + } +SWIGINTERN std::string std_vector_Sl_std_string_Sg__getitemcopy(std::vector< std::string > *self,int index){ + if (index>=0 && index<(int)self->size()) + return (*self)[index]; + else + throw std::out_of_range("index"); + } +SWIGINTERN std::vector< std::string >::value_type const &std_vector_Sl_std_string_Sg__getitem(std::vector< std::string > *self,int index){ + if (index>=0 && index<(int)self->size()) + return (*self)[index]; + else + throw std::out_of_range("index"); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__setitem(std::vector< std::string > *self,int index,std::string const &val){ + if (index>=0 && index<(int)self->size()) + (*self)[index] = val; + else + throw std::out_of_range("index"); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__AddRange(std::vector< std::string > *self,std::vector< std::string > const &values){ + self->insert(self->end(), values.begin(), values.end()); + } +SWIGINTERN std::vector< std::string > *std_vector_Sl_std_string_Sg__GetRange(std::vector< std::string > *self,int index,int count){ + if (index < 0) + throw std::out_of_range("index"); + if (count < 0) + throw std::out_of_range("count"); + if (index >= (int)self->size()+1 || index+count > (int)self->size()) + throw std::invalid_argument("invalid range"); + return new std::vector< std::string >(self->begin()+index, self->begin()+index+count); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__Insert(std::vector< std::string > *self,int index,std::string const &x){ + if (index>=0 && index<(int)self->size()+1) + self->insert(self->begin()+index, x); + else + throw std::out_of_range("index"); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__InsertRange(std::vector< std::string > *self,int index,std::vector< std::string > const &values){ + if (index>=0 && index<(int)self->size()+1) + self->insert(self->begin()+index, values.begin(), values.end()); + else + throw std::out_of_range("index"); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__RemoveAt(std::vector< std::string > *self,int index){ + if (index>=0 && index<(int)self->size()) + self->erase(self->begin() + index); + else + throw std::out_of_range("index"); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__RemoveRange(std::vector< std::string > *self,int index,int count){ + if (index < 0) + throw std::out_of_range("index"); + if (count < 0) + throw std::out_of_range("count"); + if (index >= (int)self->size()+1 || index+count > (int)self->size()) + throw std::invalid_argument("invalid range"); + self->erase(self->begin()+index, self->begin()+index+count); + } +SWIGINTERN std::vector< std::string > *std_vector_Sl_std_string_Sg__Repeat(std::string const &value,int count){ + if (count < 0) + throw std::out_of_range("count"); + return new std::vector< std::string >(count, value); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__Reverse__SWIG_0(std::vector< std::string > *self){ + std::reverse(self->begin(), self->end()); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__Reverse__SWIG_1(std::vector< std::string > *self,int index,int count){ + if (index < 0) + throw std::out_of_range("index"); + if (count < 0) + throw std::out_of_range("count"); + if (index >= (int)self->size()+1 || index+count > (int)self->size()) + throw std::invalid_argument("invalid range"); + std::reverse(self->begin()+index, self->begin()+index+count); + } +SWIGINTERN void std_vector_Sl_std_string_Sg__SetRange(std::vector< std::string > *self,int index,std::vector< std::string > const &values){ + if (index < 0) + throw std::out_of_range("index"); + if (index+values.size() > self->size()) + throw std::out_of_range("index"); + std::copy(values.begin(), values.end(), self->begin()+index); + } +SWIGINTERN bool std_vector_Sl_std_string_Sg__Contains(std::vector< std::string > *self,std::string const &value){ + return std::find(self->begin(), self->end(), value) != self->end(); + } +SWIGINTERN int std_vector_Sl_std_string_Sg__IndexOf(std::vector< std::string > *self,std::string const &value){ + int index = -1; + std::vector< std::string >::iterator it = std::find(self->begin(), self->end(), value); + if (it != self->end()) + index = (int)(it - self->begin()); + return index; + } +SWIGINTERN int std_vector_Sl_std_string_Sg__LastIndexOf(std::vector< std::string > *self,std::string const &value){ + int index = -1; + std::vector< std::string >::reverse_iterator rit = std::find(self->rbegin(), self->rend(), value); + if (rit != self->rend()) + index = (int)(self->rend() - 1 - rit); + return index; + } +SWIGINTERN bool std_vector_Sl_std_string_Sg__Remove(std::vector< std::string > *self,std::string const &value){ + std::vector< std::string >::iterator it = std::find(self->begin(), self->end(), value); + if (it != self->end()) { + self->erase(it); + return true; + } + return false; + } + +#ifdef __cplusplus +extern "C" { +#endif + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_0___() { + void * jresult ; + std::map< std::string,std::string > *result = 0 ; + + result = (std::map< std::string,std::string > *)new std::map< std::string,std::string >(); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_1___(void * jarg1) { + void * jresult ; + std::map< std::string,std::string > *arg1 = 0 ; + std::map< std::string,std::string > *result = 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + if (!arg1) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::map< std::string,std::string > const & is null", 0); + return 0; + } + result = (std::map< std::string,std::string > *)new std::map< std::string,std::string >((std::map< std::string,std::string > const &)*arg1); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_size___(void * jarg1) { + unsigned int jresult ; + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string >::size_type result; + + arg1 = (std::map< std::string,std::string > *)jarg1; + result = ((std::map< std::string,std::string > const *)arg1)->size(); + jresult = (unsigned int)result; + return jresult; +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_empty___(void * jarg1) { + unsigned int jresult ; + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + bool result; + + arg1 = (std::map< std::string,std::string > *)jarg1; + result = (bool)((std::map< std::string,std::string > const *)arg1)->empty(); + jresult = result; + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Clear___(void * jarg1) { + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + (arg1)->clear(); +} + + +SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_getitem___(void * jarg1, const char * jarg2) { + const char * jresult ; + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string >::key_type *arg2 = 0 ; + std::map< std::string,std::string >::mapped_type *result = 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::map< std::string,std::string >::key_type arg2_str(jarg2); + arg2 = &arg2_str; + try { + result = (std::map< std::string,std::string >::mapped_type *) &std_map_Sl_std_string_Sc_std_string_Sg__getitem(arg1,(std::string const &)*arg2); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return 0; + } + jresult = SWIG_csharp_string_callback(result->c_str()); + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_setitem___(void * jarg1, const char * jarg2, const char * jarg3) { + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string >::key_type *arg2 = 0 ; + std::map< std::string,std::string >::mapped_type *arg3 = 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return ; + } + std::map< std::string,std::string >::key_type arg2_str(jarg2); + arg2 = &arg2_str; + if (!jarg3) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return ; + } + std::map< std::string,std::string >::mapped_type arg3_str(jarg3); + arg3 = &arg3_str; + std_map_Sl_std_string_Sc_std_string_Sg__setitem(arg1,(std::string const &)*arg2,(std::string const &)*arg3); +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_ContainsKey___(void * jarg1, const char * jarg2) { + unsigned int jresult ; + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string >::key_type *arg2 = 0 ; + bool result; + + arg1 = (std::map< std::string,std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::map< std::string,std::string >::key_type arg2_str(jarg2); + arg2 = &arg2_str; + result = (bool)std_map_Sl_std_string_Sc_std_string_Sg__ContainsKey(arg1,(std::string const &)*arg2); + jresult = result; + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Add___(void * jarg1, const char * jarg2, const char * jarg3) { + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string >::key_type *arg2 = 0 ; + std::map< std::string,std::string >::mapped_type *arg3 = 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return ; + } + std::map< std::string,std::string >::key_type arg2_str(jarg2); + arg2 = &arg2_str; + if (!jarg3) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return ; + } + std::map< std::string,std::string >::mapped_type arg3_str(jarg3); + arg3 = &arg3_str; + try { + std_map_Sl_std_string_Sc_std_string_Sg__Add(arg1,(std::string const &)*arg2,(std::string const &)*arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Remove___(void * jarg1, const char * jarg2) { + unsigned int jresult ; + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string >::key_type *arg2 = 0 ; + bool result; + + arg1 = (std::map< std::string,std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::map< std::string,std::string >::key_type arg2_str(jarg2); + arg2 = &arg2_str; + result = (bool)std_map_Sl_std_string_Sc_std_string_Sg__Remove(arg1,(std::string const &)*arg2); + jresult = result; + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_create_iterator_begin___(void * jarg1) { + void * jresult ; + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string,std::less< std::string > >::iterator *result = 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + result = (std::map< std::string,std::string,std::less< std::string > >::iterator *)std_map_Sl_std_string_Sc_std_string_Sg__create_iterator_begin(arg1); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_get_next_key___(void * jarg1, void * jarg2) { + const char * jresult ; + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string,std::less< std::string > >::iterator *arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *) 0 ; + std::map< std::string,std::string >::key_type *result = 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *)jarg2; + result = (std::map< std::string,std::string >::key_type *) &std_map_Sl_std_string_Sc_std_string_Sg__get_next_key(arg1,arg2); + jresult = SWIG_csharp_string_callback(result->c_str()); + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_destroy_iterator___(void * jarg1, void * jarg2) { + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + std::map< std::string,std::string,std::less< std::string > >::iterator *arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *) 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *)jarg2; + std_map_Sl_std_string_Sc_std_string_Sg__destroy_iterator(arg1,arg2); +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_delete_MapStringStringSwig___(void * jarg1) { + std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; + + arg1 = (std::map< std::string,std::string > *)jarg1; + delete arg1; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_0___() { + void * jresult ; + std::vector< std::string > *result = 0 ; + + result = (std::vector< std::string > *)new std::vector< std::string >(); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_1___(void * jarg1) { + void * jresult ; + std::vector< std::string > *arg1 = 0 ; + std::vector< std::string > *result = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + if (!arg1) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); + return 0; + } + result = (std::vector< std::string > *)new std::vector< std::string >((std::vector< std::string > const &)*arg1); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Clear___(void * jarg1) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + (arg1)->clear(); +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Add___(void * jarg1, const char * jarg2) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::string *arg2 = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return ; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + (arg1)->push_back((std::string const &)*arg2); +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_size___(void * jarg1) { + unsigned int jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::vector< std::string >::size_type result; + + arg1 = (std::vector< std::string > *)jarg1; + result = ((std::vector< std::string > const *)arg1)->size(); + jresult = (unsigned int)result; + return jresult; +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_empty___(void * jarg1) { + unsigned int jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + bool result; + + arg1 = (std::vector< std::string > *)jarg1; + result = (bool)((std::vector< std::string > const *)arg1)->empty(); + jresult = result; + return jresult; +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_capacity___(void * jarg1) { + unsigned int jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::vector< std::string >::size_type result; + + arg1 = (std::vector< std::string > *)jarg1; + result = ((std::vector< std::string > const *)arg1)->capacity(); + jresult = (unsigned int)result; + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_reserve___(void * jarg1, unsigned int jarg2) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::vector< std::string >::size_type arg2 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (std::vector< std::string >::size_type)jarg2; + (arg1)->reserve(arg2); +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_2___(int jarg1) { + void * jresult ; + int arg1 ; + std::vector< std::string > *result = 0 ; + + arg1 = (int)jarg1; + try { + result = (std::vector< std::string > *)new_std_vector_Sl_std_string_Sg___SWIG_2(arg1); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return 0; + } + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitemcopy___(void * jarg1, int jarg2) { + const char * jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + std::string result; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + try { + result = std_vector_Sl_std_string_Sg__getitemcopy(arg1,arg2); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return 0; + } + jresult = SWIG_csharp_string_callback((&result)->c_str()); + return jresult; +} + + +SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitem___(void * jarg1, int jarg2) { + const char * jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + std::vector< std::string >::value_type *result = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + try { + result = (std::vector< std::string >::value_type *) &std_vector_Sl_std_string_Sg__getitem(arg1,arg2); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return 0; + } + jresult = SWIG_csharp_string_callback(result->c_str()); + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_setitem___(void * jarg1, int jarg2, const char * jarg3) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + std::string *arg3 = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + if (!jarg3) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return ; + } + std::string arg3_str(jarg3); + arg3 = &arg3_str; + try { + std_vector_Sl_std_string_Sg__setitem(arg1,arg2,(std::string const &)*arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_AddRange___(void * jarg1, void * jarg2) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::vector< std::string > *arg2 = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (std::vector< std::string > *)jarg2; + if (!arg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); + return ; + } + std_vector_Sl_std_string_Sg__AddRange(arg1,(std::vector< std::string > const &)*arg2); +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_GetRange___(void * jarg1, int jarg2, int jarg3) { + void * jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + int arg3 ; + std::vector< std::string > *result = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + arg3 = (int)jarg3; + try { + result = (std::vector< std::string > *)std_vector_Sl_std_string_Sg__GetRange(arg1,arg2,arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return 0; + } catch(std::invalid_argument &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentException, (&_e)->what(), ""); + return 0; + } + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Insert___(void * jarg1, int jarg2, const char * jarg3) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + std::string *arg3 = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + if (!jarg3) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return ; + } + std::string arg3_str(jarg3); + arg3 = &arg3_str; + try { + std_vector_Sl_std_string_Sg__Insert(arg1,arg2,(std::string const &)*arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_InsertRange___(void * jarg1, int jarg2, void * jarg3) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + std::vector< std::string > *arg3 = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + arg3 = (std::vector< std::string > *)jarg3; + if (!arg3) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); + return ; + } + try { + std_vector_Sl_std_string_Sg__InsertRange(arg1,arg2,(std::vector< std::string > const &)*arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveAt___(void * jarg1, int jarg2) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + try { + std_vector_Sl_std_string_Sg__RemoveAt(arg1,arg2); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveRange___(void * jarg1, int jarg2, int jarg3) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + int arg3 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + arg3 = (int)jarg3; + try { + std_vector_Sl_std_string_Sg__RemoveRange(arg1,arg2,arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } catch(std::invalid_argument &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentException, (&_e)->what(), ""); + return ; + } +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Repeat___(const char * jarg1, int jarg2) { + void * jresult ; + std::string *arg1 = 0 ; + int arg2 ; + std::vector< std::string > *result = 0 ; + + if (!jarg1) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg1_str(jarg1); + arg1 = &arg1_str; + arg2 = (int)jarg2; + try { + result = (std::vector< std::string > *)std_vector_Sl_std_string_Sg__Repeat((std::string const &)*arg1,arg2); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return 0; + } + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_0___(void * jarg1) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + std_vector_Sl_std_string_Sg__Reverse__SWIG_0(arg1); +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_1___(void * jarg1, int jarg2, int jarg3) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + int arg3 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + arg3 = (int)jarg3; + try { + std_vector_Sl_std_string_Sg__Reverse__SWIG_1(arg1,arg2,arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } catch(std::invalid_argument &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentException, (&_e)->what(), ""); + return ; + } +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_SetRange___(void * jarg1, int jarg2, void * jarg3) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + int arg2 ; + std::vector< std::string > *arg3 = 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + arg2 = (int)jarg2; + arg3 = (std::vector< std::string > *)jarg3; + if (!arg3) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); + return ; + } + try { + std_vector_Sl_std_string_Sg__SetRange(arg1,arg2,(std::vector< std::string > const &)*arg3); + } catch(std::out_of_range &_e) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); + return ; + } +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Contains___(void * jarg1, const char * jarg2) { + unsigned int jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::string *arg2 = 0 ; + bool result; + + arg1 = (std::vector< std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + result = (bool)std_vector_Sl_std_string_Sg__Contains(arg1,(std::string const &)*arg2); + jresult = result; + return jresult; +} + + +SWIGEXPORT int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_IndexOf___(void * jarg1, const char * jarg2) { + int jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::string *arg2 = 0 ; + int result; + + arg1 = (std::vector< std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + result = (int)std_vector_Sl_std_string_Sg__IndexOf(arg1,(std::string const &)*arg2); + jresult = result; + return jresult; +} + + +SWIGEXPORT int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_LastIndexOf___(void * jarg1, const char * jarg2) { + int jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::string *arg2 = 0 ; + int result; + + arg1 = (std::vector< std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + result = (int)std_vector_Sl_std_string_Sg__LastIndexOf(arg1,(std::string const &)*arg2); + jresult = result; + return jresult; +} + + +SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Remove___(void * jarg1, const char * jarg2) { + unsigned int jresult ; + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + std::string *arg2 = 0 ; + bool result; + + arg1 = (std::vector< std::string > *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + result = (bool)std_vector_Sl_std_string_Sg__Remove(arg1,(std::string const &)*arg2); + jresult = result; + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_delete_VectorStringSwig___(void * jarg1) { + std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; + + arg1 = (std::vector< std::string > *)jarg1; + delete arg1; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_0___() { + void * jresult ; + Transform *result = 0 ; + + result = (Transform *)new Transform(); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_1___(unsigned int jarg1) { + void * jresult ; + size_t arg1 ; + Transform *result = 0 ; + + arg1 = (size_t)jarg1; + result = (Transform *)new Transform(arg1); + jresult = (void *)result; + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_Transform_fromJsonGHEV___(void * jarg1, const char * jarg2) { + void * jresult ; + Transform *arg1 = (Transform *) 0 ; + std::string *arg2 = 0 ; + std::map< std::string,std::string,std::less< std::string > > result; + + arg1 = (Transform *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + result = (arg1)->fromJsonGHEV((std::string const &)*arg2); + jresult = new std::map< std::string,std::string,std::less< std::string > >(result); + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_Transform_fromBase64GHEV___(void * jarg1, const char * jarg2) { + void * jresult ; + Transform *arg1 = (Transform *) 0 ; + std::string *arg2 = 0 ; + std::map< std::string,std::string,std::less< std::string > > result; + + arg1 = (Transform *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + result = (arg1)->fromBase64GHEV((std::string const &)*arg2); + jresult = new std::map< std::string,std::string,std::less< std::string > >(result); + return jresult; +} + + +SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_Transform_fromSUA___(void * jarg1, const char * jarg2) { + void * jresult ; + Transform *arg1 = (Transform *) 0 ; + std::string *arg2 = 0 ; + std::map< std::string,std::string,std::less< std::string > > result; + + arg1 = (Transform *)jarg1; + if (!jarg2) { + SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); + return 0; + } + std::string arg2_str(jarg2); + arg2 = &arg2_str; + result = (arg1)->fromSUA((std::string const &)*arg2); + jresult = new std::map< std::string,std::string,std::less< std::string > >(result); + return jresult; +} + + +SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_delete_Transform___(void * jarg1) { + Transform *arg1 = (Transform *) 0 ; + + arg1 = (Transform *)jarg1; + delete arg1; +} + + +#ifdef __cplusplus +} +#endif + diff --git a/interop/dotnet/CMakeLists.txt b/interop/dotnet/CMakeLists.txt new file mode 100644 index 00000000..ee9a3f5c --- /dev/null +++ b/interop/dotnet/CMakeLists.txt @@ -0,0 +1,94 @@ +cmake_minimum_required(VERSION 3.24) +set (CMAKE_C_STANDARD 11) +set (CMAKE_CXX_STANDARD 17) + +project(CommonCXX LANGUAGES CXX C) + +if (NOT MSVC) + add_compile_options(-fPIC) +endif() + +if (MSVC) + if (CMAKE_GENERATOR_PLATFORM) + set (GEN_PLATFORM ${CMAKE_GENERATOR_PLATFORM}) + else() + set (GEN_PLATFORM ${CMAKE_VS_PLATFORM_NAME_DEFAULT}) + endif() + + if (${GEN_PLATFORM} MATCHES .*64.* AND NOT 32bit) + set(ARCH x64) + else() + set(ARCH x86) + endif() + +else() + message(STATUS "System Processor: ${CMAKE_SYSTEM_PROCESSOR}") + if (${CMAKE_SYSTEM_PROCESSOR} MATCHES ^armhf) + set(ARCH armhf) + elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES ^aarch64) + set(ARCH aarch64) + elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES ^arm64) + set(ARCH aarch64) + elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES 64) + if (32bit) + message(STATUS "32bit: ${32bit}") + set (ARCH x86) + else() + message(STATUS "32bit: ${32bit}") + set (ARCH x64) + endif() + else() + set (ARCH x86) + endif() +endif() +message(STATUS "Arch name: ${ARCH}") +message(STATUS "Generator Platform: ${CMAKE_GENERATOR_PLATFORM}") +message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") + +if (MSVC) + set (OS windows) +elseif (UNIX AND NOT APPLE) + set (OS linux) +elseif (APPLE) + set (OS macos) +endif() +message(STATUS "OS name: ${OS}") + +# include CMakeLists from common-cxx library +option(BUILD_TESTING "" OFF) +include("${CMAKE_CURRENT_LIST_DIR}/../../CMakeLists.txt" NO_POLICY_SCOPE) + +set(NAMESPACE "FiftyoneDegrees.Common") +set(DLLNAME "${NAMESPACE}.Native") +set(CXXWRAP "${CMAKE_CURRENT_LIST_DIR}/../../commonCSHARP_wrap.cxx") + +find_package(SWIG 4.0 COMPONENTS csharp) +if(SWIG_FOUND) + message("-- Found SWIG ${SWIG_EXECUTABLE}, running codegen of the wrappers:") + execute_process(COMMAND ${SWIG_EXECUTABLE} -c++ -csharp -namespace "${NAMESPACE}" -dllimport "${DLLNAME}.dll" -outdir ${CMAKE_CURRENT_LIST_DIR}/FiftyoneDegrees.Common -o "${CXXWRAP}" ${CMAKE_CURRENT_LIST_DIR}/../../common.i + COMMAND_ECHO STDOUT) +endif() + +if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") + set(OUTDIR ".") +else() + set(OUTDIR "${CMAKE_BUILD_TYPE}") +endif() + +add_library(common-cxx-dotnet SHARED "${CXXWRAP}") +target_link_libraries(common-cxx-dotnet fiftyone-common-cxx) +set_target_properties(common-cxx-dotnet PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${OUTDIR}" + LIBRARY_OUTPUT_DIRECTORY "${OUTDIR}" + OUTPUT_NAME "${DLLNAME}" + PREFIX "" + SUFFIX ".dll") + +if (MSVC) + # /wd4100 needed to disable "unreferenced formal parameter" which comes from the SWIG file + target_compile_options(common-cxx-dotnet PRIVATE "/D_CRT_SECURE_NO_WARNINGS" "/W4" "/WX" "/wd4100" "/EHsc") +else() + add_compile_options(-fPIC) + target_compile_options(common-cxx-dotnet INTERFACE "-static-libgcc -static-libstdc++") + target_link_options(common-cxx-dotnet INTERFACE "-static-libgcc -static-libstdc++") +endif() \ No newline at end of file diff --git a/interop/dotnet/FiftyoneDegrees.Common/Common.cs b/interop/dotnet/FiftyoneDegrees.Common/Common.cs new file mode 100644 index 00000000..791efebb --- /dev/null +++ b/interop/dotnet/FiftyoneDegrees.Common/Common.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// +// +// This file was automatically generated by SWIG (https://www.swig.org). +// Version 4.2.1 +// +// Do not make changes to this file unless you know what you are doing - modify +// the SWIG interface file instead. +//------------------------------------------------------------------------------ + +namespace FiftyoneDegrees.Common { + +public class Common { +} + +} diff --git a/interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs b/interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs new file mode 100644 index 00000000..02644501 --- /dev/null +++ b/interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs @@ -0,0 +1,338 @@ +//------------------------------------------------------------------------------ +// +// +// This file was automatically generated by SWIG (https://www.swig.org). +// Version 4.2.1 +// +// Do not make changes to this file unless you know what you are doing - modify +// the SWIG interface file instead. +//------------------------------------------------------------------------------ + +namespace FiftyoneDegrees.Common { + +class CommonPINVOKE { + + protected class SWIGExceptionHelper { + + public delegate void ExceptionDelegate(string message); + public delegate void ExceptionArgumentDelegate(string message, string paramName); + + static ExceptionDelegate applicationDelegate = new ExceptionDelegate(SetPendingApplicationException); + static ExceptionDelegate arithmeticDelegate = new ExceptionDelegate(SetPendingArithmeticException); + static ExceptionDelegate divideByZeroDelegate = new ExceptionDelegate(SetPendingDivideByZeroException); + static ExceptionDelegate indexOutOfRangeDelegate = new ExceptionDelegate(SetPendingIndexOutOfRangeException); + static ExceptionDelegate invalidCastDelegate = new ExceptionDelegate(SetPendingInvalidCastException); + static ExceptionDelegate invalidOperationDelegate = new ExceptionDelegate(SetPendingInvalidOperationException); + static ExceptionDelegate ioDelegate = new ExceptionDelegate(SetPendingIOException); + static ExceptionDelegate nullReferenceDelegate = new ExceptionDelegate(SetPendingNullReferenceException); + static ExceptionDelegate outOfMemoryDelegate = new ExceptionDelegate(SetPendingOutOfMemoryException); + static ExceptionDelegate overflowDelegate = new ExceptionDelegate(SetPendingOverflowException); + static ExceptionDelegate systemDelegate = new ExceptionDelegate(SetPendingSystemException); + + static ExceptionArgumentDelegate argumentDelegate = new ExceptionArgumentDelegate(SetPendingArgumentException); + static ExceptionArgumentDelegate argumentNullDelegate = new ExceptionArgumentDelegate(SetPendingArgumentNullException); + static ExceptionArgumentDelegate argumentOutOfRangeDelegate = new ExceptionArgumentDelegate(SetPendingArgumentOutOfRangeException); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="SWIGRegisterExceptionCallbacks_Common")] + public static extern void SWIGRegisterExceptionCallbacks_Common( + ExceptionDelegate applicationDelegate, + ExceptionDelegate arithmeticDelegate, + ExceptionDelegate divideByZeroDelegate, + ExceptionDelegate indexOutOfRangeDelegate, + ExceptionDelegate invalidCastDelegate, + ExceptionDelegate invalidOperationDelegate, + ExceptionDelegate ioDelegate, + ExceptionDelegate nullReferenceDelegate, + ExceptionDelegate outOfMemoryDelegate, + ExceptionDelegate overflowDelegate, + ExceptionDelegate systemExceptionDelegate); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="SWIGRegisterExceptionArgumentCallbacks_Common")] + public static extern void SWIGRegisterExceptionCallbacksArgument_Common( + ExceptionArgumentDelegate argumentDelegate, + ExceptionArgumentDelegate argumentNullDelegate, + ExceptionArgumentDelegate argumentOutOfRangeDelegate); + + static void SetPendingApplicationException(string message) { + SWIGPendingException.Set(new global::System.ApplicationException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingArithmeticException(string message) { + SWIGPendingException.Set(new global::System.ArithmeticException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingDivideByZeroException(string message) { + SWIGPendingException.Set(new global::System.DivideByZeroException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingIndexOutOfRangeException(string message) { + SWIGPendingException.Set(new global::System.IndexOutOfRangeException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingInvalidCastException(string message) { + SWIGPendingException.Set(new global::System.InvalidCastException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingInvalidOperationException(string message) { + SWIGPendingException.Set(new global::System.InvalidOperationException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingIOException(string message) { + SWIGPendingException.Set(new global::System.IO.IOException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingNullReferenceException(string message) { + SWIGPendingException.Set(new global::System.NullReferenceException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingOutOfMemoryException(string message) { + SWIGPendingException.Set(new global::System.OutOfMemoryException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingOverflowException(string message) { + SWIGPendingException.Set(new global::System.OverflowException(message, SWIGPendingException.Retrieve())); + } + static void SetPendingSystemException(string message) { + SWIGPendingException.Set(new global::System.SystemException(message, SWIGPendingException.Retrieve())); + } + + static void SetPendingArgumentException(string message, string paramName) { + SWIGPendingException.Set(new global::System.ArgumentException(message, paramName, SWIGPendingException.Retrieve())); + } + static void SetPendingArgumentNullException(string message, string paramName) { + global::System.Exception e = SWIGPendingException.Retrieve(); + if (e != null) message = message + " Inner Exception: " + e.Message; + SWIGPendingException.Set(new global::System.ArgumentNullException(paramName, message)); + } + static void SetPendingArgumentOutOfRangeException(string message, string paramName) { + global::System.Exception e = SWIGPendingException.Retrieve(); + if (e != null) message = message + " Inner Exception: " + e.Message; + SWIGPendingException.Set(new global::System.ArgumentOutOfRangeException(paramName, message)); + } + + static SWIGExceptionHelper() { + SWIGRegisterExceptionCallbacks_Common( + applicationDelegate, + arithmeticDelegate, + divideByZeroDelegate, + indexOutOfRangeDelegate, + invalidCastDelegate, + invalidOperationDelegate, + ioDelegate, + nullReferenceDelegate, + outOfMemoryDelegate, + overflowDelegate, + systemDelegate); + + SWIGRegisterExceptionCallbacksArgument_Common( + argumentDelegate, + argumentNullDelegate, + argumentOutOfRangeDelegate); + } + } + + protected static SWIGExceptionHelper swigExceptionHelper = new SWIGExceptionHelper(); + + public class SWIGPendingException { + [global::System.ThreadStatic] + private static global::System.Exception pendingException = null; + private static int numExceptionsPending = 0; + private static global::System.Object exceptionsLock = null; + + public static bool Pending { + get { + bool pending = false; + if (numExceptionsPending > 0) + if (pendingException != null) + pending = true; + return pending; + } + } + + public static void Set(global::System.Exception e) { + if (pendingException != null) + throw new global::System.ApplicationException("FATAL: An earlier pending exception from unmanaged code was missed and thus not thrown (" + pendingException.ToString() + ")", e); + pendingException = e; + lock(exceptionsLock) { + numExceptionsPending++; + } + } + + public static global::System.Exception Retrieve() { + global::System.Exception e = null; + if (numExceptionsPending > 0) { + if (pendingException != null) { + e = pendingException; + pendingException = null; + lock(exceptionsLock) { + numExceptionsPending--; + } + } + } + return e; + } + + static SWIGPendingException() { + exceptionsLock = new global::System.Object(); + } + } + + + protected class SWIGStringHelper { + + public delegate string SWIGStringDelegate(string message); + static SWIGStringDelegate stringDelegate = new SWIGStringDelegate(CreateString); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="SWIGRegisterStringCallback_Common")] + public static extern void SWIGRegisterStringCallback_Common(SWIGStringDelegate stringDelegate); + + static string CreateString(string cString) { + return cString; + } + + static SWIGStringHelper() { + SWIGRegisterStringCallback_Common(stringDelegate); + } + } + + static protected SWIGStringHelper swigStringHelper = new SWIGStringHelper(); + + + static CommonPINVOKE() { + } + + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_0___")] + public static extern global::System.IntPtr new_MapStringStringSwig__SWIG_0(); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_1___")] + public static extern global::System.IntPtr new_MapStringStringSwig__SWIG_1(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_size___")] + public static extern uint MapStringStringSwig_size(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_empty___")] + public static extern bool MapStringStringSwig_empty(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Clear___")] + public static extern void MapStringStringSwig_Clear(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_getitem___")] + public static extern string MapStringStringSwig_getitem(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_setitem___")] + public static extern void MapStringStringSwig_setitem(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2, string jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_ContainsKey___")] + public static extern bool MapStringStringSwig_ContainsKey(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Add___")] + public static extern void MapStringStringSwig_Add(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2, string jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Remove___")] + public static extern bool MapStringStringSwig_Remove(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_create_iterator_begin___")] + public static extern global::System.IntPtr MapStringStringSwig_create_iterator_begin(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_get_next_key___")] + public static extern string MapStringStringSwig_get_next_key(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.IntPtr jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_destroy_iterator___")] + public static extern void MapStringStringSwig_destroy_iterator(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.IntPtr jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_delete_MapStringStringSwig___")] + public static extern void delete_MapStringStringSwig(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_0___")] + public static extern global::System.IntPtr new_VectorStringSwig__SWIG_0(); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_1___")] + public static extern global::System.IntPtr new_VectorStringSwig__SWIG_1(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Clear___")] + public static extern void VectorStringSwig_Clear(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Add___")] + public static extern void VectorStringSwig_Add(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_size___")] + public static extern uint VectorStringSwig_size(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_empty___")] + public static extern bool VectorStringSwig_empty(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_capacity___")] + public static extern uint VectorStringSwig_capacity(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_reserve___")] + public static extern void VectorStringSwig_reserve(global::System.Runtime.InteropServices.HandleRef jarg1, uint jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_2___")] + public static extern global::System.IntPtr new_VectorStringSwig__SWIG_2(int jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitemcopy___")] + public static extern string VectorStringSwig_getitemcopy(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitem___")] + public static extern string VectorStringSwig_getitem(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_setitem___")] + public static extern void VectorStringSwig_setitem(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, string jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_AddRange___")] + public static extern void VectorStringSwig_AddRange(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_GetRange___")] + public static extern global::System.IntPtr VectorStringSwig_GetRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, int jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Insert___")] + public static extern void VectorStringSwig_Insert(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, string jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_InsertRange___")] + public static extern void VectorStringSwig_InsertRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, global::System.Runtime.InteropServices.HandleRef jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveAt___")] + public static extern void VectorStringSwig_RemoveAt(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveRange___")] + public static extern void VectorStringSwig_RemoveRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, int jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Repeat___")] + public static extern global::System.IntPtr VectorStringSwig_Repeat(string jarg1, int jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_0___")] + public static extern void VectorStringSwig_Reverse__SWIG_0(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_1___")] + public static extern void VectorStringSwig_Reverse__SWIG_1(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, int jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_SetRange___")] + public static extern void VectorStringSwig_SetRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, global::System.Runtime.InteropServices.HandleRef jarg3); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Contains___")] + public static extern bool VectorStringSwig_Contains(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_IndexOf___")] + public static extern int VectorStringSwig_IndexOf(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_LastIndexOf___")] + public static extern int VectorStringSwig_LastIndexOf(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Remove___")] + public static extern bool VectorStringSwig_Remove(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_delete_VectorStringSwig___")] + public static extern void delete_VectorStringSwig(global::System.Runtime.InteropServices.HandleRef jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_0___")] + public static extern global::System.IntPtr new_Transform__SWIG_0(); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_1___")] + public static extern global::System.IntPtr new_Transform__SWIG_1(uint jarg1); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_Transform_fromJsonGHEV___")] + public static extern global::System.IntPtr Transform_fromJsonGHEV(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_Transform_fromBase64GHEV___")] + public static extern global::System.IntPtr Transform_fromBase64GHEV(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_Transform_fromSUA___")] + public static extern global::System.IntPtr Transform_fromSUA(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); + + [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_delete_Transform___")] + public static extern void delete_Transform(global::System.Runtime.InteropServices.HandleRef jarg1); +} + +} diff --git a/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj b/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj new file mode 100644 index 00000000..6cd7ebf4 --- /dev/null +++ b/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj @@ -0,0 +1,17 @@ + + + net8.0 + enable + enable + + + + + + + + + PreserveNewest + + + diff --git a/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln b/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln new file mode 100644 index 00000000..c0981995 --- /dev/null +++ b/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35122.118 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiftyoneDegrees.Common", "FiftyoneDegrees.Common.csproj", "{D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {955F46EF-0512-44E7-BF66-D40079362FBE} + EndGlobalSection +EndGlobal diff --git a/interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs b/interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs new file mode 100644 index 00000000..3795e6fc --- /dev/null +++ b/interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs @@ -0,0 +1,313 @@ +//------------------------------------------------------------------------------ +// +// +// This file was automatically generated by SWIG (https://www.swig.org). +// Version 4.2.1 +// +// Do not make changes to this file unless you know what you are doing - modify +// the SWIG interface file instead. +//------------------------------------------------------------------------------ + +namespace FiftyoneDegrees.Common { + +public class MapStringStringSwig : global::System.IDisposable + , global::System.Collections.Generic.IDictionary + { + private global::System.Runtime.InteropServices.HandleRef swigCPtr; + protected bool swigCMemOwn; + + internal MapStringStringSwig(global::System.IntPtr cPtr, bool cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); + } + + internal static global::System.Runtime.InteropServices.HandleRef getCPtr(MapStringStringSwig obj) { + return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; + } + + internal static global::System.Runtime.InteropServices.HandleRef swigRelease(MapStringStringSwig obj) { + if (obj != null) { + if (!obj.swigCMemOwn) + throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); + global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.Dispose(); + return ptr; + } else { + return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } + + ~MapStringStringSwig() { + Dispose(false); + } + + public void Dispose() { + Dispose(true); + global::System.GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + lock(this) { + if (swigCPtr.Handle != global::System.IntPtr.Zero) { + if (swigCMemOwn) { + swigCMemOwn = false; + CommonPINVOKE.delete_MapStringStringSwig(swigCPtr); + } + swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } + } + + + public string this[string key] { + get { + return getitem(key); + } + + set { + setitem(key, value); + } + } + + public bool TryGetValue(string key, out string value) { + if (this.ContainsKey(key)) { + value = this[key]; + return true; + } + value = default(string); + return false; + } + + public bool IsEmpty { + get { + return empty(); + } + } + + public int Count { + get { + return (int)size(); + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public global::System.Collections.Generic.ICollection Keys { + get { + global::System.Collections.Generic.ICollection keys = new global::System.Collections.Generic.List(); + int size = this.Count; + if (size > 0) { + global::System.IntPtr iter = create_iterator_begin(); + for (int i = 0; i < size; i++) { + keys.Add(get_next_key(iter)); + } + destroy_iterator(iter); + } + return keys; + } + } + + public global::System.Collections.Generic.ICollection Values { + get { + global::System.Collections.Generic.ICollection vals = new global::System.Collections.Generic.List(); + foreach (global::System.Collections.Generic.KeyValuePair pair in this) { + vals.Add(pair.Value); + } + return vals; + } + } + + public void Add(global::System.Collections.Generic.KeyValuePair item) { + Add(item.Key, item.Value); + } + + public bool Remove(global::System.Collections.Generic.KeyValuePair item) { + if (Contains(item)) { + return Remove(item.Key); + } else { + return false; + } + } + + public bool Contains(global::System.Collections.Generic.KeyValuePair item) { + if (this[item.Key] == item.Value) { + return true; + } else { + return false; + } + } + + public void CopyTo(global::System.Collections.Generic.KeyValuePair[] array) { + CopyTo(array, 0); + } + + public void CopyTo(global::System.Collections.Generic.KeyValuePair[] array, int arrayIndex) { + if (array == null) + throw new global::System.ArgumentNullException("array"); + if (arrayIndex < 0) + throw new global::System.ArgumentOutOfRangeException("arrayIndex", "Value is less than zero"); + if (array.Rank > 1) + throw new global::System.ArgumentException("Multi dimensional array.", "array"); + if (arrayIndex+this.Count > array.Length) + throw new global::System.ArgumentException("Number of elements to copy is too large."); + + global::System.Collections.Generic.IList keyList = new global::System.Collections.Generic.List(this.Keys); + for (int i = 0; i < keyList.Count; i++) { + string currentKey = keyList[i]; + array.SetValue(new global::System.Collections.Generic.KeyValuePair(currentKey, this[currentKey]), arrayIndex+i); + } + } + + global::System.Collections.Generic.IEnumerator> global::System.Collections.Generic.IEnumerable>.GetEnumerator() { + return new MapStringStringSwigEnumerator(this); + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() { + return new MapStringStringSwigEnumerator(this); + } + + public MapStringStringSwigEnumerator GetEnumerator() { + return new MapStringStringSwigEnumerator(this); + } + + // Type-safe enumerator + /// Note that the IEnumerator documentation requires an InvalidOperationException to be thrown + /// whenever the collection is modified. This has been done for changes in the size of the + /// collection but not when one of the elements of the collection is modified as it is a bit + /// tricky to detect unmanaged code that modifies the collection under our feet. + public sealed class MapStringStringSwigEnumerator : global::System.Collections.IEnumerator, + global::System.Collections.Generic.IEnumerator> + { + private MapStringStringSwig collectionRef; + private global::System.Collections.Generic.IList keyCollection; + private int currentIndex; + private object currentObject; + private int currentSize; + + public MapStringStringSwigEnumerator(MapStringStringSwig collection) { + collectionRef = collection; + keyCollection = new global::System.Collections.Generic.List(collection.Keys); + currentIndex = -1; + currentObject = null; + currentSize = collectionRef.Count; + } + + // Type-safe iterator Current + public global::System.Collections.Generic.KeyValuePair Current { + get { + if (currentIndex == -1) + throw new global::System.InvalidOperationException("Enumeration not started."); + if (currentIndex > currentSize - 1) + throw new global::System.InvalidOperationException("Enumeration finished."); + if (currentObject == null) + throw new global::System.InvalidOperationException("Collection modified."); + return (global::System.Collections.Generic.KeyValuePair)currentObject; + } + } + + // Type-unsafe IEnumerator.Current + object global::System.Collections.IEnumerator.Current { + get { + return Current; + } + } + + public bool MoveNext() { + int size = collectionRef.Count; + bool moveOkay = (currentIndex+1 < size) && (size == currentSize); + if (moveOkay) { + currentIndex++; + string currentKey = keyCollection[currentIndex]; + currentObject = new global::System.Collections.Generic.KeyValuePair(currentKey, collectionRef[currentKey]); + } else { + currentObject = null; + } + return moveOkay; + } + + public void Reset() { + currentIndex = -1; + currentObject = null; + if (collectionRef.Count != currentSize) { + throw new global::System.InvalidOperationException("Collection modified."); + } + } + + public void Dispose() { + currentIndex = -1; + currentObject = null; + } + } + + + public MapStringStringSwig() : this(CommonPINVOKE.new_MapStringStringSwig__SWIG_0(), true) { + } + + public MapStringStringSwig(MapStringStringSwig other) : this(CommonPINVOKE.new_MapStringStringSwig__SWIG_1(MapStringStringSwig.getCPtr(other)), true) { + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + private uint size() { + uint ret = CommonPINVOKE.MapStringStringSwig_size(swigCPtr); + return ret; + } + + private bool empty() { + bool ret = CommonPINVOKE.MapStringStringSwig_empty(swigCPtr); + return ret; + } + + public void Clear() { + CommonPINVOKE.MapStringStringSwig_Clear(swigCPtr); + } + + private string getitem(string key) { + string ret = CommonPINVOKE.MapStringStringSwig_getitem(swigCPtr, key); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + private void setitem(string key, string x) { + CommonPINVOKE.MapStringStringSwig_setitem(swigCPtr, key, x); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public bool ContainsKey(string key) { + bool ret = CommonPINVOKE.MapStringStringSwig_ContainsKey(swigCPtr, key); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public void Add(string key, string value) { + CommonPINVOKE.MapStringStringSwig_Add(swigCPtr, key, value); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public bool Remove(string key) { + bool ret = CommonPINVOKE.MapStringStringSwig_Remove(swigCPtr, key); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + private global::System.IntPtr create_iterator_begin() { + global::System.IntPtr ret = CommonPINVOKE.MapStringStringSwig_create_iterator_begin(swigCPtr); + return ret; + } + + private string get_next_key(global::System.IntPtr swigiterator) { + string ret = CommonPINVOKE.MapStringStringSwig_get_next_key(swigCPtr, swigiterator); + return ret; + } + + private void destroy_iterator(global::System.IntPtr swigiterator) { + CommonPINVOKE.MapStringStringSwig_destroy_iterator(swigCPtr, swigiterator); + } + +} + +} diff --git a/interop/dotnet/FiftyoneDegrees.Common/Transform.cs b/interop/dotnet/FiftyoneDegrees.Common/Transform.cs new file mode 100644 index 00000000..3e753963 --- /dev/null +++ b/interop/dotnet/FiftyoneDegrees.Common/Transform.cs @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// +// +// This file was automatically generated by SWIG (https://www.swig.org). +// Version 4.2.1 +// +// Do not make changes to this file unless you know what you are doing - modify +// the SWIG interface file instead. +//------------------------------------------------------------------------------ + +namespace FiftyoneDegrees.Common { + +public class Transform : global::System.IDisposable { + private global::System.Runtime.InteropServices.HandleRef swigCPtr; + protected bool swigCMemOwn; + + internal Transform(global::System.IntPtr cPtr, bool cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); + } + + internal static global::System.Runtime.InteropServices.HandleRef getCPtr(Transform obj) { + return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; + } + + internal static global::System.Runtime.InteropServices.HandleRef swigRelease(Transform obj) { + if (obj != null) { + if (!obj.swigCMemOwn) + throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); + global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.Dispose(); + return ptr; + } else { + return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } + + ~Transform() { + Dispose(false); + } + + public void Dispose() { + Dispose(true); + global::System.GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + lock(this) { + if (swigCPtr.Handle != global::System.IntPtr.Zero) { + if (swigCMemOwn) { + swigCMemOwn = false; + CommonPINVOKE.delete_Transform(swigCPtr); + } + swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } + } + + public Transform() : this(CommonPINVOKE.new_Transform__SWIG_0(), true) { + } + + public Transform(uint capacity) : this(CommonPINVOKE.new_Transform__SWIG_1(capacity), true) { + } + + public MapStringStringSwig fromJsonGHEV(string json) { + MapStringStringSwig ret = new MapStringStringSwig(CommonPINVOKE.Transform_fromJsonGHEV(swigCPtr, json), true); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public MapStringStringSwig fromBase64GHEV(string json) { + MapStringStringSwig ret = new MapStringStringSwig(CommonPINVOKE.Transform_fromBase64GHEV(swigCPtr, json), true); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public MapStringStringSwig fromSUA(string json) { + MapStringStringSwig ret = new MapStringStringSwig(CommonPINVOKE.Transform_fromSUA(swigCPtr, json), true); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + +} + +} diff --git a/interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs b/interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs new file mode 100644 index 00000000..23afb1fe --- /dev/null +++ b/interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs @@ -0,0 +1,374 @@ +//------------------------------------------------------------------------------ +// +// +// This file was automatically generated by SWIG (https://www.swig.org). +// Version 4.2.1 +// +// Do not make changes to this file unless you know what you are doing - modify +// the SWIG interface file instead. +//------------------------------------------------------------------------------ + +namespace FiftyoneDegrees.Common { + +public class VectorStringSwig : global::System.IDisposable, global::System.Collections.IEnumerable, global::System.Collections.Generic.IList + { + private global::System.Runtime.InteropServices.HandleRef swigCPtr; + protected bool swigCMemOwn; + + internal VectorStringSwig(global::System.IntPtr cPtr, bool cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); + } + + internal static global::System.Runtime.InteropServices.HandleRef getCPtr(VectorStringSwig obj) { + return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; + } + + internal static global::System.Runtime.InteropServices.HandleRef swigRelease(VectorStringSwig obj) { + if (obj != null) { + if (!obj.swigCMemOwn) + throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); + global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.Dispose(); + return ptr; + } else { + return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } + + ~VectorStringSwig() { + Dispose(false); + } + + public void Dispose() { + Dispose(true); + global::System.GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + lock(this) { + if (swigCPtr.Handle != global::System.IntPtr.Zero) { + if (swigCMemOwn) { + swigCMemOwn = false; + CommonPINVOKE.delete_VectorStringSwig(swigCPtr); + } + swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } + } + + public VectorStringSwig(global::System.Collections.IEnumerable c) : this() { + if (c == null) + throw new global::System.ArgumentNullException("c"); + foreach (string element in c) { + this.Add(element); + } + } + + public VectorStringSwig(global::System.Collections.Generic.IEnumerable c) : this() { + if (c == null) + throw new global::System.ArgumentNullException("c"); + foreach (string element in c) { + this.Add(element); + } + } + + public bool IsFixedSize { + get { + return false; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public string this[int index] { + get { + return getitem(index); + } + set { + setitem(index, value); + } + } + + public int Capacity { + get { + return (int)capacity(); + } + set { + if (value < 0 || (uint)value < size()) + throw new global::System.ArgumentOutOfRangeException("Capacity"); + reserve((uint)value); + } + } + + public bool IsEmpty { + get { + return empty(); + } + } + + public int Count { + get { + return (int)size(); + } + } + + public bool IsSynchronized { + get { + return false; + } + } + + public void CopyTo(string[] array) + { + CopyTo(0, array, 0, this.Count); + } + + public void CopyTo(string[] array, int arrayIndex) + { + CopyTo(0, array, arrayIndex, this.Count); + } + + public void CopyTo(int index, string[] array, int arrayIndex, int count) + { + if (array == null) + throw new global::System.ArgumentNullException("array"); + if (index < 0) + throw new global::System.ArgumentOutOfRangeException("index", "Value is less than zero"); + if (arrayIndex < 0) + throw new global::System.ArgumentOutOfRangeException("arrayIndex", "Value is less than zero"); + if (count < 0) + throw new global::System.ArgumentOutOfRangeException("count", "Value is less than zero"); + if (array.Rank > 1) + throw new global::System.ArgumentException("Multi dimensional array.", "array"); + if (index+count > this.Count || arrayIndex+count > array.Length) + throw new global::System.ArgumentException("Number of elements to copy is too large."); + for (int i=0; i global::System.Collections.Generic.IEnumerable.GetEnumerator() { + return new VectorStringSwigEnumerator(this); + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() { + return new VectorStringSwigEnumerator(this); + } + + public VectorStringSwigEnumerator GetEnumerator() { + return new VectorStringSwigEnumerator(this); + } + + // Type-safe enumerator + /// Note that the IEnumerator documentation requires an InvalidOperationException to be thrown + /// whenever the collection is modified. This has been done for changes in the size of the + /// collection but not when one of the elements of the collection is modified as it is a bit + /// tricky to detect unmanaged code that modifies the collection under our feet. + public sealed class VectorStringSwigEnumerator : global::System.Collections.IEnumerator + , global::System.Collections.Generic.IEnumerator + { + private VectorStringSwig collectionRef; + private int currentIndex; + private object currentObject; + private int currentSize; + + public VectorStringSwigEnumerator(VectorStringSwig collection) { + collectionRef = collection; + currentIndex = -1; + currentObject = null; + currentSize = collectionRef.Count; + } + + // Type-safe iterator Current + public string Current { + get { + if (currentIndex == -1) + throw new global::System.InvalidOperationException("Enumeration not started."); + if (currentIndex > currentSize - 1) + throw new global::System.InvalidOperationException("Enumeration finished."); + if (currentObject == null) + throw new global::System.InvalidOperationException("Collection modified."); + return (string)currentObject; + } + } + + // Type-unsafe IEnumerator.Current + object global::System.Collections.IEnumerator.Current { + get { + return Current; + } + } + + public bool MoveNext() { + int size = collectionRef.Count; + bool moveOkay = (currentIndex+1 < size) && (size == currentSize); + if (moveOkay) { + currentIndex++; + currentObject = collectionRef[currentIndex]; + } else { + currentObject = null; + } + return moveOkay; + } + + public void Reset() { + currentIndex = -1; + currentObject = null; + if (collectionRef.Count != currentSize) { + throw new global::System.InvalidOperationException("Collection modified."); + } + } + + public void Dispose() { + currentIndex = -1; + currentObject = null; + } + } + + public VectorStringSwig() : this(CommonPINVOKE.new_VectorStringSwig__SWIG_0(), true) { + } + + public VectorStringSwig(VectorStringSwig other) : this(CommonPINVOKE.new_VectorStringSwig__SWIG_1(VectorStringSwig.getCPtr(other)), true) { + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public void Clear() { + CommonPINVOKE.VectorStringSwig_Clear(swigCPtr); + } + + public void Add(string x) { + CommonPINVOKE.VectorStringSwig_Add(swigCPtr, x); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + private uint size() { + uint ret = CommonPINVOKE.VectorStringSwig_size(swigCPtr); + return ret; + } + + private bool empty() { + bool ret = CommonPINVOKE.VectorStringSwig_empty(swigCPtr); + return ret; + } + + private uint capacity() { + uint ret = CommonPINVOKE.VectorStringSwig_capacity(swigCPtr); + return ret; + } + + private void reserve(uint n) { + CommonPINVOKE.VectorStringSwig_reserve(swigCPtr, n); + } + + public VectorStringSwig(int capacity) : this(CommonPINVOKE.new_VectorStringSwig__SWIG_2(capacity), true) { + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + private string getitemcopy(int index) { + string ret = CommonPINVOKE.VectorStringSwig_getitemcopy(swigCPtr, index); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + private string getitem(int index) { + string ret = CommonPINVOKE.VectorStringSwig_getitem(swigCPtr, index); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + private void setitem(int index, string val) { + CommonPINVOKE.VectorStringSwig_setitem(swigCPtr, index, val); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public void AddRange(VectorStringSwig values) { + CommonPINVOKE.VectorStringSwig_AddRange(swigCPtr, VectorStringSwig.getCPtr(values)); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public VectorStringSwig GetRange(int index, int count) { + global::System.IntPtr cPtr = CommonPINVOKE.VectorStringSwig_GetRange(swigCPtr, index, count); + VectorStringSwig ret = (cPtr == global::System.IntPtr.Zero) ? null : new VectorStringSwig(cPtr, true); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public void Insert(int index, string x) { + CommonPINVOKE.VectorStringSwig_Insert(swigCPtr, index, x); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public void InsertRange(int index, VectorStringSwig values) { + CommonPINVOKE.VectorStringSwig_InsertRange(swigCPtr, index, VectorStringSwig.getCPtr(values)); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public void RemoveAt(int index) { + CommonPINVOKE.VectorStringSwig_RemoveAt(swigCPtr, index); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public void RemoveRange(int index, int count) { + CommonPINVOKE.VectorStringSwig_RemoveRange(swigCPtr, index, count); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public static VectorStringSwig Repeat(string value, int count) { + global::System.IntPtr cPtr = CommonPINVOKE.VectorStringSwig_Repeat(value, count); + VectorStringSwig ret = (cPtr == global::System.IntPtr.Zero) ? null : new VectorStringSwig(cPtr, true); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public void Reverse() { + CommonPINVOKE.VectorStringSwig_Reverse__SWIG_0(swigCPtr); + } + + public void Reverse(int index, int count) { + CommonPINVOKE.VectorStringSwig_Reverse__SWIG_1(swigCPtr, index, count); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public void SetRange(int index, VectorStringSwig values) { + CommonPINVOKE.VectorStringSwig_SetRange(swigCPtr, index, VectorStringSwig.getCPtr(values)); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + } + + public bool Contains(string value) { + bool ret = CommonPINVOKE.VectorStringSwig_Contains(swigCPtr, value); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public int IndexOf(string value) { + int ret = CommonPINVOKE.VectorStringSwig_IndexOf(swigCPtr, value); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public int LastIndexOf(string value) { + int ret = CommonPINVOKE.VectorStringSwig_LastIndexOf(swigCPtr, value); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + + public bool Remove(string value) { + bool ret = CommonPINVOKE.VectorStringSwig_Remove(swigCPtr, value); + if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); + return ret; + } + +} + +} diff --git a/interop/dotnet/example/TransformExample/Program.cs b/interop/dotnet/example/TransformExample/Program.cs new file mode 100644 index 00000000..a1cef243 --- /dev/null +++ b/interop/dotnet/example/TransformExample/Program.cs @@ -0,0 +1,45 @@ +// See https://aka.ms/new-console-template for more information + +using System.Xml.Serialization; +using System; + +// Note the namespace defined as part of the SWIG-generated files +// that are referenced through the FiftyoneDegrees.Common.csproj wrapper project + +using FiftyoneDegrees.Common; + +Console.WriteLine("Current Dir: " + Directory.GetCurrentDirectory()); + +// MapStringStringSwig type is an ordinary key-value pair container +// that is corresponds to the C++ std::map type +// Can be used as Dictionary as demonstrated below +void outputResult(MapStringStringSwig result) { + foreach (var k in result.Keys) + { + Console.WriteLine(k + ": " + result[k]); + } + Console.WriteLine(""); +} + +// Instantiate the Transform object with a default buffer size of 1024 +// Optionaly set a working buffer size like `var transform = new Transform(2048);` +// It will be automatically increased if needed +// The instance is non-threadsafe, but reusable within a single thread +// i.e. can be used multiple times as in the example below +var transform = new Transform(); +{ + Console.WriteLine("From GHEV:"); + var result = transform.fromJsonGHEV("{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Chromium\",\"version\":\"128\"},{\"brand\":\"Not;A=Brand\",\"version\":\"24\"},{\"brand\":\"Google Chrome\",\"version\":\"128\"}],\"fullVersionList\":[{\"brand\":\"Chromium\",\"version\":\"128.0.6613.84\"},{\"brand\":\"Not;A=Brand\",\"version\":\"24.0.0.0\"},{\"brand\":\"Google Chrome\",\"version\":\"128.0.6613.84\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.6.1\"}"); + outputResult(result); +} +{ + Console.WriteLine("From SUA:"); + var result = transform.fromSUA("{\"browsers\":[{\"brand\":\"Chromium\",\"version\":[\"124\",\"0\",\"6367\",\"82\"]},{\"brand\":\"Google Chrome\",\"version\":[\"124\",\"0\",\"6367\",\"82\"]},{\"brand\":\"Not-A.Brand\",\"version\":[\"99\",\"0\",\"0\",\"0\"]}],\"platform\":{\"brand\":\"Android\",\"version\":[\"14\",\"0\",\"0\"]},\"mobile\":1,\"model\":\"SM-G998U\",\"source\":2}"); + outputResult(result); +} +{ + Console.WriteLine("From base64 GHEV:"); + var result = transform.fromBase64GHEV("eyJhcmNoaXRlY3R1cmUiOiJ4ODYiLCJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiQ2hyb21pdW0iLCJ2ZXJzaW9uIjoiMTI4In0seyJicmFuZCI6Ik5vdDtBPUJyYW5kIiwidmVyc2lvbiI6IjI0In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI4In1dLCJmdWxsVmVyc2lvbkxpc3QiOlt7ImJyYW5kIjoiQ2hyb21pdW0iLCJ2ZXJzaW9uIjoiMTI4LjAuNjYxMy44NCJ9LHsiYnJhbmQiOiJOb3Q7QT1CcmFuZCIsInZlcnNpb24iOiIyNC4wLjAuMCJ9LHsiYnJhbmQiOiJHb29nbGUgQ2hyb21lIiwidmVyc2lvbiI6IjEyOC4wLjY2MTMuODQifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiTWFjQm9vayBQcm8iLCJwbGF0Zm9ybSI6Im1hY09TIn0="); + outputResult(result); +} + diff --git a/interop/dotnet/example/TransformExample/TransformExample.csproj b/interop/dotnet/example/TransformExample/TransformExample.csproj new file mode 100644 index 00000000..d3328337 --- /dev/null +++ b/interop/dotnet/example/TransformExample/TransformExample.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/interop/dotnet/example/TransformExample/TransformExample.sln b/interop/dotnet/example/TransformExample/TransformExample.sln new file mode 100644 index 00000000..0cec6597 --- /dev/null +++ b/interop/dotnet/example/TransformExample/TransformExample.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35122.118 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransformExample", "TransformExample.csproj", "{8D52CC17-D1DA-425E-B039-94A0C0A20CBC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FiftyoneDegrees.Common", "..\..\FiftyoneDegrees.Common\FiftyoneDegrees.Common.csproj", "{73678907-B1B3-4904-9467-E77A52C07AB2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {73678907-B1B3-4904-9467-E77A52C07AB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73678907-B1B3-4904-9467-E77A52C07AB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73678907-B1B3-4904-9467-E77A52C07AB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73678907-B1B3-4904-9467-E77A52C07AB2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F0AA5F9F-E8C3-4DF2-9E9E-F7F69F9689BA} + EndGlobalSection +EndGlobal From 25c4f1105b16ca54daab34fdd4ff0a012fff0a1d Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Fri, 30 Aug 2024 08:58:21 +0200 Subject: [PATCH 49/73] FIX: string.c: null buffer case --- string.c | 4 +++- tests/StringsTests.cpp | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/string.c b/string.c index 25a1d6c9..ff3840dc 100644 --- a/string.c +++ b/string.c @@ -175,7 +175,9 @@ fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderComplete( builder->added++; } else { - *(builder->ptr + builder->length - 1) = '\0'; + if (builder->ptr && builder->length > 0) { + *(builder->ptr + builder->length - 1) = '\0'; + } builder->full = true; } return builder; diff --git a/tests/StringsTests.cpp b/tests/StringsTests.cpp index fd015b6b..b29642a6 100644 --- a/tests/StringsTests.cpp +++ b/tests/StringsTests.cpp @@ -171,6 +171,37 @@ TEST_F(Strings, StringBuilderAddMaxInt) { ASSERT_FALSE(builder->full); } +TEST_F(Strings, StringBuilderNullBuffer) { + StringBuilder *prev = builder; + StringBuilder local = {nullptr, 0}; + builder = &local; + fiftyoneDegreesStringBuilderInit(builder); + EXPECT_EQ(builder->remaining, 0); + fiftyoneDegreesStringBuilderAddChars(builder, (char *) "asdf", 4); + fiftyoneDegreesStringBuilderComplete(builder); + EXPECT_EQ(builder->full, true); + builder = prev; +} + +TEST_F(Strings, StringBuilderOneChar) { + StringBuilder *prev = builder; + StringBuilder local = {(char *) fiftyoneDegreesMalloc(1), 1}; + builder = &local; + + fiftyoneDegreesStringBuilderInit(builder); + EXPECT_EQ(builder->remaining, 1); + EXPECT_EQ(builder->full, false); + fiftyoneDegreesStringBuilderAddChars(builder, (char *) "a", 1); + EXPECT_EQ(builder->full, true); + fiftyoneDegreesStringBuilderComplete(builder); + EXPECT_STREQ(builder->ptr, ""); + EXPECT_EQ(builder->full, true); + + fiftyoneDegreesFree(local.ptr); + builder = prev; +} + + //Some tests below were generated using OpenAI ChatGPT TEST_F(Strings, SubString) { // Basic Match Test From 967f756541e02992498d720a414f9471b42eb5a1 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 3 Sep 2024 22:27:42 +0100 Subject: [PATCH 50/73] BUG: Relied on the including file to already have a reference to memory.h and the short form fiftyone.h. Now includes a direct reference to memory.h and uses the full name for Malloc. --- array.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/array.h b/array.h index 4f1750b3..baf08dd6 100644 --- a/array.h +++ b/array.h @@ -40,6 +40,9 @@ * @{ */ +#include +#include "memory.h" + /** * Simple array structure definition macro used for generic types. * @param t type of item @@ -64,7 +67,7 @@ typedef struct fiftyone_degrees_array_##t##_t { \ * Initialises the array. */ #define FIFTYONE_DEGREES_ARRAY_CREATE(t, i, c) \ -i = (t##Array*)Malloc(FIFTYONE_DEGREES_ARRAY_SIZE(t,c)); \ +i = (t##Array*)fiftyoneDegreesMalloc(FIFTYONE_DEGREES_ARRAY_SIZE(t,c)); \ if (i != NULL) { \ i->items = (t*)(i + 1); \ i->count = 0; \ From 6203f45e6912789432365ad671aa47ad35a0b04c Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 3 Sep 2024 22:28:41 +0100 Subject: [PATCH 51/73] FEAT/BUG: Adds a reference to the definition of size_t and now includes the macro to create an array of key value pairs. # Conflicts: # pair.h --- pair.h | 71 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/pair.h b/pair.h index af028da7..bb6a4c55 100644 --- a/pair.h +++ b/pair.h @@ -1,33 +1,38 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#ifndef FIFTYONE_DEGREES_PAIR_H_INCLUDED -#define FIFTYONE_DEGREES_PAIR_H_INCLUDED - -typedef struct fiftyone_degrees_key_value_pair_t { - char* key; - size_t keyLength; - char* value; - size_t valueLength; -} fiftyoneDegreesKeyValuePair; - -#endif +/* ********************************************************************* + * This Original Work is copyright of 51 Degrees Mobile Experts Limited. + * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, + * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. + * + * This Original Work is licensed under the European Union Public Licence + * (EUPL) v.1.2 and is subject to its terms as set out below. + * + * If a copy of the EUPL was not distributed with this file, You can obtain + * one at https://opensource.org/licenses/EUPL-1.2. + * + * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be + * amended by the European Commission) shall be deemed incompatible for + * the purposes of the Work and the provisions of the compatibility + * clause in Article 5 of the EUPL shall not apply. + * + * If using the Work as, or as part of, a network application, by + * including the attribution notice(s) required under Article 5 of the EUPL + * in the end user terms of the application under an appropriate heading, + * such notice(s) shall fulfill the requirements of that article. + * ********************************************************************* */ + +#ifndef FIFTYONE_DEGREES_PAIR_H_INCLUDED +#define FIFTYONE_DEGREES_PAIR_H_INCLUDED + +#include "array.h" +#include + +typedef struct fiftyone_degrees_key_value_pair_t { + char* key; + size_t keyLength; + char* value; + size_t valueLength; +} fiftyoneDegreesKeyValuePair; + +FIFTYONE_DEGREES_ARRAY_TYPE(fiftyoneDegreesKeyValuePair, ) + +#endif From 8fefbe7958c76240ee8fcd9faa713f53d69b4496 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 3 Sep 2024 22:31:04 +0100 Subject: [PATCH 52/73] REFACT: Integrates transform.* into the commone fiftyone.h short naming header file. Uses this header file within transform.c to remove verbosity. Move some lines around for consistency with the rest of the project and modified some comments. --- Transform.cpp | 2 +- Transform.hpp | 1 - fiftyone.h | 11 +++- transform.c | 151 +++++++++++++++++++++++++------------------------- transform.h | 69 +++++++---------------- 5 files changed, 108 insertions(+), 126 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index 7b7a8af9..2e03c93f 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -20,8 +20,8 @@ * such notice(s) shall fulfill the requirements of that article. * ********************************************************************* */ -#include "Exceptions.hpp" #include "Transform.hpp" +#include "fiftyone.h" using namespace FiftyoneDegrees::Common; diff --git a/Transform.hpp b/Transform.hpp index 928c0c69..22ae9b1d 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -29,7 +29,6 @@ #include "transform.h" - /** * A C++ wrapper class for transform.h conversion routines. * Encapsulates a working memory buffer to be reused when invoking conversion functions. The methods of a single instance can be invoked diff --git a/fiftyone.h b/fiftyone.h index e971ac1d..176e67a3 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -86,6 +86,7 @@ #include "yamlfile.h" #include "indices.h" #include "json.h" +#include "transform.h" /** * Macro used to support synonym implementation. Creates a typedef which @@ -184,6 +185,7 @@ MAP_TYPE(HeaderID) MAP_TYPE(IndexPropertyProfile) MAP_TYPE(StringBuilder) MAP_TYPE(Json) +MAP_TYPE(KeyValuePairArray) #define ProfileGetOffsetForProfileId fiftyoneDegreesProfileGetOffsetForProfileId /**< Synonym for #fiftyoneDegreesProfileGetOffsetForProfileId function. */ #define OverrideValuesAdd fiftyoneDegreesOverrideValuesAdd /**< Synonym for #fiftyoneDegreesOverrideValuesAdd function. */ @@ -356,6 +358,13 @@ MAP_TYPE(Json) #define StringBuilderAddInteger fiftyoneDegreesStringBuilderAddInteger /**< Synonym for fiftyoneDegreesStringBuilderAddInteger */ #define StringBuilderAddChars fiftyoneDegreesStringBuilderAddChars /**< Synonym for fiftyoneDegreesStringBuilderAddChars */ #define StringBuilderComplete fiftyoneDegreesStringBuilderComplete /**< Synonym for fiftyoneDegreesStringBuilderComplete */ +#define TransformGhevFromJson fiftyoneDegreesTransformGhevFromJson /**< Synonym for fiftyoneDegreesTransformGhevFromJson */ +#define TransformGhevFromBase64 fiftyoneDegreesTransformGhevFromBase64 /**< Synonym for fiftyoneDegreesTransformGhevFromBase64 */ +#define TransformSua fiftyoneDegreesTransformSua /**< Synonym for fiftyoneDegreesTransformSua */ +#define TransformIterateSua fiftyoneDegreesTransformIterateSua /**< Synonym for fiftyoneDegreesTransformIterateSua */ +#define TransformIterateGhevFromBase64 fiftyoneDegreesTransformIterateGhevFromBase64 /**< Synonym for fiftyoneDegreesTransformIterateGhevFromBase64 */ +#define TransformIterateGhevFromJson fiftyoneDegreesTransformIterateGhevFromJson /**< Synonym for fiftyoneDegreesTransformIterateGhevFromJson */ +#define TransformCallback fiftyoneDegreesTransformCallback /**< Synonym for fiftyoneDegreesTransformCallback */ /* <-- only one asterisk to avoid inclusion in documentation * Shortened macros. @@ -431,7 +440,7 @@ MAP_TYPE(Json) #define INCORRECT_IP_ADDRESS_FORMAT FIFTYONE_DEGREES_STATUS_INCORRECT_IP_ADDRESS_FORMAT /**< Synonym for #FIFTYONE_DEGREES_STATUS_INCORRECT_IP_ADDRESS_FORMAT status code. */ #define TEMP_FILE_ERROR FIFTYONE_DEGREES_STATUS_TEMP_FILE_ERROR /**< Synonym for #FIFTYONE_DEGREES_STATUS_INCORRECT_IP_ADDRESS_FORMAT status code. */ #define DATA_FILE_NEEDS_UPDATED FIFTYONE_DEGREES_STATUS_DATA_FILE_NEEDS_UPDATED /**< Synonym for #FIFTYONE_DEGREES_STATUS_DATA_FILE_NEEDS_UPDATED status code. */ - +#define INSUFFICIENT_CAPACITY FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY /**< Synonym for #FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status code. */ /** * @} */ diff --git a/transform.c b/transform.c index 1a5c7748..92c25740 100644 --- a/transform.c +++ b/transform.c @@ -21,19 +21,20 @@ * ********************************************************************* */ #include "transform.h" +#include "fiftyone.h" #define initStaticKey(x) {x, sizeof(x) - 1} -#define NotExpectSymbol(json, ch, exit) \ - if (*json == ch) { \ - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; \ - exit; \ +#define NotExpectSymbol(json, ch, exit) \ + if (*json == ch) { \ + exception->status = CORRUPT_DATA; \ + exit; \ } -#define ExpectSymbol(json, ch, exit) \ - if (*json != ch) { \ - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; \ - exit; \ +#define ExpectSymbol(json, ch, exit) \ + if (*json != ch) { \ + exception->status = CORRUPT_DATA; \ + exit; \ } #define ExpectKeySymbol(json, ch) \ @@ -42,9 +43,9 @@ return KEY_UNDEFINED; \ } -#define ValuePtr \ - (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY ? NULL \ - : begin) +#define ValuePtr \ + (exception->status == INSUFFICIENT_MEMORY ? NULL \ + : begin) #define GET_SEXTET(str, i) \ (str[i] == '=' ? 0 & i++ : base64_char_to_value(str[i++], exception)) @@ -61,11 +62,11 @@ typedef enum { KEY_UNDEFINED, // } Key; -typedef Key (*readKeyCallback)(const char**, fiftyoneDegreesException* const); +typedef Key (*readKeyCallback)(const char**, Exception* const); typedef char* (*readValueCallback)(const char** json, char* begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, Key key, - fiftyoneDegreesException* const exception); + KeyValuePair* cache, Key key, + Exception* const exception); static struct { const char* key; @@ -85,18 +86,18 @@ static struct { static inline char* safe_write_to_buffer( char* begin, const char* const end, char symbol, - fiftyoneDegreesException* const exception) { + Exception* const exception) { if (begin < end) { *begin = symbol; } else { - exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY; + exception->status = INSUFFICIENT_MEMORY; } return ++begin; } static inline uint32_t base64_char_to_value( - char c, fiftyoneDegreesException* const exception) { + char c, Exception* const exception) { static const uint32_t base64_lookup_table[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -118,7 +119,7 @@ static inline uint32_t base64_char_to_value( 255}; if (base64_lookup_table[(uint8_t)c] == 255) { - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + exception->status = CORRUPT_DATA; } return base64_lookup_table[(uint8_t)c]; @@ -126,10 +127,10 @@ static inline uint32_t base64_char_to_value( static size_t base64_decode(const char* base64_input, char* const buffer, size_t length, - fiftyoneDegreesException* const exception) { + Exception* const exception) { size_t input_length = strlen(base64_input); if (input_length % 4 != 0) { - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + exception->status = CORRUPT_DATA; return 0; // Invalid base64 input length } @@ -142,7 +143,7 @@ static size_t base64_decode(const char* base64_input, char* const buffer, uint32_t sextet_c = GET_SEXTET(base64_input, i); uint32_t sextet_d = GET_SEXTET(base64_input, i); - if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + if (exception->status == CORRUPT_DATA) { return 0; } @@ -295,8 +296,8 @@ static const char* skip_value(const char* json) { } static inline char* init_keys(char* const begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, - fiftyoneDegreesException* const exception) { + KeyValuePair* cache, + Exception* const exception) { char* ptr = begin; for (size_t k = 0; k < KEY_UNDEFINED; ++k) { @@ -313,9 +314,9 @@ static inline char* init_keys(char* const begin, const char* const end, static const char* init_parsing(const char* json, char** begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, - fiftyoneDegreesException* const exception) { - exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; + KeyValuePair* cache, + Exception* const exception) { + exception->status = SUCCESS; *begin = init_keys(*begin, end, cache, exception); @@ -325,7 +326,7 @@ static const char* init_parsing(const char* json, char** begin, } static Key read_ghev_key(const char** json, - fiftyoneDegreesException* const exception) { + Exception* const exception) { enum ReadKeyState { READ_KEY_INIT, ARCH, @@ -500,7 +501,7 @@ static Key read_ghev_key(const char** json, } static Key read_sua_key(const char** json, - fiftyoneDegreesException* const exception) { + Exception* const exception) { enum ReadKeyState { READ_KEY_INIT, BROWSERS_OR_BITNESS, @@ -643,7 +644,7 @@ static Key read_sua_key(const char** json, static char* read_string_value(const char** json, char* begin, const char* const end, - fiftyoneDegreesException* const exception) { + Exception* const exception) { *json = skip_whitespaces(*json); if (**json == 'n') { ++(*json); @@ -684,8 +685,8 @@ static char* read_string_value(const char** json, char* begin, static char* read_bool_ghev_value(const char** json, char* begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, Key key, - fiftyoneDegreesException* const exception) { + KeyValuePair* cache, Key key, + Exception* const exception) { char* ptr = begin; switch (**json) { @@ -710,7 +711,7 @@ static char* read_bool_ghev_value(const char** json, char* begin, } break; default: { - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + exception->status = CORRUPT_DATA; return begin; } break; } @@ -723,15 +724,15 @@ static char* read_bool_ghev_value(const char** json, char* begin, static char* read_bool_sua_value(const char** json, char* begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, Key key, - fiftyoneDegreesException* const exception) { + KeyValuePair* cache, Key key, + Exception* const exception) { switch (**json) { case '0': case '1': { } break; default: { - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + exception->status = CORRUPT_DATA; return begin; } break; } @@ -749,7 +750,7 @@ static char* read_bool_sua_value(const char** json, char* begin, static char* read_version_sua(const char** json, char* begin, const char* const end, - fiftyoneDegreesException* const exception) { + Exception* const exception) { enum version_state { version_read, version_skip, @@ -807,8 +808,8 @@ static char* read_version_sua(const char** json, char* begin, static char* read_brands_ghev_value(const char** json, char* begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, Key key, - fiftyoneDegreesException* const exception) { + KeyValuePair* cache, Key key, + Exception* const exception) { *json = skip_to_next_char(*json, '['); ExpectSymbol(*json, '[', return begin); @@ -894,7 +895,7 @@ static char* read_brands_ghev_value(const char** json, char* begin, } break; default: { - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + exception->status = CORRUPT_DATA; return begin; } break; } @@ -903,8 +904,8 @@ static char* read_brands_ghev_value(const char** json, char* begin, static char* read_brands_sua_value(const char** json, char* begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, Key key, - fiftyoneDegreesException* const exception) { + KeyValuePair* cache, Key key, + Exception* const exception) { *json = skip_to_next_char(*json, '['); ExpectSymbol(*json, '[', return begin); @@ -983,7 +984,7 @@ static char* read_brands_sua_value(const char** json, char* begin, } break; default: { - exception->status = FIFTYONE_DEGREES_STATUS_CORRUPT_DATA; + exception->status = CORRUPT_DATA; return begin; } break; } @@ -992,8 +993,8 @@ static char* read_brands_sua_value(const char** json, char* begin, static char* read_pure_string_value(const char** json, char* begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, Key key, - fiftyoneDegreesException* const exception) { + KeyValuePair* cache, Key key, + Exception* const exception) { char* ptr = read_string_value(json, begin, end, exception); if (ptr != NULL) { @@ -1006,8 +1007,8 @@ static char* read_pure_string_value(const char** json, char* begin, static char* read_platform_sua_value( const char** json, char* begin, const char* const end, - fiftyoneDegreesKeyValuePair* cache, Key key, - fiftyoneDegreesException* const exception) { + KeyValuePair* cache, Key key, + Exception* const exception) { *json = skip_to_next_char(*json, '{'); ExpectSymbol(*json, '{', return begin); @@ -1117,12 +1118,11 @@ static inline readValueCallback read_value_switch(Key key, int isSua) { return res; } -static bool pushToHeaders(void* ctx, fiftyoneDegreesKeyValuePair header) { - fiftyoneDegreesKeyValuePairArray* const headers = - (fiftyoneDegreesKeyValuePairArray* const)ctx; +static bool pushToHeaders(void* ctx, KeyValuePair header) { + KeyValuePairArray* const headers = (KeyValuePairArray* const)ctx; if (headers->count < headers->capacity) { - fiftyoneDegreesKeyValuePair* pair = headers->items + headers->count++; + KeyValuePair* pair = headers->items + headers->count++; pair->key = header.key; pair->keyLength = header.keyLength; @@ -1134,11 +1134,11 @@ static bool pushToHeaders(void* ctx, fiftyoneDegreesKeyValuePair header) { // ------------------------------------------------------------------------------------------------ static size_t main_parsing_body(const char* json, char* const buffer, size_t length, - fiftyoneDegreesException* const exception, + Exception* const exception, int isSua, - fiftyoneDegreesTransformCallback callback, + TransformCallback callback, void* ctx) { - fiftyoneDegreesKeyValuePair cache[KEY_UNDEFINED]; + KeyValuePair cache[KEY_UNDEFINED]; // define buffer range char* begin = buffer; @@ -1146,8 +1146,8 @@ static size_t main_parsing_body(const char* json, char* const buffer, // write keys to buffer, init cache and skip to the first key json = init_parsing(json, &begin, end, cache, exception); - if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA || - exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { + if (exception->status == CORRUPT_DATA || + exception->status == INSUFFICIENT_MEMORY) { return 0; } @@ -1172,7 +1172,7 @@ static size_t main_parsing_body(const char* json, char* const buffer, NotExpectSymbol(json, '\0', break); char* ptr = read_value(&json, begin, end, cache, key, exception); - if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + if (exception->status == CORRUPT_DATA) { break; } @@ -1199,73 +1199,76 @@ static size_t main_parsing_body(const char* json, char* const buffer, } // ------------------------------------------------------------------------------------------------ -size_t fiftyoneDegreesTransformIterateGhevFromJson( +size_t TransformIterateGhevFromJson( const char* json, char* const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { return main_parsing_body(json, buffer, length, exception, 0, callback, ctx); } -size_t fiftyoneDegreesTransformIterateGhevFromBase64( +size_t TransformIterateGhevFromBase64( const char* base64, char* buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { size_t offset = base64_decode(base64, buffer, length, exception); - if (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY || - exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + if (exception->status == INSUFFICIENT_MEMORY || + exception->status == CORRUPT_DATA) { return 0; } - return fiftyoneDegreesTransformIterateGhevFromJson( + return TransformIterateGhevFromJson( buffer, buffer + offset, length - offset, callback, ctx, exception); } -size_t fiftyoneDegreesTransformIterateSua( +size_t TransformIterateSua( const char* json, char* const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { return main_parsing_body(json, buffer, length, exception, 1, callback, ctx); } -size_t fiftyoneDegreesTransformGhevFromJson( +size_t TransformGhevFromJson( const char* json, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { - size_t calls = fiftyoneDegreesTransformIterateGhevFromJson( + uint32_t initial = headers->count; + size_t calls = TransformIterateGhevFromJson( json, buffer, length, pushToHeaders, headers, exception); - if (calls != headers->count) { - exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; + if (calls != headers->count - initial) { + exception->status = INSUFFICIENT_CAPACITY; } return calls; } -size_t fiftyoneDegreesTransformGhevFromBase64( +size_t TransformGhevFromBase64( const char* base64, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { - size_t calls = fiftyoneDegreesTransformIterateGhevFromBase64( + uint32_t initial = headers->count; + size_t calls = TransformIterateGhevFromBase64( base64, buffer, length, pushToHeaders, headers, exception); - if (calls != headers->count) { - exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; + if (calls != headers->count - initial) { + exception->status = INSUFFICIENT_CAPACITY; } return calls; } -size_t fiftyoneDegreesTransformSua( +size_t TransformSua( const char* json, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { - size_t calls = fiftyoneDegreesTransformIterateSua( + uint32_t initial = headers->count; + size_t calls = TransformIterateSua( json, buffer, length, pushToHeaders, headers, exception); - if (calls != headers->count) { - exception->status = FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY; + if (calls != headers->count - initial) { + exception->status = INSUFFICIENT_CAPACITY; } return calls; diff --git a/transform.h b/transform.h index bb246448..43aa3507 100644 --- a/transform.h +++ b/transform.h @@ -23,13 +23,12 @@ #ifndef FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED #define FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED -#include "array.h" -#include "exceptions.h" -#include "fiftyone.h" +#include #include "pair.h" +#include "exceptions.h" /** - * User-Agent Client Hints Representation Conversion Routines + * User-Agent Client Hints (UACH) Representation Conversion Routines * * 3 common ways to represent UACH are: * - [HTTP header map](https://wicg.github.io/ua-client-hints/) @@ -44,7 +43,7 @@ * - [OpenRTB 2.6 * spec](https://iabtechlab.com/wp-content/uploads/2022/04/OpenRTB-2-6_FINAL.pdf) * - * 51degrees uses HTTP header map to represent UACH and expects the evidence to + * 51Degrees uses HTTP header map to represent UACH and expects the evidence to * be provided as HTTP headers (or same name query parameters). The header * names in question are: * - Sec-CH-UA @@ -56,8 +55,10 @@ * - Sec-CH-UA-Arch * - Sec-CH-UA-Bitness * - * The conversion routines transform the GHEV or SUA input into the HTTP header - * maps Routines are provided in 2 styles: iterative (for potentially lazy + * The conversion routines transform the GetHighEntropyValues (GHEV) or + * Structured User Agent (SUA) SUA input into the HTTP header maps. + * + * Routines are provided in 2 styles: iterative (for potentially lazy * consumption) and array-results (for eager consumption). The former uses * callback to iteratively provide header name-value pairs to the caller, the * latter provides the whole header map array as output. In addition 2 variants @@ -115,14 +116,10 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * @return the number of iterations / header pairs detected (callback calls * made) */ -EXTERNAL -size_t -fiftyoneDegreesTransformIterateGhevFromJson -( +EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( const char *json, char *const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception -); + fiftyoneDegreesException *const exception); /** * Iteratively convert getHighEntropyValue() API result base64 encoded JSON @@ -155,14 +152,10 @@ fiftyoneDegreesTransformIterateGhevFromJson * @return the number of iterations / header pairs detected (callback calls * made) */ -EXTERNAL -size_t -fiftyoneDegreesTransformIterateGhevFromBase64 -( +EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( const char *base64, char *buffer, size_t length, fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception -); + fiftyoneDegreesException *const exception); /** * Iteratively convert device.sua JSON string to HTTP header representation. @@ -193,21 +186,10 @@ fiftyoneDegreesTransformIterateGhevFromBase64 * @return the number of iterations / header pairs detected (callback calls * made) */ -EXTERNAL -size_t -fiftyoneDegreesTransformIterateSua -( +EXTERNAL size_t fiftyoneDegreesTransformIterateSua( const char *json, char *buffer, size_t length, fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception -); - -/** - * A preallocated array of key-value pairs intended to be an array of HTTP - * headers - */ -#define NONE -FIFTYONE_DEGREES_ARRAY_TYPE(fiftyoneDegreesKeyValuePair, NONE); + fiftyoneDegreesException *const exception); /** * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header @@ -244,14 +226,10 @@ FIFTYONE_DEGREES_ARRAY_TYPE(fiftyoneDegreesKeyValuePair, NONE); * status and the returned capacity will signal the array size that needs to be * allocated */ -EXTERNAL -size_t -fiftyoneDegreesTransformGhevFromJson -( +EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( const char *json, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception -); + fiftyoneDegreesException *const exception); /** * Eagerly convert getHighEntropyValue() API result from base64-encoded JSON @@ -289,14 +267,10 @@ fiftyoneDegreesTransformGhevFromJson * status and the returned capacity will signal the array size that needs to be * allocated */ -EXTERNAL -size_t -fiftyoneDegreesTransformGhevFromBase64 -( +EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( const char *base64, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception -); + fiftyoneDegreesException *const exception); /** * Eagerly convert device.sua JSON string to HTTP header representation. @@ -332,12 +306,9 @@ fiftyoneDegreesTransformGhevFromBase64 * status and the returned capacity will signal the array size that needs to be * allocated */ -EXTERNAL size_t -fiftyoneDegreesTransformSua -( +EXTERNAL size_t fiftyoneDegreesTransformSua( const char *json, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception -); + fiftyoneDegreesException *const exception); #endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ From 50186214fde8fe2cee00d5658f11e200261b1d07 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 4 Sep 2024 11:15:21 +0100 Subject: [PATCH 53/73] BUG: Incorrectly truncated the external method names. --- transform.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/transform.c b/transform.c index 92c25740..6109fb30 100644 --- a/transform.c +++ b/transform.c @@ -1199,14 +1199,14 @@ static size_t main_parsing_body(const char* json, char* const buffer, } // ------------------------------------------------------------------------------------------------ -size_t TransformIterateGhevFromJson( +size_t fiftyoneDegreesTransformIterateGhevFromJson( const char* json, char* const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { return main_parsing_body(json, buffer, length, exception, 0, callback, ctx); } -size_t TransformIterateGhevFromBase64( +size_t fiftyoneDegreesTransformIterateGhevFromBase64( const char* base64, char* buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { @@ -1217,19 +1217,19 @@ size_t TransformIterateGhevFromBase64( return 0; } - return TransformIterateGhevFromJson( + return fiftyoneDegreesTransformIterateGhevFromJson( buffer, buffer + offset, length - offset, callback, ctx, exception); } -size_t TransformIterateSua( +size_t fiftyoneDegreesTransformIterateSua( const char* json, char* const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { return main_parsing_body(json, buffer, length, exception, 1, callback, ctx); } -size_t TransformGhevFromJson( +size_t fiftyoneDegreesTransformGhevFromJson( const char* json, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { @@ -1244,7 +1244,7 @@ size_t TransformGhevFromJson( return calls; } -size_t TransformGhevFromBase64( +size_t fiftyoneDegreesTransformGhevFromBase64( const char* base64, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { @@ -1259,7 +1259,7 @@ size_t TransformGhevFromBase64( return calls; } -size_t TransformSua( +size_t fiftyoneDegreesTransformSua( const char* json, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { From ac5ded4a74dbffa548e6b3a59ca4015864e3e8d5 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 4 Sep 2024 14:01:11 +0100 Subject: [PATCH 54/73] BUG: If the characters being added are not null terminated then the assert can result in a memory fault. Assert removed as there is not requirement for strings to be null terminated as the length is always provided. --- string.c | 1 - 1 file changed, 1 deletion(-) diff --git a/string.c b/string.c index ff3840dc..41c6a725 100644 --- a/string.c +++ b/string.c @@ -150,7 +150,6 @@ fiftyoneDegreesStringBuilder* fiftyoneDegreesStringBuilderAddChars( fiftyoneDegreesStringBuilder* builder, char* const value, size_t const length) { - assert(strlen(value) == length); if (length < builder->remaining && memcpy(builder->current, value, length) == builder->current) { builder->remaining -= length; From fd166c9004751f295fa54f96dafab22b74d913c8 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Sat, 7 Sep 2024 01:24:34 +0100 Subject: [PATCH 55/73] OPTIM: When iterating evidence the callback method might add new values to the array. There is no need to iterate over these additional values. The count of initial items is now used to limit the iterations rather than the current count of items in the array. --- evidence.c | 15 ++++++++------- fiftyone.h | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/evidence.c b/evidence.c index 9ff50bbb..874c3774 100644 --- a/evidence.c +++ b/evidence.c @@ -60,7 +60,7 @@ static void setPairHeader(EvidenceKeyValuePair* pair, Header* header) { } } -/* +/** * Iterate through an evidence collection and perform callback on the evidence * whose prefix matches the input prefixes. * @@ -71,24 +71,25 @@ static void setPairHeader(EvidenceKeyValuePair* pair, Header* header) { * @return number of evidence processed. */ static uint32_t evidenceIterate( - fiftyoneDegreesEvidenceKeyValuePairArray* evidence, + EvidenceKeyValuePairArray* evidence, int prefixes, void* state, - fiftyoneDegreesEvidenceIterateMethod callback) { - uint32_t i = 0, count = 0; + EvidenceIterateMethod callback) { + uint32_t i = 0, iterations = 0; + const uint32_t count = evidence->count; EvidenceKeyValuePair* pair; bool cont = true; - while (cont && i < evidence->count) { + while (cont && i < count) { pair = &evidence->items[i++]; if ((pair->prefix & prefixes) == pair->prefix) { if (pair->parsedValue == NULL) { parsePair(pair); } cont = callback(state, pair); - count++; + iterations++; } } - return count; + return iterations; } /** diff --git a/fiftyone.h b/fiftyone.h index 176e67a3..4a6c7628 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -365,6 +365,7 @@ MAP_TYPE(KeyValuePairArray) #define TransformIterateGhevFromBase64 fiftyoneDegreesTransformIterateGhevFromBase64 /**< Synonym for fiftyoneDegreesTransformIterateGhevFromBase64 */ #define TransformIterateGhevFromJson fiftyoneDegreesTransformIterateGhevFromJson /**< Synonym for fiftyoneDegreesTransformIterateGhevFromJson */ #define TransformCallback fiftyoneDegreesTransformCallback /**< Synonym for fiftyoneDegreesTransformCallback */ +#define EvidenceIterateMethod fiftyoneDegreesEvidenceIterateMethod /**< Synonym for fiftyoneDegreesEvidenceIterateMethod */ /* <-- only one asterisk to avoid inclusion in documentation * Shortened macros. From 0d56384a6299b1e9e8a7c14d5d13d13ab3795e25 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Wed, 4 Sep 2024 15:19:48 +0200 Subject: [PATCH 56/73] FIX: reinstate Exceptions.hpp include for C++ throw macro resolution --- Transform.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Transform.cpp b/Transform.cpp index 2e03c93f..b0d6b51d 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -20,6 +20,9 @@ * such notice(s) shall fulfill the requirements of that article. * ********************************************************************* */ +//Exceptions.hpp must be included before "exceptions.h" to switch to +//C++ exceptions semantics - so the macros translate into throw +#include "Exceptions.hpp" #include "Transform.hpp" #include "fiftyone.h" From f743feb91a34ac76a41c7427e4490d9e1f826fb2 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Wed, 4 Sep 2024 15:46:59 +0200 Subject: [PATCH 57/73] BUG: transform.c exception was set to SUCCESS --- tests/TransformTests.cpp | 221 +++++++++++++++++++++++---------------- transform.c | 1 - 2 files changed, 129 insertions(+), 93 deletions(-) diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 9d628970..942286c7 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -22,16 +22,16 @@ #include "../Exceptions.hpp" #include "../Transform.hpp" -#include "../memory.h" +#include "../fiftyone.h" #include "Base.hpp" + class Transform : public Base { public: virtual void SetUp(); virtual void TearDown(); static bool found; static fiftyoneDegreesKeyValuePairArray *results; - static fiftyoneDegreesException exception; static const char *expectedFieldName; static const char *expectedFieldValue; void checkFieldValue(const char *field, const char *value); @@ -42,7 +42,6 @@ bool Transform::found = false; const char *Transform::expectedFieldName = NULL; const char *Transform::expectedFieldValue = NULL; fiftyoneDegreesKeyValuePairArray *Transform::results = NULL; -fiftyoneDegreesException Transform::exception; void Transform::checkFieldValue(const char *field, const char *value) { found = false; @@ -158,13 +157,14 @@ TEST_F(Transform, GHEVIterativeJSON) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + ASSERT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 7); ASSERT_EQ(results->count, count); @@ -183,6 +183,8 @@ TEST_F(Transform, GHEVIterativeJSON) { checkFieldValue("sec-ch-ua-platform", "\"macOS\""); checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); fiftyoneDegreesFree(buffer); + + EXPECT_TRUE((bool) FIFTYONE_DEGREES_EXCEPTION_OKAY); } TEST_F(Transform, IncompleteJSON) { @@ -270,21 +272,23 @@ TEST_F(Transform, IncompleteJSON) { "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"}", "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", }; - + + EXCEPTION_CREATE; for (const std::string &j : correct) { fiftyoneDegreesTransformIterateGhevFromJson( j.c_str(), buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); + Transform::results, exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); } for (const std::string &j : corrupted) { fiftyoneDegreesTransformIterateGhevFromJson( j.c_str(), buffer, bufferLength, fillResultsCallback, - Transform::results, &Transform::exception); - - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + Transform::results, exception); + EXPECT_FALSE(EXCEPTION_OKAY); + EXPECT_TRUE(EXCEPTION_FAILED); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); } fiftyoneDegreesFree(buffer); } @@ -379,20 +383,21 @@ TEST_F(Transform, IncompleteSUA) { "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]},", }; + EXCEPTION_CREATE; for (const std::string &j : correct) { fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); } for (const std::string &j : corrupted) { fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); } fiftyoneDegreesFree(buffer); } @@ -409,13 +414,14 @@ TEST_F(Transform, GHEVIncorrectBool) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -425,13 +431,14 @@ TEST_F(Transform, SUAIncorrectBool) { size_t bufferLength = 512; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -446,14 +453,14 @@ TEST_F(Transform, GHEVIterativeNULLBrandJSON) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 6); ASSERT_EQ(results->count, count); @@ -481,13 +488,14 @@ TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 6); ASSERT_EQ(results->count, count); @@ -516,13 +524,14 @@ TEST_F(Transform, GHEVIterativeBase64) { size_t bufferLength = 686; // strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 6); ASSERT_EQ(results->count, count); @@ -556,12 +565,13 @@ TEST_F(Transform, GHEVBase64CorruptedLen) { size_t bufferLength = 686; // strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - + + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -579,11 +589,12 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol) { size_t bufferLength = 686; // strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -601,11 +612,12 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol2) { size_t bufferLength = 686; // strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -625,13 +637,14 @@ TEST_F(Transform, GHEVIterativeSua) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 7); ASSERT_EQ(results->count, count); @@ -667,13 +680,14 @@ TEST_F(Transform, SuaWeirdPlatformVersion) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 7); ASSERT_EQ(results->count, count); @@ -710,13 +724,14 @@ TEST_F(Transform, SuaNullBrandPlatform) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 5); ASSERT_EQ(results->count, count); @@ -747,12 +762,13 @@ TEST_F(Transform, GHEVArrayJSON) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, results, &Transform::exception); + ghev, buffer, bufferLength, results, exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 7); ASSERT_EQ(results->count, count); @@ -788,10 +804,11 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, headers, &Transform::exception); + ghev, buffer, bufferLength, headers, exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 2); ASSERT_EQ(headers->count, count); @@ -799,9 +816,9 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { // --- count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, empty_headers, &Transform::exception); + ghev, buffer, bufferLength, empty_headers, exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); fiftyoneDegreesFree(buffer); @@ -825,10 +842,11 @@ TEST_F(Transform, GHEVBase64InsufficientCapacity) { fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformGhevFromBase64( - ghev, buffer, bufferLength, empty_headers, &Transform::exception); + ghev, buffer, bufferLength, empty_headers, exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); fiftyoneDegreesFree(buffer); @@ -850,19 +868,21 @@ TEST_F(Transform, SUAInsufficientCapacity) { fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformSua( - sua, buffer, bufferLength, empty_headers, &Transform::exception); + sua, buffer, bufferLength, empty_headers, exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); fiftyoneDegreesKeyValuePairArray *headers = NULL; FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); + FIFTYONE_DEGREES_EXCEPTION_CLEAR; count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, - &Transform::exception); + exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 3); fiftyoneDegreesFree(headers); @@ -883,12 +903,13 @@ TEST_F(Transform, GHEVBase64) { size_t bufferLength = strlen(ghev) * 2; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformGhevFromBase64( - ghev, buffer, bufferLength, results, &Transform::exception); + ghev, buffer, bufferLength, results, exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 6); ASSERT_EQ(results->count, count); @@ -922,13 +943,14 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -945,13 +967,14 @@ TEST_F(Transform, SUANotEnoughMemory) { size_t bufferLength = 144; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -967,13 +990,13 @@ TEST_F(Transform, GHEVArraySua) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 6); ASSERT_EQ(results->count, count); @@ -1006,12 +1029,13 @@ TEST_F(Transform, SUAArrayNotEnoughMemory) { size_t bufferLength = 142; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, - &Transform::exception); + exception); // --- - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1028,12 +1052,13 @@ TEST_F(Transform, GHEVPartial) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - + + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); + exception); ASSERT_EQ(count, 4); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile @@ -1079,12 +1104,13 @@ TEST_F(Transform, GHEVIgnoreUnused) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); + exception); ASSERT_EQ(count, 8); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile @@ -1126,10 +1152,11 @@ TEST_F(Transform, GHEVCorruptInput) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + Transform::results, exception); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -1147,10 +1174,11 @@ TEST_F(Transform, GHEVBufferTooSmall) { size_t bufferLength = 20; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + Transform::results, exception); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1168,10 +1196,11 @@ TEST_F(Transform, GHEVEvidenceLowCapacity) { size_t bufferLength = 20; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, - Transform::results, &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + Transform::results, exception); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1187,11 +1216,12 @@ TEST_F(Transform, SUAHappyPath) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); + exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 7); // we expect to see these headers detected: @@ -1237,9 +1267,10 @@ TEST_F(Transform, SUAPlatformExt) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); + exception); ASSERT_EQ(count, 5); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform @@ -1247,7 +1278,7 @@ TEST_F(Transform, SUAPlatformExt) { // sec-ch-ua-full-version-list ASSERT_EQ(results->count, count); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); checkFieldAbsent("sec-ch-ua"); checkFieldValue( @@ -1274,9 +1305,10 @@ TEST_F(Transform, SUAPartial1) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); + exception); ASSERT_EQ(count, 4); // we expect to see these headers detected: // low entropy: sec-ch-ua-mobile, sec-ch-ua-platform @@ -1284,7 +1316,7 @@ TEST_F(Transform, SUAPartial1) { // sec-ch-ua-full-version-list ASSERT_EQ(results->count, count); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); checkFieldAbsent("sec-ch-ua"); checkFieldValue( @@ -1309,12 +1341,13 @@ TEST_F(Transform, SUAPartial2) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); + exception); ASSERT_EQ(count, 5); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + EXPECT_TRUE(EXCEPTION_OKAY); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, @@ -1342,10 +1375,11 @@ TEST_F(Transform, SUATolerableCorrupt) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; size_t count = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_SUCCESS); + exception); + EXPECT_TRUE(EXCEPTION_OKAY); ASSERT_EQ(count, 5); ASSERT_EQ(results->count, count); @@ -1371,10 +1405,11 @@ TEST_F(Transform, SUACorrupt2) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + exception); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -1386,11 +1421,11 @@ TEST_F(Transform, SUACorrupt3) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + exception); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); fiftyoneDegreesFree(buffer); } @@ -1406,10 +1441,11 @@ TEST_F(Transform, SUABufferTooSmall) { size_t bufferLength = 15; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + exception); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } @@ -1425,10 +1461,11 @@ TEST_F(Transform, SUAEvidenceLowCapacity) { size_t bufferLength = 15; char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, - &exception); - ASSERT_EQ(exception.status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + exception); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); } diff --git a/transform.c b/transform.c index 6109fb30..f6a023cf 100644 --- a/transform.c +++ b/transform.c @@ -316,7 +316,6 @@ static const char* init_parsing(const char* json, char** begin, const char* const end, KeyValuePair* cache, Exception* const exception) { - exception->status = SUCCESS; *begin = init_keys(*begin, end, cache, exception); From fd38ed1b602072400610e300dd5f494912449ff2 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Wed, 4 Sep 2024 20:36:16 +0200 Subject: [PATCH 58/73] REFACT: use StringBuilder in transform API --- Transform.cpp | 61 ++++++---- Transform.hpp | 2 +- tests/TransformTests.cpp | 179 ++++++++++++++++++++------- transform.c | 257 ++++++++++++++++++++------------------- transform.h | 73 ++++++----- 5 files changed, 352 insertions(+), 220 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index b0d6b51d..d23c30f0 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -30,38 +30,55 @@ using namespace FiftyoneDegrees::Common; Transform::Transform(size_t capacity) : buffer(capacity) {} -Transform::Headers Transform::apiInvoker(CTransformAPI func, - const std::string& json) { - Transform::Headers res; - EXCEPTION_CREATE; - - fiftyoneDegreesKeyValuePairArray* headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); - - func(json.c_str(), buffer.data(), buffer.size(), headers, exception); - - while (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { - headers->count = 0; - exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; - buffer.resize(buffer.size() * 2); - - func(json.c_str(), buffer.data(), buffer.size(), headers, exception); - } +Transform::Headers Transform::apiInvoker(CTransformAPI func, const std::string& json) { + class ArrayContainer { + public: + KeyValuePairArray* headers = NULL; + ArrayContainer() { + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); + } + + ~ArrayContainer() { + Free(headers); + } + }; + + // RAII + ArrayContainer container; + + bool first = true; + size_t written = 0; + EXCEPTION_CREATE; + while (first || exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { + if (!first) { + container.headers->count = 0; + size_t bufferSize = std::max(buffer.size() * 2, written); + try { + buffer.resize(bufferSize); + } catch (const std::bad_alloc &) { + // reallocation failed - we just exit this loop by throwing an exception + EXCEPTION_THROW; + } + } + first = false; + EXCEPTION_CLEAR; + StringBuilder builder = {buffer.data(), buffer.size()}; + func(json.c_str(), &builder, container.headers, exception); + written = builder.added; + } if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { - fiftyoneDegreesFree(headers); EXCEPTION_THROW; } - for (uint32_t i = 0; i < headers->count; ++i) { - fiftyoneDegreesKeyValuePair& pair = headers->items[i]; + Transform::Headers res; + for (uint32_t i = 0; i < container.headers->count; ++i) { + fiftyoneDegreesKeyValuePair& pair = container.headers->items[i]; res.emplace(std::string{pair.key, pair.keyLength}, std::string{pair.value, pair.valueLength}); } - fiftyoneDegreesFree(headers); - return res; } diff --git a/Transform.hpp b/Transform.hpp index 22ae9b1d..fed132a8 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -47,7 +47,7 @@ namespace FiftyoneDegrees { using Headers = std::map; using CTransformAPI = - size_t (*)(const char* base64, char* buffer, size_t length, + size_t (*)(const char* base64, fiftyoneDegreesStringBuilder *builder, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception); diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 942286c7..e44d60ef 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -158,8 +158,9 @@ TEST_F(Transform, GHEVIterativeJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -275,16 +276,18 @@ TEST_F(Transform, IncompleteJSON) { EXCEPTION_CREATE; for (const std::string &j : correct) { + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, fillResultsCallback, + j.c_str(), &builder, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); } for (const std::string &j : corrupted) { + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, fillResultsCallback, + j.c_str(), &builder, fillResultsCallback, Transform::results, exception); EXPECT_FALSE(EXCEPTION_OKAY); EXPECT_TRUE(EXCEPTION_FAILED); @@ -385,7 +388,8 @@ TEST_F(Transform, IncompleteSUA) { EXCEPTION_CREATE; for (const std::string &j : correct) { - fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateSua(j.c_str(), &builder, fillResultsCallback, Transform::results, exception); @@ -393,7 +397,8 @@ TEST_F(Transform, IncompleteSUA) { } for (const std::string &j : corrupted) { - fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateSua(j.c_str(), &builder, fillResultsCallback, Transform::results, exception); @@ -415,8 +420,9 @@ TEST_F(Transform, GHEVIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -432,7 +438,8 @@ TEST_F(Transform, SUAIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateSua(json, &builder, fillResultsCallback, Transform::results, exception); @@ -454,8 +461,9 @@ TEST_F(Transform, GHEVIterativeNULLBrandJSON) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -489,8 +497,9 @@ TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -525,8 +534,9 @@ TEST_F(Transform, GHEVIterativeBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -567,8 +577,9 @@ TEST_F(Transform, GHEVBase64CorruptedLen) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -590,8 +601,9 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -613,8 +625,9 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -638,8 +651,9 @@ TEST_F(Transform, GHEVIterativeSua) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -681,8 +695,9 @@ TEST_F(Transform, SuaWeirdPlatformVersion) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -725,8 +740,9 @@ TEST_F(Transform, SuaNullBrandPlatform) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -763,8 +779,9 @@ TEST_F(Transform, GHEVArrayJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, results, exception); + ghev, &builder, results, exception); // --- @@ -805,8 +822,9 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, headers, exception); + ghev, &builder, headers, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -815,8 +833,9 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { // --- + StringBuilder builder2 = {buffer, bufferLength}; count = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, empty_headers, exception); + ghev, &builder2, empty_headers, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); @@ -843,8 +862,9 @@ TEST_F(Transform, GHEVBase64InsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromBase64( - ghev, buffer, bufferLength, empty_headers, exception); + ghev, &builder, empty_headers, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); @@ -869,8 +889,9 @@ TEST_F(Transform, SUAInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformSua( - sua, buffer, bufferLength, empty_headers, exception); + sua, &builder, empty_headers, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); @@ -879,7 +900,8 @@ TEST_F(Transform, SUAInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); FIFTYONE_DEGREES_EXCEPTION_CLEAR; - count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, + StringBuilder builder2 = {buffer, bufferLength}; + count = fiftyoneDegreesTransformSua(sua, &builder2, headers, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -904,8 +926,9 @@ TEST_F(Transform, GHEVBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromBase64( - ghev, buffer, bufferLength, results, exception); + ghev, &builder, results, exception); // --- @@ -944,14 +967,24 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); // --- - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); - fiftyoneDegreesFree(buffer); + EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); + + EXCEPTION_CLEAR; + buffer = (char *)fiftyoneDegreesMalloc(builder.added); + StringBuilder builder2 = {buffer, builder.added}; + fiftyoneDegreesTransformIterateGhevFromBase64( + ghev, &builder2, fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_OKAY); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUANotEnoughMemory) { @@ -968,8 +1001,9 @@ TEST_F(Transform, SUANotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); // --- @@ -991,7 +1025,8 @@ TEST_F(Transform, GHEVArraySua) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, + StringBuilder builder = {buffer, bufferLength}; + size_t count = fiftyoneDegreesTransformSua(sua, &builder, results, exception); // --- @@ -1030,7 +1065,8 @@ TEST_F(Transform, SUAArrayNotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformSua(sua, &builder, results, exception); // --- @@ -1054,8 +1090,9 @@ TEST_F(Transform, GHEVPartial) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 4); EXPECT_TRUE(EXCEPTION_OKAY); @@ -1105,8 +1142,9 @@ TEST_F(Transform, GHEVIgnoreUnused) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 8); @@ -1153,7 +1191,8 @@ TEST_F(Transform, GHEVCorruptInput) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -1175,11 +1214,20 @@ TEST_F(Transform, GHEVBufferTooSmall) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); + buffer = (char *)fiftyoneDegreesMalloc(builder.added); + EXCEPTION_CLEAR; + StringBuilder builder2 = {buffer, builder.added}; + fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder2, + fillResultsCallback, + Transform::results, exception); + EXPECT_TRUE(EXCEPTION_OKAY); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVEvidenceLowCapacity) { @@ -1197,7 +1245,8 @@ TEST_F(Transform, GHEVEvidenceLowCapacity) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); @@ -1217,8 +1266,9 @@ TEST_F(Transform, SUAHappyPath) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -1268,8 +1318,9 @@ TEST_F(Transform, SUAPlatformExt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 5); // we expect to see these headers detected: @@ -1306,8 +1357,9 @@ TEST_F(Transform, SUAPartial1) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 4); // we expect to see these headers detected: @@ -1342,8 +1394,9 @@ TEST_F(Transform, SUAPartial2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 5); @@ -1376,8 +1429,9 @@ TEST_F(Transform, SUATolerableCorrupt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, + sua, &builder, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -1406,7 +1460,8 @@ TEST_F(Transform, SUACorrupt2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateSua(sua, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -1422,7 +1477,8 @@ TEST_F(Transform, SUACorrupt3) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateSua(sua, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -1442,11 +1498,20 @@ TEST_F(Transform, SUABufferTooSmall) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateSua(sua, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); + buffer = (char *)fiftyoneDegreesMalloc(builder.added); + EXCEPTION_CLEAR; + StringBuilder builder2 = {buffer, builder.added}; + fiftyoneDegreesTransformIterateSua(sua, &builder2, + fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_OKAY); + Free(buffer); } TEST_F(Transform, SUAEvidenceLowCapacity) { @@ -1462,7 +1527,8 @@ TEST_F(Transform, SUAEvidenceLowCapacity) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + StringBuilder builder = {buffer, bufferLength}; + fiftyoneDegreesTransformIterateSua(sua, &builder, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); @@ -1633,3 +1699,28 @@ TEST_F(Transform, emptycases) { EXPECT_TRUE(thrown); } + +TEST_F(Transform, CPPWrapperBase64Corrupt) { + FiftyoneDegrees::Common::Transform t; + bool thrown = false; + try { + auto result = t.fromBase64GHEV("base64 invalid string"); + EXPECT_EQ(result.size(), 0); + } catch (const FiftyoneDegrees::Common::FatalException &e) { + EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + thrown = true; + } + EXPECT_TRUE(thrown); +} + +TEST_F(Transform, CPPWrapper0Size) { + FiftyoneDegrees::Common::Transform t(0); + bool thrown = false; + try { + auto result = t.fromJsonGHEV("{\"architecture\":\"x86\", \"bitness\":\"32\", \"new\":\"ignored\"}"); + EXPECT_EQ(result.size(), 2); + } catch (const FiftyoneDegrees::Common::FatalException &) { + thrown = true; + } + EXPECT_FALSE(thrown); +} diff --git a/transform.c b/transform.c index f6a023cf..4d9ac305 100644 --- a/transform.c +++ b/transform.c @@ -63,8 +63,7 @@ typedef enum { } Key; typedef Key (*readKeyCallback)(const char**, Exception* const); -typedef char* (*readValueCallback)(const char** json, char* begin, - const char* const end, +typedef char* (*readValueCallback)(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception); @@ -84,16 +83,14 @@ static struct { // ---- -static inline char* safe_write_to_buffer( - char* begin, const char* const end, char symbol, - Exception* const exception) { - if (begin < end) { - *begin = symbol; - } else { - exception->status = INSUFFICIENT_MEMORY; - } - - return ++begin; +static inline char* safe_write_to_buffer(StringBuilder *builder, + char symbol, + Exception* const exception) { + StringBuilderAddChar(builder, symbol); + if (builder->full) { + exception->status = INSUFFICIENT_MEMORY; + } + return builder->current; } static inline uint32_t base64_char_to_value( @@ -125,18 +122,15 @@ static inline uint32_t base64_char_to_value( return base64_lookup_table[(uint8_t)c]; } -static size_t base64_decode(const char* base64_input, char* const buffer, - size_t length, +static size_t base64_decode(const char* base64_input, StringBuilder *builder, Exception* const exception) { + size_t before = builder->added; size_t input_length = strlen(base64_input); if (input_length % 4 != 0) { exception->status = CORRUPT_DATA; return 0; // Invalid base64 input length } - char* begin = buffer; - char* end = buffer + length; - for (size_t i = 0; i < input_length;) { uint32_t sextet_a = GET_SEXTET(base64_input, i); uint32_t sextet_b = GET_SEXTET(base64_input, i); @@ -150,14 +144,14 @@ static size_t base64_decode(const char* base64_input, char* const buffer, uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - begin = safe_write_to_buffer(begin, end, (triple >> 16) & 0xFF, exception); - begin = safe_write_to_buffer(begin, end, (triple >> 8) & 0xFF, exception); - begin = safe_write_to_buffer(begin, end, triple & 0xFF, exception); + safe_write_to_buffer(builder, (triple >> 16) & 0xFF, exception); + safe_write_to_buffer(builder, (triple >> 8) & 0xFF, exception); + safe_write_to_buffer(builder, triple & 0xFF, exception); } - begin = safe_write_to_buffer(begin, end, '\0', exception); - - return begin - buffer; + safe_write_to_buffer(builder, '\0', exception); + size_t after = builder->added; + return after - before; } static inline const char* skip_whitespaces(const char* json) { @@ -295,29 +289,25 @@ static const char* skip_value(const char* json) { return skip_to_next_char(json + 1, '"'); } -static inline char* init_keys(char* const begin, const char* const end, +static inline void init_keys(StringBuilder *builder, KeyValuePair* cache, Exception* const exception) { - char* ptr = begin; - for (size_t k = 0; k < KEY_UNDEFINED; ++k) { - cache[k].key = ptr; + cache[k].key = builder->current; cache[k].keyLength = key_map[k].len; for (size_t i = 0; i < key_map[k].len; ++i) { - ptr = safe_write_to_buffer(ptr, end, key_map[k].key[i], exception); + safe_write_to_buffer(builder, key_map[k].key[i], exception); } } - - return ptr; } -static const char* init_parsing(const char* json, char** begin, - const char* const end, +static const char* init_parsing(const char* json, + StringBuilder *builder, KeyValuePair* cache, Exception* const exception) { - *begin = init_keys(*begin, end, cache, exception); + init_keys(builder, cache, exception); json = skip_whitespaces(json); ExpectSymbol(json, '{', return json); @@ -641,9 +631,9 @@ static Key read_sua_key(const char** json, return KEY_UNDEFINED; } -static char* read_string_value(const char** json, char* begin, - const char* const end, +static char* read_string_value(const char** json, StringBuilder *builder, Exception* const exception) { + char *begin = builder->current; *json = skip_whitespaces(*json); if (**json == 'n') { ++(*json); @@ -660,10 +650,10 @@ static char* read_string_value(const char** json, char* begin, ++*json; - for (begin = safe_write_to_buffer(begin, end, '"', exception);; ++(*json)) { + for (begin = safe_write_to_buffer(builder, '"', exception);; ++(*json)) { NotExpectSymbol(*json, '\0', return begin); - begin = safe_write_to_buffer(begin, end, **json, exception); + begin = safe_write_to_buffer(builder, **json, exception); switch (**json) { case '\"': { @@ -675,19 +665,20 @@ static char* read_string_value(const char** json, char* begin, if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - begin = safe_write_to_buffer(begin, end, **json, exception); + safe_write_to_buffer(builder, **json, exception); } } break; } } } -static char* read_bool_ghev_value(const char** json, char* begin, - const char* const end, +static char* read_bool_ghev_value(const char** json, + StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { - char* ptr = begin; - + char *begin = builder->current; + char *ptr = begin; + size_t before = builder->added; switch (**json) { case 't': { ++(*json); @@ -695,8 +686,8 @@ static char* read_bool_ghev_value(const char** json, char* begin, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(ptr, end, '?', exception); - ptr = safe_write_to_buffer(ptr, end, '1', exception); + ptr = safe_write_to_buffer(builder, '?', exception); + ptr = safe_write_to_buffer(builder, '1', exception); } break; case 'f': { @@ -705,8 +696,8 @@ static char* read_bool_ghev_value(const char** json, char* begin, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(ptr, end, '?', exception); - ptr = safe_write_to_buffer(ptr, end, '0', exception); + ptr = safe_write_to_buffer(builder, '?', exception); + ptr = safe_write_to_buffer(builder, '0', exception); } break; default: { @@ -715,16 +706,18 @@ static char* read_bool_ghev_value(const char** json, char* begin, } break; } + size_t after = builder->added; cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - - return ptr; + cache[key].valueLength = after - before; + return ptr; } -static char* read_bool_sua_value(const char** json, char* begin, - const char* const end, +static char* read_bool_sua_value(const char** json, + StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { + char *begin = builder->current; + size_t before = builder->added; switch (**json) { case '0': case '1': { @@ -736,19 +729,20 @@ static char* read_bool_sua_value(const char** json, char* begin, } break; } - char* ptr = safe_write_to_buffer(begin, end, '?', exception); - ptr = safe_write_to_buffer(ptr, end, **json, exception); + char* ptr = safe_write_to_buffer(builder, '?', exception); + ptr = safe_write_to_buffer(builder, **json, exception); ++(*json); + size_t after = builder->added; cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; + cache[key].valueLength = after - before; return ptr; } -static char* read_version_sua(const char** json, char* begin, - const char* const end, +static char* read_version_sua(const char** json, + StringBuilder *builder, Exception* const exception) { enum version_state { version_read, @@ -756,8 +750,9 @@ static char* read_version_sua(const char** json, char* begin, version_exit, } state = version_skip; + char *begin = builder->current; for (char* ptr = begin;; ++(*json)) { - NotExpectSymbol(*json, '\0', return begin); + NotExpectSymbol(*json, '\0', return begin); //rollback switch (state) { case version_read: { @@ -767,16 +762,16 @@ static char* read_version_sua(const char** json, char* begin, } break; case '\\': { - ptr = safe_write_to_buffer(ptr, end, **json, exception); + ptr = safe_write_to_buffer(builder, **json, exception); if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - ptr = safe_write_to_buffer(ptr, end, **json, exception); + ptr = safe_write_to_buffer(builder, **json, exception); } } break; default: { - ptr = safe_write_to_buffer(ptr, end, **json, exception); + ptr = safe_write_to_buffer(builder, **json, exception); } break; } } break; @@ -788,7 +783,7 @@ static char* read_version_sua(const char** json, char* begin, } break; case ',': { - ptr = safe_write_to_buffer(ptr, end, '.', exception); + ptr = safe_write_to_buffer(builder, '.', exception); } break; case ']': { @@ -798,17 +793,17 @@ static char* read_version_sua(const char** json, char* begin, } break; case version_exit: { - ptr = safe_write_to_buffer(ptr, end, '"', exception); - return ptr; + ptr = safe_write_to_buffer(builder, '"', exception); + return ptr; } break; } } } -static char* read_brands_ghev_value(const char** json, char* begin, - const char* const end, +static char* read_brands_ghev_value(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { + char *begin = builder->current; *json = skip_to_next_char(*json, '['); ExpectSymbol(*json, '[', return begin); @@ -832,37 +827,37 @@ static char* read_brands_ghev_value(const char** json, char* begin, ++*json; - char* ptr2 = read_string_value(json, ptr, end, exception); + char* ptr2 = read_string_value(json, builder, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(ptr2, end, ';', exception); - ptr = safe_write_to_buffer(ptr, end, 'v', exception); - ptr = safe_write_to_buffer(ptr, end, '=', exception); + ptr = safe_write_to_buffer(builder, ';', exception); + ptr = safe_write_to_buffer(builder, 'v', exception); + ptr = safe_write_to_buffer(builder, '=', exception); *json = skip_to_next_char(*json, ','); - ExpectSymbol(*json, ',', return begin); + ExpectSymbol(*json, ',', return begin); //rollback *json = skip_to_next_char(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); + ExpectSymbol(*json, '"', return begin); //rollback ++*json; for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); + ExpectSymbol(*json, *k, return begin); //rollback } *json = skip_to_next_char(*json, ':'); - ExpectSymbol(*json, ':', return begin); + ExpectSymbol(*json, ':', return begin); //rollback ++*json; - ptr2 = read_string_value(json, ptr, end, exception); + ptr2 = read_string_value(json, builder, exception); if (ptr2 == NULL) { ptr2 = ptr; - ptr = safe_write_to_buffer(ptr, end, 'n', exception); - ptr = safe_write_to_buffer(ptr, end, 'u', exception); - ptr = safe_write_to_buffer(ptr, end, 'l', exception); - ptr = safe_write_to_buffer(ptr, end, 'l', exception); + ptr = safe_write_to_buffer(builder, 'n', exception); + ptr = safe_write_to_buffer(builder, 'u', exception); + ptr = safe_write_to_buffer(builder, 'l', exception); + ptr = safe_write_to_buffer(builder, 'l', exception); } else { ptr = ptr2; } @@ -888,8 +883,8 @@ static char* read_brands_ghev_value(const char** json, char* begin, case ',': { if (ptr2 != NULL) { - ptr = safe_write_to_buffer(ptr, end, ',', exception); - ptr = safe_write_to_buffer(ptr, end, ' ', exception); + ptr = safe_write_to_buffer(builder, ',', exception); + ptr = safe_write_to_buffer(builder, ' ', exception); } } break; @@ -901,11 +896,11 @@ static char* read_brands_ghev_value(const char** json, char* begin, } } -static char* read_brands_sua_value(const char** json, char* begin, - const char* const end, +static char* read_brands_sua_value(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { *json = skip_to_next_char(*json, '['); + char *begin = builder->current; ExpectSymbol(*json, '[', return begin); for (char* ptr = begin;; ++*json) { @@ -928,11 +923,11 @@ static char* read_brands_sua_value(const char** json, char* begin, ++*json; - char* ptr2 = read_string_value(json, ptr, end, exception); + char* ptr2 = read_string_value(json, builder, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(ptr2, end, ';', exception); - ptr = safe_write_to_buffer(ptr, end, 'v', exception); - ptr = safe_write_to_buffer(ptr, end, '=', exception); + ptr = safe_write_to_buffer(builder, ';', exception); + ptr = safe_write_to_buffer(builder, 'v', exception); + ptr = safe_write_to_buffer(builder, '=', exception); *json = skip_to_next_char(*json, ','); ExpectSymbol(*json, ',', return begin); @@ -954,8 +949,8 @@ static char* read_brands_sua_value(const char** json, char* begin, ++*json; - ptr = safe_write_to_buffer(ptr, end, '"', exception); - ptr = read_version_sua(json, ptr, end, exception); + ptr = safe_write_to_buffer(builder, '"', exception); + ptr = read_version_sua(json, builder, exception); } *json = skip_to_next_char(*json, '}'); @@ -977,8 +972,8 @@ static char* read_brands_sua_value(const char** json, char* begin, case ',': { if (ptr != begin) { - ptr = safe_write_to_buffer(ptr, end, ',', exception); - ptr = safe_write_to_buffer(ptr, end, ' ', exception); + ptr = safe_write_to_buffer(builder, ',', exception); + ptr = safe_write_to_buffer(builder, ' ', exception); } } break; @@ -990,11 +985,11 @@ static char* read_brands_sua_value(const char** json, char* begin, } } -static char* read_pure_string_value(const char** json, char* begin, - const char* const end, +static char* read_pure_string_value(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { - char* ptr = read_string_value(json, begin, end, exception); + char *begin = builder->current; + char* ptr = read_string_value(json, builder, exception); if (ptr != NULL) { cache[key].value = ValuePtr; @@ -1005,9 +1000,10 @@ static char* read_pure_string_value(const char** json, char* begin, } static char* read_platform_sua_value( - const char** json, char* begin, const char* const end, + const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { + char *begin = builder->current; *json = skip_to_next_char(*json, '{'); ExpectSymbol(*json, '{', return begin); @@ -1025,7 +1021,7 @@ static char* read_platform_sua_value( ++*json; - char* ptr = read_string_value(json, begin, end, exception); + char* ptr = read_string_value(json, builder, exception); if (ptr == NULL) { return NULL; } @@ -1064,8 +1060,8 @@ static char* read_platform_sua_value( ++*json; NotExpectSymbol(*json, '\0', return begin); - ptr = safe_write_to_buffer(ptr, end, '"', exception); - ptr = read_version_sua(json, ptr, end, exception); + ptr = safe_write_to_buffer(builder, '"', exception); + ptr = read_version_sua(json, builder, exception); cache[PLATFORMVERSION].value = ValuePtr; cache[PLATFORMVERSION].valueLength = ptr - begin; @@ -1131,22 +1127,20 @@ static bool pushToHeaders(void* ctx, KeyValuePair header) { return (headers->count < headers->capacity); } // ------------------------------------------------------------------------------------------------ -static size_t main_parsing_body(const char* json, char* const buffer, - size_t length, +static size_t main_parsing_body(const char* json, + StringBuilder *builder, Exception* const exception, int isSua, TransformCallback callback, void* ctx) { KeyValuePair cache[KEY_UNDEFINED]; + char *begin = builder->current; // define buffer range - char* begin = buffer; - const char* const end = buffer + length; // write keys to buffer, init cache and skip to the first key - json = init_parsing(json, &begin, end, cache, exception); - if (exception->status == CORRUPT_DATA || - exception->status == INSUFFICIENT_MEMORY) { + json = init_parsing(json, builder, cache, exception); + if (exception->status == CORRUPT_DATA) { return 0; } @@ -1170,7 +1164,7 @@ static size_t main_parsing_body(const char* json, char* const buffer, json = skip_whitespaces(json + 1); NotExpectSymbol(json, '\0', break); - char* ptr = read_value(&json, begin, end, cache, key, exception); + char* ptr = read_value(&json, builder, cache, key, exception); if (exception->status == CORRUPT_DATA) { break; } @@ -1197,44 +1191,61 @@ static size_t main_parsing_body(const char* json, char* const buffer, return iterations; } +// The difference of this function is that it does not initialize the builder - and assumes +// it has been initialized outside - useful for base64 and then JSON from the same buffer + +size_t TransformIterateGhevFromJsonPrivate(const char* json, StringBuilder *builder, + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { + return main_parsing_body(json, builder, exception, 0, callback, ctx); +} // ------------------------------------------------------------------------------------------------ size_t fiftyoneDegreesTransformIterateGhevFromJson( - const char* json, char* const buffer, size_t length, + const char* json, StringBuilder *builder, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - return main_parsing_body(json, buffer, length, exception, 0, callback, ctx); + StringBuilderInit(builder); + size_t result = TransformIterateGhevFromJsonPrivate(json, builder, callback, ctx, exception); + StringBuilderComplete(builder); + return result; } size_t fiftyoneDegreesTransformIterateGhevFromBase64( - const char* base64, char* buffer, size_t length, + const char* base64, StringBuilder *builder, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - size_t offset = base64_decode(base64, buffer, length, exception); + StringBuilderInit(builder); + + base64_decode(base64, builder, exception); - if (exception->status == INSUFFICIENT_MEMORY || - exception->status == CORRUPT_DATA) { + if (exception->status == CORRUPT_DATA || exception->status == INSUFFICIENT_MEMORY) { return 0; } - - return fiftyoneDegreesTransformIterateGhevFromJson( - buffer, buffer + offset, length - offset, callback, - ctx, exception); + char *json = builder->ptr; + //note we are calling a private function to reuse the initialized stringbuilder + size_t result = TransformIterateGhevFromJsonPrivate(json, builder, callback, ctx, exception); + StringBuilderComplete(builder); + return result; } size_t fiftyoneDegreesTransformIterateSua( - const char* json, char* const buffer, size_t length, + const char* json, StringBuilder *builder, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - return main_parsing_body(json, buffer, length, exception, 1, callback, ctx); + StringBuilderInit(builder); + size_t result = main_parsing_body(json, builder, exception, 1, callback, ctx); + StringBuilderComplete(builder); + return result; } +// Array methods internally relay on iterative methods size_t fiftyoneDegreesTransformGhevFromJson( - const char* json, char* buffer, size_t length, + const char* json, StringBuilder *builder, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; size_t calls = TransformIterateGhevFromJson( - json, buffer, length, pushToHeaders, headers, exception); + json, builder, pushToHeaders, headers, exception); if (calls != headers->count - initial) { exception->status = INSUFFICIENT_CAPACITY; @@ -1244,12 +1255,12 @@ size_t fiftyoneDegreesTransformGhevFromJson( } size_t fiftyoneDegreesTransformGhevFromBase64( - const char* base64, char* buffer, size_t length, + const char* base64, StringBuilder *builder, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; size_t calls = TransformIterateGhevFromBase64( - base64, buffer, length, pushToHeaders, headers, exception); + base64, builder, pushToHeaders, headers, exception); if (calls != headers->count - initial) { exception->status = INSUFFICIENT_CAPACITY; @@ -1259,12 +1270,12 @@ size_t fiftyoneDegreesTransformGhevFromBase64( } size_t fiftyoneDegreesTransformSua( - const char* json, char* buffer, size_t length, + const char* json, StringBuilder *builder, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; size_t calls = TransformIterateSua( - json, buffer, length, pushToHeaders, headers, exception); + json, builder, pushToHeaders, headers, exception); if (calls != headers->count - initial) { exception->status = INSUFFICIENT_CAPACITY; diff --git a/transform.h b/transform.h index 43aa3507..e936b5a8 100644 --- a/transform.h +++ b/transform.h @@ -26,6 +26,7 @@ #include #include "pair.h" #include "exceptions.h" +#include "string.h" /** * User-Agent Client Hints (UACH) Representation Conversion Routines @@ -90,10 +91,12 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * Iteratively convert getHighEntropyValue() API result JSON string to HTTP * header representation. * @param json a JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values. The lifetime of this buffer is managed by the - * caller - * @param length length of the buffer + * @param builder - a StringBuilder object encapsulating a preallocated working + * memory buffer used to store the converted HTTP header names and values. + * The lifetime of this buffer and builder object is managed by the + * caller. Builder allows for safe buffer management and builder->added + * will contain the needed size of the buffer if it was insufficient - check + * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -117,7 +120,7 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * made) */ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( - const char *json, char *const buffer, size_t length, + const char *json, fiftyoneDegreesStringBuilder *builder, fiftyoneDegreesTransformCallback callback, void *state, fiftyoneDegreesException *const exception); @@ -126,10 +129,12 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( * string to HTTP header representation. * @param base64 a base64 encoded JSON string with the getHighEntropyValue() API * result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer + * @param builder - a StringBuilder object encapsulating a preallocated working + * memory buffer used to store the converted HTTP header names and values. + * The lifetime of this buffer and builder object is managed by the + * caller. Builder allows for safe buffer management and builder->added + * will contain the needed size of the buffer if it was insufficient - check + * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -153,17 +158,19 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( * made) */ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( - const char *base64, char *buffer, size_t length, + const char *base64, fiftyoneDegreesStringBuilder *builder, fiftyoneDegreesTransformCallback callback, void *state, fiftyoneDegreesException *const exception); /** * Iteratively convert device.sua JSON string to HTTP header representation. * @param json a JSON string with the device.sua raw representation - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer + * @param builder - a StringBuilder object encapsulating a preallocated working + * memory buffer used to store the converted HTTP header names and values. + * The lifetime of this buffer and builder object is managed by the + * caller. Builder allows for safe buffer management and builder->added + * will contain the needed size of the buffer if it was insufficient - check + * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -187,7 +194,7 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( * made) */ EXTERNAL size_t fiftyoneDegreesTransformIterateSua( - const char *json, char *buffer, size_t length, + const char *json, fiftyoneDegreesStringBuilder *builder, fiftyoneDegreesTransformCallback callback, void *state, fiftyoneDegreesException *const exception); @@ -195,10 +202,12 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateSua( * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header * representation. * @param json a JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer + * @param builder - a StringBuilder object encapsulating a preallocated working + * memory buffer used to store the converted HTTP header names and values. + * The lifetime of this buffer and builder object is managed by the + * caller. Builder allows for safe buffer management and builder->added + * will contain the needed size of the buffer if it was insufficient - check + * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -227,7 +236,7 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateSua( * allocated */ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( - const char *json, char *buffer, size_t length, + const char *json, fiftyoneDegreesStringBuilder *builder, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); @@ -236,10 +245,12 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( * string to HTTP header representation. * @param base64 a base64-encoded JSON string with the getHighEntropyValue() API * result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer + * @param builder - a StringBuilder object encapsulating a preallocated working + * memory buffer used to store the converted HTTP header names and values. + * The lifetime of this buffer and builder object is managed by the + * caller. Builder allows for safe buffer management and builder->added + * will contain the needed size of the buffer if it was insufficient - check + * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -268,17 +279,19 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( * allocated */ EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( - const char *base64, char *buffer, size_t length, + const char *base64, fiftyoneDegreesStringBuilder *builder, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); /** * Eagerly convert device.sua JSON string to HTTP header representation. * @param json a raw JSON string with device.sua field contents - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer + * @param builder - a StringBuilder object encapsulating a preallocated working + * memory buffer used to store the converted HTTP header names and values. + * The lifetime of this buffer and builder object is managed by the + * caller. Builder allows for safe buffer management and builder->added + * will contain the needed size of the buffer if it was insufficient - check + * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -307,7 +320,7 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( * allocated */ EXTERNAL size_t fiftyoneDegreesTransformSua( - const char *json, char *buffer, size_t length, + const char *json, fiftyoneDegreesStringBuilder *builder, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); From 96f3e1242732dce360b4e6a195a264f979491645 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Fri, 6 Sep 2024 11:33:32 +0200 Subject: [PATCH 59/73] Revert "REFACT: use StringBuilder in transform API" This reverts commit fea18f1f6caf2a51c676e5c764d40b24e2f46156. --- Transform.cpp | 61 ++++------ Transform.hpp | 2 +- tests/TransformTests.cpp | 179 +++++++-------------------- transform.c | 257 +++++++++++++++++++-------------------- transform.h | 73 +++++------ 5 files changed, 220 insertions(+), 352 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index d23c30f0..b0d6b51d 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -30,55 +30,38 @@ using namespace FiftyoneDegrees::Common; Transform::Transform(size_t capacity) : buffer(capacity) {} -Transform::Headers Transform::apiInvoker(CTransformAPI func, const std::string& json) { - class ArrayContainer { - public: - KeyValuePairArray* headers = NULL; - ArrayContainer() { - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); - } - - ~ArrayContainer() { - Free(headers); - } - }; - - // RAII - ArrayContainer container; - - bool first = true; - size_t written = 0; - EXCEPTION_CREATE; - while (first || exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { - if (!first) { - container.headers->count = 0; - size_t bufferSize = std::max(buffer.size() * 2, written); - try { - buffer.resize(bufferSize); - } catch (const std::bad_alloc &) { - // reallocation failed - we just exit this loop by throwing an exception - EXCEPTION_THROW; - } - } - first = false; - EXCEPTION_CLEAR; - StringBuilder builder = {buffer.data(), buffer.size()}; - func(json.c_str(), &builder, container.headers, exception); - written = builder.added; - } +Transform::Headers Transform::apiInvoker(CTransformAPI func, + const std::string& json) { + Transform::Headers res; + EXCEPTION_CREATE; + + fiftyoneDegreesKeyValuePairArray* headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); + + func(json.c_str(), buffer.data(), buffer.size(), headers, exception); + + while (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { + headers->count = 0; + exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; + buffer.resize(buffer.size() * 2); + + func(json.c_str(), buffer.data(), buffer.size(), headers, exception); + } if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + fiftyoneDegreesFree(headers); EXCEPTION_THROW; } - Transform::Headers res; - for (uint32_t i = 0; i < container.headers->count; ++i) { - fiftyoneDegreesKeyValuePair& pair = container.headers->items[i]; + for (uint32_t i = 0; i < headers->count; ++i) { + fiftyoneDegreesKeyValuePair& pair = headers->items[i]; res.emplace(std::string{pair.key, pair.keyLength}, std::string{pair.value, pair.valueLength}); } + fiftyoneDegreesFree(headers); + return res; } diff --git a/Transform.hpp b/Transform.hpp index fed132a8..22ae9b1d 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -47,7 +47,7 @@ namespace FiftyoneDegrees { using Headers = std::map; using CTransformAPI = - size_t (*)(const char* base64, fiftyoneDegreesStringBuilder *builder, + size_t (*)(const char* base64, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception); diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index e44d60ef..942286c7 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -158,9 +158,8 @@ TEST_F(Transform, GHEVIterativeJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -276,18 +275,16 @@ TEST_F(Transform, IncompleteJSON) { EXCEPTION_CREATE; for (const std::string &j : correct) { - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), &builder, fillResultsCallback, + j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); } for (const std::string &j : corrupted) { - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), &builder, fillResultsCallback, + j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); EXPECT_FALSE(EXCEPTION_OKAY); EXPECT_TRUE(EXCEPTION_FAILED); @@ -388,8 +385,7 @@ TEST_F(Transform, IncompleteSUA) { EXCEPTION_CREATE; for (const std::string &j : correct) { - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateSua(j.c_str(), &builder, + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -397,8 +393,7 @@ TEST_F(Transform, IncompleteSUA) { } for (const std::string &j : corrupted) { - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateSua(j.c_str(), &builder, + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -420,9 +415,8 @@ TEST_F(Transform, GHEVIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromJson( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -438,8 +432,7 @@ TEST_F(Transform, SUAIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateSua(json, &builder, + fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -461,9 +454,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandJSON) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -497,9 +489,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -534,9 +525,8 @@ TEST_F(Transform, GHEVIterativeBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -577,9 +567,8 @@ TEST_F(Transform, GHEVBase64CorruptedLen) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -601,9 +590,8 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -625,9 +613,8 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -651,9 +638,8 @@ TEST_F(Transform, GHEVIterativeSua) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -695,9 +681,8 @@ TEST_F(Transform, SuaWeirdPlatformVersion) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -740,9 +725,8 @@ TEST_F(Transform, SuaNullBrandPlatform) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -779,9 +763,8 @@ TEST_F(Transform, GHEVArrayJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, &builder, results, exception); + ghev, buffer, bufferLength, results, exception); // --- @@ -822,9 +805,8 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromJson( - ghev, &builder, headers, exception); + ghev, buffer, bufferLength, headers, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -833,9 +815,8 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { // --- - StringBuilder builder2 = {buffer, bufferLength}; count = fiftyoneDegreesTransformGhevFromJson( - ghev, &builder2, empty_headers, exception); + ghev, buffer, bufferLength, empty_headers, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); @@ -862,9 +843,8 @@ TEST_F(Transform, GHEVBase64InsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromBase64( - ghev, &builder, empty_headers, exception); + ghev, buffer, bufferLength, empty_headers, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); @@ -889,9 +869,8 @@ TEST_F(Transform, SUAInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformSua( - sua, &builder, empty_headers, exception); + sua, buffer, bufferLength, empty_headers, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); ASSERT_EQ(count, 1); @@ -900,8 +879,7 @@ TEST_F(Transform, SUAInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); FIFTYONE_DEGREES_EXCEPTION_CLEAR; - StringBuilder builder2 = {buffer, bufferLength}; - count = fiftyoneDegreesTransformSua(sua, &builder2, headers, + count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -926,9 +904,8 @@ TEST_F(Transform, GHEVBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformGhevFromBase64( - ghev, &builder, results, exception); + ghev, buffer, bufferLength, results, exception); // --- @@ -967,24 +944,14 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); - fiftyoneDegreesFree(buffer); - - EXCEPTION_CLEAR; - buffer = (char *)fiftyoneDegreesMalloc(builder.added); - StringBuilder builder2 = {buffer, builder.added}; - fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, &builder2, fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_OKAY); - fiftyoneDegreesFree(buffer); + ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUANotEnoughMemory) { @@ -1001,9 +968,8 @@ TEST_F(Transform, SUANotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- @@ -1025,8 +991,7 @@ TEST_F(Transform, GHEVArraySua) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - size_t count = fiftyoneDegreesTransformSua(sua, &builder, results, + size_t count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, exception); // --- @@ -1065,8 +1030,7 @@ TEST_F(Transform, SUAArrayNotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformSua(sua, &builder, results, + fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, exception); // --- @@ -1090,9 +1054,8 @@ TEST_F(Transform, GHEVPartial) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 4); EXPECT_TRUE(EXCEPTION_OKAY); @@ -1142,9 +1105,8 @@ TEST_F(Transform, GHEVIgnoreUnused) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, &builder, fillResultsCallback, Transform::results, + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 8); @@ -1191,8 +1153,7 @@ TEST_F(Transform, GHEVCorruptInput) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder, + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -1214,20 +1175,11 @@ TEST_F(Transform, GHEVBufferTooSmall) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder, + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); fiftyoneDegreesFree(buffer); - buffer = (char *)fiftyoneDegreesMalloc(builder.added); - EXCEPTION_CLEAR; - StringBuilder builder2 = {buffer, builder.added}; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder2, - fillResultsCallback, - Transform::results, exception); - EXPECT_TRUE(EXCEPTION_OKAY); - fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVEvidenceLowCapacity) { @@ -1245,8 +1197,7 @@ TEST_F(Transform, GHEVEvidenceLowCapacity) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, &builder, + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); @@ -1266,9 +1217,8 @@ TEST_F(Transform, SUAHappyPath) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -1318,9 +1268,8 @@ TEST_F(Transform, SUAPlatformExt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 5); // we expect to see these headers detected: @@ -1357,9 +1306,8 @@ TEST_F(Transform, SUAPartial1) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 4); // we expect to see these headers detected: @@ -1394,9 +1342,8 @@ TEST_F(Transform, SUAPartial2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(count, 5); @@ -1429,9 +1376,8 @@ TEST_F(Transform, SUATolerableCorrupt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; size_t count = fiftyoneDegreesTransformIterateSua( - sua, &builder, fillResultsCallback, Transform::results, + sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); @@ -1460,8 +1406,7 @@ TEST_F(Transform, SUACorrupt2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateSua(sua, &builder, + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -1477,8 +1422,7 @@ TEST_F(Transform, SUACorrupt3) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateSua(sua, &builder, + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); @@ -1498,20 +1442,11 @@ TEST_F(Transform, SUABufferTooSmall) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateSua(sua, &builder, + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); - fiftyoneDegreesFree(buffer); - buffer = (char *)fiftyoneDegreesMalloc(builder.added); - EXCEPTION_CLEAR; - StringBuilder builder2 = {buffer, builder.added}; - fiftyoneDegreesTransformIterateSua(sua, &builder2, - fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_OKAY); - Free(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAEvidenceLowCapacity) { @@ -1527,8 +1462,7 @@ TEST_F(Transform, SUAEvidenceLowCapacity) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - StringBuilder builder = {buffer, bufferLength}; - fiftyoneDegreesTransformIterateSua(sua, &builder, + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); @@ -1699,28 +1633,3 @@ TEST_F(Transform, emptycases) { EXPECT_TRUE(thrown); } - -TEST_F(Transform, CPPWrapperBase64Corrupt) { - FiftyoneDegrees::Common::Transform t; - bool thrown = false; - try { - auto result = t.fromBase64GHEV("base64 invalid string"); - EXPECT_EQ(result.size(), 0); - } catch (const FiftyoneDegrees::Common::FatalException &e) { - EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); - thrown = true; - } - EXPECT_TRUE(thrown); -} - -TEST_F(Transform, CPPWrapper0Size) { - FiftyoneDegrees::Common::Transform t(0); - bool thrown = false; - try { - auto result = t.fromJsonGHEV("{\"architecture\":\"x86\", \"bitness\":\"32\", \"new\":\"ignored\"}"); - EXPECT_EQ(result.size(), 2); - } catch (const FiftyoneDegrees::Common::FatalException &) { - thrown = true; - } - EXPECT_FALSE(thrown); -} diff --git a/transform.c b/transform.c index 4d9ac305..f6a023cf 100644 --- a/transform.c +++ b/transform.c @@ -63,7 +63,8 @@ typedef enum { } Key; typedef Key (*readKeyCallback)(const char**, Exception* const); -typedef char* (*readValueCallback)(const char** json, StringBuilder *builder, +typedef char* (*readValueCallback)(const char** json, char* begin, + const char* const end, KeyValuePair* cache, Key key, Exception* const exception); @@ -83,14 +84,16 @@ static struct { // ---- -static inline char* safe_write_to_buffer(StringBuilder *builder, - char symbol, - Exception* const exception) { - StringBuilderAddChar(builder, symbol); - if (builder->full) { - exception->status = INSUFFICIENT_MEMORY; - } - return builder->current; +static inline char* safe_write_to_buffer( + char* begin, const char* const end, char symbol, + Exception* const exception) { + if (begin < end) { + *begin = symbol; + } else { + exception->status = INSUFFICIENT_MEMORY; + } + + return ++begin; } static inline uint32_t base64_char_to_value( @@ -122,15 +125,18 @@ static inline uint32_t base64_char_to_value( return base64_lookup_table[(uint8_t)c]; } -static size_t base64_decode(const char* base64_input, StringBuilder *builder, +static size_t base64_decode(const char* base64_input, char* const buffer, + size_t length, Exception* const exception) { - size_t before = builder->added; size_t input_length = strlen(base64_input); if (input_length % 4 != 0) { exception->status = CORRUPT_DATA; return 0; // Invalid base64 input length } + char* begin = buffer; + char* end = buffer + length; + for (size_t i = 0; i < input_length;) { uint32_t sextet_a = GET_SEXTET(base64_input, i); uint32_t sextet_b = GET_SEXTET(base64_input, i); @@ -144,14 +150,14 @@ static size_t base64_decode(const char* base64_input, StringBuilder *builder, uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - safe_write_to_buffer(builder, (triple >> 16) & 0xFF, exception); - safe_write_to_buffer(builder, (triple >> 8) & 0xFF, exception); - safe_write_to_buffer(builder, triple & 0xFF, exception); + begin = safe_write_to_buffer(begin, end, (triple >> 16) & 0xFF, exception); + begin = safe_write_to_buffer(begin, end, (triple >> 8) & 0xFF, exception); + begin = safe_write_to_buffer(begin, end, triple & 0xFF, exception); } - safe_write_to_buffer(builder, '\0', exception); - size_t after = builder->added; - return after - before; + begin = safe_write_to_buffer(begin, end, '\0', exception); + + return begin - buffer; } static inline const char* skip_whitespaces(const char* json) { @@ -289,25 +295,29 @@ static const char* skip_value(const char* json) { return skip_to_next_char(json + 1, '"'); } -static inline void init_keys(StringBuilder *builder, +static inline char* init_keys(char* const begin, const char* const end, KeyValuePair* cache, Exception* const exception) { + char* ptr = begin; + for (size_t k = 0; k < KEY_UNDEFINED; ++k) { - cache[k].key = builder->current; + cache[k].key = ptr; cache[k].keyLength = key_map[k].len; for (size_t i = 0; i < key_map[k].len; ++i) { - safe_write_to_buffer(builder, key_map[k].key[i], exception); + ptr = safe_write_to_buffer(ptr, end, key_map[k].key[i], exception); } } + + return ptr; } -static const char* init_parsing(const char* json, - StringBuilder *builder, +static const char* init_parsing(const char* json, char** begin, + const char* const end, KeyValuePair* cache, Exception* const exception) { - init_keys(builder, cache, exception); + *begin = init_keys(*begin, end, cache, exception); json = skip_whitespaces(json); ExpectSymbol(json, '{', return json); @@ -631,9 +641,9 @@ static Key read_sua_key(const char** json, return KEY_UNDEFINED; } -static char* read_string_value(const char** json, StringBuilder *builder, +static char* read_string_value(const char** json, char* begin, + const char* const end, Exception* const exception) { - char *begin = builder->current; *json = skip_whitespaces(*json); if (**json == 'n') { ++(*json); @@ -650,10 +660,10 @@ static char* read_string_value(const char** json, StringBuilder *builder, ++*json; - for (begin = safe_write_to_buffer(builder, '"', exception);; ++(*json)) { + for (begin = safe_write_to_buffer(begin, end, '"', exception);; ++(*json)) { NotExpectSymbol(*json, '\0', return begin); - begin = safe_write_to_buffer(builder, **json, exception); + begin = safe_write_to_buffer(begin, end, **json, exception); switch (**json) { case '\"': { @@ -665,20 +675,19 @@ static char* read_string_value(const char** json, StringBuilder *builder, if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - safe_write_to_buffer(builder, **json, exception); + begin = safe_write_to_buffer(begin, end, **json, exception); } } break; } } } -static char* read_bool_ghev_value(const char** json, - StringBuilder *builder, +static char* read_bool_ghev_value(const char** json, char* begin, + const char* const end, KeyValuePair* cache, Key key, Exception* const exception) { - char *begin = builder->current; - char *ptr = begin; - size_t before = builder->added; + char* ptr = begin; + switch (**json) { case 't': { ++(*json); @@ -686,8 +695,8 @@ static char* read_bool_ghev_value(const char** json, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(builder, '?', exception); - ptr = safe_write_to_buffer(builder, '1', exception); + ptr = safe_write_to_buffer(ptr, end, '?', exception); + ptr = safe_write_to_buffer(ptr, end, '1', exception); } break; case 'f': { @@ -696,8 +705,8 @@ static char* read_bool_ghev_value(const char** json, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(builder, '?', exception); - ptr = safe_write_to_buffer(builder, '0', exception); + ptr = safe_write_to_buffer(ptr, end, '?', exception); + ptr = safe_write_to_buffer(ptr, end, '0', exception); } break; default: { @@ -706,18 +715,16 @@ static char* read_bool_ghev_value(const char** json, } break; } - size_t after = builder->added; cache[key].value = ValuePtr; - cache[key].valueLength = after - before; - return ptr; + cache[key].valueLength = ptr - begin; + + return ptr; } -static char* read_bool_sua_value(const char** json, - StringBuilder *builder, +static char* read_bool_sua_value(const char** json, char* begin, + const char* const end, KeyValuePair* cache, Key key, Exception* const exception) { - char *begin = builder->current; - size_t before = builder->added; switch (**json) { case '0': case '1': { @@ -729,20 +736,19 @@ static char* read_bool_sua_value(const char** json, } break; } - char* ptr = safe_write_to_buffer(builder, '?', exception); - ptr = safe_write_to_buffer(builder, **json, exception); + char* ptr = safe_write_to_buffer(begin, end, '?', exception); + ptr = safe_write_to_buffer(ptr, end, **json, exception); ++(*json); - size_t after = builder->added; cache[key].value = ValuePtr; - cache[key].valueLength = after - before; + cache[key].valueLength = ptr - begin; return ptr; } -static char* read_version_sua(const char** json, - StringBuilder *builder, +static char* read_version_sua(const char** json, char* begin, + const char* const end, Exception* const exception) { enum version_state { version_read, @@ -750,9 +756,8 @@ static char* read_version_sua(const char** json, version_exit, } state = version_skip; - char *begin = builder->current; for (char* ptr = begin;; ++(*json)) { - NotExpectSymbol(*json, '\0', return begin); //rollback + NotExpectSymbol(*json, '\0', return begin); switch (state) { case version_read: { @@ -762,16 +767,16 @@ static char* read_version_sua(const char** json, } break; case '\\': { - ptr = safe_write_to_buffer(builder, **json, exception); + ptr = safe_write_to_buffer(ptr, end, **json, exception); if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - ptr = safe_write_to_buffer(builder, **json, exception); + ptr = safe_write_to_buffer(ptr, end, **json, exception); } } break; default: { - ptr = safe_write_to_buffer(builder, **json, exception); + ptr = safe_write_to_buffer(ptr, end, **json, exception); } break; } } break; @@ -783,7 +788,7 @@ static char* read_version_sua(const char** json, } break; case ',': { - ptr = safe_write_to_buffer(builder, '.', exception); + ptr = safe_write_to_buffer(ptr, end, '.', exception); } break; case ']': { @@ -793,17 +798,17 @@ static char* read_version_sua(const char** json, } break; case version_exit: { - ptr = safe_write_to_buffer(builder, '"', exception); - return ptr; + ptr = safe_write_to_buffer(ptr, end, '"', exception); + return ptr; } break; } } } -static char* read_brands_ghev_value(const char** json, StringBuilder *builder, +static char* read_brands_ghev_value(const char** json, char* begin, + const char* const end, KeyValuePair* cache, Key key, Exception* const exception) { - char *begin = builder->current; *json = skip_to_next_char(*json, '['); ExpectSymbol(*json, '[', return begin); @@ -827,37 +832,37 @@ static char* read_brands_ghev_value(const char** json, StringBuilder *builder, ++*json; - char* ptr2 = read_string_value(json, builder, exception); + char* ptr2 = read_string_value(json, ptr, end, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(builder, ';', exception); - ptr = safe_write_to_buffer(builder, 'v', exception); - ptr = safe_write_to_buffer(builder, '=', exception); + ptr = safe_write_to_buffer(ptr2, end, ';', exception); + ptr = safe_write_to_buffer(ptr, end, 'v', exception); + ptr = safe_write_to_buffer(ptr, end, '=', exception); *json = skip_to_next_char(*json, ','); - ExpectSymbol(*json, ',', return begin); //rollback + ExpectSymbol(*json, ',', return begin); *json = skip_to_next_char(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); //rollback + ExpectSymbol(*json, '"', return begin); ++*json; for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); //rollback + ExpectSymbol(*json, *k, return begin); } *json = skip_to_next_char(*json, ':'); - ExpectSymbol(*json, ':', return begin); //rollback + ExpectSymbol(*json, ':', return begin); ++*json; - ptr2 = read_string_value(json, builder, exception); + ptr2 = read_string_value(json, ptr, end, exception); if (ptr2 == NULL) { ptr2 = ptr; - ptr = safe_write_to_buffer(builder, 'n', exception); - ptr = safe_write_to_buffer(builder, 'u', exception); - ptr = safe_write_to_buffer(builder, 'l', exception); - ptr = safe_write_to_buffer(builder, 'l', exception); + ptr = safe_write_to_buffer(ptr, end, 'n', exception); + ptr = safe_write_to_buffer(ptr, end, 'u', exception); + ptr = safe_write_to_buffer(ptr, end, 'l', exception); + ptr = safe_write_to_buffer(ptr, end, 'l', exception); } else { ptr = ptr2; } @@ -883,8 +888,8 @@ static char* read_brands_ghev_value(const char** json, StringBuilder *builder, case ',': { if (ptr2 != NULL) { - ptr = safe_write_to_buffer(builder, ',', exception); - ptr = safe_write_to_buffer(builder, ' ', exception); + ptr = safe_write_to_buffer(ptr, end, ',', exception); + ptr = safe_write_to_buffer(ptr, end, ' ', exception); } } break; @@ -896,11 +901,11 @@ static char* read_brands_ghev_value(const char** json, StringBuilder *builder, } } -static char* read_brands_sua_value(const char** json, StringBuilder *builder, +static char* read_brands_sua_value(const char** json, char* begin, + const char* const end, KeyValuePair* cache, Key key, Exception* const exception) { *json = skip_to_next_char(*json, '['); - char *begin = builder->current; ExpectSymbol(*json, '[', return begin); for (char* ptr = begin;; ++*json) { @@ -923,11 +928,11 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, ++*json; - char* ptr2 = read_string_value(json, builder, exception); + char* ptr2 = read_string_value(json, ptr, end, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(builder, ';', exception); - ptr = safe_write_to_buffer(builder, 'v', exception); - ptr = safe_write_to_buffer(builder, '=', exception); + ptr = safe_write_to_buffer(ptr2, end, ';', exception); + ptr = safe_write_to_buffer(ptr, end, 'v', exception); + ptr = safe_write_to_buffer(ptr, end, '=', exception); *json = skip_to_next_char(*json, ','); ExpectSymbol(*json, ',', return begin); @@ -949,8 +954,8 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, ++*json; - ptr = safe_write_to_buffer(builder, '"', exception); - ptr = read_version_sua(json, builder, exception); + ptr = safe_write_to_buffer(ptr, end, '"', exception); + ptr = read_version_sua(json, ptr, end, exception); } *json = skip_to_next_char(*json, '}'); @@ -972,8 +977,8 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, case ',': { if (ptr != begin) { - ptr = safe_write_to_buffer(builder, ',', exception); - ptr = safe_write_to_buffer(builder, ' ', exception); + ptr = safe_write_to_buffer(ptr, end, ',', exception); + ptr = safe_write_to_buffer(ptr, end, ' ', exception); } } break; @@ -985,11 +990,11 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, } } -static char* read_pure_string_value(const char** json, StringBuilder *builder, +static char* read_pure_string_value(const char** json, char* begin, + const char* const end, KeyValuePair* cache, Key key, Exception* const exception) { - char *begin = builder->current; - char* ptr = read_string_value(json, builder, exception); + char* ptr = read_string_value(json, begin, end, exception); if (ptr != NULL) { cache[key].value = ValuePtr; @@ -1000,10 +1005,9 @@ static char* read_pure_string_value(const char** json, StringBuilder *builder, } static char* read_platform_sua_value( - const char** json, StringBuilder *builder, + const char** json, char* begin, const char* const end, KeyValuePair* cache, Key key, Exception* const exception) { - char *begin = builder->current; *json = skip_to_next_char(*json, '{'); ExpectSymbol(*json, '{', return begin); @@ -1021,7 +1025,7 @@ static char* read_platform_sua_value( ++*json; - char* ptr = read_string_value(json, builder, exception); + char* ptr = read_string_value(json, begin, end, exception); if (ptr == NULL) { return NULL; } @@ -1060,8 +1064,8 @@ static char* read_platform_sua_value( ++*json; NotExpectSymbol(*json, '\0', return begin); - ptr = safe_write_to_buffer(builder, '"', exception); - ptr = read_version_sua(json, builder, exception); + ptr = safe_write_to_buffer(ptr, end, '"', exception); + ptr = read_version_sua(json, ptr, end, exception); cache[PLATFORMVERSION].value = ValuePtr; cache[PLATFORMVERSION].valueLength = ptr - begin; @@ -1127,20 +1131,22 @@ static bool pushToHeaders(void* ctx, KeyValuePair header) { return (headers->count < headers->capacity); } // ------------------------------------------------------------------------------------------------ -static size_t main_parsing_body(const char* json, - StringBuilder *builder, +static size_t main_parsing_body(const char* json, char* const buffer, + size_t length, Exception* const exception, int isSua, TransformCallback callback, void* ctx) { KeyValuePair cache[KEY_UNDEFINED]; - char *begin = builder->current; // define buffer range + char* begin = buffer; + const char* const end = buffer + length; // write keys to buffer, init cache and skip to the first key - json = init_parsing(json, builder, cache, exception); - if (exception->status == CORRUPT_DATA) { + json = init_parsing(json, &begin, end, cache, exception); + if (exception->status == CORRUPT_DATA || + exception->status == INSUFFICIENT_MEMORY) { return 0; } @@ -1164,7 +1170,7 @@ static size_t main_parsing_body(const char* json, json = skip_whitespaces(json + 1); NotExpectSymbol(json, '\0', break); - char* ptr = read_value(&json, builder, cache, key, exception); + char* ptr = read_value(&json, begin, end, cache, key, exception); if (exception->status == CORRUPT_DATA) { break; } @@ -1191,61 +1197,44 @@ static size_t main_parsing_body(const char* json, return iterations; } -// The difference of this function is that it does not initialize the builder - and assumes -// it has been initialized outside - useful for base64 and then JSON from the same buffer - -size_t TransformIterateGhevFromJsonPrivate(const char* json, StringBuilder *builder, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { - return main_parsing_body(json, builder, exception, 0, callback, ctx); -} // ------------------------------------------------------------------------------------------------ size_t fiftyoneDegreesTransformIterateGhevFromJson( - const char* json, StringBuilder *builder, + const char* json, char* const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - StringBuilderInit(builder); - size_t result = TransformIterateGhevFromJsonPrivate(json, builder, callback, ctx, exception); - StringBuilderComplete(builder); - return result; + return main_parsing_body(json, buffer, length, exception, 0, callback, ctx); } size_t fiftyoneDegreesTransformIterateGhevFromBase64( - const char* base64, StringBuilder *builder, + const char* base64, char* buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - StringBuilderInit(builder); - - base64_decode(base64, builder, exception); + size_t offset = base64_decode(base64, buffer, length, exception); - if (exception->status == CORRUPT_DATA || exception->status == INSUFFICIENT_MEMORY) { + if (exception->status == INSUFFICIENT_MEMORY || + exception->status == CORRUPT_DATA) { return 0; } - char *json = builder->ptr; - //note we are calling a private function to reuse the initialized stringbuilder - size_t result = TransformIterateGhevFromJsonPrivate(json, builder, callback, ctx, exception); - StringBuilderComplete(builder); - return result; + + return fiftyoneDegreesTransformIterateGhevFromJson( + buffer, buffer + offset, length - offset, callback, + ctx, exception); } size_t fiftyoneDegreesTransformIterateSua( - const char* json, StringBuilder *builder, + const char* json, char* const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - StringBuilderInit(builder); - size_t result = main_parsing_body(json, builder, exception, 1, callback, ctx); - StringBuilderComplete(builder); - return result; + return main_parsing_body(json, buffer, length, exception, 1, callback, ctx); } -// Array methods internally relay on iterative methods size_t fiftyoneDegreesTransformGhevFromJson( - const char* json, StringBuilder *builder, + const char* json, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; size_t calls = TransformIterateGhevFromJson( - json, builder, pushToHeaders, headers, exception); + json, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count - initial) { exception->status = INSUFFICIENT_CAPACITY; @@ -1255,12 +1244,12 @@ size_t fiftyoneDegreesTransformGhevFromJson( } size_t fiftyoneDegreesTransformGhevFromBase64( - const char* base64, StringBuilder *builder, + const char* base64, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; size_t calls = TransformIterateGhevFromBase64( - base64, builder, pushToHeaders, headers, exception); + base64, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count - initial) { exception->status = INSUFFICIENT_CAPACITY; @@ -1270,12 +1259,12 @@ size_t fiftyoneDegreesTransformGhevFromBase64( } size_t fiftyoneDegreesTransformSua( - const char* json, StringBuilder *builder, + const char* json, char* buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; size_t calls = TransformIterateSua( - json, builder, pushToHeaders, headers, exception); + json, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count - initial) { exception->status = INSUFFICIENT_CAPACITY; diff --git a/transform.h b/transform.h index e936b5a8..43aa3507 100644 --- a/transform.h +++ b/transform.h @@ -26,7 +26,6 @@ #include #include "pair.h" #include "exceptions.h" -#include "string.h" /** * User-Agent Client Hints (UACH) Representation Conversion Routines @@ -91,12 +90,10 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * Iteratively convert getHighEntropyValue() API result JSON string to HTTP * header representation. * @param json a JSON string with the getHighEntropyValue() API result - * @param builder - a StringBuilder object encapsulating a preallocated working - * memory buffer used to store the converted HTTP header names and values. - * The lifetime of this buffer and builder object is managed by the - * caller. Builder allows for safe buffer management and builder->added - * will contain the needed size of the buffer if it was insufficient - check - * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values. The lifetime of this buffer is managed by the + * caller + * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -120,7 +117,7 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * made) */ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( - const char *json, fiftyoneDegreesStringBuilder *builder, + const char *json, char *const buffer, size_t length, fiftyoneDegreesTransformCallback callback, void *state, fiftyoneDegreesException *const exception); @@ -129,12 +126,10 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( * string to HTTP header representation. * @param base64 a base64 encoded JSON string with the getHighEntropyValue() API * result - * @param builder - a StringBuilder object encapsulating a preallocated working - * memory buffer used to store the converted HTTP header names and values. - * The lifetime of this buffer and builder object is managed by the - * caller. Builder allows for safe buffer management and builder->added - * will contain the needed size of the buffer if it was insufficient - check - * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -158,19 +153,17 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( * made) */ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( - const char *base64, fiftyoneDegreesStringBuilder *builder, + const char *base64, char *buffer, size_t length, fiftyoneDegreesTransformCallback callback, void *state, fiftyoneDegreesException *const exception); /** * Iteratively convert device.sua JSON string to HTTP header representation. * @param json a JSON string with the device.sua raw representation - * @param builder - a StringBuilder object encapsulating a preallocated working - * memory buffer used to store the converted HTTP header names and values. - * The lifetime of this buffer and builder object is managed by the - * caller. Builder allows for safe buffer management and builder->added - * will contain the needed size of the buffer if it was insufficient - check - * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -194,7 +187,7 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( * made) */ EXTERNAL size_t fiftyoneDegreesTransformIterateSua( - const char *json, fiftyoneDegreesStringBuilder *builder, + const char *json, char *buffer, size_t length, fiftyoneDegreesTransformCallback callback, void *state, fiftyoneDegreesException *const exception); @@ -202,12 +195,10 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateSua( * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header * representation. * @param json a JSON string with the getHighEntropyValue() API result - * @param builder - a StringBuilder object encapsulating a preallocated working - * memory buffer used to store the converted HTTP header names and values. - * The lifetime of this buffer and builder object is managed by the - * caller. Builder allows for safe buffer management and builder->added - * will contain the needed size of the buffer if it was insufficient - check - * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -236,7 +227,7 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateSua( * allocated */ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( - const char *json, fiftyoneDegreesStringBuilder *builder, + const char *json, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); @@ -245,12 +236,10 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( * string to HTTP header representation. * @param base64 a base64-encoded JSON string with the getHighEntropyValue() API * result - * @param builder - a StringBuilder object encapsulating a preallocated working - * memory buffer used to store the converted HTTP header names and values. - * The lifetime of this buffer and builder object is managed by the - * caller. Builder allows for safe buffer management and builder->added - * will contain the needed size of the buffer if it was insufficient - check - * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -279,19 +268,17 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( * allocated */ EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( - const char *base64, fiftyoneDegreesStringBuilder *builder, + const char *base64, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); /** * Eagerly convert device.sua JSON string to HTTP header representation. * @param json a raw JSON string with device.sua field contents - * @param builder - a StringBuilder object encapsulating a preallocated working - * memory buffer used to store the converted HTTP header names and values. - * The lifetime of this buffer and builder object is managed by the - * caller. Builder allows for safe buffer management and builder->added - * will contain the needed size of the buffer if it was insufficient - check - * if exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY + * @param buffer preallocated working memory buffer used to store the converted + * HTTP header names and values with a new line separator (\n) between each + * header key-value pair. The lifetime of this buffer is managed by the caller + * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS @@ -320,7 +307,7 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( * allocated */ EXTERNAL size_t fiftyoneDegreesTransformSua( - const char *json, fiftyoneDegreesStringBuilder *builder, + const char *json, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); From 546c284cde81be9c47656cd7de8b710db0654eb2 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Mon, 9 Sep 2024 16:18:08 +0100 Subject: [PATCH 60/73] FEAT: Added a common constant for the cookie prefix that so far has not been needed in C/C++ code. --- common.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common.h b/common.h index a98399f1..cbaf51a9 100644 --- a/common.h +++ b/common.h @@ -41,4 +41,10 @@ #define EXTERNAL_VAR extern #endif +// The characters at the start of a cookie or other storage key that indicate +// the item is related to 51Degrees. +#ifndef FIFTYONE_DEGREES_COMMON_COOKIE_PREFIX +#define FIFTYONE_DEGREES_COMMON_COOKIE_PREFIX "51D_" +#endif + #endif From ce94e9ec4f198e3e61ba1c7623f560352528bbb5 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 10 Sep 2024 11:20:31 +0100 Subject: [PATCH 61/73] DOC/REFACT: Added documentation to indices.h. Refactored the method names to use "indices" to reflect the revised name of the file. --- dataset.c | 2 +- dataset.h | 2 +- fiftyone.h | 8 ++--- indices.c | 24 ++++++------- indices.h | 101 ++++++++++++++++++++++++++++++++++++++++------------- profile.c | 4 +-- profile.h | 2 +- 7 files changed, 98 insertions(+), 45 deletions(-) diff --git a/dataset.c b/dataset.c index 7bf87650..f48e841c 100644 --- a/dataset.c +++ b/dataset.c @@ -72,7 +72,7 @@ void fiftyoneDegreesDataSetFree(fiftyoneDegreesDataSetBase *dataSet) { // Free the memory used for the index of property and profile values. if (dataSet->indexPropertyProfile != NULL) { - IndexPropertyProfileFree(dataSet->indexPropertyProfile); + IndicesPropertyProfileFree(dataSet->indexPropertyProfile); dataSet->indexPropertyProfile = NULL; } diff --git a/dataset.h b/dataset.h index 29e9c163..1f203cd7 100644 --- a/dataset.h +++ b/dataset.h @@ -127,7 +127,7 @@ typedef struct fiftyone_degrees_dataset_base_t { fiftyoneDegreesOverridePropertyArray *overridable; /**< Array of properties that can be overridden */ - fiftyoneDegreesIndexPropertyProfile* indexPropertyProfile; /**< Index to + fiftyoneDegreesIndicesPropertyProfile* indexPropertyProfile; /**< Index to look up profile values by property */ diff --git a/fiftyone.h b/fiftyone.h index 4a6c7628..11d1a468 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -182,7 +182,7 @@ MAP_TYPE(Float) MAP_TYPE(Coordinate) MAP_TYPE(KeyValuePair) MAP_TYPE(HeaderID) -MAP_TYPE(IndexPropertyProfile) +MAP_TYPE(IndicesPropertyProfile) MAP_TYPE(StringBuilder) MAP_TYPE(Json) MAP_TYPE(KeyValuePairArray) @@ -344,9 +344,9 @@ MAP_TYPE(KeyValuePairArray) #define ProcessGetId fiftyoneDegreesProcessGetId /**< Synonym for fiftyoneDegreesProcessGetId */ #define YamlFileIterate fiftyoneDegreesYamlFileIterate /**< Synonym for fiftyoneDegreesYamlFileIterate */ #define YamlFileIterateWithLimit fiftyoneDegreesYamlFileIterateWithLimit /**< Synonym for fiftyoneDegreesYamlFileIterateWithLimit */ -#define IndexPropertyProfileCreate fiftyoneDegreesIndexPropertyProfileCreate /**< Synonym for fiftyoneDegreesIndexPropertyProfileCreate */ -#define IndexPropertyProfileFree fiftyoneDegreesIndexPropertyProfileFree /**< Synonym for fiftyoneDegreesIndexPropertyProfileFree */ -#define IndexPropertyProfileLookup fiftyoneDegreesIndexPropertyProfileLookup /**< Synonym for fiftyoneDegreesIndexPropertyProfileLookup */ +#define IndicesPropertyProfileCreate fiftyoneDegreesIndicesPropertyProfileCreate /**< Synonym for fiftyoneDegreesIndicesPropertyProfileCreate */ +#define IndicesPropertyProfileFree fiftyoneDegreesIndicesPropertyProfileFree /**< Synonym for fiftyoneDegreesIndicesPropertyProfileFree */ +#define IndicesPropertyProfileLookup fiftyoneDegreesIndicesPropertyProfileLookup /**< Synonym for fiftyoneDegreesIndicesPropertyProfileLookup */ #define JsonDocumentStart fiftyoneDegreesJsonDocumentStart /**< Synonym for fiftyoneDegreesJsonDocumentStart */ #define JsonDocumentEnd fiftyoneDegreesJsonDocumentEnd /**< Synonym for fiftyoneDegreesJsonDocumentEnd */ #define JsonPropertyStart fiftyoneDegreesJsonPropertyStart /**< Synonym for fiftyoneDegreesJsonPropertyStart */ diff --git a/indices.c b/indices.c index c31d9806..ed3d7d8b 100644 --- a/indices.c +++ b/indices.c @@ -23,15 +23,15 @@ #include "indices.h" #include "fiftyone.h" +// Working data structure used to construct the index. typedef struct map_t { uint32_t availableProperty; // available property index int16_t propertyIndex; // index in the properties collection } map; - // Gets the index of the profile id in the property profile index. static uint32_t getProfileIndex( - IndexPropertyProfile* index, + IndicesPropertyProfile* index, uint32_t profileId) { return profileId - index->minProfileId; } @@ -40,7 +40,7 @@ static uint32_t getProfileIndex( // the position for the property and profile to the first value index from the // profile. static void addProfileValuesMethod( - IndexPropertyProfile* index, // index in use or null if not available + IndicesPropertyProfile* index, // index in use or null if not available map* propertyIndexes, // property indexes in ascending order fiftyoneDegreesCollection* values, // collection of values Profile* profile, @@ -91,7 +91,7 @@ static void addProfileValuesMethod( static void iterateProfiles( fiftyoneDegreesCollection* profiles, fiftyoneDegreesCollection* profileOffsets, - IndexPropertyProfile* index, // index in use or null if not available + IndicesPropertyProfile* index, // index in use or null if not available map* propertyIndexes, // property indexes in ascending order fiftyoneDegreesCollection* values, // collection of values Exception *exception) { @@ -172,8 +172,8 @@ static map* createPropertyIndexes( return index; } -fiftyoneDegreesIndexPropertyProfile* -fiftyoneDegreesIndexPropertyProfileCreate( +fiftyoneDegreesIndicesPropertyProfile* +fiftyoneDegreesIndicesPropertyProfileCreate( fiftyoneDegreesCollection* profiles, fiftyoneDegreesCollection* profileOffsets, fiftyoneDegreesPropertiesAvailable* available, @@ -187,8 +187,8 @@ fiftyoneDegreesIndexPropertyProfileCreate( } // Allocate memory for the index and set the fields. - IndexPropertyProfile* index = (IndexPropertyProfile*)Malloc( - sizeof(IndexPropertyProfile)); + IndicesPropertyProfile* index = (IndicesPropertyProfile*)Malloc( + sizeof(IndicesPropertyProfile)); if (index == NULL) { EXCEPTION_SET(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); return NULL; @@ -245,14 +245,14 @@ fiftyoneDegreesIndexPropertyProfileCreate( } } -void fiftyoneDegreesIndexPropertyProfileFree( - fiftyoneDegreesIndexPropertyProfile* index) { +void fiftyoneDegreesIndicesPropertyProfileFree( + fiftyoneDegreesIndicesPropertyProfile* index) { Free(index->valueIndexes); Free(index); } -uint32_t fiftyoneDegreesIndexPropertyProfileLookup( - fiftyoneDegreesIndexPropertyProfile* index, +uint32_t fiftyoneDegreesIndicesPropertyProfileLookup( + fiftyoneDegreesIndicesPropertyProfile* index, uint32_t profileId, uint32_t availablePropertyIndex) { uint32_t valueIndex = diff --git a/indices.h b/indices.h index f06c8b76..f000228f 100644 --- a/indices.h +++ b/indices.h @@ -20,17 +20,67 @@ * such notice(s) shall fulfill the requirements of that article. * ********************************************************************* */ -#ifndef FIFTYONE_DEGREES_INDEXES_H_INCLUDED -#define FIFTYONE_DEGREES_INDEXES_H_INCLUDED +#ifndef FIFTYONE_DEGREES_INDICES_H_INCLUDED +#define FIFTYONE_DEGREES_INDICES_H_INCLUDED /** * @ingroup FiftyOneDegreesCommon - * @defgroup FiftyOneDegreesIndexes Indexes + * @defgroup FiftyOneDegreesIndices Indices * - * A look up list for profile and property index to the first value index. + * A look up structure for profile and property index to the first value + * associated with the property and profile. * * ## Introduction - * + * + * Data sets relate profiles to the values associated with them. Values are + * associated with properties. The values associated with a profile are + * ordered in ascending order of property. Therefore when a request is made to + * obtain the value for a property and profile the values needed to be + * searched using a binary search to find a value related to the property. + * Then the list of prior values is checked until the first value for the + * property is found. + * + * The indices methods provide common functionality to create a structure + * that directly relates profile ids and required property indexes to the + * first value index thus increasing the efficiency of retrieving values. + * + * It is expected these methods will be used during data set initialization. + * + * ## Structure + * + * A sparse array of profile ids and required property indexes is used. Whilst + * this consumes more linear memory than a binary tree or other structure it + * is extremely fast to retrieve values from. As the difference between the + * lowest and highest profile id is relatively small the memory associated + * with absent profile ids is considered justifiable considering the + * performance benefit. A further optimization is to use the required property + * index rather than the index of all possible properties contained in the + * data set. In most use cases the caller only requires a sub set of + * properties to be available for retrieval. + * + * ## Create + * + * fiftyoneDegreesIndicesPropertyProfileCreate should be called once the data + * set is initialized with the required data structures. Memory is allocated + * by the method and a pointer to the index data structure is returned. The + * caller is not expected to use the returned data structure directly. + * + * Some working memory is allocated during the indexing process. Therefore + * this method must be called before a freeze on allocating new memory is + * required. + * + * ## Free + * + * fiftyoneDegreesIndicesPropertyProfileFree is used to free the memory used + * by the index returned from Create. Must be called during the freeing of the + * related data set. + * + * ## Lookup + * + * fiftyoneDegreesIndicesPropertyProfileLookup is used to return the index in + * the values associated with the profile for the profile id and the required + * property index. + * * @{ */ @@ -53,7 +103,8 @@ /** * Maps the profile index and the property index to the first value index of * the profile for the property. Is an array of uint32_t with entries equal to - * the number of properties multiplied by the number of profiles. + * the number of properties multiplied by the difference between the lowest and + * highest profile id. */ typedef struct fiftyone_degrees_index_property_profile{ uint32_t* valueIndexes; // array of value indexes @@ -63,12 +114,12 @@ typedef struct fiftyone_degrees_index_property_profile{ uint32_t profileCount; // total number of profiles uint32_t size; // number elements in the valueIndexes array uint32_t filled; // number of elements with values -} fiftyoneDegreesIndexPropertyProfile; +} fiftyoneDegreesIndicesPropertyProfile; /** * Create an index for the profiles, available properties, and values provided * such that given the index to a property and profile the index of the first - * value can be returned by calling fiftyoneDegreesIndexPropertyProfileLookup. + * value can be returned by calling fiftyoneDegreesIndicesPropertyProfileLookup. * @param profiles collection of variable sized profiles to be indexed * @param profileOffsets collection of fixed offsets to profiles to be indexed * @param available properties provided by the caller @@ -77,8 +128,8 @@ typedef struct fiftyone_degrees_index_property_profile{ * exception occurs. See exceptions.h * @return pointer to the index memory structure */ -EXTERNAL fiftyoneDegreesIndexPropertyProfile* -fiftyoneDegreesIndexPropertyProfileCreate( +EXTERNAL fiftyoneDegreesIndicesPropertyProfile* +fiftyoneDegreesIndicesPropertyProfileCreate( fiftyoneDegreesCollection* profiles, fiftyoneDegreesCollection* profileOffsets, fiftyoneDegreesPropertiesAvailable* available, @@ -87,28 +138,30 @@ fiftyoneDegreesIndexPropertyProfileCreate( /** * Frees an index previously created by - * fiftyoneDegreesIndexPropertyProfileCreate. + * fiftyoneDegreesIndicesPropertyProfileCreate. * @param index to be freed */ -EXTERNAL void fiftyoneDegreesIndexPropertyProfileFree( - fiftyoneDegreesIndexPropertyProfile* index); +EXTERNAL void fiftyoneDegreesIndicesPropertyProfileFree( + fiftyoneDegreesIndicesPropertyProfile* index); /** - * For a given profile id and property index returns the first value index, or - * null if a first index can not be determined from the index. The indexes - * relate to the collections for profiles, properties, and values provided to - * the fiftyoneDegreesIndexPropertyProfileCreate method when the index was - * created. - * @param index to use for the lookup - * @param profileId the values relate to - * @param propertyIndex in the collection of properties + * For a given profile id and available property index returns the first value + * index, or null if a first index can not be determined from the index. The + * indexes relate to the collections for profiles, properties, and values + * provided to the fiftyoneDegreesIndicesPropertyProfileCreate method when the + * index was created. The availablePropertyIndex is not the index of all + * possible properties, but the index of the ones the data set was created + * expecting to return. + * @param index from fiftyoneDegreesIndicesPropertyProfileCreate to use + * @param profileId the values need to relate to + * @param availablePropertyIndex in the list of required properties * @return the index in the list of values for the profile for the first value * associated with the property */ -EXTERNAL uint32_t fiftyoneDegreesIndexPropertyProfileLookup( - fiftyoneDegreesIndexPropertyProfile* index, +EXTERNAL uint32_t fiftyoneDegreesIndicesPropertyProfileLookup( + fiftyoneDegreesIndicesPropertyProfile* index, uint32_t profileId, - uint32_t propertyIndex); + uint32_t availablePropertyIndex); /** * @} diff --git a/profile.c b/profile.c index e1d99d7d..2478825e 100644 --- a/profile.c +++ b/profile.c @@ -291,14 +291,14 @@ uint32_t fiftyoneDegreesProfileIterateValuesForProperty( uint32_t fiftyoneDegreesProfileIterateValuesForPropertyWithIndex( fiftyoneDegreesCollection* values, - fiftyoneDegreesIndexPropertyProfile* index, + fiftyoneDegreesIndicesPropertyProfile* index, uint32_t availablePropertyIndex, fiftyoneDegreesProfile* profile, fiftyoneDegreesProperty* property, void* state, fiftyoneDegreesProfileIterateMethod callback, fiftyoneDegreesException* exception) { - uint32_t i = IndexPropertyProfileLookup( + uint32_t i = IndicesPropertyProfileLookup( index, profile->profileId, availablePropertyIndex); diff --git a/profile.h b/profile.h index 66666c3f..2bb62845 100644 --- a/profile.h +++ b/profile.h @@ -224,7 +224,7 @@ EXTERNAL uint32_t fiftyoneDegreesProfileIterateValuesForProperty( */ EXTERNAL uint32_t fiftyoneDegreesProfileIterateValuesForPropertyWithIndex( fiftyoneDegreesCollection* values, - fiftyoneDegreesIndexPropertyProfile* index, + fiftyoneDegreesIndicesPropertyProfile* index, uint32_t availablePropertyIndex, fiftyoneDegreesProfile* profile, fiftyoneDegreesProperty* property, From 94c617cb8303d5d794054c732fbe06c7fabfc017 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 10 Sep 2024 13:50:49 +0100 Subject: [PATCH 62/73] REFACT: Updated to handle exceptions being disabled via the directive FIFTYONE_DEGREES_EXCEPTIONS_DISABLED. Tests won't pass with this directive enabled as some rely on the exception to test the fidelity of the failure. This is expected. Some modification of transform.* requiired to support status codes where boolean return codes can't be used. --- Exceptions.cpp | 8 ++++++++ Transform.cpp | 6 +++--- exceptions.h | 19 ++++++++++++++---- fiftyone.h | 1 + file.c | 3 ++- tests/JsonTests.cpp | 26 ++++++++++++------------ tests/PoolTests.cpp | 8 +++++--- tests/ProfileTests.cpp | 14 ++++++------- tests/TransformTests.cpp | 40 ++++++++++++++++++------------------- transform.c | 43 ++++++++++++++++++---------------------- 10 files changed, 93 insertions(+), 75 deletions(-) diff --git a/Exceptions.cpp b/Exceptions.cpp index f236bf4b..a38b2df8 100644 --- a/Exceptions.cpp +++ b/Exceptions.cpp @@ -57,6 +57,7 @@ fiftyoneDegreesStatusCode StatusCodeException::getCode() const noexcept { return statusCode; } +#ifndef FIFTYONE_DEGREES_EXCEPTIONS_DISABLED FatalException::FatalException( fiftyoneDegreesException *exception) : StatusCodeException(exception->status) { const char *exceptionMessage = ExceptionGetMessage(exception); @@ -65,6 +66,13 @@ FatalException::FatalException( Free((void*)exceptionMessage); } } +#else +FatalException::FatalException( + fiftyoneDegreesException* exception) : StatusCodeException(NOT_SET) { + message.append( + "Exceptions disabled with FIFTYONE_DEGREES_EXCEPTIONS_DISABLED"); +} +#endif NotImplementedException::NotImplementedException() : runtime_error("Function not yet implemented") { diff --git a/Transform.cpp b/Transform.cpp index b0d6b51d..50699857 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -40,15 +40,15 @@ Transform::Headers Transform::apiInvoker(CTransformAPI func, func(json.c_str(), buffer.data(), buffer.size(), headers, exception); - while (exception->status == FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY) { + while (EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { headers->count = 0; - exception->status = FIFTYONE_DEGREES_STATUS_SUCCESS; + EXCEPTION_SET(SUCCESS); buffer.resize(buffer.size() * 2); func(json.c_str(), buffer.data(), buffer.size(), headers, exception); } - if (exception->status == FIFTYONE_DEGREES_STATUS_CORRUPT_DATA) { + if (EXCEPTION_CHECK(CORRUPT_DATA)) { fiftyoneDegreesFree(headers); EXCEPTION_THROW; } diff --git a/exceptions.h b/exceptions.h index ed7af9a4..93c92001 100644 --- a/exceptions.h +++ b/exceptions.h @@ -136,11 +136,20 @@ exception->func = NULL; \ exception->line = -1; \ exception->status = FIFTYONE_DEGREES_STATUS_NOT_SET; +/** +* Macro used to check if an exception status equals the value of t. +* Warning: this macro should be avoided in anything other than test code as +* when exceptions are disabled there will be unpredictable results. Using a +* local status variable for checks is a better pattern. +*/ +#define FIFTYONE_DEGREES_EXCEPTION_CHECK(t) \ +(exception == NULL || exception->status == t) + /** * Macro used to check if there is no exception currently. */ #define FIFTYONE_DEGREES_EXCEPTION_OKAY \ -(exception == NULL || exception->status == FIFTYONE_DEGREES_STATUS_NOT_SET) +FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_NOT_SET) #ifdef FIFTYONE_DEGREES_EXCEPTIONS_HPP @@ -167,14 +176,16 @@ fiftyoneDegreesExceptionCheckAndExit(exception); EXTERNAL typedef void* fiftyoneDegreesException; -#define FIFTYONE_DEGREES_EXCEPTION_CLEAR exception = NULL; +#define FIFTYONE_DEGREES_EXCEPTION_CLEAR -#define FIFTYONE_DEGREES_EXCEPTION_SET(s) exception = NULL; +#define FIFTYONE_DEGREES_EXCEPTION_SET(s) -#define FIFTYONE_DEGREES_EXCEPTION_OKAY (exception == exception) +#define FIFTYONE_DEGREES_EXCEPTION_OKAY true #define FIFTYONE_DEGREES_EXCEPTION_THROW +#define FIFTYONE_DEGREES_EXCEPTION_CHECK(t) false + #endif /** diff --git a/fiftyone.h b/fiftyone.h index 11d1a468..f378b86a 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -377,6 +377,7 @@ MAP_TYPE(KeyValuePairArray) #define EXCEPTION_OKAY FIFTYONE_DEGREES_EXCEPTION_OKAY /**< Synonym for #FIFTYONE_DEGREES_EXCEPTION_OKAY macro. */ #define EXCEPTION_FAILED FIFTYONE_DEGREES_EXCEPTION_FAILED /**< Synonym for #FIFTYONE_DEGREES_EXCEPTION_FAILED macro. */ #define EXCEPTION_THROW FIFTYONE_DEGREES_EXCEPTION_THROW /**< Synonym for #FIFTYONE_DEGREES_EXCEPTION_THROW macro. */ +#define EXCEPTION_CHECK FIFTYONE_DEGREES_EXCEPTION_CHECK /**< Synonym for #FIFTYONE_DEGREES_EXCEPTION_CHECK macro. */ #define STRING FIFTYONE_DEGREES_STRING /**< Synonym for #FIFTYONE_DEGREES_STRING macro. */ #define IP_ADDRESS FIFTYONE_DEGREES_IP_ADDRESS /**< Synonym for #FIFTYONE_DEGREES_IP_ADDRESS macro. */ #define COLLECTION_RELEASE FIFTYONE_DEGREES_COLLECTION_RELEASE /**< Synonym for #FIFTYONE_DEGREES_COLLECTION_RELEASE macro. */ diff --git a/file.c b/file.c index 84622c83..0027dabf 100644 --- a/file.c +++ b/file.c @@ -723,7 +723,6 @@ bool fiftyoneDegreesFileGetExistingTempFile( return false; } - fiftyoneDegreesStatusCode fiftyoneDegreesFileAddTempFileName( const char* masterFileName, char* destination, @@ -979,7 +978,9 @@ fiftyoneDegreesStatusCode fiftyoneDegreesFilePoolInit( } } else if (EXCEPTION_FAILED) { +#ifndef FIFTYONE_DEGREES_EXCEPTIONS_DISABLED status = exception->status; +#endif } else { status = INSUFFICIENT_MEMORY; diff --git a/tests/JsonTests.cpp b/tests/JsonTests.cpp index c8f57bd8..29785feb 100644 --- a/tests/JsonTests.cpp +++ b/tests/JsonTests.cpp @@ -189,23 +189,23 @@ TEST_F(JsonTests, unhappyPaths) { { EXCEPTION_CLEAR; fiftyoneDegreesJsonDocumentStart(&json); - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NOT_SET); + EXPECT_TRUE(EXCEPTION_OKAY); - EXCEPTION_CLEAR; - fiftyoneDegreesJsonPropertyStart(&json); - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NULL_POINTER); - - EXCEPTION_CLEAR; - fiftyoneDegreesJsonPropertyValues(&json); - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NULL_POINTER); - - EXCEPTION_CLEAR; - fiftyoneDegreesJsonPropertyEnd(&json); - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NULL_POINTER); + EXCEPTION_CLEAR; + fiftyoneDegreesJsonPropertyStart(&json); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_NULL_POINTER)); + + EXCEPTION_CLEAR; + fiftyoneDegreesJsonPropertyValues(&json); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_NULL_POINTER)); + + EXCEPTION_CLEAR; + fiftyoneDegreesJsonPropertyEnd(&json); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_NULL_POINTER)); EXCEPTION_CLEAR; fiftyoneDegreesJsonDocumentEnd(&json); - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_NOT_SET); + EXPECT_TRUE(EXCEPTION_OKAY); EXPECT_STREQ(builder.ptr, "{}"); } diff --git a/tests/PoolTests.cpp b/tests/PoolTests.cpp index c08e810a..b2c2ab7a 100644 --- a/tests/PoolTests.cpp +++ b/tests/PoolTests.cpp @@ -28,6 +28,8 @@ #include "../exceptions.h" #include "../pool.h" +// Note: fiftyone.h short names can't be used as conflicts with Pool class. + #ifndef _MSC_VER #define _rmdir rmdir #endif @@ -288,7 +290,7 @@ TEST_F(Pool, PoolGetInsufficientHandles) { // Assert ASSERT_EQ(NULL, item2); ASSERT_FALSE(FIFTYONE_DEGREES_EXCEPTION_OKAY); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_HANDLES); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_HANDLES)); // Cleanup fiftyoneDegreesPoolItemRelease(item); @@ -328,7 +330,7 @@ TEST_F(Pool, PoolGetInsufficientHandlesMultiple) { // Assert ASSERT_EQ(NULL, item3); ASSERT_FALSE(FIFTYONE_DEGREES_EXCEPTION_OKAY); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_HANDLES); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_HANDLES)); // Cleanup fiftyoneDegreesPoolItemRelease(item); @@ -363,7 +365,7 @@ TEST_F(Pool, PoolGetHandlesReturned) { exception); ASSERT_EQ(NULL, item2); ASSERT_FALSE(FIFTYONE_DEGREES_EXCEPTION_OKAY); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_HANDLES); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_HANDLES)); FIFTYONE_DEGREES_EXCEPTION_CLEAR; fiftyoneDegreesPoolItemRelease(item); item2 = fiftyoneDegreesPoolItemGet( diff --git a/tests/ProfileTests.cpp b/tests/ProfileTests.cpp index d3c24063..1891a396 100644 --- a/tests/ProfileTests.cpp +++ b/tests/ProfileTests.cpp @@ -375,7 +375,7 @@ void ProfileTests::iterateValueIndicesForAvailableProperties(std::vectorvalue, p); }); EXPECT_NE(&profileValueNames[i][0] + N_PROPERTIES, found); //belongs to current profile } - EXPECT_TRUE(EXCEPTION_OKAY) << exception->status; + EXPECT_TRUE(EXCEPTION_OKAY); } fiftyoneDegreesFree(propertiesAvailable); @@ -422,7 +422,7 @@ TEST_F(ProfileTests, profileIterateForPropertyAndValue) { void ProfileTests::indicesLookup(std::vector &propertyNames) { EXCEPTION_CREATE fiftyoneDegreesPropertiesAvailable *availableProperties = createAvailableProperties(propertyNames); - fiftyoneDegreesIndexPropertyProfile *index = fiftyoneDegreesIndexPropertyProfileCreate(profilesCollection, profileOffsetsCollection, availableProperties, valuesCollection, exception); + fiftyoneDegreesIndicesPropertyProfile *index = fiftyoneDegreesIndicesPropertyProfileCreate(profilesCollection, profileOffsetsCollection, availableProperties, valuesCollection, exception); for (int i=0;i &propertyNames) { for (uint32_t j=0;jcount;j++) { fiftyoneDegreesPropertyAvailable property = availableProperties->items[j]; //lookup property value Index within the profile with profileId for the available property indexed at j in the array of availableProperties - uint32_t valueIdxIdxWithinProfile = fiftyoneDegreesIndexPropertyProfileLookup(index, profileId, j); + uint32_t valueIdxIdxWithinProfile = fiftyoneDegreesIndicesPropertyProfileLookup(index, profileId, j); //convert valueIdxIdxWithinProfile to the valueIndex in the actual values collection by looking it up with the profile collection uint32_t *first = (uint32_t*)(profile + 1); uint32_t valueIdx = *(first + valueIdxIdxWithinProfile); @@ -440,7 +440,7 @@ void ProfileTests::indicesLookup(std::vector &propertyNames) { } } - fiftyoneDegreesIndexPropertyProfileFree(index); + fiftyoneDegreesIndicesPropertyProfileFree(index); fiftyoneDegreesFree(availableProperties); } @@ -462,7 +462,7 @@ bool collectValues(void *state, fiftyoneDegreesCollectionItem *item) { void ProfileTests::iterateValueIndicesForEachAvailableProperty(std::vector &propertyNames) { EXCEPTION_CREATE fiftyoneDegreesPropertiesAvailable *availableProperties = createAvailableProperties(propertyNames); - fiftyoneDegreesIndexPropertyProfile *index = fiftyoneDegreesIndexPropertyProfileCreate(profilesCollection, profileOffsetsCollection, availableProperties, valuesCollection, exception); + fiftyoneDegreesIndicesPropertyProfile *index = fiftyoneDegreesIndicesPropertyProfileCreate(profilesCollection, profileOffsetsCollection, availableProperties, valuesCollection, exception); for (int i=0;istatus, FIFTYONE_DEGREES_STATUS_PROFILE_EMPTY); + EXPECT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_PROFILE_EMPTY)); // non-existent profileId EXCEPTION_CLEAR diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 942286c7..904deca2 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -288,7 +288,7 @@ TEST_F(Transform, IncompleteJSON) { Transform::results, exception); EXPECT_FALSE(EXCEPTION_OKAY); EXPECT_TRUE(EXCEPTION_FAILED); - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + EXPECT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); } fiftyoneDegreesFree(buffer); } @@ -397,7 +397,7 @@ TEST_F(Transform, IncompleteSUA) { fillResultsCallback, Transform::results, exception); - EXPECT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + EXPECT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); } fiftyoneDegreesFree(buffer); } @@ -421,7 +421,7 @@ TEST_F(Transform, GHEVIncorrectBool) { // --- - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -438,7 +438,7 @@ TEST_F(Transform, SUAIncorrectBool) { // --- - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -571,7 +571,7 @@ TEST_F(Transform, GHEVBase64CorruptedLen) { ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -594,7 +594,7 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol) { ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -617,7 +617,7 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol2) { ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -818,7 +818,7 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { count = fiftyoneDegreesTransformGhevFromJson( ghev, buffer, bufferLength, empty_headers, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); ASSERT_EQ(count, 1); fiftyoneDegreesFree(buffer); @@ -846,7 +846,7 @@ TEST_F(Transform, GHEVBase64InsufficientCapacity) { size_t count = fiftyoneDegreesTransformGhevFromBase64( ghev, buffer, bufferLength, empty_headers, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); ASSERT_EQ(count, 1); fiftyoneDegreesFree(buffer); @@ -872,7 +872,7 @@ TEST_F(Transform, SUAInsufficientCapacity) { size_t count = fiftyoneDegreesTransformSua( sua, buffer, bufferLength, empty_headers, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); ASSERT_EQ(count, 1); fiftyoneDegreesKeyValuePairArray *headers = NULL; @@ -950,7 +950,7 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { // --- - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -974,7 +974,7 @@ TEST_F(Transform, SUANotEnoughMemory) { // --- - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1035,7 +1035,7 @@ TEST_F(Transform, SUAArrayNotEnoughMemory) { // --- - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1156,7 +1156,7 @@ TEST_F(Transform, GHEVCorruptInput) { fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -1178,7 +1178,7 @@ TEST_F(Transform, GHEVBufferTooSmall) { fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1200,7 +1200,7 @@ TEST_F(Transform, GHEVEvidenceLowCapacity) { fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1409,7 +1409,7 @@ TEST_F(Transform, SUACorrupt2) { fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -1425,7 +1425,7 @@ TEST_F(Transform, SUACorrupt3) { fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -1445,7 +1445,7 @@ TEST_F(Transform, SUABufferTooSmall) { fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1465,7 +1465,7 @@ TEST_F(Transform, SUAEvidenceLowCapacity) { fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(exception->status, FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY); + ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } diff --git a/transform.c b/transform.c index f6a023cf..3d8a8f6d 100644 --- a/transform.c +++ b/transform.c @@ -27,13 +27,13 @@ #define NotExpectSymbol(json, ch, exit) \ if (*json == ch) { \ - exception->status = CORRUPT_DATA; \ + EXCEPTION_SET(CORRUPT_DATA); \ exit; \ } #define ExpectSymbol(json, ch, exit) \ if (*json != ch) { \ - exception->status = CORRUPT_DATA; \ + EXCEPTION_SET(CORRUPT_DATA); \ exit; \ } @@ -44,8 +44,7 @@ } #define ValuePtr \ - (exception->status == INSUFFICIENT_MEMORY ? NULL \ - : begin) + (EXCEPTION_CHECK(INSUFFICIENT_MEMORY) ? NULL : begin) #define GET_SEXTET(str, i) \ (str[i] == '=' ? 0 & i++ : base64_char_to_value(str[i++], exception)) @@ -90,7 +89,7 @@ static inline char* safe_write_to_buffer( if (begin < end) { *begin = symbol; } else { - exception->status = INSUFFICIENT_MEMORY; + EXCEPTION_SET(INSUFFICIENT_MEMORY); } return ++begin; @@ -119,7 +118,7 @@ static inline uint32_t base64_char_to_value( 255}; if (base64_lookup_table[(uint8_t)c] == 255) { - exception->status = CORRUPT_DATA; + EXCEPTION_SET(CORRUPT_DATA); } return base64_lookup_table[(uint8_t)c]; @@ -130,7 +129,7 @@ static size_t base64_decode(const char* base64_input, char* const buffer, Exception* const exception) { size_t input_length = strlen(base64_input); if (input_length % 4 != 0) { - exception->status = CORRUPT_DATA; + EXCEPTION_SET(CORRUPT_DATA) return 0; // Invalid base64 input length } @@ -143,10 +142,6 @@ static size_t base64_decode(const char* base64_input, char* const buffer, uint32_t sextet_c = GET_SEXTET(base64_input, i); uint32_t sextet_d = GET_SEXTET(base64_input, i); - if (exception->status == CORRUPT_DATA) { - return 0; - } - uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; @@ -710,7 +705,7 @@ static char* read_bool_ghev_value(const char** json, char* begin, } break; default: { - exception->status = CORRUPT_DATA; + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } @@ -731,7 +726,7 @@ static char* read_bool_sua_value(const char** json, char* begin, } break; default: { - exception->status = CORRUPT_DATA; + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } @@ -894,7 +889,7 @@ static char* read_brands_ghev_value(const char** json, char* begin, } break; default: { - exception->status = CORRUPT_DATA; + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } @@ -983,7 +978,7 @@ static char* read_brands_sua_value(const char** json, char* begin, } break; default: { - exception->status = CORRUPT_DATA; + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } @@ -1145,8 +1140,8 @@ static size_t main_parsing_body(const char* json, char* const buffer, // write keys to buffer, init cache and skip to the first key json = init_parsing(json, &begin, end, cache, exception); - if (exception->status == CORRUPT_DATA || - exception->status == INSUFFICIENT_MEMORY) { + if (EXCEPTION_CHECK(CORRUPT_DATA) || + EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { return 0; } @@ -1171,7 +1166,7 @@ static size_t main_parsing_body(const char* json, char* const buffer, NotExpectSymbol(json, '\0', break); char* ptr = read_value(&json, begin, end, cache, key, exception); - if (exception->status == CORRUPT_DATA) { + if (EXCEPTION_CHECK(CORRUPT_DATA)) { break; } @@ -1211,8 +1206,8 @@ size_t fiftyoneDegreesTransformIterateGhevFromBase64( fiftyoneDegreesException* const exception) { size_t offset = base64_decode(base64, buffer, length, exception); - if (exception->status == INSUFFICIENT_MEMORY || - exception->status == CORRUPT_DATA) { + if (EXCEPTION_CHECK(INSUFFICIENT_MEMORY) || + EXCEPTION_CHECK(CORRUPT_DATA)) { return 0; } @@ -1237,7 +1232,7 @@ size_t fiftyoneDegreesTransformGhevFromJson( json, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count - initial) { - exception->status = INSUFFICIENT_CAPACITY; + EXCEPTION_SET(INSUFFICIENT_CAPACITY); } return calls; @@ -1252,7 +1247,7 @@ size_t fiftyoneDegreesTransformGhevFromBase64( base64, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count - initial) { - exception->status = INSUFFICIENT_CAPACITY; + EXCEPTION_SET(INSUFFICIENT_CAPACITY); } return calls; @@ -1267,8 +1262,8 @@ size_t fiftyoneDegreesTransformSua( json, buffer, length, pushToHeaders, headers, exception); if (calls != headers->count - initial) { - exception->status = INSUFFICIENT_CAPACITY; + EXCEPTION_SET(INSUFFICIENT_CAPACITY); } return calls; -} +} \ No newline at end of file From 0ea62dda5693f5f3febb5bbba553bc1128f9be12 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 10 Sep 2024 13:51:54 +0100 Subject: [PATCH 63/73] DOC: Tidy up of line length and spelling. --- exceptions.h | 2 +- indices.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exceptions.h b/exceptions.h index 93c92001..0c26ae1f 100644 --- a/exceptions.h +++ b/exceptions.h @@ -61,7 +61,7 @@ * exception message will be printed to standard output, then the process will * exit. * - * **C++** : As C++ supports exceptions, an fatal exception with the message + * **C++** : As C++ supports exceptions, a fatal exception with the message * will be thrown. This can then be caught or handled in whichever way the * caller sees fit. * diff --git a/indices.c b/indices.c index ed3d7d8b..0cbb7b77 100644 --- a/indices.c +++ b/indices.c @@ -67,9 +67,9 @@ static void addProfileValuesMethod( // If the value doesn't relate to the next property index then // move to the next property index. - while (p < index->availablePropertyCount && //first check validity of the subscript and then use it - propertyIndexes[p].propertyIndex < value->propertyIndex - ) { + while (p < index->availablePropertyCount && // first check validity + // of the subscript and then use it + propertyIndexes[p].propertyIndex < value->propertyIndex) { p++; } From 26f31e362e898a9497180918bc0e9c07bfa7c9fc Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Tue, 10 Sep 2024 13:24:17 +0200 Subject: [PATCH 64/73] REFACT: transform: StringBuilder + fiftyoneDegreesTransformIterateResult This reverts commit 5c06e7100ce551fe73ece7c4104aca53297ab17a. +REFACT: use fiftyoneDegreesTransformIterateResult reintroduce exceptions after rebase --- Transform.cpp | 64 +++++--- Transform.hpp | 2 +- tests/TransformTests.cpp | 275 ++++++++++++++++++++++----------- transform.c | 322 +++++++++++++++++++++------------------ transform.h | 79 +++++----- 5 files changed, 445 insertions(+), 297 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index 50699857..0f4bafb5 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -30,38 +30,54 @@ using namespace FiftyoneDegrees::Common; Transform::Transform(size_t capacity) : buffer(capacity) {} -Transform::Headers Transform::apiInvoker(CTransformAPI func, - const std::string& json) { - Transform::Headers res; - EXCEPTION_CREATE; - - fiftyoneDegreesKeyValuePairArray* headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); - - func(json.c_str(), buffer.data(), buffer.size(), headers, exception); - - while (EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { - headers->count = 0; - EXCEPTION_SET(SUCCESS); - buffer.resize(buffer.size() * 2); - - func(json.c_str(), buffer.data(), buffer.size(), headers, exception); - } - - if (EXCEPTION_CHECK(CORRUPT_DATA)) { - fiftyoneDegreesFree(headers); +Transform::Headers Transform::apiInvoker(CTransformAPI func, const std::string& json) { + class ArrayContainer { + public: + KeyValuePairArray* headers = NULL; + ArrayContainer() { + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); + } + + ~ArrayContainer() { + Free(headers); + } + }; + + // RAII + ArrayContainer container; + + bool first = true; + size_t written = 0; + EXCEPTION_CREATE; + while (first || EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)) { + if (!first) { + container.headers->count = 0; + size_t bufferSize = std::max(buffer.size() * 2, written + 1); //+1 to avoid 0 size + try { + buffer.resize(bufferSize); + } catch (const std::bad_alloc &) { + // reallocation failed - we just exit this loop by throwing an exception + EXCEPTION_THROW; + } + } + first = false; + EXCEPTION_CLEAR; + fiftyoneDegreesTransformIterateResult result = func(json.c_str(), buffer.data(), buffer.size(), container.headers, exception); + written = result.written; + } + + if (EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)) { EXCEPTION_THROW; } - for (uint32_t i = 0; i < headers->count; ++i) { - fiftyoneDegreesKeyValuePair& pair = headers->items[i]; + Transform::Headers res; + for (uint32_t i = 0; i < container.headers->count; ++i) { + fiftyoneDegreesKeyValuePair& pair = container.headers->items[i]; res.emplace(std::string{pair.key, pair.keyLength}, std::string{pair.value, pair.valueLength}); } - fiftyoneDegreesFree(headers); - return res; } diff --git a/Transform.hpp b/Transform.hpp index 22ae9b1d..e1031318 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -47,7 +47,7 @@ namespace FiftyoneDegrees { using Headers = std::map; using CTransformAPI = - size_t (*)(const char* base64, char* buffer, size_t length, + fiftyoneDegreesTransformIterateResult (*)(const char* base64, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception); diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 904deca2..343d94a9 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -158,7 +158,7 @@ TEST_F(Transform, GHEVIterativeJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -166,8 +166,8 @@ TEST_F(Transform, GHEVIterativeJSON) { ASSERT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 7); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 7); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldValue("sec-ch-ua", @@ -275,6 +275,7 @@ TEST_F(Transform, IncompleteJSON) { EXCEPTION_CREATE; for (const std::string &j : correct) { + fiftyoneDegreesTransformIterateGhevFromJson( j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -283,12 +284,13 @@ TEST_F(Transform, IncompleteJSON) { } for (const std::string &j : corrupted) { + fiftyoneDegreesTransformIterateGhevFromJson( j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); EXPECT_FALSE(EXCEPTION_OKAY); EXPECT_TRUE(EXCEPTION_FAILED); - EXPECT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); } fiftyoneDegreesFree(buffer); } @@ -385,6 +387,7 @@ TEST_F(Transform, IncompleteSUA) { EXCEPTION_CREATE; for (const std::string &j : correct) { + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -393,11 +396,12 @@ TEST_F(Transform, IncompleteSUA) { } for (const std::string &j : corrupted) { + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, fillResultsCallback, Transform::results, exception); - EXPECT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); } fiftyoneDegreesFree(buffer); } @@ -415,13 +419,14 @@ TEST_F(Transform, GHEVIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -432,13 +437,14 @@ TEST_F(Transform, SUAIncorrectBool) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -454,7 +460,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandJSON) { size_t bufferLength = strlen(ghev); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -462,8 +469,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandJSON) { EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 6); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 6); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldValue("sec-ch-ua", "\"Google Chrome\";v=\"126\""); @@ -489,7 +496,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -497,8 +505,8 @@ TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 6); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 6); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldValue("sec-ch-ua", "\"one\";v=null, \"Google Chrome\";v=\"126\""); @@ -525,7 +533,8 @@ TEST_F(Transform, GHEVIterativeBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateGhevFromBase64( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -533,8 +542,8 @@ TEST_F(Transform, GHEVIterativeBase64) { EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 6); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 6); + ASSERT_EQ(results->count, result.iterations); checkFieldAbsent("sec-ch-ua-arch"); checkFieldValue("sec-ch-ua", @@ -567,11 +576,12 @@ TEST_F(Transform, GHEVBase64CorruptedLen) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -590,11 +600,12 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -613,11 +624,12 @@ TEST_F(Transform, GHEVBase64CorruptedSymbol2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -638,7 +650,8 @@ TEST_F(Transform, GHEVIterativeSua) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -646,8 +659,8 @@ TEST_F(Transform, GHEVIterativeSua) { EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 7); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 7); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldAbsent("sec-ch-ua"); @@ -681,7 +694,8 @@ TEST_F(Transform, SuaWeirdPlatformVersion) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -689,8 +703,8 @@ TEST_F(Transform, SuaWeirdPlatformVersion) { EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 7); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 7); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldAbsent("sec-ch-ua"); @@ -725,7 +739,8 @@ TEST_F(Transform, SuaNullBrandPlatform) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); @@ -733,8 +748,8 @@ TEST_F(Transform, SuaNullBrandPlatform) { EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 5); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 5); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldAbsent("sec-ch-ua"); @@ -763,15 +778,16 @@ TEST_F(Transform, GHEVArrayJSON) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformGhevFromJson( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromJson( ghev, buffer, bufferLength, results, exception); // --- EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 7); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 7); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldValue("sec-ch-ua", @@ -805,21 +821,22 @@ TEST_F(Transform, GHEVArrayInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformGhevFromJson( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromJson( ghev, buffer, bufferLength, headers, exception); EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 2); - ASSERT_EQ(headers->count, count); + ASSERT_EQ(result.iterations, 2); + ASSERT_EQ(headers->count, result.iterations); // --- - count = fiftyoneDegreesTransformGhevFromJson( + result = fiftyoneDegreesTransformGhevFromJson( ghev, buffer, bufferLength, empty_headers, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - ASSERT_EQ(count, 1); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); + ASSERT_EQ(result.iterations, 1); fiftyoneDegreesFree(buffer); fiftyoneDegreesFree(headers); @@ -843,11 +860,12 @@ TEST_F(Transform, GHEVBase64InsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformGhevFromBase64( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromBase64( ghev, buffer, bufferLength, empty_headers, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - ASSERT_EQ(count, 1); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); + ASSERT_EQ(result.iterations, 1); fiftyoneDegreesFree(buffer); fiftyoneDegreesFree(empty_headers); @@ -869,21 +887,22 @@ TEST_F(Transform, SUAInsufficientCapacity) { FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformSua( sua, buffer, bufferLength, empty_headers, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - ASSERT_EQ(count, 1); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); + ASSERT_EQ(result.iterations, 1); fiftyoneDegreesKeyValuePairArray *headers = NULL; FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); FIFTYONE_DEGREES_EXCEPTION_CLEAR; - count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, + result = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, exception); EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 3); + ASSERT_EQ(result.iterations, 3); fiftyoneDegreesFree(headers); fiftyoneDegreesFree(empty_headers); @@ -904,15 +923,16 @@ TEST_F(Transform, GHEVBase64) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformGhevFromBase64( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromBase64( ghev, buffer, bufferLength, results, exception); // --- EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 6); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 6); + ASSERT_EQ(results->count, result.iterations); checkFieldAbsent("sec-ch-ua-arch"); checkFieldValue("sec-ch-ua", @@ -944,14 +964,26 @@ TEST_F(Transform, GHEVBase64NotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateGhevFromBase64( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromBase64( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - // --- + // --- + EXPECT_TRUE(result.bufferTooSmall); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); + fiftyoneDegreesFree(buffer); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); + EXCEPTION_CLEAR; + buffer = (char *)fiftyoneDegreesMalloc(result.written); + + result = fiftyoneDegreesTransformIterateGhevFromBase64( + ghev, buffer, result.written, fillResultsCallback, Transform::results, + exception); + EXPECT_FALSE(result.bufferTooSmall); + EXPECT_TRUE(EXCEPTION_OKAY); + + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUANotEnoughMemory) { @@ -968,13 +1000,14 @@ TEST_F(Transform, SUANotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); // --- - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -991,15 +1024,16 @@ TEST_F(Transform, GHEVArraySua) { size_t bufferLength = strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, exception); // --- EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 6); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 6); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua-arch", "\"x86\""); checkFieldValue("sec-ch-ua-model", "\"MacBook\""); @@ -1030,12 +1064,13 @@ TEST_F(Transform, SUAArrayNotEnoughMemory) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, exception); // --- - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1054,10 +1089,11 @@ TEST_F(Transform, GHEVPartial) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(count, 4); + ASSERT_EQ(result.iterations, 4); EXPECT_TRUE(EXCEPTION_OKAY); // we expect to see these headers detected: @@ -1068,7 +1104,7 @@ TEST_F(Transform, GHEVPartial) { // output the policy is - we don't output empty data - no value == no evidence // field - ASSERT_EQ(results->count, count); + ASSERT_EQ(results->count, result.iterations); checkFieldValue("sec-ch-ua", "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " @@ -1105,11 +1141,12 @@ TEST_F(Transform, GHEVIgnoreUnused) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateGhevFromJson( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(count, 8); + ASSERT_EQ(result.iterations, 8); EXPECT_TRUE(EXCEPTION_OKAY); // we expect to see these headers detected: @@ -1153,10 +1190,11 @@ TEST_F(Transform, GHEVCorruptInput) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -1175,11 +1213,24 @@ TEST_F(Transform, GHEVBufferTooSmall) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); + EXPECT_TRUE(result.bufferTooSmall); + fiftyoneDegreesFree(buffer); + + + + buffer = (char *)fiftyoneDegreesMalloc(result.written); + EXCEPTION_CLEAR; + result = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, result.written, + fillResultsCallback, + Transform::results, exception); + EXPECT_TRUE(EXCEPTION_OKAY); + EXPECT_FALSE(result.bufferTooSmall); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVEvidenceLowCapacity) { @@ -1197,10 +1248,11 @@ TEST_F(Transform, GHEVEvidenceLowCapacity) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1217,19 +1269,20 @@ TEST_F(Transform, SUAHappyPath) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 7); + ASSERT_EQ(result.iterations, 7); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, // sec-ch-ua-full-version-list - ASSERT_EQ(results->count, count); + ASSERT_EQ(results->count, result.iterations); // In device.sua representation there is no distinction between // sec-ch-ua and sec-ch-ua-full-version-list @@ -1268,16 +1321,17 @@ TEST_F(Transform, SUAPlatformExt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(count, 5); + ASSERT_EQ(result.iterations, 5); // we expect to see these headers detected: // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, // sec-ch-ua-full-version-list - ASSERT_EQ(results->count, count); + ASSERT_EQ(results->count, result.iterations); EXPECT_TRUE(EXCEPTION_OKAY); checkFieldAbsent("sec-ch-ua"); @@ -1306,16 +1360,17 @@ TEST_F(Transform, SUAPartial1) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(count, 4); + ASSERT_EQ(result.iterations, 4); // we expect to see these headers detected: // low entropy: sec-ch-ua-mobile, sec-ch-ua-platform // high entropy: sec-ch-ua-model, sec-ch-ua-arch, // sec-ch-ua-full-version-list - ASSERT_EQ(results->count, count); + ASSERT_EQ(results->count, result.iterations); EXPECT_TRUE(EXCEPTION_OKAY); checkFieldAbsent("sec-ch-ua"); @@ -1342,10 +1397,11 @@ TEST_F(Transform, SUAPartial2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_EQ(count, 5); + ASSERT_EQ(result.iterations, 5); EXPECT_TRUE(EXCEPTION_OKAY); // we expect to see these headers detected: @@ -1353,7 +1409,7 @@ TEST_F(Transform, SUAPartial2) { // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, // sec-ch-ua-full-version-list - ASSERT_EQ(results->count, count); + ASSERT_EQ(results->count, result.iterations); checkFieldAbsent("sec-ch-ua"); checkFieldAbsent("sec-ch-ua-full-version-list"); @@ -1376,13 +1432,14 @@ TEST_F(Transform, SUATolerableCorrupt) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - size_t count = fiftyoneDegreesTransformIterateSua( + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(count, 5); - ASSERT_EQ(results->count, count); + ASSERT_EQ(result.iterations, 5); + ASSERT_EQ(results->count, result.iterations); checkFieldAbsent("sec-ch-ua"); checkFieldAbsent("sec-ch-ua-full-version-list"); @@ -1406,10 +1463,11 @@ TEST_F(Transform, SUACorrupt2) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -1422,10 +1480,11 @@ TEST_F(Transform, SUACorrupt3) { size_t bufferLength = 2 * strlen(sua); char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); fiftyoneDegreesFree(buffer); } @@ -1442,11 +1501,25 @@ TEST_F(Transform, SUABufferTooSmall) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + + fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); + EXPECT_TRUE(result.bufferTooSmall); + + fiftyoneDegreesFree(buffer); + + + buffer = (char *)fiftyoneDegreesMalloc(result.written); + EXCEPTION_CLEAR; + + result = fiftyoneDegreesTransformIterateSua(sua, buffer, result.written, + fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_OKAY); + EXPECT_FALSE(result.bufferTooSmall); + Free(buffer); } TEST_F(Transform, SUAEvidenceLowCapacity) { @@ -1462,10 +1535,11 @@ TEST_F(Transform, SUAEvidenceLowCapacity) { char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, fillResultsCallback, Transform::results, exception); - ASSERT_TRUE(FIFTYONE_DEGREES_EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); fiftyoneDegreesFree(buffer); } @@ -1633,3 +1707,28 @@ TEST_F(Transform, emptycases) { EXPECT_TRUE(thrown); } + +TEST_F(Transform, CPPWrapperBase64Corrupt) { + FiftyoneDegrees::Common::Transform t; + bool thrown = false; + try { + auto result = t.fromBase64GHEV("base64 invalid string"); + EXPECT_EQ(result.size(), 0); + } catch (const FiftyoneDegrees::Common::FatalException &e) { + EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + thrown = true; + } + EXPECT_TRUE(thrown); +} + +TEST_F(Transform, CPPWrapper0Size) { + FiftyoneDegrees::Common::Transform t(0); + bool thrown = false; + try { + auto result = t.fromJsonGHEV("{\"architecture\":\"x86\", \"bitness\":\"32\", \"new\":\"ignored\"}"); + EXPECT_EQ(result.size(), 2); + } catch (const FiftyoneDegrees::Common::FatalException &) { + thrown = true; + } + EXPECT_FALSE(thrown); +} diff --git a/transform.c b/transform.c index 3d8a8f6d..c026fa8b 100644 --- a/transform.c +++ b/transform.c @@ -27,13 +27,13 @@ #define NotExpectSymbol(json, ch, exit) \ if (*json == ch) { \ - EXCEPTION_SET(CORRUPT_DATA); \ + EXCEPTION_SET(CORRUPT_DATA); \ exit; \ } #define ExpectSymbol(json, ch, exit) \ if (*json != ch) { \ - EXCEPTION_SET(CORRUPT_DATA); \ + EXCEPTION_SET(CORRUPT_DATA); \ exit; \ } @@ -44,7 +44,8 @@ } #define ValuePtr \ - (EXCEPTION_CHECK(INSUFFICIENT_MEMORY) ? NULL : begin) + (EXCEPTION_CHECK(INSUFFICIENT_MEMORY) ? NULL \ + : begin) #define GET_SEXTET(str, i) \ (str[i] == '=' ? 0 & i++ : base64_char_to_value(str[i++], exception)) @@ -62,8 +63,7 @@ typedef enum { } Key; typedef Key (*readKeyCallback)(const char**, Exception* const); -typedef char* (*readValueCallback)(const char** json, char* begin, - const char* const end, +typedef char* (*readValueCallback)(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception); @@ -83,16 +83,14 @@ static struct { // ---- -static inline char* safe_write_to_buffer( - char* begin, const char* const end, char symbol, - Exception* const exception) { - if (begin < end) { - *begin = symbol; - } else { - EXCEPTION_SET(INSUFFICIENT_MEMORY); - } - - return ++begin; +static inline char* safe_write_to_buffer(StringBuilder *builder, + char symbol, + Exception* const exception) { + StringBuilderAddChar(builder, symbol); + if (builder->full) { + EXCEPTION_SET(INSUFFICIENT_MEMORY); + } + return builder->current; } static inline uint32_t base64_char_to_value( @@ -124,35 +122,36 @@ static inline uint32_t base64_char_to_value( return base64_lookup_table[(uint8_t)c]; } -static size_t base64_decode(const char* base64_input, char* const buffer, - size_t length, +static size_t base64_decode(const char* base64_input, StringBuilder *builder, Exception* const exception) { + size_t before = builder->added; size_t input_length = strlen(base64_input); if (input_length % 4 != 0) { - EXCEPTION_SET(CORRUPT_DATA) + EXCEPTION_SET(CORRUPT_DATA); return 0; // Invalid base64 input length } - char* begin = buffer; - char* end = buffer + length; - for (size_t i = 0; i < input_length;) { uint32_t sextet_a = GET_SEXTET(base64_input, i); uint32_t sextet_b = GET_SEXTET(base64_input, i); uint32_t sextet_c = GET_SEXTET(base64_input, i); uint32_t sextet_d = GET_SEXTET(base64_input, i); + if (EXCEPTION_CHECK(CORRUPT_DATA)) { + return 0; + } + uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - begin = safe_write_to_buffer(begin, end, (triple >> 16) & 0xFF, exception); - begin = safe_write_to_buffer(begin, end, (triple >> 8) & 0xFF, exception); - begin = safe_write_to_buffer(begin, end, triple & 0xFF, exception); + safe_write_to_buffer(builder, (triple >> 16) & 0xFF, exception); + safe_write_to_buffer(builder, (triple >> 8) & 0xFF, exception); + safe_write_to_buffer(builder, triple & 0xFF, exception); } - begin = safe_write_to_buffer(begin, end, '\0', exception); - - return begin - buffer; + safe_write_to_buffer(builder, '\0', exception); + size_t after = builder->added; + return after - before; } static inline const char* skip_whitespaces(const char* json) { @@ -290,29 +289,25 @@ static const char* skip_value(const char* json) { return skip_to_next_char(json + 1, '"'); } -static inline char* init_keys(char* const begin, const char* const end, +static inline void init_keys(StringBuilder *builder, KeyValuePair* cache, Exception* const exception) { - char* ptr = begin; - for (size_t k = 0; k < KEY_UNDEFINED; ++k) { - cache[k].key = ptr; + cache[k].key = builder->current; cache[k].keyLength = key_map[k].len; for (size_t i = 0; i < key_map[k].len; ++i) { - ptr = safe_write_to_buffer(ptr, end, key_map[k].key[i], exception); + safe_write_to_buffer(builder, key_map[k].key[i], exception); } } - - return ptr; } -static const char* init_parsing(const char* json, char** begin, - const char* const end, +static const char* init_parsing(const char* json, + StringBuilder *builder, KeyValuePair* cache, Exception* const exception) { - *begin = init_keys(*begin, end, cache, exception); + init_keys(builder, cache, exception); json = skip_whitespaces(json); ExpectSymbol(json, '{', return json); @@ -636,9 +631,9 @@ static Key read_sua_key(const char** json, return KEY_UNDEFINED; } -static char* read_string_value(const char** json, char* begin, - const char* const end, +static char* read_string_value(const char** json, StringBuilder *builder, Exception* const exception) { + char *begin = builder->current; *json = skip_whitespaces(*json); if (**json == 'n') { ++(*json); @@ -655,10 +650,10 @@ static char* read_string_value(const char** json, char* begin, ++*json; - for (begin = safe_write_to_buffer(begin, end, '"', exception);; ++(*json)) { + for (begin = safe_write_to_buffer(builder, '"', exception);; ++(*json)) { NotExpectSymbol(*json, '\0', return begin); - begin = safe_write_to_buffer(begin, end, **json, exception); + begin = safe_write_to_buffer(builder, **json, exception); switch (**json) { case '\"': { @@ -670,19 +665,20 @@ static char* read_string_value(const char** json, char* begin, if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - begin = safe_write_to_buffer(begin, end, **json, exception); + safe_write_to_buffer(builder, **json, exception); } } break; } } } -static char* read_bool_ghev_value(const char** json, char* begin, - const char* const end, +static char* read_bool_ghev_value(const char** json, + StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { - char* ptr = begin; - + char *begin = builder->current; + char *ptr = begin; + size_t before = builder->added; switch (**json) { case 't': { ++(*json); @@ -690,8 +686,8 @@ static char* read_bool_ghev_value(const char** json, char* begin, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(ptr, end, '?', exception); - ptr = safe_write_to_buffer(ptr, end, '1', exception); + ptr = safe_write_to_buffer(builder, '?', exception); + ptr = safe_write_to_buffer(builder, '1', exception); } break; case 'f': { @@ -700,50 +696,53 @@ static char* read_bool_ghev_value(const char** json, char* begin, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(ptr, end, '?', exception); - ptr = safe_write_to_buffer(ptr, end, '0', exception); + ptr = safe_write_to_buffer(builder, '?', exception); + ptr = safe_write_to_buffer(builder, '0', exception); } break; default: { - EXCEPTION_SET(CORRUPT_DATA); + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } + size_t after = builder->added; cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - - return ptr; + cache[key].valueLength = after - before; + return ptr; } -static char* read_bool_sua_value(const char** json, char* begin, - const char* const end, +static char* read_bool_sua_value(const char** json, + StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { + char *begin = builder->current; + size_t before = builder->added; switch (**json) { case '0': case '1': { } break; default: { - EXCEPTION_SET(CORRUPT_DATA); + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } - char* ptr = safe_write_to_buffer(begin, end, '?', exception); - ptr = safe_write_to_buffer(ptr, end, **json, exception); + char* ptr = safe_write_to_buffer(builder, '?', exception); + ptr = safe_write_to_buffer(builder, **json, exception); ++(*json); + size_t after = builder->added; cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; + cache[key].valueLength = after - before; return ptr; } -static char* read_version_sua(const char** json, char* begin, - const char* const end, +static char* read_version_sua(const char** json, + StringBuilder *builder, Exception* const exception) { enum version_state { version_read, @@ -751,8 +750,9 @@ static char* read_version_sua(const char** json, char* begin, version_exit, } state = version_skip; + char *begin = builder->current; for (char* ptr = begin;; ++(*json)) { - NotExpectSymbol(*json, '\0', return begin); + NotExpectSymbol(*json, '\0', return begin); //rollback switch (state) { case version_read: { @@ -762,16 +762,16 @@ static char* read_version_sua(const char** json, char* begin, } break; case '\\': { - ptr = safe_write_to_buffer(ptr, end, **json, exception); + ptr = safe_write_to_buffer(builder, **json, exception); if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - ptr = safe_write_to_buffer(ptr, end, **json, exception); + ptr = safe_write_to_buffer(builder, **json, exception); } } break; default: { - ptr = safe_write_to_buffer(ptr, end, **json, exception); + ptr = safe_write_to_buffer(builder, **json, exception); } break; } } break; @@ -783,7 +783,7 @@ static char* read_version_sua(const char** json, char* begin, } break; case ',': { - ptr = safe_write_to_buffer(ptr, end, '.', exception); + ptr = safe_write_to_buffer(builder, '.', exception); } break; case ']': { @@ -793,17 +793,17 @@ static char* read_version_sua(const char** json, char* begin, } break; case version_exit: { - ptr = safe_write_to_buffer(ptr, end, '"', exception); - return ptr; + ptr = safe_write_to_buffer(builder, '"', exception); + return ptr; } break; } } } -static char* read_brands_ghev_value(const char** json, char* begin, - const char* const end, +static char* read_brands_ghev_value(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { + char *begin = builder->current; *json = skip_to_next_char(*json, '['); ExpectSymbol(*json, '[', return begin); @@ -827,37 +827,37 @@ static char* read_brands_ghev_value(const char** json, char* begin, ++*json; - char* ptr2 = read_string_value(json, ptr, end, exception); + char* ptr2 = read_string_value(json, builder, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(ptr2, end, ';', exception); - ptr = safe_write_to_buffer(ptr, end, 'v', exception); - ptr = safe_write_to_buffer(ptr, end, '=', exception); + ptr = safe_write_to_buffer(builder, ';', exception); + ptr = safe_write_to_buffer(builder, 'v', exception); + ptr = safe_write_to_buffer(builder, '=', exception); *json = skip_to_next_char(*json, ','); - ExpectSymbol(*json, ',', return begin); + ExpectSymbol(*json, ',', return begin); //rollback *json = skip_to_next_char(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); + ExpectSymbol(*json, '"', return begin); //rollback ++*json; for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); + ExpectSymbol(*json, *k, return begin); //rollback } *json = skip_to_next_char(*json, ':'); - ExpectSymbol(*json, ':', return begin); + ExpectSymbol(*json, ':', return begin); //rollback ++*json; - ptr2 = read_string_value(json, ptr, end, exception); + ptr2 = read_string_value(json, builder, exception); if (ptr2 == NULL) { ptr2 = ptr; - ptr = safe_write_to_buffer(ptr, end, 'n', exception); - ptr = safe_write_to_buffer(ptr, end, 'u', exception); - ptr = safe_write_to_buffer(ptr, end, 'l', exception); - ptr = safe_write_to_buffer(ptr, end, 'l', exception); + ptr = safe_write_to_buffer(builder, 'n', exception); + ptr = safe_write_to_buffer(builder, 'u', exception); + ptr = safe_write_to_buffer(builder, 'l', exception); + ptr = safe_write_to_buffer(builder, 'l', exception); } else { ptr = ptr2; } @@ -883,24 +883,24 @@ static char* read_brands_ghev_value(const char** json, char* begin, case ',': { if (ptr2 != NULL) { - ptr = safe_write_to_buffer(ptr, end, ',', exception); - ptr = safe_write_to_buffer(ptr, end, ' ', exception); + ptr = safe_write_to_buffer(builder, ',', exception); + ptr = safe_write_to_buffer(builder, ' ', exception); } } break; default: { - EXCEPTION_SET(CORRUPT_DATA); + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } } } -static char* read_brands_sua_value(const char** json, char* begin, - const char* const end, +static char* read_brands_sua_value(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { *json = skip_to_next_char(*json, '['); + char *begin = builder->current; ExpectSymbol(*json, '[', return begin); for (char* ptr = begin;; ++*json) { @@ -923,11 +923,11 @@ static char* read_brands_sua_value(const char** json, char* begin, ++*json; - char* ptr2 = read_string_value(json, ptr, end, exception); + char* ptr2 = read_string_value(json, builder, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(ptr2, end, ';', exception); - ptr = safe_write_to_buffer(ptr, end, 'v', exception); - ptr = safe_write_to_buffer(ptr, end, '=', exception); + ptr = safe_write_to_buffer(builder, ';', exception); + ptr = safe_write_to_buffer(builder, 'v', exception); + ptr = safe_write_to_buffer(builder, '=', exception); *json = skip_to_next_char(*json, ','); ExpectSymbol(*json, ',', return begin); @@ -949,8 +949,8 @@ static char* read_brands_sua_value(const char** json, char* begin, ++*json; - ptr = safe_write_to_buffer(ptr, end, '"', exception); - ptr = read_version_sua(json, ptr, end, exception); + ptr = safe_write_to_buffer(builder, '"', exception); + ptr = read_version_sua(json, builder, exception); } *json = skip_to_next_char(*json, '}'); @@ -972,24 +972,24 @@ static char* read_brands_sua_value(const char** json, char* begin, case ',': { if (ptr != begin) { - ptr = safe_write_to_buffer(ptr, end, ',', exception); - ptr = safe_write_to_buffer(ptr, end, ' ', exception); + ptr = safe_write_to_buffer(builder, ',', exception); + ptr = safe_write_to_buffer(builder, ' ', exception); } } break; default: { - EXCEPTION_SET(CORRUPT_DATA); + EXCEPTION_SET(CORRUPT_DATA); return begin; } break; } } } -static char* read_pure_string_value(const char** json, char* begin, - const char* const end, +static char* read_pure_string_value(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { - char* ptr = read_string_value(json, begin, end, exception); + char *begin = builder->current; + char* ptr = read_string_value(json, builder, exception); if (ptr != NULL) { cache[key].value = ValuePtr; @@ -1000,9 +1000,10 @@ static char* read_pure_string_value(const char** json, char* begin, } static char* read_platform_sua_value( - const char** json, char* begin, const char* const end, + const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { + char *begin = builder->current; *json = skip_to_next_char(*json, '{'); ExpectSymbol(*json, '{', return begin); @@ -1020,7 +1021,7 @@ static char* read_platform_sua_value( ++*json; - char* ptr = read_string_value(json, begin, end, exception); + char* ptr = read_string_value(json, builder, exception); if (ptr == NULL) { return NULL; } @@ -1059,8 +1060,8 @@ static char* read_platform_sua_value( ++*json; NotExpectSymbol(*json, '\0', return begin); - ptr = safe_write_to_buffer(ptr, end, '"', exception); - ptr = read_version_sua(json, ptr, end, exception); + ptr = safe_write_to_buffer(builder, '"', exception); + ptr = read_version_sua(json, builder, exception); cache[PLATFORMVERSION].value = ValuePtr; cache[PLATFORMVERSION].valueLength = ptr - begin; @@ -1126,26 +1127,24 @@ static bool pushToHeaders(void* ctx, KeyValuePair header) { return (headers->count < headers->capacity); } // ------------------------------------------------------------------------------------------------ -static size_t main_parsing_body(const char* json, char* const buffer, - size_t length, +static uint32_t main_parsing_body(const char* json, + StringBuilder *builder, Exception* const exception, int isSua, TransformCallback callback, void* ctx) { KeyValuePair cache[KEY_UNDEFINED]; + char *begin = builder->current; // define buffer range - char* begin = buffer; - const char* const end = buffer + length; // write keys to buffer, init cache and skip to the first key - json = init_parsing(json, &begin, end, cache, exception); - if (EXCEPTION_CHECK(CORRUPT_DATA) || - EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { + json = init_parsing(json, builder, cache, exception); + if (EXCEPTION_CHECK(CORRUPT_DATA)) { return 0; } - size_t iterations = 0; // total number of parsed key-value pairs + uint32_t iterations = 0; // total number of parsed key-value pairs // main reading loop readKeyCallback read_key = isSua ? read_sua_key : read_ghev_key; @@ -1165,7 +1164,7 @@ static size_t main_parsing_body(const char* json, char* const buffer, json = skip_whitespaces(json + 1); NotExpectSymbol(json, '\0', break); - char* ptr = read_value(&json, begin, end, cache, key, exception); + char* ptr = read_value(&json, builder, cache, key, exception); if (EXCEPTION_CHECK(CORRUPT_DATA)) { break; } @@ -1175,13 +1174,13 @@ static size_t main_parsing_body(const char* json, char* const buffer, ++iterations; if (!callback(ctx, cache[key])) { - return iterations; + break; } if (key == PLATFORM && isSua && cache[PLATFORMVERSION].valueLength != 0) { ++iterations; if (!callback(ctx, cache[PLATFORMVERSION])) { - return iterations; + break; } } } @@ -1192,78 +1191,101 @@ static size_t main_parsing_body(const char* json, char* const buffer, return iterations; } +// The difference of this function is that it does not initialize the builder - and assumes +// it has been initialized outside - useful for base64 and then JSON from the same buffer + +uint32_t TransformIterateGhevFromJsonPrivate(const char* json, StringBuilder *builder, + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { + return main_parsing_body(json, builder, exception, 0, callback, ctx); +} // ------------------------------------------------------------------------------------------------ -size_t fiftyoneDegreesTransformIterateGhevFromJson( - const char* json, char* const buffer, size_t length, +fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromJson( + const char* json, char *buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - return main_parsing_body(json, buffer, length, exception, 0, callback, ctx); + StringBuilder builder = {buffer, length}; + StringBuilderInit(&builder); + uint32_t iterations = TransformIterateGhevFromJsonPrivate(json, &builder, callback, ctx, exception); + StringBuilderComplete(&builder); + fiftyoneDegreesTransformIterateResult result = {iterations, builder.added, builder.added > builder.length}; + return result; } -size_t fiftyoneDegreesTransformIterateGhevFromBase64( - const char* base64, char* buffer, size_t length, +fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromBase64( + const char* base64, char *buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - size_t offset = base64_decode(base64, buffer, length, exception); - - if (EXCEPTION_CHECK(INSUFFICIENT_MEMORY) || - EXCEPTION_CHECK(CORRUPT_DATA)) { - return 0; + StringBuilder builder = {buffer, length}; + StringBuilderInit(&builder); + base64_decode(base64, &builder, exception); + fiftyoneDegreesTransformIterateResult result = {0, builder.added, builder.added > builder.length}; + if (EXCEPTION_CHECK(CORRUPT_DATA) || EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { + return result; } - - return fiftyoneDegreesTransformIterateGhevFromJson( - buffer, buffer + offset, length - offset, callback, - ctx, exception); + char *json = builder.ptr; + //note we are calling a private function to reuse the initialized stringbuilder + uint32_t iterations = TransformIterateGhevFromJsonPrivate(json, &builder, callback, ctx, exception); + StringBuilderComplete(&builder); + result.iterations = iterations; + result.written = builder.added; + result.bufferTooSmall = builder.added > builder.length; + return result; } -size_t fiftyoneDegreesTransformIterateSua( - const char* json, char* const buffer, size_t length, +fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateSua( + const char* json, char *buffer, size_t length, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - return main_parsing_body(json, buffer, length, exception, 1, callback, ctx); + StringBuilder builder = {buffer, length}; + StringBuilderInit(&builder); + uint32_t iterations = main_parsing_body(json, &builder, exception, 1, callback, ctx); + StringBuilderComplete(&builder); + fiftyoneDegreesTransformIterateResult result = {iterations, builder.added, builder.added > builder.length}; + return result; } -size_t fiftyoneDegreesTransformGhevFromJson( - const char* json, char* buffer, size_t length, +// Array methods internally relay on iterative methods +fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromJson( + const char* json, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; - size_t calls = TransformIterateGhevFromJson( + fiftyoneDegreesTransformIterateResult result = TransformIterateGhevFromJson( json, buffer, length, pushToHeaders, headers, exception); - if (calls != headers->count - initial) { + if (result.iterations != headers->count - initial) { EXCEPTION_SET(INSUFFICIENT_CAPACITY); } - - return calls; + return result; } -size_t fiftyoneDegreesTransformGhevFromBase64( - const char* base64, char* buffer, size_t length, +fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromBase64( + const char* base64, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; - size_t calls = TransformIterateGhevFromBase64( + fiftyoneDegreesTransformIterateResult result = TransformIterateGhevFromBase64( base64, buffer, length, pushToHeaders, headers, exception); - if (calls != headers->count - initial) { + if (result.iterations != headers->count - initial) { EXCEPTION_SET(INSUFFICIENT_CAPACITY); } - return calls; + return result; } -size_t fiftyoneDegreesTransformSua( - const char* json, char* buffer, size_t length, +fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformSua( + const char* json, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray* const headers, fiftyoneDegreesException* const exception) { uint32_t initial = headers->count; - size_t calls = TransformIterateSua( + fiftyoneDegreesTransformIterateResult result = TransformIterateSua( json, buffer, length, pushToHeaders, headers, exception); - if (calls != headers->count - initial) { + if (result.iterations != headers->count - initial) { EXCEPTION_SET(INSUFFICIENT_CAPACITY); } - return calls; -} \ No newline at end of file + return result; +} diff --git a/transform.h b/transform.h index 43aa3507..d27ba4e1 100644 --- a/transform.h +++ b/transform.h @@ -55,10 +55,10 @@ * - Sec-CH-UA-Arch * - Sec-CH-UA-Bitness * - * The conversion routines transform the GetHighEntropyValues (GHEV) or + * The conversion routines transform the GetHighEntropyValues (GHEV) or * Structured User Agent (SUA) SUA input into the HTTP header maps. - * - * Routines are provided in 2 styles: iterative (for potentially lazy + * + * Routines are provided in 2 styles: iterative (for potentially lazy * consumption) and array-results (for eager consumption). The former uses * callback to iteratively provide header name-value pairs to the caller, the * latter provides the whole header map array as output. In addition 2 variants @@ -71,6 +71,17 @@ * outlive the last output evidence use. */ +/** + * Used as a return type from the conversion routines to carry information about + * the operation results to the caller, allows the caller to f.e. judge about the buffer utilization, + * and whether the buffer was of sufficient size + */ +typedef struct { + uint32_t iterations; // number of pairs of evidence extracted or would have been extracted and correspondingly calls to the callback made + size_t written; // number of characters written or that would have been written to the buffer, reflects required buffer size + bool bufferTooSmall; // the caller should check this flag and reallocate the buffer to be of at least `written` size +} fiftyoneDegreesTransformIterateResult; + /** * A callback function type definition that is called every time a header * name-value pair is formed and allows the caller to decide how to handle the @@ -84,7 +95,7 @@ * stop */ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( - void *state, fiftyoneDegreesKeyValuePair header); + void *state, fiftyoneDegreesKeyValuePair header); /** * Iteratively convert getHighEntropyValue() API result JSON string to HTTP @@ -114,12 +125,12 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * @param state an external context state to pass to be used by the callback * function * @return the number of iterations / header pairs detected (callback calls - * made) + * made) and buffer utilization information */ -EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( - const char *json, char *const buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromJson( + const char *json, char *const buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception); /** * Iteratively convert getHighEntropyValue() API result base64 encoded JSON @@ -150,12 +161,12 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromJson( * @param state an external context state to pass to be used by the callback * function * @return the number of iterations / header pairs detected (callback calls - * made) + * made) and buffer utilization information */ -EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( - const char *base64, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromBase64( + const char *base64, char *buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception); /** * Iteratively convert device.sua JSON string to HTTP header representation. @@ -184,12 +195,12 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateGhevFromBase64( * @param state an external context state to pass to be used by the callback * function * @return the number of iterations / header pairs detected (callback calls - * made) + * made) and buffer utilization information */ -EXTERNAL size_t fiftyoneDegreesTransformIterateSua( - const char *json, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateSua( + const char *json, char *buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception); /** * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header @@ -220,13 +231,13 @@ EXTERNAL size_t fiftyoneDegreesTransformIterateSua( * on the heap. must not be NULL and has to be memory managed outside of the * function, its lifetime should be long enough to survive the last use of the * returned headers - * @return the number of headers that was written or should have been written to - * the array in case this number is higher than headers->capacity in the latter + * @return result.iterations specifies the number of headers that was written or + * should have been written to the array. in case this number is higher than headers->capacity * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY * status and the returned capacity will signal the array size that needs to be - * allocated + * allocated. result.written and result.bufferTooSmall provide buffer utilization information */ -EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( +EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromJson( const char *json, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); @@ -261,13 +272,13 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromJson( * on the heap. must not be NULL and has to be memory managed outside of the * function, its lifetime should be long enough to survive the last use of the * returned headers - * @return the number of headers that was written or should have been written to - * the array in case this number is higher than headers->capacity in the latter + * @return result.iterations specifies the number of headers that was written or + * should have been written to the array. in case this number is higher than headers->capacity * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY * status and the returned capacity will signal the array size that needs to be - * allocated + * allocated. result.written and result.bufferTooSmall provide buffer utilization information */ -EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( +EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromBase64( const char *base64, char *buffer, size_t length, fiftyoneDegreesKeyValuePairArray *const headers, fiftyoneDegreesException *const exception); @@ -300,15 +311,15 @@ EXTERNAL size_t fiftyoneDegreesTransformGhevFromBase64( * on the heap. must not be NULL and has to be memory managed outside of the * function, its lifetime should be long enough to survive the last use of the * returned headers - * @return the number of headers that was written or should have been written to - * the array in case this number is higher than headers->capacity in the latter + * @return result.iterations specifies the number of headers that was written or + * should have been written to the array. in case this number is higher than headers->capacity * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY * status and the returned capacity will signal the array size that needs to be - * allocated + * allocated. result.written and result.bufferTooSmall provide buffer utilization information */ -EXTERNAL size_t fiftyoneDegreesTransformSua( - const char *json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformSua( + const char *json, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray *const headers, + fiftyoneDegreesException *const exception); #endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ From a3beb5c32e76dcc7b6bb1b782f1fab34fb9c1d35 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Tue, 10 Sep 2024 17:33:50 +0200 Subject: [PATCH 65/73] REFACT: transform internal routines to camelCase --- transform.c | 250 ++++++++++++++++++++++++++-------------------------- transform.h | 1 + 2 files changed, 126 insertions(+), 125 deletions(-) diff --git a/transform.c b/transform.c index c026fa8b..a728be05 100644 --- a/transform.c +++ b/transform.c @@ -39,7 +39,7 @@ #define ExpectKeySymbol(json, ch) \ if (*json != ch) { \ - json = skip_to_next_char(json, '"'); \ + json = skipToNextChar(json, '"'); \ return KEY_UNDEFINED; \ } @@ -48,7 +48,7 @@ : begin) #define GET_SEXTET(str, i) \ - (str[i] == '=' ? 0 & i++ : base64_char_to_value(str[i++], exception)) + (str[i] == '=' ? 0 & i++ : base64CharToValue(str[i++], exception)) typedef enum { ARCHITECTURE, // sec-ch-ua-arch @@ -83,7 +83,7 @@ static struct { // ---- -static inline char* safe_write_to_buffer(StringBuilder *builder, +static inline char* safeWriteToBuffer(StringBuilder *builder, char symbol, Exception* const exception) { StringBuilderAddChar(builder, symbol); @@ -93,7 +93,7 @@ static inline char* safe_write_to_buffer(StringBuilder *builder, return builder->current; } -static inline uint32_t base64_char_to_value( +static inline uint32_t base64CharToValue( char c, Exception* const exception) { static const uint32_t base64_lookup_table[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -122,7 +122,7 @@ static inline uint32_t base64_char_to_value( return base64_lookup_table[(uint8_t)c]; } -static size_t base64_decode(const char* base64_input, StringBuilder *builder, +static size_t base64Decode(const char* base64_input, StringBuilder *builder, Exception* const exception) { size_t before = builder->added; size_t input_length = strlen(base64_input); @@ -144,17 +144,17 @@ static size_t base64_decode(const char* base64_input, StringBuilder *builder, uint32_t triple = (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - safe_write_to_buffer(builder, (triple >> 16) & 0xFF, exception); - safe_write_to_buffer(builder, (triple >> 8) & 0xFF, exception); - safe_write_to_buffer(builder, triple & 0xFF, exception); + safeWriteToBuffer(builder, (triple >> 16) & 0xFF, exception); + safeWriteToBuffer(builder, (triple >> 8) & 0xFF, exception); + safeWriteToBuffer(builder, triple & 0xFF, exception); } - safe_write_to_buffer(builder, '\0', exception); + safeWriteToBuffer(builder, '\0', exception); size_t after = builder->added; return after - before; } -static inline const char* skip_whitespaces(const char* json) { +static inline const char* skipWhitespaces(const char* json) { for (;; ++json) { switch (*json) { case ' ': @@ -171,7 +171,7 @@ static inline const char* skip_whitespaces(const char* json) { } } -static inline const char* skip_to_next_char(const char* json, +static inline const char* skipToNextChar(const char* json, const char target) { for (; *json != target; ++json) { switch (*json) { @@ -190,13 +190,13 @@ static inline const char* skip_to_next_char(const char* json, return json; } -static const char* skip_value(const char* json) { - json = skip_to_next_char(json, ':'); +static const char* skipValue(const char* json) { + json = skipToNextChar(json, ':'); if (*json == '\0') { return json; } - json = skip_whitespaces(json + 1); + json = skipWhitespaces(json + 1); switch (*json) { case '\0': { @@ -221,7 +221,7 @@ static const char* skip_value(const char* json) { } break; case '"': { - json = skip_to_next_char(json + 1, '"'); + json = skipToNextChar(json + 1, '"'); if (*json == '\0') { return json; } @@ -248,7 +248,7 @@ static const char* skip_value(const char* json) { } break; case '"': { - json = skip_to_next_char(json + 1, '"'); + json = skipToNextChar(json + 1, '"'); if (*json == '\0') { return json; } @@ -258,7 +258,7 @@ static const char* skip_value(const char* json) { } break; case '"': { - json = skip_to_next_char(json + 1, '"'); + json = skipToNextChar(json + 1, '"'); } break; default: { @@ -286,10 +286,10 @@ static const char* skip_value(const char* json) { return json; } - return skip_to_next_char(json + 1, '"'); + return skipToNextChar(json + 1, '"'); } -static inline void init_keys(StringBuilder *builder, +static inline void initKeys(StringBuilder *builder, KeyValuePair* cache, Exception* const exception) { for (size_t k = 0; k < KEY_UNDEFINED; ++k) { @@ -297,24 +297,24 @@ static inline void init_keys(StringBuilder *builder, cache[k].keyLength = key_map[k].len; for (size_t i = 0; i < key_map[k].len; ++i) { - safe_write_to_buffer(builder, key_map[k].key[i], exception); + safeWriteToBuffer(builder, key_map[k].key[i], exception); } } } -static const char* init_parsing(const char* json, +static const char* initParsing(const char* json, StringBuilder *builder, KeyValuePair* cache, Exception* const exception) { - init_keys(builder, cache, exception); + initKeys(builder, cache, exception); - json = skip_whitespaces(json); + json = skipWhitespaces(json); ExpectSymbol(json, '{', return json); - return skip_to_next_char(json, '"'); + return skipToNextChar(json, '"'); } -static Key read_ghev_key(const char** json, +static Key readGhevKey(const char** json, Exception* const exception) { enum ReadKeyState { READ_KEY_INIT, @@ -358,7 +358,7 @@ static Key read_ghev_key(const char** json, } break; default: { - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); return KEY_UNDEFINED; } break; @@ -395,7 +395,7 @@ static Key read_ghev_key(const char** json, } break; default: { - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); return KEY_UNDEFINED; } break; } @@ -417,7 +417,7 @@ static Key read_ghev_key(const char** json, } break; default: { - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); return KEY_UNDEFINED; } break; } @@ -438,7 +438,7 @@ static Key read_ghev_key(const char** json, } break; default: { - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); return KEY_UNDEFINED; } break; } @@ -489,7 +489,7 @@ static Key read_ghev_key(const char** json, return KEY_UNDEFINED; } -static Key read_sua_key(const char** json, +static Key readSuaKey(const char** json, Exception* const exception) { enum ReadKeyState { READ_KEY_INIT, @@ -527,7 +527,7 @@ static Key read_sua_key(const char** json, } break; default: { - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); return KEY_UNDEFINED; } break; @@ -556,7 +556,7 @@ static Key read_sua_key(const char** json, } break; default: { - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); return KEY_UNDEFINED; } break; } @@ -578,7 +578,7 @@ static Key read_sua_key(const char** json, } break; default: { - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); return KEY_UNDEFINED; } break; } @@ -631,10 +631,10 @@ static Key read_sua_key(const char** json, return KEY_UNDEFINED; } -static char* read_string_value(const char** json, StringBuilder *builder, +static char* readStringValue(const char** json, StringBuilder *builder, Exception* const exception) { char *begin = builder->current; - *json = skip_whitespaces(*json); + *json = skipWhitespaces(*json); if (**json == 'n') { ++(*json); NotExpectSymbol(*json, '\0', return begin); @@ -650,10 +650,10 @@ static char* read_string_value(const char** json, StringBuilder *builder, ++*json; - for (begin = safe_write_to_buffer(builder, '"', exception);; ++(*json)) { + for (begin = safeWriteToBuffer(builder, '"', exception);; ++(*json)) { NotExpectSymbol(*json, '\0', return begin); - begin = safe_write_to_buffer(builder, **json, exception); + begin = safeWriteToBuffer(builder, **json, exception); switch (**json) { case '\"': { @@ -665,14 +665,14 @@ static char* read_string_value(const char** json, StringBuilder *builder, if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - safe_write_to_buffer(builder, **json, exception); + safeWriteToBuffer(builder, **json, exception); } } break; } } } -static char* read_bool_ghev_value(const char** json, +static char* readBoolGhevValue(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { @@ -686,8 +686,8 @@ static char* read_bool_ghev_value(const char** json, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(builder, '?', exception); - ptr = safe_write_to_buffer(builder, '1', exception); + ptr = safeWriteToBuffer(builder, '?', exception); + ptr = safeWriteToBuffer(builder, '1', exception); } break; case 'f': { @@ -696,8 +696,8 @@ static char* read_bool_ghev_value(const char** json, ExpectSymbol(*json, *i, return begin); } - ptr = safe_write_to_buffer(builder, '?', exception); - ptr = safe_write_to_buffer(builder, '0', exception); + ptr = safeWriteToBuffer(builder, '?', exception); + ptr = safeWriteToBuffer(builder, '0', exception); } break; default: { @@ -712,7 +712,7 @@ static char* read_bool_ghev_value(const char** json, return ptr; } -static char* read_bool_sua_value(const char** json, +static char* readBoolSuaValue(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { @@ -729,8 +729,8 @@ static char* read_bool_sua_value(const char** json, } break; } - char* ptr = safe_write_to_buffer(builder, '?', exception); - ptr = safe_write_to_buffer(builder, **json, exception); + char* ptr = safeWriteToBuffer(builder, '?', exception); + ptr = safeWriteToBuffer(builder, **json, exception); ++(*json); @@ -741,7 +741,7 @@ static char* read_bool_sua_value(const char** json, return ptr; } -static char* read_version_sua(const char** json, +static char* readVersionSua(const char** json, StringBuilder *builder, Exception* const exception) { enum version_state { @@ -762,16 +762,16 @@ static char* read_version_sua(const char** json, } break; case '\\': { - ptr = safe_write_to_buffer(builder, **json, exception); + ptr = safeWriteToBuffer(builder, **json, exception); if ((*json)[1] == '\\' || (*json)[1] == '"') { ++(*json); - ptr = safe_write_to_buffer(builder, **json, exception); + ptr = safeWriteToBuffer(builder, **json, exception); } } break; default: { - ptr = safe_write_to_buffer(builder, **json, exception); + ptr = safeWriteToBuffer(builder, **json, exception); } break; } } break; @@ -783,7 +783,7 @@ static char* read_version_sua(const char** json, } break; case ',': { - ptr = safe_write_to_buffer(builder, '.', exception); + ptr = safeWriteToBuffer(builder, '.', exception); } break; case ']': { @@ -793,27 +793,27 @@ static char* read_version_sua(const char** json, } break; case version_exit: { - ptr = safe_write_to_buffer(builder, '"', exception); + ptr = safeWriteToBuffer(builder, '"', exception); return ptr; } break; } } } -static char* read_brands_ghev_value(const char** json, StringBuilder *builder, +static char* readBrandsGhevValue(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { char *begin = builder->current; - *json = skip_to_next_char(*json, '['); + *json = skipToNextChar(*json, '['); ExpectSymbol(*json, '[', return begin); ++*json; for (char* ptr = begin;; ++*json) { - *json = skip_to_next_char(*json, '{'); + *json = skipToNextChar(*json, '{'); ExpectSymbol(*json, '{', return begin); - *json = skip_to_next_char(*json + 1, '"'); + *json = skipToNextChar(*json + 1, '"'); ExpectSymbol(*json, '"', return begin); ++*json; @@ -822,21 +822,21 @@ static char* read_brands_ghev_value(const char** json, StringBuilder *builder, ExpectSymbol(*json, *k, return begin); } - *json = skip_to_next_char(*json, ':'); + *json = skipToNextChar(*json, ':'); ExpectSymbol(*json, ':', return begin); ++*json; - char* ptr2 = read_string_value(json, builder, exception); + char* ptr2 = readStringValue(json, builder, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(builder, ';', exception); - ptr = safe_write_to_buffer(builder, 'v', exception); - ptr = safe_write_to_buffer(builder, '=', exception); + ptr = safeWriteToBuffer(builder, ';', exception); + ptr = safeWriteToBuffer(builder, 'v', exception); + ptr = safeWriteToBuffer(builder, '=', exception); - *json = skip_to_next_char(*json, ','); + *json = skipToNextChar(*json, ','); ExpectSymbol(*json, ',', return begin); //rollback - *json = skip_to_next_char(*json + 1, '"'); + *json = skipToNextChar(*json + 1, '"'); ExpectSymbol(*json, '"', return begin); //rollback ++*json; @@ -845,28 +845,28 @@ static char* read_brands_ghev_value(const char** json, StringBuilder *builder, ExpectSymbol(*json, *k, return begin); //rollback } - *json = skip_to_next_char(*json, ':'); + *json = skipToNextChar(*json, ':'); ExpectSymbol(*json, ':', return begin); //rollback ++*json; - ptr2 = read_string_value(json, builder, exception); + ptr2 = readStringValue(json, builder, exception); if (ptr2 == NULL) { ptr2 = ptr; - ptr = safe_write_to_buffer(builder, 'n', exception); - ptr = safe_write_to_buffer(builder, 'u', exception); - ptr = safe_write_to_buffer(builder, 'l', exception); - ptr = safe_write_to_buffer(builder, 'l', exception); + ptr = safeWriteToBuffer(builder, 'n', exception); + ptr = safeWriteToBuffer(builder, 'u', exception); + ptr = safeWriteToBuffer(builder, 'l', exception); + ptr = safeWriteToBuffer(builder, 'l', exception); } else { ptr = ptr2; } } - *json = skip_to_next_char(*json, '}'); + *json = skipToNextChar(*json, '}'); ExpectSymbol(*json, '}', return begin); - *json = skip_whitespaces(*json + 1); + *json = skipWhitespaces(*json + 1); NotExpectSymbol(*json, '\0', return begin); switch (**json) { @@ -883,8 +883,8 @@ static char* read_brands_ghev_value(const char** json, StringBuilder *builder, case ',': { if (ptr2 != NULL) { - ptr = safe_write_to_buffer(builder, ',', exception); - ptr = safe_write_to_buffer(builder, ' ', exception); + ptr = safeWriteToBuffer(builder, ',', exception); + ptr = safeWriteToBuffer(builder, ' ', exception); } } break; @@ -896,20 +896,20 @@ static char* read_brands_ghev_value(const char** json, StringBuilder *builder, } } -static char* read_brands_sua_value(const char** json, StringBuilder *builder, +static char* readBrandsSuaValue(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { - *json = skip_to_next_char(*json, '['); + *json = skipToNextChar(*json, '['); char *begin = builder->current; ExpectSymbol(*json, '[', return begin); for (char* ptr = begin;; ++*json) { NotExpectSymbol(*json, '\0', return begin); - *json = skip_to_next_char(*json, '{'); + *json = skipToNextChar(*json, '{'); ExpectSymbol(*json, '{', return begin); - *json = skip_to_next_char(*json, '"'); + *json = skipToNextChar(*json, '"'); ExpectSymbol(*json, '"', return begin); ++*json; @@ -918,21 +918,21 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, ExpectSymbol(*json, *k, return begin); } - *json = skip_to_next_char(*json, ':'); + *json = skipToNextChar(*json, ':'); ExpectSymbol(*json, ':', return begin); ++*json; - char* ptr2 = read_string_value(json, builder, exception); + char* ptr2 = readStringValue(json, builder, exception); if (ptr2 != NULL) { - ptr = safe_write_to_buffer(builder, ';', exception); - ptr = safe_write_to_buffer(builder, 'v', exception); - ptr = safe_write_to_buffer(builder, '=', exception); + ptr = safeWriteToBuffer(builder, ';', exception); + ptr = safeWriteToBuffer(builder, 'v', exception); + ptr = safeWriteToBuffer(builder, '=', exception); - *json = skip_to_next_char(*json, ','); + *json = skipToNextChar(*json, ','); ExpectSymbol(*json, ',', return begin); - *json = skip_to_next_char(*json + 1, '"'); + *json = skipToNextChar(*json + 1, '"'); ExpectSymbol(*json, '"', return begin); ++*json; @@ -941,22 +941,22 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, ExpectSymbol(*json, *k, return begin); } - *json = skip_to_next_char(*json, ':'); + *json = skipToNextChar(*json, ':'); ExpectSymbol(*json, ':', return begin); - *json = skip_to_next_char(*json, '['); + *json = skipToNextChar(*json, '['); ExpectSymbol(*json, '[', return begin); ++*json; - ptr = safe_write_to_buffer(builder, '"', exception); - ptr = read_version_sua(json, builder, exception); + ptr = safeWriteToBuffer(builder, '"', exception); + ptr = readVersionSua(json, builder, exception); } - *json = skip_to_next_char(*json, '}'); + *json = skipToNextChar(*json, '}'); ExpectSymbol(*json, '}', return begin); - *json = skip_whitespaces(*json + 1); + *json = skipWhitespaces(*json + 1); NotExpectSymbol(*json, '\0', return begin); switch (**json) { @@ -972,8 +972,8 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, case ',': { if (ptr != begin) { - ptr = safe_write_to_buffer(builder, ',', exception); - ptr = safe_write_to_buffer(builder, ' ', exception); + ptr = safeWriteToBuffer(builder, ',', exception); + ptr = safeWriteToBuffer(builder, ' ', exception); } } break; @@ -985,11 +985,11 @@ static char* read_brands_sua_value(const char** json, StringBuilder *builder, } } -static char* read_pure_string_value(const char** json, StringBuilder *builder, +static char* readPureStringValue(const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { char *begin = builder->current; - char* ptr = read_string_value(json, builder, exception); + char* ptr = readStringValue(json, builder, exception); if (ptr != NULL) { cache[key].value = ValuePtr; @@ -999,15 +999,15 @@ static char* read_pure_string_value(const char** json, StringBuilder *builder, return ptr; } -static char* read_platform_sua_value( +static char* readPlatformSuaValue( const char** json, StringBuilder *builder, KeyValuePair* cache, Key key, Exception* const exception) { char *begin = builder->current; - *json = skip_to_next_char(*json, '{'); + *json = skipToNextChar(*json, '{'); ExpectSymbol(*json, '{', return begin); - *json = skip_to_next_char(*json + 1, '"'); + *json = skipToNextChar(*json + 1, '"'); ExpectSymbol(*json, '"', return begin); ++*json; @@ -1016,12 +1016,12 @@ static char* read_platform_sua_value( ExpectSymbol(*json, *k, return begin); } - *json = skip_to_next_char(*json, ':'); + *json = skipToNextChar(*json, ':'); ExpectSymbol(*json, ':', return begin); ++*json; - char* ptr = read_string_value(json, builder, exception); + char* ptr = readStringValue(json, builder, exception); if (ptr == NULL) { return NULL; } @@ -1034,7 +1034,7 @@ static char* read_platform_sua_value( begin = ptr; - *json = skip_whitespaces(*json); + *json = skipWhitespaces(*json); if (**json == '}') { return begin; @@ -1042,7 +1042,7 @@ static char* read_platform_sua_value( ExpectSymbol(*json, ',', return begin); - *json = skip_to_next_char(*json + 1, '"'); + *json = skipToNextChar(*json + 1, '"'); ExpectSymbol(*json, '"', return begin); ++*json; @@ -1051,17 +1051,17 @@ static char* read_platform_sua_value( ExpectSymbol(*json, *k, return begin); } - *json = skip_to_next_char(*json, ':'); + *json = skipToNextChar(*json, ':'); ExpectSymbol(*json, ':', return begin); - *json = skip_to_next_char(*json + 1, '['); + *json = skipToNextChar(*json + 1, '['); ExpectSymbol(*json, '[', return begin); ++*json; NotExpectSymbol(*json, '\0', return begin); - ptr = safe_write_to_buffer(builder, '"', exception); - ptr = read_version_sua(json, builder, exception); + ptr = safeWriteToBuffer(builder, '"', exception); + ptr = readVersionSua(json, builder, exception); cache[PLATFORMVERSION].value = ValuePtr; cache[PLATFORMVERSION].valueLength = ptr - begin; @@ -1069,40 +1069,40 @@ static char* read_platform_sua_value( return ptr; } -static inline readValueCallback read_value_switch(Key key, int isSua) { +static inline readValueCallback readValueSwitch(Key key, int isSua) { readValueCallback res = NULL; switch (key) { case ARCHITECTURE: { - res = read_pure_string_value; + res = readPureStringValue; } break; case BITNESS: { - res = read_pure_string_value; + res = readPureStringValue; } break; case BRANDS: { - res = isSua ? NULL : read_brands_ghev_value; + res = isSua ? NULL : readBrandsGhevValue; } break; case FULLVERSIONLIST: { - res = isSua ? read_brands_sua_value : read_brands_ghev_value; + res = isSua ? readBrandsSuaValue : readBrandsGhevValue; } break; case MOBILE: { - res = isSua ? read_bool_sua_value : read_bool_ghev_value; + res = isSua ? readBoolSuaValue : readBoolGhevValue; } break; case MODEL: { - res = read_pure_string_value; + res = readPureStringValue; } break; case PLATFORM: { - res = isSua ? read_platform_sua_value : read_pure_string_value; + res = isSua ? readPlatformSuaValue : readPureStringValue; } break; case PLATFORMVERSION: { - res = isSua ? NULL : read_pure_string_value; + res = isSua ? NULL : readPureStringValue; } break; case KEY_UNDEFINED: { @@ -1127,7 +1127,7 @@ static bool pushToHeaders(void* ctx, KeyValuePair header) { return (headers->count < headers->capacity); } // ------------------------------------------------------------------------------------------------ -static uint32_t main_parsing_body(const char* json, +static uint32_t mainParsingBody(const char* json, StringBuilder *builder, Exception* const exception, int isSua, @@ -1139,7 +1139,7 @@ static uint32_t main_parsing_body(const char* json, // define buffer range // write keys to buffer, init cache and skip to the first key - json = init_parsing(json, builder, cache, exception); + json = initParsing(json, builder, cache, exception); if (EXCEPTION_CHECK(CORRUPT_DATA)) { return 0; } @@ -1147,21 +1147,21 @@ static uint32_t main_parsing_body(const char* json, uint32_t iterations = 0; // total number of parsed key-value pairs // main reading loop - readKeyCallback read_key = isSua ? read_sua_key : read_ghev_key; + readKeyCallback read_key = isSua ? readSuaKey : readGhevKey; while (*json != '\0') { Key key = read_key(&json, exception); ExpectSymbol(json, '"', break); - readValueCallback read_value = read_value_switch(key, isSua); + readValueCallback read_value = readValueSwitch(key, isSua); if (key == KEY_UNDEFINED || read_value == NULL) { - json = skip_value(json + 1); + json = skipValue(json + 1); continue; } - json = skip_to_next_char(json, ':'); + json = skipToNextChar(json, ':'); ExpectSymbol(json, ':', break); - json = skip_whitespaces(json + 1); + json = skipWhitespaces(json + 1); NotExpectSymbol(json, '\0', break); char* ptr = read_value(&json, builder, cache, key, exception); @@ -1185,7 +1185,7 @@ static uint32_t main_parsing_body(const char* json, } } - json = skip_to_next_char(json, '"'); + json = skipToNextChar(json, '"'); } return iterations; @@ -1197,7 +1197,7 @@ static uint32_t main_parsing_body(const char* json, uint32_t TransformIterateGhevFromJsonPrivate(const char* json, StringBuilder *builder, fiftyoneDegreesTransformCallback callback, void* ctx, fiftyoneDegreesException* const exception) { - return main_parsing_body(json, builder, exception, 0, callback, ctx); + return mainParsingBody(json, builder, exception, 0, callback, ctx); } // ------------------------------------------------------------------------------------------------ fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromJson( @@ -1218,7 +1218,7 @@ fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromBas fiftyoneDegreesException* const exception) { StringBuilder builder = {buffer, length}; StringBuilderInit(&builder); - base64_decode(base64, &builder, exception); + base64Decode(base64, &builder, exception); fiftyoneDegreesTransformIterateResult result = {0, builder.added, builder.added > builder.length}; if (EXCEPTION_CHECK(CORRUPT_DATA) || EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { return result; @@ -1239,7 +1239,7 @@ fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateSua( fiftyoneDegreesException* const exception) { StringBuilder builder = {buffer, length}; StringBuilderInit(&builder); - uint32_t iterations = main_parsing_body(json, &builder, exception, 1, callback, ctx); + uint32_t iterations = mainParsingBody(json, &builder, exception, 1, callback, ctx); StringBuilderComplete(&builder); fiftyoneDegreesTransformIterateResult result = {iterations, builder.added, builder.added > builder.length}; return result; diff --git a/transform.h b/transform.h index d27ba4e1..04481da6 100644 --- a/transform.h +++ b/transform.h @@ -323,3 +323,4 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformSua( fiftyoneDegreesException *const exception); #endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ + \ No newline at end of file From f0fc0ff80824b8717776223575ce77631dbd9a54 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Tue, 10 Sep 2024 21:08:44 +0200 Subject: [PATCH 66/73] FIX: transform: reindent, width 80 columns --- Transform.cpp | 112 +- Transform.hpp | 114 +- tests/TransformTests.cpp | 3098 +++++++++++++++++++------------------- transform.c | 2308 ++++++++++++++-------------- transform.h | 124 +- 5 files changed, 2931 insertions(+), 2825 deletions(-) diff --git a/Transform.cpp b/Transform.cpp index 0f4bafb5..e7e32283 100644 --- a/Transform.cpp +++ b/Transform.cpp @@ -21,7 +21,7 @@ * ********************************************************************* */ //Exceptions.hpp must be included before "exceptions.h" to switch to -//C++ exceptions semantics - so the macros translate into throw +//C++ exceptions semantics - so the macros translate into throw #include "Exceptions.hpp" #include "Transform.hpp" #include "fiftyone.h" @@ -30,65 +30,73 @@ using namespace FiftyoneDegrees::Common; Transform::Transform(size_t capacity) : buffer(capacity) {} -Transform::Headers Transform::apiInvoker(CTransformAPI func, const std::string& json) { - class ArrayContainer { - public: - KeyValuePairArray* headers = NULL; - ArrayContainer() { - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 8); - } - - ~ArrayContainer() { - Free(headers); - } - }; - - // RAII - ArrayContainer container; - - bool first = true; - size_t written = 0; - EXCEPTION_CREATE; - while (first || EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)) { - if (!first) { - container.headers->count = 0; - size_t bufferSize = std::max(buffer.size() * 2, written + 1); //+1 to avoid 0 size - try { - buffer.resize(bufferSize); - } catch (const std::bad_alloc &) { - // reallocation failed - we just exit this loop by throwing an exception - EXCEPTION_THROW; - } - } - first = false; - EXCEPTION_CLEAR; - fiftyoneDegreesTransformIterateResult result = func(json.c_str(), buffer.data(), buffer.size(), container.headers, exception); +Transform::Headers Transform::apiInvoker(CTransformAPI func, + const std::string& json) { + class ArrayContainer { + public: + KeyValuePairArray* headers = NULL; + ArrayContainer() { + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, + headers, 8); + } + + ~ArrayContainer() { + Free(headers); + } + }; + + // RAII + ArrayContainer container; + + bool first = true; + size_t written = 0; + EXCEPTION_CREATE; + while (first || + EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)) { + if (!first) { + container.headers->count = 0; + //+1 to avoid 0 size + size_t bufferSize = std::max(buffer.size() * 2, + written + 1); + try { + buffer.resize(bufferSize); + } catch (const std::bad_alloc &) { + // reallocation failed - + // we just exit this loop by throwing an exception + EXCEPTION_THROW; + } + } + first = false; + EXCEPTION_CLEAR; + fiftyoneDegreesTransformIterateResult result = + func(json.c_str(), buffer.data(), buffer.size(), container.headers, + exception); written = result.written; - } - - if (EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)) { - EXCEPTION_THROW; - } - - Transform::Headers res; - for (uint32_t i = 0; i < container.headers->count; ++i) { - fiftyoneDegreesKeyValuePair& pair = container.headers->items[i]; - - res.emplace(std::string{pair.key, pair.keyLength}, - std::string{pair.value, pair.valueLength}); - } - - return res; + } + + if (EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)) { + EXCEPTION_THROW; + } + + Transform::Headers res; + for (uint32_t i = 0; i < container.headers->count; ++i) { + fiftyoneDegreesKeyValuePair& pair = container.headers->items[i]; + + res.emplace(std::string{pair.key, pair.keyLength}, + std::string{pair.value, pair.valueLength}); + } + + return res; } Transform::Headers Transform::fromJsonGHEV(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformGhevFromJson, json); + return apiInvoker(fiftyoneDegreesTransformGhevFromJson, json); } Transform::Headers Transform::fromBase64GHEV(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformGhevFromBase64, json); + return apiInvoker(fiftyoneDegreesTransformGhevFromBase64, json); } Transform::Headers Transform::fromSUA(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformSua, json); + return apiInvoker(fiftyoneDegreesTransformSua, json); } diff --git a/Transform.hpp b/Transform.hpp index e1031318..f756bba4 100644 --- a/Transform.hpp +++ b/Transform.hpp @@ -31,64 +31,76 @@ /** * A C++ wrapper class for transform.h conversion routines. - * Encapsulates a working memory buffer to be reused when invoking conversion functions. The methods of a single instance can be invoked + * Encapsulates a working memory buffer to be reused when invoking conversion functions. + * The methods of a single instance can be invoked * however many times synchronously / serially on a single thread. * The instance is not thread safe, for access to the working buffer memory is not synchronized. - * Thus to be used in a multi-threaded setting - an instance per thread must be created. This is also true for the SWIG-generated + * Thus to be used in a multi-threaded setting - an instance per thread must be created. + * This is also true for the SWIG-generated * wrappers of this class. * - * Note the return values of the functions are copied value-types (map) - thus memory in the internal buffer is safely - * overwritten without harming the previously returned results - which is ideal for the (memory-)managed languages s.a. C#. + * Note the return values of the functions are copied value-types (map) - + * thus memory in the internal buffer is safely + * overwritten without harming the previously returned results - which is ideal for the (memory-) + * managed languages s.a. C#. */ namespace FiftyoneDegrees { - namespace Common { - class Transform { - using Headers = std::map; - - using CTransformAPI = - fiftyoneDegreesTransformIterateResult (*)(const char* base64, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception); - - Headers apiInvoker(CTransformAPI func, const std::string& json); - - public: - /** - * Due to the default param value Transform() ctor is also available in C++ and managed language s.a. C# - * @param capacity the size of the working memory buffer The buffer size will automatically double - * if there is not enough memory to perform the conversion and the conversion will repeat with a bigger buffer. - */ - Transform(size_t capacity = 1024); - - /** - * Convert from getHighEntropyValues() results JSON string into HTTP header key-value pairs, - * see help section to the `fiftyoneDegreesTransformGhevFromJson` routine defined in transform.h - * @param json string representation of the GHEV JSON - * @return the converted HTTP header map - */ - Headers fromJsonGHEV(const std::string& json); - - /** - * Convert from the base64 encoded getHighEntropyValues() results JSON string into HTTP header key-value pairs, - * see help section to the `fiftyoneDegreesTransformGhevFromBase64` routine defined in transform.h - * @param base64 base64-encoded string representation of the GHEV JSON - * @return the converted HTTP header map - */ - Headers fromBase64GHEV(const std::string& base64); - - /** - * Convert from the oRTB 2.6 Structured User-Agent JSON string into HTTP header key-value pairs, - * see help section to the `fiftyoneDegreesTransformSUA` routine defined in transform.h - * @param json string representation of the oRTB SUA JSON - * @return the converted HTTP header map - */ - Headers fromSUA(const std::string& json); - - private: - std::vector buffer; - }; - } // namespace Common + namespace Common { + class Transform { + using Headers = std::map; + + using CTransformAPI = + fiftyoneDegreesTransformIterateResult (*) + (const char* base64, + char *buffer, + size_t length, + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception); + + Headers apiInvoker(CTransformAPI func, const std::string& json); + + public: + /** + * Due to the default param value Transform() ctor is also available in C++ and managed + * language s.a. C# + * @param capacity the size of the working memory buffer The buffer size will + * automatically double if there is not enough memory to perform the conversion and the + * conversion will repeat with a bigger buffer. + */ + Transform(size_t capacity = 1024); + + /** + * Convert from getHighEntropyValues() results JSON string into HTTP header key-value + * pairs, see help section to the `fiftyoneDegreesTransformGhevFromJson` routine + * defined in transform.h + * @param json string representation of the GHEV JSON + * @return the converted HTTP header map + */ + Headers fromJsonGHEV(const std::string& json); + + /** + * Convert from the base64 encoded getHighEntropyValues() results JSON string into HTTP + * header key-value pairs, see help section to the + * `fiftyoneDegreesTransformGhevFromBase64` routine defined in transform.h + * @param base64 base64-encoded string representation of the GHEV JSON + * @return the converted HTTP header map + */ + Headers fromBase64GHEV(const std::string& base64); + + /** + * Convert from the oRTB 2.6 Structured User-Agent JSON string into HTTP header + * key-value pairs, see help section to the `fiftyoneDegreesTransformSUA` routine + * defined in transform.h + * @param json string representation of the oRTB SUA JSON + * @return the converted HTTP header map + */ + Headers fromSUA(const std::string& json); + + private: + std::vector buffer; + }; + } // namespace Common } // namespace FiftyoneDegrees #endif // FIFTYONE_DEGREES_TRANSFORM_HPP diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp index 343d94a9..30eb4b50 100644 --- a/tests/TransformTests.cpp +++ b/tests/TransformTests.cpp @@ -27,15 +27,15 @@ class Transform : public Base { - public: - virtual void SetUp(); - virtual void TearDown(); - static bool found; - static fiftyoneDegreesKeyValuePairArray *results; - static const char *expectedFieldName; - static const char *expectedFieldValue; - void checkFieldValue(const char *field, const char *value); - void checkFieldAbsent(const char *field); +public: + virtual void SetUp(); + virtual void TearDown(); + static bool found; + static fiftyoneDegreesKeyValuePairArray *results; + static const char *expectedFieldName; + static const char *expectedFieldValue; + void checkFieldValue(const char *field, const char *value); + void checkFieldAbsent(const char *field); }; bool Transform::found = false; @@ -44,1691 +44,1749 @@ const char *Transform::expectedFieldValue = NULL; fiftyoneDegreesKeyValuePairArray *Transform::results = NULL; void Transform::checkFieldValue(const char *field, const char *value) { - found = false; - expectedFieldName = field; - expectedFieldValue = value; - size_t expectedFieldName_len = strlen(expectedFieldName); - size_t expectedFieldValue_len = strlen(expectedFieldValue); - - for (size_t i = 0; i < results->count; ++i) { - fiftyoneDegreesKeyValuePair *pair = &results->items[i]; - - if (expectedFieldName_len == pair->keyLength) { - found = true; - - for (size_t j = 0; j < pair->keyLength; ++j) { - if (pair->key[j] != expectedFieldName[j]) { - found = false; - break; - } - } - - if (found) { - EXPECT_TRUE(expectedFieldValue_len == pair->valueLength) - << L"Expected value len to be '" << expectedFieldValue_len - << "' not '" << pair->valueLength << "'"; - - if (expectedFieldValue_len == pair->valueLength) { - bool value_compare = true; - - for (size_t j = 0; j < pair->valueLength; ++j) { - if (pair->value[j] != expectedFieldValue[j]) { - value_compare = false; - break; - } - } - - EXPECT_TRUE(value_compare) - << L"Expected value to be '" << expectedFieldValue << "' not '" - << (const char *)pair->value << "'"; - - break; - } - } - } - } - - ASSERT_TRUE(found) << "Field " << field << " was not found should be " - << value; + found = false; + expectedFieldName = field; + expectedFieldValue = value; + size_t expectedFieldName_len = strlen(expectedFieldName); + size_t expectedFieldValue_len = strlen(expectedFieldValue); + + for (size_t i = 0; i < results->count; ++i) { + fiftyoneDegreesKeyValuePair *pair = &results->items[i]; + + if (expectedFieldName_len == pair->keyLength) { + found = true; + + for (size_t j = 0; j < pair->keyLength; ++j) { + if (pair->key[j] != expectedFieldName[j]) { + found = false; + break; + } + } + + if (found) { + EXPECT_TRUE(expectedFieldValue_len == pair->valueLength) + << L"Expected value len to be '" << expectedFieldValue_len + << "' not '" << pair->valueLength << "'"; + + if (expectedFieldValue_len == pair->valueLength) { + bool value_compare = true; + + for (size_t j = 0; j < pair->valueLength; ++j) { + if (pair->value[j] != expectedFieldValue[j]) { + value_compare = false; + break; + } + } + + EXPECT_TRUE(value_compare) + << L"Expected value to be '" << expectedFieldValue << "' not '" + << (const char *)pair->value << "'"; + + break; + } + } + } + } + + EXPECT_TRUE(found) << "Field " << field << " was not found should be " + << value; } void Transform::checkFieldAbsent(const char *field) { - found = false; - expectedFieldName = field; - size_t expectedFieldName_len = strlen(expectedFieldName); - - for (size_t i = 0; i < results->count; ++i) { - fiftyoneDegreesKeyValuePair *pair = &results->items[i]; - - if (expectedFieldName_len == pair->keyLength) { - found = true; - - for (size_t j = 0; j < pair->keyLength; ++j) { - if (pair->key[j] != expectedFieldName[j]) { - found = false; - break; - } - } - - if (found) { - break; - } - } - } - - ASSERT_FALSE(found) << "Field " << field << " should be absent"; + found = false; + expectedFieldName = field; + size_t expectedFieldName_len = strlen(expectedFieldName); + + for (size_t i = 0; i < results->count; ++i) { + fiftyoneDegreesKeyValuePair *pair = &results->items[i]; + + if (expectedFieldName_len == pair->keyLength) { + found = true; + + for (size_t j = 0; j < pair->keyLength; ++j) { + if (pair->key[j] != expectedFieldName[j]) { + found = false; + break; + } + } + + if (found) { + break; + } + } + } + + EXPECT_FALSE(found) << "Field " << field << " should be absent"; } bool fillResultsCallback(void *ctx, fiftyoneDegreesKeyValuePair pair) { - fiftyoneDegreesKeyValuePairArray *results = - (fiftyoneDegreesKeyValuePairArray *)ctx; - - if (results->count < results->capacity) { - results->items[results->count++] = pair; - return true; - } - - return false; + fiftyoneDegreesKeyValuePairArray *results = + (fiftyoneDegreesKeyValuePairArray *)ctx; + + if (results->count < results->capacity) { + results->items[results->count++] = pair; + return true; + } + + return false; } void Transform::SetUp() { - Base::SetUp(); - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, results, 8) + Base::SetUp(); + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, results, 8) } void Transform::TearDown() { - fiftyoneDegreesFree(results); - Base::TearDown(); + fiftyoneDegreesFree(results); + Base::TearDown(); } // Tests // ------------------------------------------------------------------------------------------ TEST_F(Transform, GHEVIterativeJSON) { - const char *ghev = - "{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - ASSERT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 7); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " - "\"Google Chrome\";v=\"126.0.6478.61\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); - - EXPECT_TRUE((bool) FIFTYONE_DEGREES_EXCEPTION_OKAY); + const char *ghev = + "{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromJson + (ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 7); + EXPECT_EQ(results->count, result.iterations); + EXPECT_FALSE(result.bufferTooSmall); + EXPECT_TRUE(result.written < bufferLength); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " + "\"Google Chrome\";v=\"126.0.6478.61\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); + + EXPECT_TRUE((bool) FIFTYONE_DEGREES_EXCEPTION_OKAY); } TEST_F(Transform, IncompleteJSON) { - size_t bufferLength = 4096; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - std::vector correct{ - "{ \"key_without_value\" }", - - "{ \"key_without_value\": ", - - "{\"architecture\":\"x86\"," - " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", - - "{ \"incomplete_string\": \" \n", - "{ \"complete_string\": \" \" \n", - - "{\"incomplete_unknown_object\": { \"other_nested_object", - - "{\"incomplete_unknown_array\": [ \"other_nested_string", - - "{\"incomplete_unknown_array\": [", - - "{\"incomplete_unknown_array\": [[", - - "{\"incomplete_unknown_array\": [[],\"", - "{\"incomplete_unknown_array\": [[],\"\"", - "{\"complete_unknown_array\": [],", - - "{ \"incomplete_bool\": false", - - "{ \"arch\": \"x86\" }", - "{ \"full\" : \"\" } ", - "{ \"min\": -1 }", - "{ \"placebo\": \"___\" }", - "{ \"bind\": true }", - "{ \"break\": true }", - "{ \"mode\": \"default\" }", - - "{ \"\": \"empty_key\" }", - - "{\"bool\": true}", - - "{\"more\": true}", - "{\"moby\": 1}", - - "{\"platformer\": 0}", - "{\"platformVer\": 0}", - - "{ " - "\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]", - }; - - std::vector corrupted{ - "{ \"model\": n", - "{ \"model\": nu", - "{ \"model\": \"he", - "{ \"model\": \"he\\", - "", - "{ \"", - "{ \"mobile\":", - "{ \"mobile\": t", - "{ \"mobile\": f", - "{ \"mo", - "{\"a", - "{\"brands\":[{\"brand\": \"one\", \"version\":null}}", - "{ \"brands\":", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", - "{ \"brands\":{", - "{ \"brands\":[", - "{ \"brands\":[{", - "{ \"brands\":[{\"bra", - "{ \"brands\":[{\"brand\"", - "{ \"brands\":[{\"brand\":", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"ver", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"}", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", - }; - - EXCEPTION_CREATE; - for (const std::string &j : correct) { - - fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, fillResultsCallback, - Transform::results, exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - } - - for (const std::string &j : corrupted) { - - fiftyoneDegreesTransformIterateGhevFromJson( - j.c_str(), buffer, bufferLength, fillResultsCallback, - Transform::results, exception); - EXPECT_FALSE(EXCEPTION_OKAY); - EXPECT_TRUE(EXCEPTION_FAILED); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - } - fiftyoneDegreesFree(buffer); + size_t bufferLength = 4096; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + std::vector correct{ + "{ \"key_without_value\" }", + + "{ \"key_without_value\": ", + + "{\"architecture\":\"x86\"," + " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", + + "{ \"incomplete_string\": \" \n", + "{ \"complete_string\": \" \" \n", + + "{\"incomplete_unknown_object\": { \"other_nested_object", + + "{\"incomplete_unknown_array\": [ \"other_nested_string", + + "{\"incomplete_unknown_array\": [", + + "{\"incomplete_unknown_array\": [[", + + "{\"incomplete_unknown_array\": [[],\"", + "{\"incomplete_unknown_array\": [[],\"\"", + "{\"complete_unknown_array\": [],", + + "{ \"incomplete_bool\": false", + + "{ \"arch\": \"x86\" }", + "{ \"full\" : \"\" } ", + "{ \"min\": -1 }", + "{ \"placebo\": \"___\" }", + "{ \"bind\": true }", + "{ \"break\": true }", + "{ \"mode\": \"default\" }", + + "{ \"\": \"empty_key\" }", + + "{\"bool\": true}", + + "{\"more\": true}", + "{\"moby\": 1}", + + "{\"platformer\": 0}", + "{\"platformVer\": 0}", + + "{ " + "\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]", + }; + + std::vector corrupted{ + "{ \"model\": n", + "{ \"model\": nu", + "{ \"model\": \"he", + "{ \"model\": \"he\\", + "", + "{ \"", + "{ \"mobile\":", + "{ \"mobile\": t", + "{ \"mobile\": f", + "{ \"mo", + "{\"a", + "{\"brands\":[{\"brand\": \"one\", \"version\":null}}", + "{ \"brands\":", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", + "{ \"brands\":{", + "{ \"brands\":[", + "{ \"brands\":[{", + "{ \"brands\":[{\"bra", + "{ \"brands\":[{\"brand\"", + "{ \"brands\":[{\"brand\":", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"ver", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"}", + "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", + }; + + EXCEPTION_CREATE; + for (const std::string &j : correct) { + + fiftyoneDegreesTransformIterateGhevFromJson + (j.c_str(), buffer, bufferLength, fillResultsCallback, + Transform::results, exception); + + EXPECT_TRUE(EXCEPTION_OKAY); + } + + for (const std::string &j : corrupted) { + + fiftyoneDegreesTransformIterateGhevFromJson + ( + j.c_str(), buffer, bufferLength, fillResultsCallback, + Transform::results, exception); + EXPECT_FALSE(EXCEPTION_OKAY); + EXPECT_TRUE(EXCEPTION_FAILED); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + } + fiftyoneDegreesFree(buffer); } TEST_F(Transform, IncompleteSUA) { - size_t bufferLength = 4096; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - std::vector correct{ - "{\"browsers\":[{\"brand\": \"\", \"version\": [\"\\\"123\\\\\"]}]}", - - "{ \"key_without_value\" }", - - "{ \"key_without_value\": ", - - "{ \"skip\": { \"key\": \"\\\"\\n\\\\\"value\\\"\" } }", - - "{\"architecture\":\"x86\\\"\"," - " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", - - "{ \"incomplete_string\": \" \n", - "{ \"complete_string\": \" \" \n", - - "{\"incomplete_unknown_object\": { \"other_nested_object", - - "{\"incomplete_unknown_array\": [ \"other_nested_string", - - "{\"incomplete_unknown_array\": [", - - "{\"incomplete_unknown_array\": [[", - - "{\"incomplete_unknown_array\": [[],\"", - "{\"incomplete_unknown_array\": [[],\"\"", - "{\"complete_unknown_array\": [],", - - "{ \"incomplete_bool\": false", - - "{ \"\": \"empty_key\" }", - - "{\"bool\": true}", - - "{ \"arch\": \"x86\" }", - "{ \"full\" : \"\" } ", - "{ \"min\": -1 }", - "{ \"placebo\": \"___\" }", - "{ \"bind\": true }", - "{ \"break\": true }", - "{ \"mode\": \"default\" }", - - "{\"more\": true}", - "{\"browsers\":[{\"brand\": null}]}", - "{\"platformer\": 0}", - "{\"platformVer\": 0}", - }; - - std::vector corrupted{ - "", - "{ \"", - "{\"a", - "{ \"mo", - "{ \"mobile\":", - "{\"browsers\":[{\"brand\": null}}}", - - "{\n \"browsers\"", - "{\n \"browsers\":{", - "{\n \"browsers\":[", - "{\n \"browsers\":[{", - "{\n \"browsers\":[{\"", - "{\n \"browsers\":[{\"bra", - "{\n \"browsers\":[{\"brand", - "{\n \"browsers\":[{\"brand\"", - "{\n \"browsers\":[{\"brand\":", - "{\n \"browsers\":[{\"brand\":\"", - "{\n \"browsers\":[{\"brand\":\"google", - "{\n \"browsers\":[{\"brand\":\"google\"", - "{\n \"browsers\":[{\"brand\":\"google\",", - "{\n \"browsers\":[{\"brand\":\"google\",\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"ver", - "{\n \"browsers\":[{\"brand\":\"google\",\"version", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]}", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]},", - }; - - EXCEPTION_CREATE; - for (const std::string &j : correct) { - - fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - } - - for (const std::string &j : corrupted) { - - fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - } - fiftyoneDegreesFree(buffer); + size_t bufferLength = 4096; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + std::vector correct{ + "{\"browsers\":[{\"brand\": \"\", \"version\": [\"\\\"123\\\\\"]}]}", + + "{ \"key_without_value\" }", + + "{ \"key_without_value\": ", + + "{ \"skip\": { \"key\": \"\\\"\\n\\\\\"value\\\"\" } }", + + "{\"architecture\":\"x86\\\"\"," + " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", + + "{ \"incomplete_string\": \" \n", + "{ \"complete_string\": \" \" \n", + + "{\"incomplete_unknown_object\": { \"other_nested_object", + + "{\"incomplete_unknown_array\": [ \"other_nested_string", + + "{\"incomplete_unknown_array\": [", + + "{\"incomplete_unknown_array\": [[", + + "{\"incomplete_unknown_array\": [[],\"", + "{\"incomplete_unknown_array\": [[],\"\"", + "{\"complete_unknown_array\": [],", + + "{ \"incomplete_bool\": false", + + "{ \"\": \"empty_key\" }", + + "{\"bool\": true}", + + "{ \"arch\": \"x86\" }", + "{ \"full\" : \"\" } ", + "{ \"min\": -1 }", + "{ \"placebo\": \"___\" }", + "{ \"bind\": true }", + "{ \"break\": true }", + "{ \"mode\": \"default\" }", + + "{\"more\": true}", + "{\"browsers\":[{\"brand\": null}]}", + "{\"platformer\": 0}", + "{\"platformVer\": 0}", + }; + + std::vector corrupted{ + "", + "{ \"", + "{\"a", + "{ \"mo", + "{ \"mobile\":", + "{\"browsers\":[{\"brand\": null}}}", + + "{\n \"browsers\"", + "{\n \"browsers\":{", + "{\n \"browsers\":[", + "{\n \"browsers\":[{", + "{\n \"browsers\":[{\"", + "{\n \"browsers\":[{\"bra", + "{\n \"browsers\":[{\"brand", + "{\n \"browsers\":[{\"brand\"", + "{\n \"browsers\":[{\"brand\":", + "{\n \"browsers\":[{\"brand\":\"", + "{\n \"browsers\":[{\"brand\":\"google", + "{\n \"browsers\":[{\"brand\":\"google\"", + "{\n \"browsers\":[{\"brand\":\"google\",", + "{\n \"browsers\":[{\"brand\":\"google\",\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"ver", + "{\n \"browsers\":[{\"brand\":\"google\",\"version", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]}", + "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]},", + }; + + EXCEPTION_CREATE; + for (const std::string &j : correct) { + + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, + fillResultsCallback, Transform::results, + exception); + + EXPECT_TRUE(EXCEPTION_OKAY); + } + + for (const std::string &j : corrupted) { + + fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, + fillResultsCallback, Transform::results, + exception); + + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); + } + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVIncorrectBool) { - const char *ghev = - "{\"architecture\":\"x86\"," - "\"brands\":[{\"brand\": null, \"version\":\"8\"}," - "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," - "\"fullVersionList\":[{\"brand\": null}]," - "\"mobile\": 0,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - + const char *ghev = + "{\"architecture\":\"x86\"," + "\"brands\":[{\"brand\": null, \"version\":\"8\"}," + "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," + "\"fullVersionList\":[{\"brand\": null}]," + "\"mobile\": 0,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateGhevFromJson + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAIncorrectBool) { - const char *json = "{\"mobile\": false,\"model\":\"\"}"; - - size_t bufferLength = 512; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - - // --- - + const char *json = "{\"mobile\": false,\"model\":\"\"}"; + + size_t bufferLength = 512; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, + fillResultsCallback, Transform::results, + exception); + + // --- + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVIterativeNULLBrandJSON) { - const char *ghev = - "{\"architecture\":\"x86\"," - "\"brands\":[{\"brand\": null, \"version\":\"8\"}," - "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," - "\"fullVersionList\":[{\"brand\": null}]," - "\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 6); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", "\"Google Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); + const char *ghev = + "{\"architecture\":\"x86\"," + "\"brands\":[{\"brand\": null, \"version\":\"8\"}," + "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," + "\"fullVersionList\":[{\"brand\": null}]," + "\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromJson + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 6); + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", "\"Google Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { - const char *ghev = - "{\"architecture\":\"x86\"," - "\"brands\":[{\"brand\": \"one\", \"version\":null}," - "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," - "\"fullVersionList\":[{\"brand\": null}]," - "\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 6); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", "\"one\";v=null, \"Google Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); + const char *ghev = + "{\"architecture\":\"x86\"," + "\"brands\":[{\"brand\": \"one\", \"version\":null}," + "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," + "\"fullVersionList\":[{\"brand\": null}]," + "\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromJson + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 6); + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", "\"one\";v=null, \"Google Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVIterativeBase64) { - const char *ghev = - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 6); - ASSERT_EQ(results->count, result.iterations); - - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); + const char *ghev = + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromBase64 + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 6); + EXPECT_EQ(results->count, result.iterations); + + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVBase64CorruptedLen) { - const char *ghev = - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>" - "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - + const char *ghev = + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>" + "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateGhevFromBase64 + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVBase64CorruptedSymbol) { - const char *ghev = - "====cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" - "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - + const char *ghev = + "====cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" + "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateGhevFromBase64 + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVBase64CorruptedSymbol2) { - const char *ghev = - "&&&&cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" - "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - + const char *ghev = + "&&&&cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" + "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = 686; // strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateGhevFromBase64 + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVIterativeSua) { - const char *sua = - "{\n \"source\": 2,\n \"platform\": {\n " - "\"brand\": \"macOS\",\n \"version\": [\"14\", \"5\", " - "\"0\"]\n },\n \"browsers\": [{\n " - "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " - "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " - "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " - "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " - " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " - " }],\n \"mobile\": 0,\n \"model\": \"\",\n " - "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 7); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldAbsent("sec-ch-ua"); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\n \"source\": 2,\n \"platform\": {\n " + "\"brand\": \"macOS\",\n \"version\": [\"14\", \"5\", " + "\"0\"]\n },\n \"browsers\": [{\n " + "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " + "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " + "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " + "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " + " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " + " }],\n \"mobile\": 0,\n \"model\": \"\",\n " + "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 7); + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldAbsent("sec-ch-ua"); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SuaWeirdPlatformVersion) { - const char *sua = - "{\n \"source\": 2,\n \"platform\": {\n " - "\"brand\": \"macOS\",\n \"version\": [\"\\\"x\\\"\", " - "\"\\\"y\\\"\", " - "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " - "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " - "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " - "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " - "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " - " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " - " }],\n \"mobile\": 0,\n \"model\": \"\",\n " - "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 7); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldAbsent("sec-ch-ua"); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", - "\"\\\"x\\\".\\\"y\\\".\\\"z\\\"\""); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\n \"source\": 2,\n \"platform\": {\n " + "\"brand\": \"macOS\",\n \"version\": [\"\\\"x\\\"\", " + "\"\\\"y\\\"\", " + "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " + "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " + "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " + "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " + "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " + " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " + " }],\n \"mobile\": 0,\n \"model\": \"\",\n " + "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 7); + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldAbsent("sec-ch-ua"); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", + "\"\\\"x\\\".\\\"y\\\".\\\"z\\\"\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SuaNullBrandPlatform) { - const char *sua = - "{\n \"source\": 2,\n \"platform\": {\n " - "\"brand\": null,\n \"version\": [\"\\\"x\\\"\", " - "\"\\\"y\\\"\", " - "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " - "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " - "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " - "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " - "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " - " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " - " }],\n \"mobile\": 0,\n \"model\": \"\",\n " - "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 5); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldAbsent("sec-ch-ua"); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldAbsent("sec-ch-ua-platform"); - checkFieldAbsent("sec-ch-ua-platform-version"); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\n \"source\": 2,\n \"platform\": {\n " + "\"brand\": null,\n \"version\": [\"\\\"x\\\"\", " + "\"\\\"y\\\"\", " + "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " + "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " + "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " + "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " + "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " + " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " + " }],\n \"mobile\": 0,\n \"model\": \"\",\n " + "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 5); + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldAbsent("sec-ch-ua"); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldAbsent("sec-ch-ua-platform"); + checkFieldAbsent("sec-ch-ua-platform-version"); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVArrayJSON) { - const char *ghev = - "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" - "\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " - " : false, \"model\" : \"MacBook\" , \"platform\" : " - "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, results, exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 7); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"MacBook\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); + const char *ghev = + "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" + "\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " + " : false, \"model\" : \"MacBook\" , \"platform\" : " + "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformGhevFromJson + ( + ghev, buffer, bufferLength, results, exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 7); + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"MacBook\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVArrayInsufficientCapacity) { - const char *ghev = - "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" - "\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " - " : false, \"model\" : \"MacBook\" , \"platform\" : " - "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - fiftyoneDegreesKeyValuePairArray *headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 2); - - fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, headers, exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 2); - ASSERT_EQ(headers->count, result.iterations); - - // --- - + const char *ghev = + "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" + "\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " + " : false, \"model\" : \"MacBook\" , \"platform\" : " + "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesKeyValuePairArray *headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 2); + + fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformGhevFromJson + ( + ghev, buffer, bufferLength, headers, exception); + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 2); + EXPECT_EQ(headers->count, result.iterations); + + // --- + result = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, empty_headers, exception); - + ghev, buffer, bufferLength, empty_headers, exception); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - ASSERT_EQ(result.iterations, 1); - - fiftyoneDegreesFree(buffer); - fiftyoneDegreesFree(headers); - fiftyoneDegreesFree(empty_headers); + EXPECT_EQ(result.iterations, 1); + + fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(headers); + fiftyoneDegreesFree(empty_headers); } TEST_F(Transform, GHEVBase64InsufficientCapacity) { - const char *ghev = - "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" - "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" - "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" - "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" - "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" - "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" - "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; - - size_t bufferLength = strlen(ghev) * 3; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromBase64( - ghev, buffer, bufferLength, empty_headers, exception); - + const char *ghev = + "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" + "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" + "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" + "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" + "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" + "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" + "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; + + size_t bufferLength = strlen(ghev) * 3; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformGhevFromBase64 + ( + ghev, buffer, bufferLength, empty_headers, exception); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - ASSERT_EQ(result.iterations, 1); - - fiftyoneDegreesFree(buffer); - fiftyoneDegreesFree(empty_headers); + EXPECT_EQ(result.iterations, 1); + + fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(empty_headers); } TEST_F(Transform, SUAInsufficientCapacity) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformSua( - sua, buffer, bufferLength, empty_headers, exception); - + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformSua + ( + sua, buffer, bufferLength, empty_headers, exception); + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - ASSERT_EQ(result.iterations, 1); - - fiftyoneDegreesKeyValuePairArray *headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); - - FIFTYONE_DEGREES_EXCEPTION_CLEAR; + EXPECT_EQ(result.iterations, 1); + + fiftyoneDegreesKeyValuePairArray *headers = NULL; + FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); + + FIFTYONE_DEGREES_EXCEPTION_CLEAR; result = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, - exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - ASSERT_EQ(result.iterations, 3); - - fiftyoneDegreesFree(headers); - fiftyoneDegreesFree(empty_headers); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVBase64) { - const char *ghev = - "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" - "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" - "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" - "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" - "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" - "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" - "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; - - size_t bufferLength = strlen(ghev) * 2; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformGhevFromBase64( - ghev, buffer, bufferLength, results, exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 6); - ASSERT_EQ(results->count, result.iterations); + exception); + + EXPECT_TRUE(EXCEPTION_OKAY); + EXPECT_EQ(result.iterations, 3); + + fiftyoneDegreesFree(headers); + fiftyoneDegreesFree(empty_headers); + fiftyoneDegreesFree(buffer); +} - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldAbsent("sec-ch-ua-platform-version"); - fiftyoneDegreesFree(buffer); +TEST_F(Transform, GHEVBase64) { + const char *ghev = + "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" + "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" + "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" + "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" + "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" + "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" + "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; + + size_t bufferLength = strlen(ghev) * 2; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformGhevFromBase64 + ( + ghev, buffer, bufferLength, results, exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 6); + EXPECT_EQ(results->count, result.iterations); + + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldAbsent("sec-ch-ua-platform-version"); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVBase64NotEnoughMemory) { - const char *ghev = - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - + const char *ghev = + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromBase64 + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + // --- EXPECT_TRUE(result.bufferTooSmall); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); - - EXCEPTION_CLEAR; - buffer = (char *)fiftyoneDegreesMalloc(result.written); - - result = fiftyoneDegreesTransformIterateGhevFromBase64( - ghev, buffer, result.written, fillResultsCallback, Transform::results, - exception); + fiftyoneDegreesFree(buffer); + + EXCEPTION_CLEAR; + buffer = (char *)fiftyoneDegreesMalloc(result.written); + + result = fiftyoneDegreesTransformIterateGhevFromBase64 + ( + ghev, buffer, result.written, fillResultsCallback, Transform::results, + exception); EXPECT_FALSE(result.bufferTooSmall); - EXPECT_TRUE(EXCEPTION_OKAY); + EXPECT_TRUE(EXCEPTION_OKAY); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUANotEnoughMemory) { - const char *sua = - "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," - "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; - - size_t bufferLength = 144; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - + const char *sua = + "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," + "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; + + size_t bufferLength = 144; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + // --- + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVArraySua) { - const char *sua = - "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," - "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 6); - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua-model", "\"MacBook\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/" - "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " - "Chrome\";v=\"126.0.6478.127\""); - - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua"); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," + "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, + exception); + + // --- + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 6); + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua-model", "\"MacBook\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/" + "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " + "Chrome\";v=\"126.0.6478.127\""); + + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldAbsent("sec-ch-ua"); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAArrayNotEnoughMemory) { - const char *sua = - "{\"source\":2,\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; - - size_t bufferLength = 142; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, - exception); - - // --- - + const char *sua = + "{\"source\":2,\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; + + size_t bufferLength = 142; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, + exception); + + // --- + EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVPartial) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":null}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - ASSERT_EQ(result.iterations, 4); - EXPECT_TRUE(EXCEPTION_OKAY); - - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile - // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list - // we check that either empty value (model), null-value (platform) - // or entire absence of key (platformVersion) result in no header in the - // output the policy is - we don't output empty data - no value == no evidence - // field - - ASSERT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " - "\"Google Chrome\";v=\"126.0.6478.61\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - - checkFieldAbsent("sec-ch-ua-platform"); - checkFieldAbsent("sec-ch-ua-platform-version"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - fiftyoneDegreesFree(buffer); + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":null}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromJson + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_EQ(result.iterations, 4); + EXPECT_TRUE(EXCEPTION_OKAY); + + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile + // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list + // we check that either empty value (model), null-value (platform) + // or entire absence of key (platformVersion) result in no header in the + // output the policy is - we don't output empty data - no value == no evidence + // field + + EXPECT_EQ(results->count, result.iterations); + + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " + "\"Google Chrome\";v=\"126.0.6478.61\""); + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-model", "\"\""); + + checkFieldAbsent("sec-ch-ua-platform"); + checkFieldAbsent("sec-ch-ua-platform-version"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVIgnoreUnused) { - const char *ghev = - "{ \"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{ " - "\"brand\" : " - "\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\" , " - "\"version\" : \"126.0.6478.127\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.127\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":" - "false}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - ASSERT_EQ(result.iterations, 8); - EXPECT_TRUE(EXCEPTION_OKAY); - - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile - // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list - // we check that either empty value (model), null-value (platform) - // or entire absence of key (platformVersion) result in no header in the - // output the policy is - we don't output empty data - no value == no evidence - // field - - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - fiftyoneDegreesFree(buffer); + const char *ghev = + "{ \"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{ " + "\"brand\" : " + "\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\" , " + "\"version\" : \"126.0.6478.127\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.127\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":" + "false}"; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromJson + ( + ghev, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + EXPECT_EQ(result.iterations, 8); + EXPECT_TRUE(EXCEPTION_OKAY); + + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile + // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list + // we check that either empty value (model), null-value (platform) + // or entire absence of key (platformVersion) result in no header in the + // output the policy is - we don't output empty data - no value == no evidence + // field + + checkFieldValue("sec-ch-ua", + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + + checkFieldValue("sec-ch-ua-mobile", "?0"); + checkFieldValue("sec-ch-ua-platform", "\"macOS\""); + checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); + checkFieldValue("sec-ch-ua-model", "\"\""); + checkFieldValue("sec-ch-ua-arch", "\"x86\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVCorruptInput) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\""; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - fillResultsCallback, - Transform::results, exception); + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\""; + + size_t bufferLength = strlen(ghev); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + fillResultsCallback, + Transform::results, exception); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVBufferTooSmall) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\"}"; - - size_t bufferLength = 20; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - fillResultsCallback, - Transform::results, exception); + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\"}"; + + size_t bufferLength = 20; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateGhevFromJson + (ghev, buffer, bufferLength, + fillResultsCallback, + Transform::results, exception); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); EXPECT_TRUE(result.bufferTooSmall); fiftyoneDegreesFree(buffer); - - buffer = (char *)fiftyoneDegreesMalloc(result.written); - EXCEPTION_CLEAR; - result = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, result.written, - fillResultsCallback, - Transform::results, exception); - EXPECT_TRUE(EXCEPTION_OKAY); + + buffer = (char *)fiftyoneDegreesMalloc(result.written); + EXCEPTION_CLEAR; + result = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, result.written, + fillResultsCallback, + Transform::results, exception); + EXPECT_TRUE(EXCEPTION_OKAY); EXPECT_FALSE(result.bufferTooSmall); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, GHEVEvidenceLowCapacity) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\"}"; - - size_t bufferLength = 20; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - fillResultsCallback, - Transform::results, exception); + const char *ghev = + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":\"macOS\"}"; + + size_t bufferLength = 20; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, + fillResultsCallback, + Transform::results, exception); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAHappyPath) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 7); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, result.iterations); - - // In device.sua representation there is no distinction between - // sec-ch-ua and sec-ch-ua-full-version-list - // checkFieldValue( - // "sec-ch-ua", - // "\"Not A;Brand\";v=\"99.0.0.0\",\"Chromium\";v=\"99.0.4844.88\"," - // "\"Google Chrome\";v=\"99.0.4844.88\"\n"); - - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " - "\"Google Chrome\";v=\"99.0.4844.88\""); - - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"12\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-arch", "\"arm\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue("sec-ch-ua-model", "\"Pixel6\""); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 7); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + EXPECT_EQ(results->count, result.iterations); + + // In device.sua representation there is no distinction between + // sec-ch-ua and sec-ch-ua-full-version-list + // checkFieldValue( + // "sec-ch-ua", + // "\"Not A;Brand\";v=\"99.0.0.0\",\"Chromium\";v=\"99.0.4844.88\"," + // "\"Google Chrome\";v=\"99.0.4844.88\"\n"); + + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " + "\"Google Chrome\";v=\"99.0.4844.88\""); + + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"12\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-arch", "\"arm\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue("sec-ch-ua-model", "\"Pixel6\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAPlatformExt) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\", \"version\": [\"13\"], " - "\"ext\": { \"some_random_key\" : [ \"some\", \"random\", \"\\n\", " - "\" \\\" values \" ], " - "\"another_random_key\": null}}," - "\"mobile\": 1,\"model\": \"\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - ASSERT_EQ(result.iterations, 5); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, result.iterations); - EXPECT_TRUE(EXCEPTION_OKAY); - - checkFieldAbsent("sec-ch-ua"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " - "\"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"13\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue("sec-ch-ua-model", "\"\""); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\", \"version\": [\"13\"], " + "\"ext\": { \"some_random_key\" : [ \"some\", \"random\", \"\\n\", " + "\" \\\" values \" ], " + "\"another_random_key\": null}}," + "\"mobile\": 1,\"model\": \"\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_EQ(result.iterations, 5); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + EXPECT_EQ(results->count, result.iterations); + EXPECT_TRUE(EXCEPTION_OKAY); + + checkFieldAbsent("sec-ch-ua"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " + "\"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"13\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue("sec-ch-ua-model", "\"\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAPartial1) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\"},\"mobile\": 1,\"model\": \"\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - ASSERT_EQ(result.iterations, 4); - // we expect to see these headers detected: - // low entropy: sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, result.iterations); - EXPECT_TRUE(EXCEPTION_OKAY); - - checkFieldAbsent("sec-ch-ua"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " - "\"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldAbsent("sec-ch-ua-platform-version"); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue("sec-ch-ua-model", "\"\""); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\"},\"mobile\": 1,\"model\": \"\"}"; + + size_t bufferLength = strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_EQ(result.iterations, 4); + // we expect to see these headers detected: + // low entropy: sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + EXPECT_EQ(results->count, result.iterations); + EXPECT_TRUE(EXCEPTION_OKAY); + + checkFieldAbsent("sec-ch-ua"); + checkFieldValue( + "sec-ch-ua-full-version-list", + "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " + "\"Google Chrome\";v=\"99.0.4844.88\""); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldAbsent("sec-ch-ua-platform-version"); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-arch"); + checkFieldAbsent("sec-ch-ua-bitness"); + checkFieldValue("sec-ch-ua-model", "\"\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUAPartial2) { - const char *sua = - "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " - "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - ASSERT_EQ(result.iterations, 5); - - EXPECT_TRUE(EXCEPTION_OKAY); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - ASSERT_EQ(results->count, result.iterations); - - checkFieldAbsent("sec-ch-ua"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"12\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-arch", "\"arm\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldAbsent("sec-ch-ua-model"); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " + "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_EQ(result.iterations, 5); + + EXPECT_TRUE(EXCEPTION_OKAY); + // we expect to see these headers detected: + // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform + // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, + // sec-ch-ua-full-version-list + + EXPECT_EQ(results->count, result.iterations); + + checkFieldAbsent("sec-ch-ua"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"12\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldValue("sec-ch-ua-arch", "\"arm\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldAbsent("sec-ch-ua-model"); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUATolerableCorrupt) { - const char *sua = - "{\"source\": 2,,\"platform\": {\"brand\": \"Android\",\"version\": " - "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_OKAY); - - ASSERT_EQ(result.iterations, 5); - ASSERT_EQ(results->count, result.iterations); - - checkFieldAbsent("sec-ch-ua"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - - checkFieldValue("sec-ch-ua-arch", "\"arm\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-model"); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"12\""); - fiftyoneDegreesFree(buffer); + const char *sua = + "{\"source\": 2,,\"platform\": {\"brand\": \"Android\",\"version\": " + "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + ( + sua, buffer, bufferLength, fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_OKAY); + + EXPECT_EQ(result.iterations, 5); + EXPECT_EQ(results->count, result.iterations); + + checkFieldAbsent("sec-ch-ua"); + checkFieldAbsent("sec-ch-ua-full-version-list"); + + checkFieldValue("sec-ch-ua-arch", "\"arm\""); + checkFieldValue("sec-ch-ua-bitness", "\"64\""); + checkFieldValue("sec-ch-ua-mobile", "?1"); + checkFieldAbsent("sec-ch-ua-model"); + checkFieldValue("sec-ch-ua-platform", "\"Android\""); + checkFieldValue("sec-ch-ua-platform-version", "\"12\""); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUACorrupt2) { - const char *sua = - "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " - "[12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); + const char *sua = + "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " + "[12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + fillResultsCallback, Transform::results, + exception); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUACorrupt3) { - const char *sua = - "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " - "\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); + const char *sua = + "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " + "\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " + "\"64\",\"model\": null}"; + + size_t bufferLength = 2 * strlen(sua); + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + fillResultsCallback, Transform::results, + exception); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, SUABufferTooSmall) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; - - size_t bufferLength = 15; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + + size_t bufferLength = 15; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateResult result = + fiftyoneDegreesTransformIterateSua + (sua, buffer, bufferLength, + fillResultsCallback, Transform::results, + exception); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); EXPECT_TRUE(result.bufferTooSmall); - fiftyoneDegreesFree(buffer); - - - buffer = (char *)fiftyoneDegreesMalloc(result.written); - EXCEPTION_CLEAR; - - result = fiftyoneDegreesTransformIterateSua(sua, buffer, result.written, - fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_OKAY); + fiftyoneDegreesFree(buffer); + + + buffer = (char *)fiftyoneDegreesMalloc(result.written); + EXCEPTION_CLEAR; + + result = fiftyoneDegreesTransformIterateSua(sua, buffer, result.written, + fillResultsCallback, Transform::results, + exception); + EXPECT_TRUE(EXCEPTION_OKAY); EXPECT_FALSE(result.bufferTooSmall); - Free(buffer); + Free(buffer); } TEST_F(Transform, SUAEvidenceLowCapacity) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; - - size_t bufferLength = 15; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); + const char *sua = + "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " + "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " + "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " + "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " + "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " + "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; + + size_t bufferLength = 15; + char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); + + EXCEPTION_CREATE; + + fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, + fillResultsCallback, Transform::results, + exception); EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); + fiftyoneDegreesFree(buffer); } TEST_F(Transform, CPPWrapperGHEV) { - FiftyoneDegrees::Common::Transform t; - - auto h = t.fromJsonGHEV( - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\"," - "\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":null}"); - - ASSERT_EQ(h.size(), 4); - - EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - - ASSERT_EQ(h["sec-ch-ua"], - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - ASSERT_EQ(h["sec-ch-ua-full-version-list"], - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " - "\"Google Chrome\";v=\"126.0.6478.61\""); - - ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); - ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); - - EXPECT_FALSE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-platform-version") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); + FiftyoneDegrees::Common::Transform t; + + auto h = t.fromJsonGHEV + ( + "{\"brands\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" + "\"126\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\"," + "\"version\":" + "\"126.0.6478.61\"},{\"brand\":\"Google " + "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" + "\"\",\"platform\":null}"); + + EXPECT_EQ(h.size(), 4); + + EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + + EXPECT_EQ(h["sec-ch-ua"], + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + EXPECT_EQ(h["sec-ch-ua-full-version-list"], + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " + "\"Google Chrome\";v=\"126.0.6478.61\""); + + EXPECT_EQ(h["sec-ch-ua-mobile"], "?0"); + EXPECT_EQ(h["sec-ch-ua-model"], "\"\""); + + EXPECT_FALSE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-platform-version") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } TEST_F(Transform, CPPWrapperBase64) { - FiftyoneDegrees::Common::Transform t; - - auto h = t.fromBase64GHEV( - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); - - ASSERT_EQ(h.size(), 6); - - EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); - - ASSERT_EQ(h["sec-ch-ua"], - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - ASSERT_EQ(h["sec-ch-ua-full-version-list"], - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - - ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); - ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); - ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); - ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); + FiftyoneDegrees::Common::Transform t; + + auto h = t.fromBase64GHEV + ( + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); + + EXPECT_EQ(h.size(), 6); + + EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); + + EXPECT_EQ(h["sec-ch-ua"], + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + EXPECT_EQ(h["sec-ch-ua-full-version-list"], + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + + EXPECT_EQ(h["sec-ch-ua-mobile"], "?0"); + EXPECT_EQ(h["sec-ch-ua-model"], "\"\""); + EXPECT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); + EXPECT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); + + EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } TEST_F(Transform, CPPWrapperBase64InsufficientMemory) { - FiftyoneDegrees::Common::Transform t(128); - - auto h = t.fromBase64GHEV( - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); - - ASSERT_EQ(h.size(), 6); - - EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); - - ASSERT_EQ(h["sec-ch-ua"], - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - ASSERT_EQ(h["sec-ch-ua-full-version-list"], - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - - ASSERT_EQ(h["sec-ch-ua-mobile"], "?0"); - ASSERT_EQ(h["sec-ch-ua-model"], "\"\""); - ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); - ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); + FiftyoneDegrees::Common::Transform t(128); + + auto h = t.fromBase64GHEV + ( + "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" + "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" + "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" + "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" + "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" + "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" + "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); + + EXPECT_EQ(h.size(), 6); + + EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); + + EXPECT_EQ(h["sec-ch-ua"], + "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " + "Chrome\";v=\"126\""); + + EXPECT_EQ(h["sec-ch-ua-full-version-list"], + "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " + "\"Google Chrome\";v=\"126.0.6478.127\""); + + EXPECT_EQ(h["sec-ch-ua-mobile"], "?0"); + EXPECT_EQ(h["sec-ch-ua-model"], "\"\""); + EXPECT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); + EXPECT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); + + EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } TEST_F(Transform, CPPWrapperSUA) { - FiftyoneDegrees::Common::Transform t; - - auto h = t.fromSUA( - "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," - "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"); - - ASSERT_EQ(h.size(), 6); - - EXPECT_TRUE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - - ASSERT_EQ(h["sec-ch-ua-arch"], "\"x86\""); - ASSERT_EQ(h["sec-ch-ua-model"], "\"MacBook\""); - ASSERT_EQ(h["sec-ch-ua-mobile"], "?1"); - ASSERT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); - ASSERT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - ASSERT_EQ( - h["sec-ch-ua-full-version-list"], - "\"Not/" - "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " - "Chrome\";v=\"126.0.6478.127\""); - - EXPECT_FALSE(h.find("sec-ch-ua") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); + FiftyoneDegrees::Common::Transform t; + + auto h = t.fromSUA + ( + "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," + "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" + "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" + "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" + "\"Google " + "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," + "\"model\":\"MacBook\",\"architecture\":\"x86\"}"); + + EXPECT_EQ(h.size(), 6); + + EXPECT_TRUE(h.find("sec-ch-ua-arch") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); + EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); + + EXPECT_EQ(h["sec-ch-ua-arch"], "\"x86\""); + EXPECT_EQ(h["sec-ch-ua-model"], "\"MacBook\""); + EXPECT_EQ(h["sec-ch-ua-mobile"], "?1"); + EXPECT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); + EXPECT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); + EXPECT_EQ( + h["sec-ch-ua-full-version-list"], + "\"Not/" + "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " + "Chrome\";v=\"126.0.6478.127\""); + + EXPECT_FALSE(h.find("sec-ch-ua") != h.end()); + EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); } TEST_F(Transform, emptycases) { - FiftyoneDegrees::Common::Transform t; - auto result = t.fromJsonGHEV("{}"); - EXPECT_EQ(result.size(), 0); - bool thrown = false; - try { - t.fromJsonGHEV(""); - } catch (const FiftyoneDegrees::Common::FatalException &e) { - EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); - thrown = true; - } - - EXPECT_TRUE(thrown); + FiftyoneDegrees::Common::Transform t; + auto result = t.fromJsonGHEV("{}"); + EXPECT_EQ(result.size(), 0); + bool thrown = false; + try { + t.fromJsonGHEV(""); + } catch (const FiftyoneDegrees::Common::FatalException &e) { + EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + thrown = true; + } + + EXPECT_TRUE(thrown); } TEST_F(Transform, CPPWrapperBase64Corrupt) { - FiftyoneDegrees::Common::Transform t; - bool thrown = false; - try { - auto result = t.fromBase64GHEV("base64 invalid string"); - EXPECT_EQ(result.size(), 0); - } catch (const FiftyoneDegrees::Common::FatalException &e) { - EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); - thrown = true; - } - EXPECT_TRUE(thrown); + FiftyoneDegrees::Common::Transform t; + bool thrown = false; + try { + auto result = t.fromBase64GHEV("base64 invalid string"); + EXPECT_EQ(result.size(), 0); + } catch (const FiftyoneDegrees::Common::FatalException &e) { + EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); + thrown = true; + } + EXPECT_TRUE(thrown); } TEST_F(Transform, CPPWrapper0Size) { - FiftyoneDegrees::Common::Transform t(0); - bool thrown = false; - try { - auto result = t.fromJsonGHEV("{\"architecture\":\"x86\", \"bitness\":\"32\", \"new\":\"ignored\"}"); - EXPECT_EQ(result.size(), 2); - } catch (const FiftyoneDegrees::Common::FatalException &) { - thrown = true; - } - EXPECT_FALSE(thrown); + FiftyoneDegrees::Common::Transform t(0); + bool thrown = false; + try { + auto result = t.fromJsonGHEV + ("{\"architecture\":\"x86\", \"bitness\":\"32\", \"new\":\"ignored\"}"); + EXPECT_EQ(result.size(), 2); + } catch (const FiftyoneDegrees::Common::FatalException &) { + thrown = true; + } + EXPECT_FALSE(thrown); } diff --git a/transform.c b/transform.c index a728be05..15eafc86 100644 --- a/transform.c +++ b/transform.c @@ -26,1266 +26,1276 @@ #define initStaticKey(x) {x, sizeof(x) - 1} #define NotExpectSymbol(json, ch, exit) \ - if (*json == ch) { \ - EXCEPTION_SET(CORRUPT_DATA); \ - exit; \ - } +if (*json == ch) { \ +EXCEPTION_SET(CORRUPT_DATA); \ +exit; \ +} #define ExpectSymbol(json, ch, exit) \ - if (*json != ch) { \ - EXCEPTION_SET(CORRUPT_DATA); \ - exit; \ - } +if (*json != ch) { \ +EXCEPTION_SET(CORRUPT_DATA); \ +exit; \ +} #define ExpectKeySymbol(json, ch) \ - if (*json != ch) { \ - json = skipToNextChar(json, '"'); \ - return KEY_UNDEFINED; \ - } +if (*json != ch) { \ +json = skipToNextChar(json, '"'); \ +return KEY_UNDEFINED; \ +} #define ValuePtr \ - (EXCEPTION_CHECK(INSUFFICIENT_MEMORY) ? NULL \ - : begin) +(EXCEPTION_CHECK(INSUFFICIENT_MEMORY) ? NULL \ +: begin) #define GET_SEXTET(str, i) \ - (str[i] == '=' ? 0 & i++ : base64CharToValue(str[i++], exception)) +(str[i] == '=' ? 0 & i++ : base64CharToValue(str[i++], exception)) typedef enum { - ARCHITECTURE, // sec-ch-ua-arch - BRANDS, // sec-ch-ua - BITNESS, // sec-ch-ua-bitness - FULLVERSIONLIST, // sec-ch-ua-full-version-list - MOBILE, // sec-ch-ua-mobile - MODEL, // sec-ch-ua-model - PLATFORM, // sec-ch-ua-platform - PLATFORMVERSION, // sec-ch-ua-platform-version - KEY_UNDEFINED, // + ARCHITECTURE, // sec-ch-ua-arch + BRANDS, // sec-ch-ua + BITNESS, // sec-ch-ua-bitness + FULLVERSIONLIST, // sec-ch-ua-full-version-list + MOBILE, // sec-ch-ua-mobile + MODEL, // sec-ch-ua-model + PLATFORM, // sec-ch-ua-platform + PLATFORMVERSION, // sec-ch-ua-platform-version + KEY_UNDEFINED, // } Key; typedef Key (*readKeyCallback)(const char**, Exception* const); typedef char* (*readValueCallback)(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception); + KeyValuePair* cache, Key key, + Exception* const exception); static struct { - const char* key; - size_t len; + const char* key; + size_t len; } key_map[] = { - initStaticKey("sec-ch-ua-arch"), - initStaticKey("sec-ch-ua"), - initStaticKey("sec-ch-ua-bitness"), - initStaticKey("sec-ch-ua-full-version-list"), - initStaticKey("sec-ch-ua-mobile"), - initStaticKey("sec-ch-ua-model"), - initStaticKey("sec-ch-ua-platform"), - initStaticKey("sec-ch-ua-platform-version"), + initStaticKey("sec-ch-ua-arch"), + initStaticKey("sec-ch-ua"), + initStaticKey("sec-ch-ua-bitness"), + initStaticKey("sec-ch-ua-full-version-list"), + initStaticKey("sec-ch-ua-mobile"), + initStaticKey("sec-ch-ua-model"), + initStaticKey("sec-ch-ua-platform"), + initStaticKey("sec-ch-ua-platform-version"), }; // ---- static inline char* safeWriteToBuffer(StringBuilder *builder, - char symbol, - Exception* const exception) { - StringBuilderAddChar(builder, symbol); - if (builder->full) { - EXCEPTION_SET(INSUFFICIENT_MEMORY); - } - return builder->current; + char symbol, + Exception* const exception) { + StringBuilderAddChar(builder, symbol); + if (builder->full) { + EXCEPTION_SET(INSUFFICIENT_MEMORY); + } + return builder->current; } static inline uint32_t base64CharToValue( - char c, Exception* const exception) { - static const uint32_t base64_lookup_table[256] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, - 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255}; - - if (base64_lookup_table[(uint8_t)c] == 255) { - EXCEPTION_SET(CORRUPT_DATA); - } - - return base64_lookup_table[(uint8_t)c]; + char c, Exception* const exception) { + static const uint32_t base64_lookup_table[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255}; + + if (base64_lookup_table[(uint8_t)c] == 255) { + EXCEPTION_SET(CORRUPT_DATA); + } + + return base64_lookup_table[(uint8_t)c]; } static size_t base64Decode(const char* base64_input, StringBuilder *builder, - Exception* const exception) { - size_t before = builder->added; - size_t input_length = strlen(base64_input); - if (input_length % 4 != 0) { - EXCEPTION_SET(CORRUPT_DATA); - return 0; // Invalid base64 input length - } - - for (size_t i = 0; i < input_length;) { - uint32_t sextet_a = GET_SEXTET(base64_input, i); - uint32_t sextet_b = GET_SEXTET(base64_input, i); - uint32_t sextet_c = GET_SEXTET(base64_input, i); - uint32_t sextet_d = GET_SEXTET(base64_input, i); - - if (EXCEPTION_CHECK(CORRUPT_DATA)) { - return 0; - } - - uint32_t triple = - (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - - safeWriteToBuffer(builder, (triple >> 16) & 0xFF, exception); - safeWriteToBuffer(builder, (triple >> 8) & 0xFF, exception); - safeWriteToBuffer(builder, triple & 0xFF, exception); - } - - safeWriteToBuffer(builder, '\0', exception); - size_t after = builder->added; - return after - before; + Exception* const exception) { + size_t before = builder->added; + size_t input_length = strlen(base64_input); + if (input_length % 4 != 0) { + EXCEPTION_SET(CORRUPT_DATA); + return 0; // Invalid base64 input length + } + + for (size_t i = 0; i < input_length;) { + uint32_t sextet_a = GET_SEXTET(base64_input, i); + uint32_t sextet_b = GET_SEXTET(base64_input, i); + uint32_t sextet_c = GET_SEXTET(base64_input, i); + uint32_t sextet_d = GET_SEXTET(base64_input, i); + + if (EXCEPTION_CHECK(CORRUPT_DATA)) { + return 0; + } + + uint32_t triple = + (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; + + safeWriteToBuffer(builder, (triple >> 16) & 0xFF, exception); + safeWriteToBuffer(builder, (triple >> 8) & 0xFF, exception); + safeWriteToBuffer(builder, triple & 0xFF, exception); + } + + safeWriteToBuffer(builder, '\0', exception); + size_t after = builder->added; + return after - before; } static inline const char* skipWhitespaces(const char* json) { - for (;; ++json) { - switch (*json) { - case ' ': - case '\t': - case '\n': - case '\v': - case '\r': - case '\f': - break; - - default: - return json; - } - } + for (;; ++json) { + switch (*json) { + case ' ': + case '\t': + case '\n': + case '\v': + case '\r': + case '\f': + break; + + default: + return json; + } + } } static inline const char* skipToNextChar(const char* json, - const char target) { - for (; *json != target; ++json) { - switch (*json) { - case '\0': { - return json; - } break; - - case '\\': { - if (json[1] == target || json[1] == '\\') { - ++json; - } - } break; - } - } - - return json; + const char target) { + for (; *json != target; ++json) { + switch (*json) { + case '\0': { + return json; + } break; + + case '\\': { + if (json[1] == target || json[1] == '\\') { + ++json; + } + } break; + } + } + + return json; } static const char* skipValue(const char* json) { - json = skipToNextChar(json, ':'); - if (*json == '\0') { - return json; - } - - json = skipWhitespaces(json + 1); - - switch (*json) { - case '\0': { - return json; - } break; - - case '{': { // skip nested object - ++json; - - for (int nesting_level = 1; nesting_level > 0; ++json) { - switch (*json) { - case '\0': { - return json; - } break; - - case '{': { - ++nesting_level; - } break; - - case '}': { - --nesting_level; - } break; - - case '"': { - json = skipToNextChar(json + 1, '"'); - if (*json == '\0') { - return json; - } - } break; - } - } - } break; - - case '[': { - ++json; - - for (int nesting_level = 1; nesting_level > 0; ++json) { - switch (*json) { - case '\0': { - return json; - } break; - - case '[': { - ++nesting_level; - } break; - - case ']': { - --nesting_level; - } break; - - case '"': { - json = skipToNextChar(json + 1, '"'); - if (*json == '\0') { - return json; - } - } break; - } - } - } break; - - case '"': { - json = skipToNextChar(json + 1, '"'); - } break; - - default: { - for (int flag = 1; flag;) { - switch (*json) { - case '\0': { - return json; - } break; - - case ',': - case '}': - case ']': { - flag = 0; - } break; - - default: { - ++json; - } break; - } - } - } break; - } - - if (*json == '\0') { - return json; - } - - return skipToNextChar(json + 1, '"'); + json = skipToNextChar(json, ':'); + if (*json == '\0') { + return json; + } + + json = skipWhitespaces(json + 1); + + switch (*json) { + case '\0': { + return json; + } break; + + case '{': { // skip nested object + ++json; + + for (int nesting_level = 1; nesting_level > 0; ++json) { + switch (*json) { + case '\0': { + return json; + } break; + + case '{': { + ++nesting_level; + } break; + + case '}': { + --nesting_level; + } break; + + case '"': { + json = skipToNextChar(json + 1, '"'); + if (*json == '\0') { + return json; + } + } break; + } + } + } break; + + case '[': { + ++json; + + for (int nesting_level = 1; nesting_level > 0; ++json) { + switch (*json) { + case '\0': { + return json; + } break; + + case '[': { + ++nesting_level; + } break; + + case ']': { + --nesting_level; + } break; + + case '"': { + json = skipToNextChar(json + 1, '"'); + if (*json == '\0') { + return json; + } + } break; + } + } + } break; + + case '"': { + json = skipToNextChar(json + 1, '"'); + } break; + + default: { + for (int flag = 1; flag;) { + switch (*json) { + case '\0': { + return json; + } break; + + case ',': + case '}': + case ']': { + flag = 0; + } break; + + default: { + ++json; + } break; + } + } + } break; + } + + if (*json == '\0') { + return json; + } + + return skipToNextChar(json + 1, '"'); } static inline void initKeys(StringBuilder *builder, - KeyValuePair* cache, - Exception* const exception) { - for (size_t k = 0; k < KEY_UNDEFINED; ++k) { - cache[k].key = builder->current; - cache[k].keyLength = key_map[k].len; - - for (size_t i = 0; i < key_map[k].len; ++i) { - safeWriteToBuffer(builder, key_map[k].key[i], exception); - } - } + KeyValuePair* cache, + Exception* const exception) { + for (size_t k = 0; k < KEY_UNDEFINED; ++k) { + cache[k].key = builder->current; + cache[k].keyLength = key_map[k].len; + + for (size_t i = 0; i < key_map[k].len; ++i) { + safeWriteToBuffer(builder, key_map[k].key[i], exception); + } + } } -static const char* initParsing(const char* json, - StringBuilder *builder, - KeyValuePair* cache, - Exception* const exception) { - - initKeys(builder, cache, exception); - - json = skipWhitespaces(json); - ExpectSymbol(json, '{', return json); - return skipToNextChar(json, '"'); +static const char* initParsing(const char* json, + StringBuilder *builder, + KeyValuePair* cache, + Exception* const exception) { + + initKeys(builder, cache, exception); + + json = skipWhitespaces(json); + ExpectSymbol(json, '{', return json); + return skipToNextChar(json, '"'); } static Key readGhevKey(const char** json, - Exception* const exception) { - enum ReadKeyState { - READ_KEY_INIT, - ARCH, - BRANDS_OR_BITNESS, - FULL_VERSION_LIST, - MOBILE_OR_MODEL, - PLATFORM_OR_VERSION, - READ_KEY_BRANDS, - READ_KEY_BITNESS, - READ_KEY_MOBILE, - READ_KEY_MODEL, - READ_KEY_PLATFORMVERSION, - }; - - ++*json; - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { - switch (state) { - case READ_KEY_INIT: { - switch (**json) { - case 'a': { - state = ARCH; - } break; - - case 'f': { - state = FULL_VERSION_LIST; - } break; - - case 'b': { - state = BRANDS_OR_BITNESS; - } break; - - case 'm': { - state = MOBILE_OR_MODEL; - } break; - - case 'p': { - state = PLATFORM_OR_VERSION; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - - case '"': { - return KEY_UNDEFINED; - } break; - } - } break; - - case ARCH: { - for (const char* i = "rchitecture"; *i != '\0'; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return ARCHITECTURE; - } break; - - case FULL_VERSION_LIST: { - for (const char* i = "ullVersionList"; *i != '\0'; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return FULLVERSIONLIST; - } break; - - case BRANDS_OR_BITNESS: { - switch (**json) { - case 'r': { - state = READ_KEY_BRANDS; - } break; - - case 'i': { - state = READ_KEY_BITNESS; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case MOBILE_OR_MODEL: { - ExpectKeySymbol(*json, 'o'); - - ++(*json); - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - switch (**json) { - case 'b': { - state = READ_KEY_MOBILE; - } break; - - case 'd': { - state = READ_KEY_MODEL; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case PLATFORM_OR_VERSION: { - for (const char* i = "latform"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - switch (**json) { - case '"': { - return PLATFORM; - } break; - - case 'V': { - state = READ_KEY_PLATFORMVERSION; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case READ_KEY_BRANDS: { - for (const char* i = "ands"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return BRANDS; - } break; - - case READ_KEY_BITNESS: { - for (const char* i = "tness"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return BITNESS; - } break; - - case READ_KEY_MOBILE: { - for (const char* i = "ile"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MOBILE; - } break; - - case READ_KEY_MODEL: { - for (const char* i = "el"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MODEL; - } break; - - case READ_KEY_PLATFORMVERSION: { - for (const char* i = "ersion"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return PLATFORMVERSION; - } break; - } - } - - return KEY_UNDEFINED; + Exception* const exception) { + enum ReadKeyState { + READ_KEY_INIT, + ARCH, + BRANDS_OR_BITNESS, + FULL_VERSION_LIST, + MOBILE_OR_MODEL, + PLATFORM_OR_VERSION, + READ_KEY_BRANDS, + READ_KEY_BITNESS, + READ_KEY_MOBILE, + READ_KEY_MODEL, + READ_KEY_PLATFORMVERSION, + }; + + ++*json; + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { + switch (state) { + case READ_KEY_INIT: { + switch (**json) { + case 'a': { + state = ARCH; + } break; + + case 'f': { + state = FULL_VERSION_LIST; + } break; + + case 'b': { + state = BRANDS_OR_BITNESS; + } break; + + case 'm': { + state = MOBILE_OR_MODEL; + } break; + + case 'p': { + state = PLATFORM_OR_VERSION; + } break; + + default: { + *json = skipToNextChar(*json, '"'); + return KEY_UNDEFINED; + } break; + + case '"': { + return KEY_UNDEFINED; + } break; + } + } break; + + case ARCH: { + for (const char* i = "rchitecture"; *i != '\0'; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return ARCHITECTURE; + } break; + + case FULL_VERSION_LIST: { + for (const char* i = "ullVersionList"; *i != '\0'; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return FULLVERSIONLIST; + } break; + + case BRANDS_OR_BITNESS: { + switch (**json) { + case 'r': { + state = READ_KEY_BRANDS; + } break; + + case 'i': { + state = READ_KEY_BITNESS; + } break; + + default: { + *json = skipToNextChar(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case MOBILE_OR_MODEL: { + ExpectKeySymbol(*json, 'o'); + + ++(*json); + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + switch (**json) { + case 'b': { + state = READ_KEY_MOBILE; + } break; + + case 'd': { + state = READ_KEY_MODEL; + } break; + + default: { + *json = skipToNextChar(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case PLATFORM_OR_VERSION: { + for (const char* i = "latform"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + switch (**json) { + case '"': { + return PLATFORM; + } break; + + case 'V': { + state = READ_KEY_PLATFORMVERSION; + } break; + + default: { + *json = skipToNextChar(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case READ_KEY_BRANDS: { + for (const char* i = "ands"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return BRANDS; + } break; + + case READ_KEY_BITNESS: { + for (const char* i = "tness"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return BITNESS; + } break; + + case READ_KEY_MOBILE: { + for (const char* i = "ile"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return MOBILE; + } break; + + case READ_KEY_MODEL: { + for (const char* i = "el"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return MODEL; + } break; + + case READ_KEY_PLATFORMVERSION: { + for (const char* i = "ersion"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return PLATFORMVERSION; + } break; + } + } + + return KEY_UNDEFINED; } static Key readSuaKey(const char** json, - Exception* const exception) { - enum ReadKeyState { - READ_KEY_INIT, - BROWSERS_OR_BITNESS, - MOBILE_OR_MODEL, - READ_KEY_BITNESS, - ARCH, - READ_KEY_MOBILE, - READ_KEY_MODEL, - READ_KEY_PLATFORM, - READ_KEY_BROWSERS, - }; - - ++*json; - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { - switch (state) { - case READ_KEY_INIT: { - switch (**json) { - case 'a': { - state = ARCH; - } break; - - case 'b': { - state = BROWSERS_OR_BITNESS; - } break; - - case 'm': { - state = MOBILE_OR_MODEL; - } break; - - case 'p': { - state = READ_KEY_PLATFORM; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - - case '"': { - return KEY_UNDEFINED; - } break; - } - } break; - - case ARCH: { - for (const char* i = "rchitecture"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return ARCHITECTURE; - } break; - - case BROWSERS_OR_BITNESS: { - switch (**json) { - case 'r': { - state = READ_KEY_BROWSERS; - } break; - - case 'i': { - state = READ_KEY_BITNESS; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case MOBILE_OR_MODEL: { - ExpectKeySymbol(*json, 'o'); - - ++(*json); - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - switch (**json) { - case 'b': { - state = READ_KEY_MOBILE; - } break; - - case 'd': { - state = READ_KEY_MODEL; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case READ_KEY_PLATFORM: { - for (const char* i = "latform"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - ExpectKeySymbol(*json, '"'); - - return PLATFORM; - } break; - - case READ_KEY_BROWSERS: { - for (const char* i = "owsers"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return FULLVERSIONLIST; - } break; - - case READ_KEY_BITNESS: { - for (const char* i = "tness"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return BITNESS; - } break; - - case READ_KEY_MOBILE: { - for (const char* i = "ile"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MOBILE; - } break; - - case READ_KEY_MODEL: { - for (const char* i = "el"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MODEL; - } break; - } - } - - return KEY_UNDEFINED; + Exception* const exception) { + enum ReadKeyState { + READ_KEY_INIT, + BROWSERS_OR_BITNESS, + MOBILE_OR_MODEL, + READ_KEY_BITNESS, + ARCH, + READ_KEY_MOBILE, + READ_KEY_MODEL, + READ_KEY_PLATFORM, + READ_KEY_BROWSERS, + }; + + ++*json; + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { + switch (state) { + case READ_KEY_INIT: { + switch (**json) { + case 'a': { + state = ARCH; + } break; + + case 'b': { + state = BROWSERS_OR_BITNESS; + } break; + + case 'm': { + state = MOBILE_OR_MODEL; + } break; + + case 'p': { + state = READ_KEY_PLATFORM; + } break; + + default: { + *json = skipToNextChar(*json, '"'); + return KEY_UNDEFINED; + } break; + + case '"': { + return KEY_UNDEFINED; + } break; + } + } break; + + case ARCH: { + for (const char* i = "rchitecture"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return ARCHITECTURE; + } break; + + case BROWSERS_OR_BITNESS: { + switch (**json) { + case 'r': { + state = READ_KEY_BROWSERS; + } break; + + case 'i': { + state = READ_KEY_BITNESS; + } break; + + default: { + *json = skipToNextChar(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case MOBILE_OR_MODEL: { + ExpectKeySymbol(*json, 'o'); + + ++(*json); + NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); + + switch (**json) { + case 'b': { + state = READ_KEY_MOBILE; + } break; + + case 'd': { + state = READ_KEY_MODEL; + } break; + + default: { + *json = skipToNextChar(*json, '"'); + return KEY_UNDEFINED; + } break; + } + } break; + + case READ_KEY_PLATFORM: { + for (const char* i = "latform"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + ExpectKeySymbol(*json, '"'); + + return PLATFORM; + } break; + + case READ_KEY_BROWSERS: { + for (const char* i = "owsers"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return FULLVERSIONLIST; + } break; + + case READ_KEY_BITNESS: { + for (const char* i = "tness"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return BITNESS; + } break; + + case READ_KEY_MOBILE: { + for (const char* i = "ile"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return MOBILE; + } break; + + case READ_KEY_MODEL: { + for (const char* i = "el"; *i; ++(*json), ++i) { + ExpectKeySymbol(*json, *i); + } + + return MODEL; + } break; + } + } + + return KEY_UNDEFINED; } static char* readStringValue(const char** json, StringBuilder *builder, - Exception* const exception) { - char *begin = builder->current; - *json = skipWhitespaces(*json); - if (**json == 'n') { - ++(*json); - NotExpectSymbol(*json, '\0', return begin); - - for (const char* i = "ull"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, return begin); - } - - return NULL; - } - - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (begin = safeWriteToBuffer(builder, '"', exception);; ++(*json)) { - NotExpectSymbol(*json, '\0', return begin); - - begin = safeWriteToBuffer(builder, **json, exception); - - switch (**json) { - case '\"': { - ++(*json); - return begin; - } - - case '\\': { - if ((*json)[1] == '\\' || (*json)[1] == '"') { - ++(*json); - - safeWriteToBuffer(builder, **json, exception); - } - } break; - } - } + Exception* const exception) { + char *begin = builder->current; + *json = skipWhitespaces(*json); + if (**json == 'n') { + ++(*json); + NotExpectSymbol(*json, '\0', return begin); + + for (const char* i = "ull"; *i; ++(*json), ++i) { + ExpectSymbol(*json, *i, return begin); + } + + return NULL; + } + + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (begin = safeWriteToBuffer(builder, '"', exception);; ++(*json)) { + NotExpectSymbol(*json, '\0', return begin); + + begin = safeWriteToBuffer(builder, **json, exception); + + switch (**json) { + case '\"': { + ++(*json); + return begin; + } + + case '\\': { + if ((*json)[1] == '\\' || (*json)[1] == '"') { + ++(*json); + + safeWriteToBuffer(builder, **json, exception); + } + } break; + } + } } static char* readBoolGhevValue(const char** json, - StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - char *ptr = begin; - size_t before = builder->added; - switch (**json) { - case 't': { - ++(*json); - for (const char* i = "rue"; *i != '\0'; ++(*json), ++i) { - ExpectSymbol(*json, *i, return begin); - } - - ptr = safeWriteToBuffer(builder, '?', exception); - ptr = safeWriteToBuffer(builder, '1', exception); - } break; - - case 'f': { - ++(*json); - for (const char* i = "alse"; *i != '\0'; ++(*json), ++i) { - ExpectSymbol(*json, *i, return begin); - } - - ptr = safeWriteToBuffer(builder, '?', exception); - ptr = safeWriteToBuffer(builder, '0', exception); - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - - size_t after = builder->added; - cache[key].value = ValuePtr; - cache[key].valueLength = after - before; - return ptr; + StringBuilder *builder, + KeyValuePair* cache, Key key, + Exception* const exception) { + char *begin = builder->current; + char *ptr = begin; + size_t before = builder->added; + switch (**json) { + case 't': { + ++(*json); + for (const char* i = "rue"; *i != '\0'; ++(*json), ++i) { + ExpectSymbol(*json, *i, return begin); + } + + ptr = safeWriteToBuffer(builder, '?', exception); + ptr = safeWriteToBuffer(builder, '1', exception); + } break; + + case 'f': { + ++(*json); + for (const char* i = "alse"; *i != '\0'; ++(*json), ++i) { + ExpectSymbol(*json, *i, return begin); + } + + ptr = safeWriteToBuffer(builder, '?', exception); + ptr = safeWriteToBuffer(builder, '0', exception); + } break; + + default: { + EXCEPTION_SET(CORRUPT_DATA); + return begin; + } break; + } + + size_t after = builder->added; + cache[key].value = ValuePtr; + cache[key].valueLength = after - before; + return ptr; } static char* readBoolSuaValue(const char** json, - StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - size_t before = builder->added; - switch (**json) { - case '0': - case '1': { - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - - char* ptr = safeWriteToBuffer(builder, '?', exception); - ptr = safeWriteToBuffer(builder, **json, exception); - - ++(*json); - - size_t after = builder->added; - cache[key].value = ValuePtr; - cache[key].valueLength = after - before; - - return ptr; + StringBuilder *builder, + KeyValuePair* cache, Key key, + Exception* const exception) { + char *begin = builder->current; + size_t before = builder->added; + switch (**json) { + case '0': + case '1': { + } break; + + default: { + EXCEPTION_SET(CORRUPT_DATA); + return begin; + } break; + } + + char* ptr = safeWriteToBuffer(builder, '?', exception); + ptr = safeWriteToBuffer(builder, **json, exception); + + ++(*json); + + size_t after = builder->added; + cache[key].value = ValuePtr; + cache[key].valueLength = after - before; + + return ptr; } static char* readVersionSua(const char** json, - StringBuilder *builder, - Exception* const exception) { - enum version_state { - version_read, - version_skip, - version_exit, - } state = version_skip; - - char *begin = builder->current; - for (char* ptr = begin;; ++(*json)) { - NotExpectSymbol(*json, '\0', return begin); //rollback - - switch (state) { - case version_read: { - switch (**json) { - case '\"': { - state = version_skip; - } break; - - case '\\': { - ptr = safeWriteToBuffer(builder, **json, exception); - - if ((*json)[1] == '\\' || (*json)[1] == '"') { - ++(*json); - ptr = safeWriteToBuffer(builder, **json, exception); - } - } break; - - default: { - ptr = safeWriteToBuffer(builder, **json, exception); - } break; - } - } break; - - case version_skip: { - switch (**json) { - case '"': { - state = version_read; - } break; - - case ',': { - ptr = safeWriteToBuffer(builder, '.', exception); - } break; - - case ']': { - state = version_exit; - } break; - } - } break; - - case version_exit: { - ptr = safeWriteToBuffer(builder, '"', exception); - return ptr; - } break; - } - } + StringBuilder *builder, + Exception* const exception) { + enum version_state { + version_read, + version_skip, + version_exit, + } state = version_skip; + + char *begin = builder->current; + for (char* ptr = begin;; ++(*json)) { + NotExpectSymbol(*json, '\0', return begin); //rollback + + switch (state) { + case version_read: { + switch (**json) { + case '\"': { + state = version_skip; + } break; + + case '\\': { + ptr = safeWriteToBuffer(builder, **json, exception); + + if ((*json)[1] == '\\' || (*json)[1] == '"') { + ++(*json); + ptr = safeWriteToBuffer(builder, **json, exception); + } + } break; + + default: { + ptr = safeWriteToBuffer(builder, **json, exception); + } break; + } + } break; + + case version_skip: { + switch (**json) { + case '"': { + state = version_read; + } break; + + case ',': { + ptr = safeWriteToBuffer(builder, '.', exception); + } break; + + case ']': { + state = version_exit; + } break; + } + } break; + + case version_exit: { + ptr = safeWriteToBuffer(builder, '"', exception); + return ptr; + } break; + } + } } static char* readBrandsGhevValue(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - *json = skipToNextChar(*json, '['); - ExpectSymbol(*json, '[', return begin); - - ++*json; - - for (char* ptr = begin;; ++*json) { - *json = skipToNextChar(*json, '{'); - ExpectSymbol(*json, '{', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - ++*json; - - char* ptr2 = readStringValue(json, builder, exception); - if (ptr2 != NULL) { - ptr = safeWriteToBuffer(builder, ';', exception); - ptr = safeWriteToBuffer(builder, 'v', exception); - ptr = safeWriteToBuffer(builder, '=', exception); - - *json = skipToNextChar(*json, ','); - ExpectSymbol(*json, ',', return begin); //rollback - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); //rollback - - ++*json; - - for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); //rollback - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); //rollback - - ++*json; - - ptr2 = readStringValue(json, builder, exception); - if (ptr2 == NULL) { - ptr2 = ptr; - - ptr = safeWriteToBuffer(builder, 'n', exception); - ptr = safeWriteToBuffer(builder, 'u', exception); - ptr = safeWriteToBuffer(builder, 'l', exception); - ptr = safeWriteToBuffer(builder, 'l', exception); - } else { - ptr = ptr2; - } - } - - *json = skipToNextChar(*json, '}'); - ExpectSymbol(*json, '}', return begin); - - *json = skipWhitespaces(*json + 1); - NotExpectSymbol(*json, '\0', return begin); - - switch (**json) { - case ']': { - if (ptr != begin) { - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - return ptr; - } else { - return NULL; - } - - } break; - - case ',': { - if (ptr2 != NULL) { - ptr = safeWriteToBuffer(builder, ',', exception); - ptr = safeWriteToBuffer(builder, ' ', exception); - } - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - } + KeyValuePair* cache, Key key, + Exception* const exception) { + char *begin = builder->current; + *json = skipToNextChar(*json, '['); + ExpectSymbol(*json, '[', return begin); + + ++*json; + + for (char* ptr = begin;; ++*json) { + *json = skipToNextChar(*json, '{'); + ExpectSymbol(*json, '{', return begin); + + *json = skipToNextChar(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skipToNextChar(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + ++*json; + + char* ptr2 = readStringValue(json, builder, exception); + if (ptr2 != NULL) { + ptr = safeWriteToBuffer(builder, ';', exception); + ptr = safeWriteToBuffer(builder, 'v', exception); + ptr = safeWriteToBuffer(builder, '=', exception); + + *json = skipToNextChar(*json, ','); + ExpectSymbol(*json, ',', return begin); //rollback + + *json = skipToNextChar(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); //rollback + + ++*json; + + for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); //rollback + } + + *json = skipToNextChar(*json, ':'); + ExpectSymbol(*json, ':', return begin); //rollback + + ++*json; + + ptr2 = readStringValue(json, builder, exception); + if (ptr2 == NULL) { + ptr2 = ptr; + + ptr = safeWriteToBuffer(builder, 'n', exception); + ptr = safeWriteToBuffer(builder, 'u', exception); + ptr = safeWriteToBuffer(builder, 'l', exception); + ptr = safeWriteToBuffer(builder, 'l', exception); + } else { + ptr = ptr2; + } + } + + *json = skipToNextChar(*json, '}'); + ExpectSymbol(*json, '}', return begin); + + *json = skipWhitespaces(*json + 1); + NotExpectSymbol(*json, '\0', return begin); + + switch (**json) { + case ']': { + if (ptr != begin) { + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + return ptr; + } else { + return NULL; + } + + } break; + + case ',': { + if (ptr2 != NULL) { + ptr = safeWriteToBuffer(builder, ',', exception); + ptr = safeWriteToBuffer(builder, ' ', exception); + } + } break; + + default: { + EXCEPTION_SET(CORRUPT_DATA); + return begin; + } break; + } + } } static char* readBrandsSuaValue(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - *json = skipToNextChar(*json, '['); - char *begin = builder->current; - ExpectSymbol(*json, '[', return begin); - - for (char* ptr = begin;; ++*json) { - NotExpectSymbol(*json, '\0', return begin); - - *json = skipToNextChar(*json, '{'); - ExpectSymbol(*json, '{', return begin); - - *json = skipToNextChar(*json, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - ++*json; - - char* ptr2 = readStringValue(json, builder, exception); - if (ptr2 != NULL) { - ptr = safeWriteToBuffer(builder, ';', exception); - ptr = safeWriteToBuffer(builder, 'v', exception); - ptr = safeWriteToBuffer(builder, '=', exception); - - *json = skipToNextChar(*json, ','); - ExpectSymbol(*json, ',', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - *json = skipToNextChar(*json, '['); - ExpectSymbol(*json, '[', return begin); - - ++*json; - - ptr = safeWriteToBuffer(builder, '"', exception); - ptr = readVersionSua(json, builder, exception); - } - - *json = skipToNextChar(*json, '}'); - ExpectSymbol(*json, '}', return begin); - - *json = skipWhitespaces(*json + 1); - NotExpectSymbol(*json, '\0', return begin); - - switch (**json) { - case ']': { - if (ptr != begin) { - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - return ptr; - } else { - return NULL; - } - } break; - - case ',': { - if (ptr != begin) { - ptr = safeWriteToBuffer(builder, ',', exception); - ptr = safeWriteToBuffer(builder, ' ', exception); - } - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - } + KeyValuePair* cache, Key key, + Exception* const exception) { + *json = skipToNextChar(*json, '['); + char *begin = builder->current; + ExpectSymbol(*json, '[', return begin); + + for (char* ptr = begin;; ++*json) { + NotExpectSymbol(*json, '\0', return begin); + + *json = skipToNextChar(*json, '{'); + ExpectSymbol(*json, '{', return begin); + + *json = skipToNextChar(*json, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skipToNextChar(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + ++*json; + + char* ptr2 = readStringValue(json, builder, exception); + if (ptr2 != NULL) { + ptr = safeWriteToBuffer(builder, ';', exception); + ptr = safeWriteToBuffer(builder, 'v', exception); + ptr = safeWriteToBuffer(builder, '=', exception); + + *json = skipToNextChar(*json, ','); + ExpectSymbol(*json, ',', return begin); + + *json = skipToNextChar(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skipToNextChar(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + *json = skipToNextChar(*json, '['); + ExpectSymbol(*json, '[', return begin); + + ++*json; + + ptr = safeWriteToBuffer(builder, '"', exception); + ptr = readVersionSua(json, builder, exception); + } + + *json = skipToNextChar(*json, '}'); + ExpectSymbol(*json, '}', return begin); + + *json = skipWhitespaces(*json + 1); + NotExpectSymbol(*json, '\0', return begin); + + switch (**json) { + case ']': { + if (ptr != begin) { + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + return ptr; + } else { + return NULL; + } + } break; + + case ',': { + if (ptr != begin) { + ptr = safeWriteToBuffer(builder, ',', exception); + ptr = safeWriteToBuffer(builder, ' ', exception); + } + } break; + + default: { + EXCEPTION_SET(CORRUPT_DATA); + return begin; + } break; + } + } } static char* readPureStringValue(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - char* ptr = readStringValue(json, builder, exception); - - if (ptr != NULL) { - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - } - - return ptr; + KeyValuePair* cache, Key key, + Exception* const exception) { + char *begin = builder->current; + char* ptr = readStringValue(json, builder, exception); + + if (ptr != NULL) { + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + } + + return ptr; } static char* readPlatformSuaValue( - const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - *json = skipToNextChar(*json, '{'); - ExpectSymbol(*json, '{', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - ++*json; - - char* ptr = readStringValue(json, builder, exception); - if (ptr == NULL) { - return NULL; - } - - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - - cache[PLATFORMVERSION].value = NULL; - cache[PLATFORMVERSION].valueLength = 0; - - begin = ptr; - - *json = skipWhitespaces(*json); - - if (**json == '}') { - return begin; - } - - ExpectSymbol(*json, ',', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - *json = skipToNextChar(*json + 1, '['); - ExpectSymbol(*json, '[', return begin); - - ++*json; - NotExpectSymbol(*json, '\0', return begin); - - ptr = safeWriteToBuffer(builder, '"', exception); - ptr = readVersionSua(json, builder, exception); - - cache[PLATFORMVERSION].value = ValuePtr; - cache[PLATFORMVERSION].valueLength = ptr - begin; - - return ptr; + const char** json, StringBuilder *builder, + KeyValuePair* cache, Key key, + Exception* const exception) { + char *begin = builder->current; + *json = skipToNextChar(*json, '{'); + ExpectSymbol(*json, '{', return begin); + + *json = skipToNextChar(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skipToNextChar(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + ++*json; + + char* ptr = readStringValue(json, builder, exception); + if (ptr == NULL) { + return NULL; + } + + cache[key].value = ValuePtr; + cache[key].valueLength = ptr - begin; + + cache[PLATFORMVERSION].value = NULL; + cache[PLATFORMVERSION].valueLength = 0; + + begin = ptr; + + *json = skipWhitespaces(*json); + + if (**json == '}') { + return begin; + } + + ExpectSymbol(*json, ',', return begin); + + *json = skipToNextChar(*json + 1, '"'); + ExpectSymbol(*json, '"', return begin); + + ++*json; + + for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { + ExpectSymbol(*json, *k, return begin); + } + + *json = skipToNextChar(*json, ':'); + ExpectSymbol(*json, ':', return begin); + + *json = skipToNextChar(*json + 1, '['); + ExpectSymbol(*json, '[', return begin); + + ++*json; + NotExpectSymbol(*json, '\0', return begin); + + ptr = safeWriteToBuffer(builder, '"', exception); + ptr = readVersionSua(json, builder, exception); + + cache[PLATFORMVERSION].value = ValuePtr; + cache[PLATFORMVERSION].valueLength = ptr - begin; + + return ptr; } static inline readValueCallback readValueSwitch(Key key, int isSua) { - readValueCallback res = NULL; - - switch (key) { - case ARCHITECTURE: { - res = readPureStringValue; - } break; - - case BITNESS: { - res = readPureStringValue; - } break; - - case BRANDS: { - res = isSua ? NULL : readBrandsGhevValue; - } break; - - case FULLVERSIONLIST: { - res = isSua ? readBrandsSuaValue : readBrandsGhevValue; - } break; - - case MOBILE: { - res = isSua ? readBoolSuaValue : readBoolGhevValue; - } break; - - case MODEL: { - res = readPureStringValue; - } break; - - case PLATFORM: { - res = isSua ? readPlatformSuaValue : readPureStringValue; - } break; - - case PLATFORMVERSION: { - res = isSua ? NULL : readPureStringValue; - } break; - - case KEY_UNDEFINED: { - res = NULL; - } break; - } - - return res; + readValueCallback res = NULL; + + switch (key) { + case ARCHITECTURE: { + res = readPureStringValue; + } break; + + case BITNESS: { + res = readPureStringValue; + } break; + + case BRANDS: { + res = isSua ? NULL : readBrandsGhevValue; + } break; + + case FULLVERSIONLIST: { + res = isSua ? readBrandsSuaValue : readBrandsGhevValue; + } break; + + case MOBILE: { + res = isSua ? readBoolSuaValue : readBoolGhevValue; + } break; + + case MODEL: { + res = readPureStringValue; + } break; + + case PLATFORM: { + res = isSua ? readPlatformSuaValue : readPureStringValue; + } break; + + case PLATFORMVERSION: { + res = isSua ? NULL : readPureStringValue; + } break; + + case KEY_UNDEFINED: { + res = NULL; + } break; + } + + return res; } static bool pushToHeaders(void* ctx, KeyValuePair header) { - KeyValuePairArray* const headers = (KeyValuePairArray* const)ctx; - - if (headers->count < headers->capacity) { - KeyValuePair* pair = headers->items + headers->count++; - - pair->key = header.key; - pair->keyLength = header.keyLength; - pair->value = header.value; - pair->valueLength = header.valueLength; - } - return (headers->count < headers->capacity); + KeyValuePairArray* const headers = (KeyValuePairArray* const)ctx; + + if (headers->count < headers->capacity) { + KeyValuePair* pair = headers->items + headers->count++; + + pair->key = header.key; + pair->keyLength = header.keyLength; + pair->value = header.value; + pair->valueLength = header.valueLength; + } + return (headers->count < headers->capacity); } -// ------------------------------------------------------------------------------------------------ +// ---------------------------------------------------------------------------- static uint32_t mainParsingBody(const char* json, - StringBuilder *builder, - Exception* const exception, - int isSua, - TransformCallback callback, - void* ctx) { - KeyValuePair cache[KEY_UNDEFINED]; - - char *begin = builder->current; - // define buffer range - - // write keys to buffer, init cache and skip to the first key - json = initParsing(json, builder, cache, exception); - if (EXCEPTION_CHECK(CORRUPT_DATA)) { - return 0; - } - + StringBuilder *builder, + Exception* const exception, + int isSua, + TransformCallback callback, + void* ctx) { + KeyValuePair cache[KEY_UNDEFINED]; + + char *begin = builder->current; + // define buffer range + + // write keys to buffer, init cache and skip to the first key + json = initParsing(json, builder, cache, exception); + if (EXCEPTION_CHECK(CORRUPT_DATA)) { + return 0; + } + uint32_t iterations = 0; // total number of parsed key-value pairs - - // main reading loop - readKeyCallback read_key = isSua ? readSuaKey : readGhevKey; - while (*json != '\0') { - Key key = read_key(&json, exception); - ExpectSymbol(json, '"', break); - - readValueCallback read_value = readValueSwitch(key, isSua); - if (key == KEY_UNDEFINED || read_value == NULL) { - json = skipValue(json + 1); - continue; - } - - json = skipToNextChar(json, ':'); - ExpectSymbol(json, ':', break); - - json = skipWhitespaces(json + 1); - NotExpectSymbol(json, '\0', break); - - char* ptr = read_value(&json, builder, cache, key, exception); - if (EXCEPTION_CHECK(CORRUPT_DATA)) { - break; - } - - if (ptr != NULL) { - begin = ptr; - - ++iterations; - if (!callback(ctx, cache[key])) { - break; - } - - if (key == PLATFORM && isSua && cache[PLATFORMVERSION].valueLength != 0) { - ++iterations; - if (!callback(ctx, cache[PLATFORMVERSION])) { + + // main reading loop + readKeyCallback read_key = isSua ? readSuaKey : readGhevKey; + while (*json != '\0') { + Key key = read_key(&json, exception); + ExpectSymbol(json, '"', break); + + readValueCallback read_value = readValueSwitch(key, isSua); + if (key == KEY_UNDEFINED || read_value == NULL) { + json = skipValue(json + 1); + continue; + } + + json = skipToNextChar(json, ':'); + ExpectSymbol(json, ':', break); + + json = skipWhitespaces(json + 1); + NotExpectSymbol(json, '\0', break); + + char* ptr = read_value(&json, builder, cache, key, exception); + if (EXCEPTION_CHECK(CORRUPT_DATA)) { break; - } - } - } - - json = skipToNextChar(json, '"'); - } - - return iterations; + } + + if (ptr != NULL) { + begin = ptr; + + ++iterations; + if (!callback(ctx, cache[key])) { + break; + } + + if (key == PLATFORM && isSua && cache[PLATFORMVERSION].valueLength != 0) { + ++iterations; + if (!callback(ctx, cache[PLATFORMVERSION])) { + break; + } + } + } + + json = skipToNextChar(json, '"'); + } + + return iterations; } // The difference of this function is that it does not initialize the builder - and assumes // it has been initialized outside - useful for base64 and then JSON from the same buffer -uint32_t TransformIterateGhevFromJsonPrivate(const char* json, StringBuilder *builder, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { - return mainParsingBody(json, builder, exception, 0, callback, ctx); +uint32_t +TransformIterateGhevFromJsonPrivate + (const char* json, StringBuilder *builder, + fiftyoneDegreesTransformCallback callback, + void* ctx, + fiftyoneDegreesException* const exception) { + return mainParsingBody(json, builder, exception, 0, callback, ctx); } // ------------------------------------------------------------------------------------------------ -fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromJson( - const char* json, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { +fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformIterateGhevFromJson + (const char* json, char *buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { StringBuilder builder = {buffer, length}; - StringBuilderInit(&builder); + StringBuilderInit(&builder); uint32_t iterations = TransformIterateGhevFromJsonPrivate(json, &builder, callback, ctx, exception); - StringBuilderComplete(&builder); + StringBuilderComplete(&builder); fiftyoneDegreesTransformIterateResult result = {iterations, builder.added, builder.added > builder.length}; return result; } -fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromBase64( - const char* base64, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { +fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformIterateGhevFromBase64 + (const char* base64, char *buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { StringBuilder builder = {buffer, length}; - StringBuilderInit(&builder); - base64Decode(base64, &builder, exception); - fiftyoneDegreesTransformIterateResult result = {0, builder.added, builder.added > builder.length}; - if (EXCEPTION_CHECK(CORRUPT_DATA) || EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { - return result; - } - char *json = builder.ptr; - //note we are calling a private function to reuse the initialized stringbuilder + StringBuilderInit(&builder); + base64Decode(base64, &builder, exception); + fiftyoneDegreesTransformIterateResult result = {0, builder.added, + builder.added > builder.length}; + if (EXCEPTION_CHECK(CORRUPT_DATA) || EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { + return result; + } + char *json = builder.ptr; + //note we are calling a private function to reuse the initialized stringbuilder uint32_t iterations = TransformIterateGhevFromJsonPrivate(json, &builder, callback, ctx, exception); - StringBuilderComplete(&builder); + StringBuilderComplete(&builder); result.iterations = iterations; result.written = builder.added; result.bufferTooSmall = builder.added > builder.length; return result; } -fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateSua( - const char* json, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { +fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformIterateSua + (const char* json, char *buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void* ctx, + fiftyoneDegreesException* const exception) { StringBuilder builder = {buffer, length}; - StringBuilderInit(&builder); + StringBuilderInit(&builder); uint32_t iterations = mainParsingBody(json, &builder, exception, 1, callback, ctx); - StringBuilderComplete(&builder); + StringBuilderComplete(&builder); fiftyoneDegreesTransformIterateResult result = {iterations, builder.added, builder.added > builder.length}; - return result; + return result; } // Array methods internally relay on iterative methods -fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromJson( - const char* json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception) { - uint32_t initial = headers->count; - fiftyoneDegreesTransformIterateResult result = TransformIterateGhevFromJson( - json, buffer, length, pushToHeaders, headers, exception); - - if (result.iterations != headers->count - initial) { - EXCEPTION_SET(INSUFFICIENT_CAPACITY); - } - return result; +fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformGhevFromJson + (const char* json, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception) { + uint32_t initial = headers->count; + fiftyoneDegreesTransformIterateResult result = + TransformIterateGhevFromJson(json, buffer, length, pushToHeaders, + headers, exception); + + if (result.iterations != headers->count - initial) { + EXCEPTION_SET(INSUFFICIENT_CAPACITY); + } + return result; } -fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromBase64( - const char* base64, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception) { - uint32_t initial = headers->count; - fiftyoneDegreesTransformIterateResult result = TransformIterateGhevFromBase64( - base64, buffer, length, pushToHeaders, headers, exception); - - if (result.iterations != headers->count - initial) { - EXCEPTION_SET(INSUFFICIENT_CAPACITY); - } - - return result; +fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromBase64 + (const char* base64, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception) { + uint32_t initial = headers->count; + fiftyoneDegreesTransformIterateResult result = + TransformIterateGhevFromBase64(base64, buffer, length, pushToHeaders, headers, exception); + + if (result.iterations != headers->count - initial) { + EXCEPTION_SET(INSUFFICIENT_CAPACITY); + } + + return result; } -fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformSua( - const char* json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception) { - uint32_t initial = headers->count; - fiftyoneDegreesTransformIterateResult result = TransformIterateSua( - json, buffer, length, pushToHeaders, headers, exception); - - if (result.iterations != headers->count - initial) { - EXCEPTION_SET(INSUFFICIENT_CAPACITY); - } - - return result; +fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformSua + (const char* json, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray* const headers, + fiftyoneDegreesException* const exception) { + uint32_t initial = headers->count; + fiftyoneDegreesTransformIterateResult result = + TransformIterateSua(json, buffer, length, pushToHeaders, headers, exception); + + if (result.iterations != headers->count - initial) { + EXCEPTION_SET(INSUFFICIENT_CAPACITY); + } + + return result; } diff --git a/transform.h b/transform.h index 04481da6..1b2c8bf4 100644 --- a/transform.h +++ b/transform.h @@ -77,9 +77,22 @@ * and whether the buffer was of sufficient size */ typedef struct { - uint32_t iterations; // number of pairs of evidence extracted or would have been extracted and correspondingly calls to the callback made - size_t written; // number of characters written or that would have been written to the buffer, reflects required buffer size - bool bufferTooSmall; // the caller should check this flag and reallocate the buffer to be of at least `written` size + /** + * number of pairs of evidence extracted or would have been extracted and correspondingly calls + * to the callback made + */ + uint32_t iterations; + + /** + * number of characters written or that would have been written to the buffer, reflects required buffer size + */ + size_t written; + + /** + * the caller should check this flag and reallocate the buffer to be of at least `written` size + * if this flag is set + */ + bool bufferTooSmall; // } fiftyoneDegreesTransformIterateResult; /** @@ -94,8 +107,8 @@ typedef struct { * @return the implementer returns true to continue the iteration or false to * stop */ -EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( - void *state, fiftyoneDegreesKeyValuePair header); +EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback) +(void *state, fiftyoneDegreesKeyValuePair header); /** * Iteratively convert getHighEntropyValue() API result JSON string to HTTP @@ -107,15 +120,15 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` * if the conversion was successful, or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of * insufficient size, in that case the callback will still be called, but value * will be NULL and valueLength will indicate the length necessary to store the * value in the buffer - this info can be then used by the caller to allocate * the buffer of sufficient size and execute another call - essentially * resulting in a dry run before allocation. - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that * case callback will likely not be called, or will be called a limited number * of times until the corruption becomes obvious to the iterator as no lookahead * logic is implemented @@ -127,10 +140,11 @@ EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback)( * @return the number of iterations / header pairs detected (callback calls * made) and buffer utilization information */ -EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromJson( - const char *json, char *const buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformIterateGhevFromJson +(const char *json, char *const buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception); /** * Iteratively convert getHighEntropyValue() API result base64 encoded JSON @@ -143,15 +157,15 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGh * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` * if the conversion was successful, or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of * insufficient size, in that case the callback will still be called, but value * will be NULL and valueLength will indicate the length necessary to store the * value in the buffer - this info can be then used by the caller to allocate * the buffer of sufficient size and execute another call - essentially * resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that * case callback will likely not be called, or will be called a limited number * of times until the corruption becomes obvious to the iterator as no lookahead * logic is intended @@ -163,10 +177,11 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGh * @return the number of iterations / header pairs detected (callback calls * made) and buffer utilization information */ -EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGhevFromBase64( - const char *base64, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformIterateGhevFromBase64 +(const char *base64, char *buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception); /** * Iteratively convert device.sua JSON string to HTTP header representation. @@ -177,15 +192,15 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGh * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` * if the conversion was successful, or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of * insufficient size, in that case the callback will still be called, but value * will be NULL and valueLength will indicate the length necessary to store the * value in the buffer - this info can be then used by the caller to allocate * the buffer of sufficient size and execute another call - essentially * resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that * case callback will likely not be called, or will be called a limited number * of times until the corruption becomes obvious to the iterator as no lookahead * logic is intended @@ -197,10 +212,11 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateGh * @return the number of iterations / header pairs detected (callback calls * made) and buffer utilization information */ -EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateSua( - const char *json, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformIterateSua +(const char *json, char *buffer, size_t length, + fiftyoneDegreesTransformCallback callback, void *state, + fiftyoneDegreesException *const exception); /** * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header @@ -212,19 +228,19 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateSu * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` * if the conversion was successful, or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of * insufficient size, in that case the callback will still be called, but value * will be NULL and valueLength will indicate the length necessary to store the * value in the buffer - this info can be then used by the caller to allocate * the buffer of sufficient size and execute another call - essentially * resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that * case callback will likely not be called, or will be called a limited number * of times until the corruption becomes obvious to the iterator as no lookahead * logic is intended - * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array + * @param headers a preallocated (via `FIFTYONE_DEGREES_ARRAY_CREATE` macro) array * of capacity enough to hold up to 8 UACH headers; upon function return will * contain the output http header names and value const char * pointers either * to the DATA segment allocated (names) string constants or preallocated buffer @@ -233,14 +249,15 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformIterateSu * returned headers * @return result.iterations specifies the number of headers that was written or * should have been written to the array. in case this number is higher than headers->capacity - * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY + * case the exception will have `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY` * status and the returned capacity will signal the array size that needs to be * allocated. result.written and result.bufferTooSmall provide buffer utilization information */ -EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromJson( - const char *json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformGhevFromJson +(const char *json, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray *const headers, + fiftyoneDegreesException *const exception); /** * Eagerly convert getHighEntropyValue() API result from base64-encoded JSON @@ -253,19 +270,19 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromJ * @param length length of the buffer * @param exception - a constant pointer to a (preallocated) exception object * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS + * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` * if the conversion was successful, or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of * insufficient size, in that case the callback will still be called, but value * will be NULL and valueLength will indicate the length necessary to store the * value in the buffer - this info can be then used by the caller to allocate * the buffer of sufficient size and execute another call - essentially * resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that * case callback will likely not be called, or will be called a limited number * of times until the corruption becomes obvious to the iterator as no lookahead * logic is intended - * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array + * @param headers a preallocated (via `FIFTYONE_DEGREES_ARRAY_CREATE` macro) array * of capacity enough to hold up to 8 UACH headers; upon function return will * contain the output http header names and value const char * pointers either * to the DATA segment allocated (names) string constants or preallocated buffer @@ -274,14 +291,15 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromJ * returned headers * @return result.iterations specifies the number of headers that was written or * should have been written to the array. in case this number is higher than headers->capacity - * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY + * case the exception will have `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY` * status and the returned capacity will signal the array size that needs to be * allocated. result.written and result.bufferTooSmall provide buffer utilization information */ -EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromBase64( - const char *base64, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformGhevFromBase64 +(const char *base64, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray *const headers, + fiftyoneDegreesException *const exception); /** * Eagerly convert device.sua JSON string to HTTP header representation. @@ -294,17 +312,17 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromB * that is filled in case any errors occurred. must be checked by the caller * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS * if the conversion was successful, or will indicate error otherwise, s.a. - * - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY if provided buffer was of + * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of * insufficient size, in that case the callback will still be called, but value * will be NULL and valueLength will indicate the length necessary to store the * value in the buffer - this info can be then used by the caller to allocate * the buffer of sufficient size and execute another call - essentially * resulting in a dry run before allocation... - * - FIFTYONE_DEGREES_STATUS_CORRUPT_DATA if f.e. JSON was malformed - in that + * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that * case callback will likely not be called, or will be called a limited number * of times until the corruption becomes obvious to the iterator as no lookahead * logic is intended - * @param headers a preallocated (via FIFTYONE_DEGREES_ARRAY_CREATE macro) array + * @param headers a preallocated (via `FIFTYONE_DEGREES_ARRAY_CREATE` macro) array * of capacity enough to hold up to 8 UACH headers; upon function return will * contain the output http header names and value const char * pointers either * to the DATA segment allocated (names) string constants or preallocated buffer @@ -313,14 +331,14 @@ EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromB * returned headers * @return result.iterations specifies the number of headers that was written or * should have been written to the array. in case this number is higher than headers->capacity - * case the exception will have FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY + * case the exception will have `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY` * status and the returned capacity will signal the array size that needs to be * allocated. result.written and result.bufferTooSmall provide buffer utilization information */ -EXTERNAL fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformSua( - const char *json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception); +EXTERNAL fiftyoneDegreesTransformIterateResult +fiftyoneDegreesTransformSua +(const char *json, char *buffer, size_t length, + fiftyoneDegreesKeyValuePairArray *const headers, + fiftyoneDegreesException *const exception); #endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ - \ No newline at end of file From 4b09a5cf2ffb403ad6dbb2379b36dfdde8e6bc1f Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Wed, 11 Sep 2024 11:05:05 +0200 Subject: [PATCH 67/73] move transform** to device-detection-cxx --- Transform.cpp | 102 --- Transform.hpp | 106 --- Transform.i | 34 - fiftyone.h | 8 - tests/TransformTests.cpp | 1792 -------------------------------------- transform.c | 1301 --------------------------- transform.h | 344 -------- 7 files changed, 3687 deletions(-) delete mode 100644 Transform.cpp delete mode 100644 Transform.hpp delete mode 100644 Transform.i delete mode 100644 tests/TransformTests.cpp delete mode 100644 transform.c delete mode 100644 transform.h diff --git a/Transform.cpp b/Transform.cpp deleted file mode 100644 index e7e32283..00000000 --- a/Transform.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -//Exceptions.hpp must be included before "exceptions.h" to switch to -//C++ exceptions semantics - so the macros translate into throw -#include "Exceptions.hpp" -#include "Transform.hpp" -#include "fiftyone.h" - -using namespace FiftyoneDegrees::Common; - -Transform::Transform(size_t capacity) : buffer(capacity) {} - -Transform::Headers Transform::apiInvoker(CTransformAPI func, - const std::string& json) { - class ArrayContainer { - public: - KeyValuePairArray* headers = NULL; - ArrayContainer() { - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, - headers, 8); - } - - ~ArrayContainer() { - Free(headers); - } - }; - - // RAII - ArrayContainer container; - - bool first = true; - size_t written = 0; - EXCEPTION_CREATE; - while (first || - EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)) { - if (!first) { - container.headers->count = 0; - //+1 to avoid 0 size - size_t bufferSize = std::max(buffer.size() * 2, - written + 1); - try { - buffer.resize(bufferSize); - } catch (const std::bad_alloc &) { - // reallocation failed - - // we just exit this loop by throwing an exception - EXCEPTION_THROW; - } - } - first = false; - EXCEPTION_CLEAR; - fiftyoneDegreesTransformIterateResult result = - func(json.c_str(), buffer.data(), buffer.size(), container.headers, - exception); - written = result.written; - } - - if (EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)) { - EXCEPTION_THROW; - } - - Transform::Headers res; - for (uint32_t i = 0; i < container.headers->count; ++i) { - fiftyoneDegreesKeyValuePair& pair = container.headers->items[i]; - - res.emplace(std::string{pair.key, pair.keyLength}, - std::string{pair.value, pair.valueLength}); - } - - return res; -} - -Transform::Headers Transform::fromJsonGHEV(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformGhevFromJson, json); -} - -Transform::Headers Transform::fromBase64GHEV(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformGhevFromBase64, json); -} - -Transform::Headers Transform::fromSUA(const std::string& json) { - return apiInvoker(fiftyoneDegreesTransformSua, json); -} diff --git a/Transform.hpp b/Transform.hpp deleted file mode 100644 index f756bba4..00000000 --- a/Transform.hpp +++ /dev/null @@ -1,106 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#ifndef FIFTYONE_DEGREES_TRANSFORM_HPP -#define FIFTYONE_DEGREES_TRANSFORM_HPP - -#include -#include -#include - -#include "transform.h" - -/** - * A C++ wrapper class for transform.h conversion routines. - * Encapsulates a working memory buffer to be reused when invoking conversion functions. - * The methods of a single instance can be invoked - * however many times synchronously / serially on a single thread. - * The instance is not thread safe, for access to the working buffer memory is not synchronized. - * Thus to be used in a multi-threaded setting - an instance per thread must be created. - * This is also true for the SWIG-generated - * wrappers of this class. - * - * Note the return values of the functions are copied value-types (map) - - * thus memory in the internal buffer is safely - * overwritten without harming the previously returned results - which is ideal for the (memory-) - * managed languages s.a. C#. - */ - -namespace FiftyoneDegrees { - namespace Common { - class Transform { - using Headers = std::map; - - using CTransformAPI = - fiftyoneDegreesTransformIterateResult (*) - (const char* base64, - char *buffer, - size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception); - - Headers apiInvoker(CTransformAPI func, const std::string& json); - - public: - /** - * Due to the default param value Transform() ctor is also available in C++ and managed - * language s.a. C# - * @param capacity the size of the working memory buffer The buffer size will - * automatically double if there is not enough memory to perform the conversion and the - * conversion will repeat with a bigger buffer. - */ - Transform(size_t capacity = 1024); - - /** - * Convert from getHighEntropyValues() results JSON string into HTTP header key-value - * pairs, see help section to the `fiftyoneDegreesTransformGhevFromJson` routine - * defined in transform.h - * @param json string representation of the GHEV JSON - * @return the converted HTTP header map - */ - Headers fromJsonGHEV(const std::string& json); - - /** - * Convert from the base64 encoded getHighEntropyValues() results JSON string into HTTP - * header key-value pairs, see help section to the - * `fiftyoneDegreesTransformGhevFromBase64` routine defined in transform.h - * @param base64 base64-encoded string representation of the GHEV JSON - * @return the converted HTTP header map - */ - Headers fromBase64GHEV(const std::string& base64); - - /** - * Convert from the oRTB 2.6 Structured User-Agent JSON string into HTTP header - * key-value pairs, see help section to the `fiftyoneDegreesTransformSUA` routine - * defined in transform.h - * @param json string representation of the oRTB SUA JSON - * @return the converted HTTP header map - */ - Headers fromSUA(const std::string& json); - - private: - std::vector buffer; - }; - } // namespace Common -} // namespace FiftyoneDegrees - -#endif // FIFTYONE_DEGREES_TRANSFORM_HPP diff --git a/Transform.i b/Transform.i deleted file mode 100644 index e753fc5f..00000000 --- a/Transform.i +++ /dev/null @@ -1,34 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -%include Types.i - -class Transform { -public: - Transform(); - Transform(size_t capacity); - - std::map fromJsonGHEV(const std::string& json); - std::map fromBase64GHEV(const std::string& json); - std::map fromSUA(const std::string& json); -}; - diff --git a/fiftyone.h b/fiftyone.h index f378b86a..d7cffebc 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -86,7 +86,6 @@ #include "yamlfile.h" #include "indices.h" #include "json.h" -#include "transform.h" /** * Macro used to support synonym implementation. Creates a typedef which @@ -358,13 +357,6 @@ MAP_TYPE(KeyValuePairArray) #define StringBuilderAddInteger fiftyoneDegreesStringBuilderAddInteger /**< Synonym for fiftyoneDegreesStringBuilderAddInteger */ #define StringBuilderAddChars fiftyoneDegreesStringBuilderAddChars /**< Synonym for fiftyoneDegreesStringBuilderAddChars */ #define StringBuilderComplete fiftyoneDegreesStringBuilderComplete /**< Synonym for fiftyoneDegreesStringBuilderComplete */ -#define TransformGhevFromJson fiftyoneDegreesTransformGhevFromJson /**< Synonym for fiftyoneDegreesTransformGhevFromJson */ -#define TransformGhevFromBase64 fiftyoneDegreesTransformGhevFromBase64 /**< Synonym for fiftyoneDegreesTransformGhevFromBase64 */ -#define TransformSua fiftyoneDegreesTransformSua /**< Synonym for fiftyoneDegreesTransformSua */ -#define TransformIterateSua fiftyoneDegreesTransformIterateSua /**< Synonym for fiftyoneDegreesTransformIterateSua */ -#define TransformIterateGhevFromBase64 fiftyoneDegreesTransformIterateGhevFromBase64 /**< Synonym for fiftyoneDegreesTransformIterateGhevFromBase64 */ -#define TransformIterateGhevFromJson fiftyoneDegreesTransformIterateGhevFromJson /**< Synonym for fiftyoneDegreesTransformIterateGhevFromJson */ -#define TransformCallback fiftyoneDegreesTransformCallback /**< Synonym for fiftyoneDegreesTransformCallback */ #define EvidenceIterateMethod fiftyoneDegreesEvidenceIterateMethod /**< Synonym for fiftyoneDegreesEvidenceIterateMethod */ /* <-- only one asterisk to avoid inclusion in documentation diff --git a/tests/TransformTests.cpp b/tests/TransformTests.cpp deleted file mode 100644 index 30eb4b50..00000000 --- a/tests/TransformTests.cpp +++ /dev/null @@ -1,1792 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#include "../Exceptions.hpp" -#include "../Transform.hpp" -#include "../fiftyone.h" -#include "Base.hpp" - - -class Transform : public Base { -public: - virtual void SetUp(); - virtual void TearDown(); - static bool found; - static fiftyoneDegreesKeyValuePairArray *results; - static const char *expectedFieldName; - static const char *expectedFieldValue; - void checkFieldValue(const char *field, const char *value); - void checkFieldAbsent(const char *field); -}; - -bool Transform::found = false; -const char *Transform::expectedFieldName = NULL; -const char *Transform::expectedFieldValue = NULL; -fiftyoneDegreesKeyValuePairArray *Transform::results = NULL; - -void Transform::checkFieldValue(const char *field, const char *value) { - found = false; - expectedFieldName = field; - expectedFieldValue = value; - size_t expectedFieldName_len = strlen(expectedFieldName); - size_t expectedFieldValue_len = strlen(expectedFieldValue); - - for (size_t i = 0; i < results->count; ++i) { - fiftyoneDegreesKeyValuePair *pair = &results->items[i]; - - if (expectedFieldName_len == pair->keyLength) { - found = true; - - for (size_t j = 0; j < pair->keyLength; ++j) { - if (pair->key[j] != expectedFieldName[j]) { - found = false; - break; - } - } - - if (found) { - EXPECT_TRUE(expectedFieldValue_len == pair->valueLength) - << L"Expected value len to be '" << expectedFieldValue_len - << "' not '" << pair->valueLength << "'"; - - if (expectedFieldValue_len == pair->valueLength) { - bool value_compare = true; - - for (size_t j = 0; j < pair->valueLength; ++j) { - if (pair->value[j] != expectedFieldValue[j]) { - value_compare = false; - break; - } - } - - EXPECT_TRUE(value_compare) - << L"Expected value to be '" << expectedFieldValue << "' not '" - << (const char *)pair->value << "'"; - - break; - } - } - } - } - - EXPECT_TRUE(found) << "Field " << field << " was not found should be " - << value; -} - -void Transform::checkFieldAbsent(const char *field) { - found = false; - expectedFieldName = field; - size_t expectedFieldName_len = strlen(expectedFieldName); - - for (size_t i = 0; i < results->count; ++i) { - fiftyoneDegreesKeyValuePair *pair = &results->items[i]; - - if (expectedFieldName_len == pair->keyLength) { - found = true; - - for (size_t j = 0; j < pair->keyLength; ++j) { - if (pair->key[j] != expectedFieldName[j]) { - found = false; - break; - } - } - - if (found) { - break; - } - } - } - - EXPECT_FALSE(found) << "Field " << field << " should be absent"; -} - -bool fillResultsCallback(void *ctx, fiftyoneDegreesKeyValuePair pair) { - fiftyoneDegreesKeyValuePairArray *results = - (fiftyoneDegreesKeyValuePairArray *)ctx; - - if (results->count < results->capacity) { - results->items[results->count++] = pair; - return true; - } - - return false; -} - -void Transform::SetUp() { - Base::SetUp(); - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, results, 8) -} - -void Transform::TearDown() { - fiftyoneDegreesFree(results); - Base::TearDown(); -} -// Tests -// ------------------------------------------------------------------------------------------ - -TEST_F(Transform, GHEVIterativeJSON) { - const char *ghev = - "{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromJson - (ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 7); - EXPECT_EQ(results->count, result.iterations); - EXPECT_FALSE(result.bufferTooSmall); - EXPECT_TRUE(result.written < bufferLength); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " - "\"Google Chrome\";v=\"126.0.6478.61\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); - - EXPECT_TRUE((bool) FIFTYONE_DEGREES_EXCEPTION_OKAY); -} - -TEST_F(Transform, IncompleteJSON) { - size_t bufferLength = 4096; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - std::vector correct{ - "{ \"key_without_value\" }", - - "{ \"key_without_value\": ", - - "{\"architecture\":\"x86\"," - " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", - - "{ \"incomplete_string\": \" \n", - "{ \"complete_string\": \" \" \n", - - "{\"incomplete_unknown_object\": { \"other_nested_object", - - "{\"incomplete_unknown_array\": [ \"other_nested_string", - - "{\"incomplete_unknown_array\": [", - - "{\"incomplete_unknown_array\": [[", - - "{\"incomplete_unknown_array\": [[],\"", - "{\"incomplete_unknown_array\": [[],\"\"", - "{\"complete_unknown_array\": [],", - - "{ \"incomplete_bool\": false", - - "{ \"arch\": \"x86\" }", - "{ \"full\" : \"\" } ", - "{ \"min\": -1 }", - "{ \"placebo\": \"___\" }", - "{ \"bind\": true }", - "{ \"break\": true }", - "{ \"mode\": \"default\" }", - - "{ \"\": \"empty_key\" }", - - "{\"bool\": true}", - - "{\"more\": true}", - "{\"moby\": 1}", - - "{\"platformer\": 0}", - "{\"platformVer\": 0}", - - "{ " - "\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]", - }; - - std::vector corrupted{ - "{ \"model\": n", - "{ \"model\": nu", - "{ \"model\": \"he", - "{ \"model\": \"he\\", - "", - "{ \"", - "{ \"mobile\":", - "{ \"mobile\": t", - "{ \"mobile\": f", - "{ \"mo", - "{\"a", - "{\"brands\":[{\"brand\": \"one\", \"version\":null}}", - "{ \"brands\":", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", - "{ \"brands\":{", - "{ \"brands\":[", - "{ \"brands\":[{", - "{ \"brands\":[{\"bra", - "{ \"brands\":[{\"brand\"", - "{ \"brands\":[{\"brand\":", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"ver", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"}", - "{ \"brands\":[{\"brand\":\"Not/A)Brand\",\"version\":\"8\"},", - }; - - EXCEPTION_CREATE; - for (const std::string &j : correct) { - - fiftyoneDegreesTransformIterateGhevFromJson - (j.c_str(), buffer, bufferLength, fillResultsCallback, - Transform::results, exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - } - - for (const std::string &j : corrupted) { - - fiftyoneDegreesTransformIterateGhevFromJson - ( - j.c_str(), buffer, bufferLength, fillResultsCallback, - Transform::results, exception); - EXPECT_FALSE(EXCEPTION_OKAY); - EXPECT_TRUE(EXCEPTION_FAILED); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - } - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, IncompleteSUA) { - size_t bufferLength = 4096; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - std::vector correct{ - "{\"browsers\":[{\"brand\": \"\", \"version\": [\"\\\"123\\\\\"]}]}", - - "{ \"key_without_value\" }", - - "{ \"key_without_value\": ", - - "{ \"skip\": { \"key\": \"\\\"\\n\\\\\"value\\\"\" } }", - - "{\"architecture\":\"x86\\\"\"," - " \"incomplete_unknown_object\": { \"other_nested_object\": { \n", - - "{ \"incomplete_string\": \" \n", - "{ \"complete_string\": \" \" \n", - - "{\"incomplete_unknown_object\": { \"other_nested_object", - - "{\"incomplete_unknown_array\": [ \"other_nested_string", - - "{\"incomplete_unknown_array\": [", - - "{\"incomplete_unknown_array\": [[", - - "{\"incomplete_unknown_array\": [[],\"", - "{\"incomplete_unknown_array\": [[],\"\"", - "{\"complete_unknown_array\": [],", - - "{ \"incomplete_bool\": false", - - "{ \"\": \"empty_key\" }", - - "{\"bool\": true}", - - "{ \"arch\": \"x86\" }", - "{ \"full\" : \"\" } ", - "{ \"min\": -1 }", - "{ \"placebo\": \"___\" }", - "{ \"bind\": true }", - "{ \"break\": true }", - "{ \"mode\": \"default\" }", - - "{\"more\": true}", - "{\"browsers\":[{\"brand\": null}]}", - "{\"platformer\": 0}", - "{\"platformVer\": 0}", - }; - - std::vector corrupted{ - "", - "{ \"", - "{\"a", - "{ \"mo", - "{ \"mobile\":", - "{\"browsers\":[{\"brand\": null}}}", - - "{\n \"browsers\"", - "{\n \"browsers\":{", - "{\n \"browsers\":[", - "{\n \"browsers\":[{", - "{\n \"browsers\":[{\"", - "{\n \"browsers\":[{\"bra", - "{\n \"browsers\":[{\"brand", - "{\n \"browsers\":[{\"brand\"", - "{\n \"browsers\":[{\"brand\":", - "{\n \"browsers\":[{\"brand\":\"", - "{\n \"browsers\":[{\"brand\":\"google", - "{\n \"browsers\":[{\"brand\":\"google\"", - "{\n \"browsers\":[{\"brand\":\"google\",", - "{\n \"browsers\":[{\"brand\":\"google\",\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"ver", - "{\n \"browsers\":[{\"brand\":\"google\",\"version", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]}", - "{\n \"browsers\":[{\"brand\":\"google\",\"version\":[\"42\",\"0\"]},", - }; - - EXCEPTION_CREATE; - for (const std::string &j : correct) { - - fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - } - - for (const std::string &j : corrupted) { - - fiftyoneDegreesTransformIterateSua(j.c_str(), buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - } - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVIncorrectBool) { - const char *ghev = - "{\"architecture\":\"x86\"," - "\"brands\":[{\"brand\": null, \"version\":\"8\"}," - "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," - "\"fullVersionList\":[{\"brand\": null}]," - "\"mobile\": 0,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromJson - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUAIncorrectBool) { - const char *json = "{\"mobile\": false,\"model\":\"\"}"; - - size_t bufferLength = 512; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(json, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVIterativeNULLBrandJSON) { - const char *ghev = - "{\"architecture\":\"x86\"," - "\"brands\":[{\"brand\": null, \"version\":\"8\"}," - "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," - "\"fullVersionList\":[{\"brand\": null}]," - "\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromJson - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 6); - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", "\"Google Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVIterativeNULLBrandVersionJSON) { - const char *ghev = - "{\"architecture\":\"x86\"," - "\"brands\":[{\"brand\": \"one\", \"version\":null}," - "{\"brand\": null\n},{\"brand\":\"Google Chrome\",\"version\":\"126\"}]," - "\"fullVersionList\":[{\"brand\": null}]," - "\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\"}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromJson - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 6); - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", "\"one\";v=null, \"Google Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVIterativeBase64) { - const char *ghev = - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromBase64 - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 6); - EXPECT_EQ(results->count, result.iterations); - - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVBase64CorruptedLen) { - const char *ghev = - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>" - "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromBase64 - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVBase64CorruptedSymbol) { - const char *ghev = - "====cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" - "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromBase64 - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVBase64CorruptedSymbol2) { - const char *ghev = - "&&&&cmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4Lj>>>>" - "AuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = 686; // strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromBase64 - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVIterativeSua) { - const char *sua = - "{\n \"source\": 2,\n \"platform\": {\n " - "\"brand\": \"macOS\",\n \"version\": [\"14\", \"5\", " - "\"0\"]\n },\n \"browsers\": [{\n " - "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " - "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " - "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " - "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " - " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " - " }],\n \"mobile\": 0,\n \"model\": \"\",\n " - "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 7); - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldAbsent("sec-ch-ua"); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SuaWeirdPlatformVersion) { - const char *sua = - "{\n \"source\": 2,\n \"platform\": {\n " - "\"brand\": \"macOS\",\n \"version\": [\"\\\"x\\\"\", " - "\"\\\"y\\\"\", " - "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " - "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " - "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " - "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " - "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " - " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " - " }],\n \"mobile\": 0,\n \"model\": \"\",\n " - "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 7); - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldAbsent("sec-ch-ua"); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", - "\"\\\"x\\\".\\\"y\\\".\\\"z\\\"\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SuaNullBrandPlatform) { - const char *sua = - "{\n \"source\": 2,\n \"platform\": {\n " - "\"brand\": null,\n \"version\": [\"\\\"x\\\"\", " - "\"\\\"y\\\"\", " - "\"\\\"z\\\"\"]\n },\n \"browsers\": [{\n " - "\"brand\": \"Not/A)Brand\",\n \"version\": [\"8\", " - "\"0\", \"0\", \"0\"]\n }, {\n \"brand\": " - "\"Chromium\",\n \"version\": [\"126\", \"0\", \"6478\", " - "\"127\"]\n }, {\n \"brand\": \"Google Chrome\",\n " - " \"version\": [\"126\", \"0\", \"6478\", \"127\"]\n " - " }],\n \"mobile\": 0,\n \"model\": \"\",\n " - "\"architecture\": \"x86\",\n \"bitness\": \"64\"\n}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 5); - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldAbsent("sec-ch-ua"); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldAbsent("sec-ch-ua-platform"); - checkFieldAbsent("sec-ch-ua-platform-version"); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVArrayJSON) { - const char *ghev = - "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" - "\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " - " : false, \"model\" : \"MacBook\" , \"platform\" : " - "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformGhevFromJson - ( - ghev, buffer, bufferLength, results, exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 7); - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"MacBook\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVArrayInsufficientCapacity) { - const char *ghev = - "{\"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{\"brand\":" - "\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google Chrome\",\"version\":\"126\"}],\"mobile\" " - " : false, \"model\" : \"MacBook\" , \"platform\" : " - "\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":false}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - fiftyoneDegreesKeyValuePairArray *headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 2); - - fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformGhevFromJson - ( - ghev, buffer, bufferLength, headers, exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 2); - EXPECT_EQ(headers->count, result.iterations); - - // --- - - result = fiftyoneDegreesTransformGhevFromJson( - ghev, buffer, bufferLength, empty_headers, exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - EXPECT_EQ(result.iterations, 1); - - fiftyoneDegreesFree(buffer); - fiftyoneDegreesFree(headers); - fiftyoneDegreesFree(empty_headers); -} - -TEST_F(Transform, GHEVBase64InsufficientCapacity) { - const char *ghev = - "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" - "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" - "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" - "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" - "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" - "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" - "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; - - size_t bufferLength = strlen(ghev) * 3; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformGhevFromBase64 - ( - ghev, buffer, bufferLength, empty_headers, exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - EXPECT_EQ(result.iterations, 1); - - fiftyoneDegreesFree(buffer); - fiftyoneDegreesFree(empty_headers); -} - -TEST_F(Transform, SUAInsufficientCapacity) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - fiftyoneDegreesKeyValuePairArray *empty_headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, empty_headers, 0); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformSua - ( - sua, buffer, bufferLength, empty_headers, exception); - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY)); - EXPECT_EQ(result.iterations, 1); - - fiftyoneDegreesKeyValuePairArray *headers = NULL; - FIFTYONE_DEGREES_ARRAY_CREATE(fiftyoneDegreesKeyValuePair, headers, 3); - - FIFTYONE_DEGREES_EXCEPTION_CLEAR; - result = fiftyoneDegreesTransformSua(sua, buffer, bufferLength, headers, - exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - EXPECT_EQ(result.iterations, 3); - - fiftyoneDegreesFree(headers); - fiftyoneDegreesFree(empty_headers); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVBase64) { - const char *ghev = - "eyJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJz" - "aW9uIjoiOCJ9LHsiYnJhbmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5k" - "IjoiR29vZ2xlIENocm9tZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6" - "W3siYnJhbmQiOiJOb3QvQSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6" - "IkNocm9taXVtIiwidmVyc2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2ds" - "ZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6dHJ1ZSwi" - "bW9kZWwiOiIiLCJwbGF0Zm9ybSI6Im1hY09TIiwid293NjQiOmZhbHNlfQ=="; - - size_t bufferLength = strlen(ghev) * 2; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformGhevFromBase64 - ( - ghev, buffer, bufferLength, results, exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 6); - EXPECT_EQ(results->count, result.iterations); - - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldAbsent("sec-ch-ua-platform-version"); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVBase64NotEnoughMemory) { - const char *ghev = - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromBase64 - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - EXPECT_TRUE(result.bufferTooSmall); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); - - EXCEPTION_CLEAR; - buffer = (char *)fiftyoneDegreesMalloc(result.written); - - result = fiftyoneDegreesTransformIterateGhevFromBase64 - ( - ghev, buffer, result.written, fillResultsCallback, Transform::results, - exception); - EXPECT_FALSE(result.bufferTooSmall); - EXPECT_TRUE(EXCEPTION_OKAY); - - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUANotEnoughMemory) { - const char *sua = - "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," - "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; - - size_t bufferLength = 144; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVArraySua) { - const char *sua = - "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," - "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 6); - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua-model", "\"MacBook\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/" - "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " - "Chrome\";v=\"126.0.6478.127\""); - - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldAbsent("sec-ch-ua"); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUAArrayNotEnoughMemory) { - const char *sua = - "{\"source\":2,\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"; - - size_t bufferLength = 142; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformSua(sua, buffer, bufferLength, results, - exception); - - // --- - - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVPartial) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":null}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromJson - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - EXPECT_EQ(result.iterations, 4); - EXPECT_TRUE(EXCEPTION_OKAY); - - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile - // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list - // we check that either empty value (model), null-value (platform) - // or entire absence of key (platformVersion) result in no header in the - // output the policy is - we don't output empty data - no value == no evidence - // field - - EXPECT_EQ(results->count, result.iterations); - - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " - "\"Google Chrome\";v=\"126.0.6478.61\""); - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-model", "\"\""); - - checkFieldAbsent("sec-ch-ua-platform"); - checkFieldAbsent("sec-ch-ua-platform-version"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVIgnoreUnused) { - const char *ghev = - "{ \"architecture\":\"x86\",\"bitness\":\"64\",\"brands\":[{ " - "\"brand\" : " - "\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\" , " - "\"version\" : \"126.0.6478.127\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.127\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.5.0\",\"wow64\":" - "false}"; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromJson - ( - ghev, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - EXPECT_EQ(result.iterations, 8); - EXPECT_TRUE(EXCEPTION_OKAY); - - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile - // high entropy: sec-ch-ua-model, sec-ch-ua-full-version-list - // we check that either empty value (model), null-value (platform) - // or entire absence of key (platformVersion) result in no header in the - // output the policy is - we don't output empty data - no value == no evidence - // field - - checkFieldValue("sec-ch-ua", - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - - checkFieldValue("sec-ch-ua-mobile", "?0"); - checkFieldValue("sec-ch-ua-platform", "\"macOS\""); - checkFieldValue("sec-ch-ua-platform-version", "\"14.5.0\""); - checkFieldValue("sec-ch-ua-model", "\"\""); - checkFieldValue("sec-ch-ua-arch", "\"x86\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVCorruptInput) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\""; - - size_t bufferLength = strlen(ghev); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - fillResultsCallback, - Transform::results, exception); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVBufferTooSmall) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\"}"; - - size_t bufferLength = 20; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateGhevFromJson - (ghev, buffer, bufferLength, - fillResultsCallback, - Transform::results, exception); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - EXPECT_TRUE(result.bufferTooSmall); - fiftyoneDegreesFree(buffer); - - - - buffer = (char *)fiftyoneDegreesMalloc(result.written); - EXCEPTION_CLEAR; - result = fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, result.written, - fillResultsCallback, - Transform::results, exception); - EXPECT_TRUE(EXCEPTION_OKAY); - EXPECT_FALSE(result.bufferTooSmall); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, GHEVEvidenceLowCapacity) { - const char *ghev = - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\",\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":\"macOS\"}"; - - size_t bufferLength = 20; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateGhevFromJson(ghev, buffer, bufferLength, - fillResultsCallback, - Transform::results, exception); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUAHappyPath) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\":" - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel6\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 7); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - EXPECT_EQ(results->count, result.iterations); - - // In device.sua representation there is no distinction between - // sec-ch-ua and sec-ch-ua-full-version-list - // checkFieldValue( - // "sec-ch-ua", - // "\"Not A;Brand\";v=\"99.0.0.0\",\"Chromium\";v=\"99.0.4844.88\"," - // "\"Google Chrome\";v=\"99.0.4844.88\"\n"); - - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " - "\"Google Chrome\";v=\"99.0.4844.88\""); - - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"12\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-arch", "\"arm\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue("sec-ch-ua-model", "\"Pixel6\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUAPlatformExt) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\", \"version\": [\"13\"], " - "\"ext\": { \"some_random_key\" : [ \"some\", \"random\", \"\\n\", " - "\" \\\" values \" ], " - "\"another_random_key\": null}}," - "\"mobile\": 1,\"model\": \"\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - EXPECT_EQ(result.iterations, 5); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - EXPECT_EQ(results->count, result.iterations); - EXPECT_TRUE(EXCEPTION_OKAY); - - checkFieldAbsent("sec-ch-ua"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " - "\"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"13\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue("sec-ch-ua-model", "\"\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUAPartial1) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\"},\"mobile\": 1,\"model\": \"\"}"; - - size_t bufferLength = strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - EXPECT_EQ(result.iterations, 4); - // we expect to see these headers detected: - // low entropy: sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - EXPECT_EQ(results->count, result.iterations); - EXPECT_TRUE(EXCEPTION_OKAY); - - checkFieldAbsent("sec-ch-ua"); - checkFieldValue( - "sec-ch-ua-full-version-list", - "\"Not A;Brand\";v=\"99.0.0.0\", \"Chromium\";v=\"99.0.4844.88\", " - "\"Google Chrome\";v=\"99.0.4844.88\""); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldAbsent("sec-ch-ua-platform-version"); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-arch"); - checkFieldAbsent("sec-ch-ua-bitness"); - checkFieldValue("sec-ch-ua-model", "\"\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUAPartial2) { - const char *sua = - "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " - "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - EXPECT_EQ(result.iterations, 5); - - EXPECT_TRUE(EXCEPTION_OKAY); - // we expect to see these headers detected: - // low entropy: sec-ch-ua, sec-ch-ua-mobile, sec-ch-ua-platform - // high entropy: sec-ch-ua-platform-version, sec-ch-ua-model, sec-ch-ua-arch, - // sec-ch-ua-full-version-list - - EXPECT_EQ(results->count, result.iterations); - - checkFieldAbsent("sec-ch-ua"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"12\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldValue("sec-ch-ua-arch", "\"arm\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldAbsent("sec-ch-ua-model"); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUATolerableCorrupt) { - const char *sua = - "{\"source\": 2,,\"platform\": {\"brand\": \"Android\",\"version\": " - "[\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - ( - sua, buffer, bufferLength, fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_OKAY); - - EXPECT_EQ(result.iterations, 5); - EXPECT_EQ(results->count, result.iterations); - - checkFieldAbsent("sec-ch-ua"); - checkFieldAbsent("sec-ch-ua-full-version-list"); - - checkFieldValue("sec-ch-ua-arch", "\"arm\""); - checkFieldValue("sec-ch-ua-bitness", "\"64\""); - checkFieldValue("sec-ch-ua-mobile", "?1"); - checkFieldAbsent("sec-ch-ua-model"); - checkFieldValue("sec-ch-ua-platform", "\"Android\""); - checkFieldValue("sec-ch-ua-platform-version", "\"12\""); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUACorrupt2) { - const char *sua = - "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " - "[12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUACorrupt3) { - const char *sua = - "{\"source\": 2,\"platform\": {\"brand\": \"Android\",\"version\": " - "\"12\"]},\"mobile\": 1,\"architecture\": \"arm\",\"bitness\": " - "\"64\",\"model\": null}"; - - size_t bufferLength = 2 * strlen(sua); - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_CORRUPT_DATA)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, SUABufferTooSmall) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; - - size_t bufferLength = 15; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateResult result = - fiftyoneDegreesTransformIterateSua - (sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - EXPECT_TRUE(result.bufferTooSmall); - - fiftyoneDegreesFree(buffer); - - - buffer = (char *)fiftyoneDegreesMalloc(result.written); - EXCEPTION_CLEAR; - - result = fiftyoneDegreesTransformIterateSua(sua, buffer, result.written, - fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_OKAY); - EXPECT_FALSE(result.bufferTooSmall); - Free(buffer); -} - -TEST_F(Transform, SUAEvidenceLowCapacity) { - const char *sua = - "{\"source\": 2,\"browsers\": [{\"brand\": \"Not A;Brand\",\"version\": " - "[\"99\",\"0\",\"0\",\"0\"]},{\"brand\": \"Chromium\",\"version\": " - "[\"99\",\"0\",\"4844\",\"88\"]},{\"brand\": \"Google " - "Chrome\",\"version\": [\"99\",\"0\",\"4844\",\"88\"]}],\"platform\": " - "{\"brand\": \"Android\",\"version\": [\"12\"]},\"mobile\": " - "1,\"architecture\": \"arm\",\"bitness\": \"64\",\"model\": \"Pixel 6\"}"; - - size_t bufferLength = 15; - char *buffer = (char *)fiftyoneDegreesMalloc(bufferLength); - - EXCEPTION_CREATE; - - fiftyoneDegreesTransformIterateSua(sua, buffer, bufferLength, - fillResultsCallback, Transform::results, - exception); - EXPECT_TRUE(EXCEPTION_CHECK(FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY)); - fiftyoneDegreesFree(buffer); -} - -TEST_F(Transform, CPPWrapperGHEV) { - FiftyoneDegrees::Common::Transform t; - - auto h = t.fromJsonGHEV - ( - "{\"brands\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8\"},{\"brand\":\"Chromium\",\"version\":" - "\"126\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126\"}],\"fullVersionList\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":\"8.0.0.0\"},{\"brand\":\"Chromium\"," - "\"version\":" - "\"126.0.6478.61\"},{\"brand\":\"Google " - "Chrome\",\"version\":\"126.0.6478.61\"}],\"mobile\":false,\"model\":" - "\"\",\"platform\":null}"); - - EXPECT_EQ(h.size(), 4); - - EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - - EXPECT_EQ(h["sec-ch-ua"], - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - EXPECT_EQ(h["sec-ch-ua-full-version-list"], - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.61\", " - "\"Google Chrome\";v=\"126.0.6478.61\""); - - EXPECT_EQ(h["sec-ch-ua-mobile"], "?0"); - EXPECT_EQ(h["sec-ch-ua-model"], "\"\""); - - EXPECT_FALSE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-platform-version") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); -} - -TEST_F(Transform, CPPWrapperBase64) { - FiftyoneDegrees::Common::Transform t; - - auto h = t.fromBase64GHEV - ( - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); - - EXPECT_EQ(h.size(), 6); - - EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); - - EXPECT_EQ(h["sec-ch-ua"], - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - EXPECT_EQ(h["sec-ch-ua-full-version-list"], - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - - EXPECT_EQ(h["sec-ch-ua-mobile"], "?0"); - EXPECT_EQ(h["sec-ch-ua-model"], "\"\""); - EXPECT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); - EXPECT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); -} - -TEST_F(Transform, CPPWrapperBase64InsufficientMemory) { - FiftyoneDegrees::Common::Transform t(128); - - auto h = t.fromBase64GHEV - ( - "eyJicmFuZHMiOlt7ImJyYW5kIjoiTm90L0EpQnJhbmQiLCJ2ZXJzaW9uIjoiOCJ9LHsiYnJh" - "bmQiOiJDaHJvbWl1bSIsInZlcnNpb24iOiIxMjYifSx7ImJyYW5kIjoiR29vZ2xlIENocm9t" - "ZSIsInZlcnNpb24iOiIxMjYifV0sImZ1bGxWZXJzaW9uTGlzdCI6W3siYnJhbmQiOiJOb3Qv" - "QSlCcmFuZCIsInZlcnNpb24iOiI4LjAuMC4wIn0seyJicmFuZCI6IkNocm9taXVtIiwidmVy" - "c2lvbiI6IjEyNi4wLjY0NzguMTI3In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJz" - "aW9uIjoiMTI2LjAuNjQ3OC4xMjcifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiIiwicGxh" - "dGZvcm0iOiJtYWNPUyIsInBsYXRmb3JtVmVyc2lvbiI6IjE0LjUuMCJ9"); - - EXPECT_EQ(h.size(), 6); - - EXPECT_TRUE(h.find("sec-ch-ua") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); - - EXPECT_EQ(h["sec-ch-ua"], - "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google " - "Chrome\";v=\"126\""); - - EXPECT_EQ(h["sec-ch-ua-full-version-list"], - "\"Not/A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", " - "\"Google Chrome\";v=\"126.0.6478.127\""); - - EXPECT_EQ(h["sec-ch-ua-mobile"], "?0"); - EXPECT_EQ(h["sec-ch-ua-model"], "\"\""); - EXPECT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); - EXPECT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - - EXPECT_FALSE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); -} - -TEST_F(Transform, CPPWrapperSUA) { - FiftyoneDegrees::Common::Transform t; - - auto h = t.fromSUA - ( - "{\"source\":2,\"platform\":{\"brand\":\"macOS\",\"version\":[\"14\"," - "\"5\",\"0\"]},\"browsers\":[{\"brand\":\"Not/" - "A)Brand\",\"version\":[\"8\",\"0\",\"0\",\"0\"]},{\"brand\":" - "\"Chromium\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]},{\"brand\":" - "\"Google " - "Chrome\",\"version\":[\"126\",\"0\",\"6478\",\"127\"]}],\"mobile\":1," - "\"model\":\"MacBook\",\"architecture\":\"x86\"}"); - - EXPECT_EQ(h.size(), 6); - - EXPECT_TRUE(h.find("sec-ch-ua-arch") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-model") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-mobile") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-platform-version") != h.end()); - EXPECT_TRUE(h.find("sec-ch-ua-full-version-list") != h.end()); - - EXPECT_EQ(h["sec-ch-ua-arch"], "\"x86\""); - EXPECT_EQ(h["sec-ch-ua-model"], "\"MacBook\""); - EXPECT_EQ(h["sec-ch-ua-mobile"], "?1"); - EXPECT_EQ(h["sec-ch-ua-platform"], "\"macOS\""); - EXPECT_EQ(h["sec-ch-ua-platform-version"], "\"14.5.0\""); - EXPECT_EQ( - h["sec-ch-ua-full-version-list"], - "\"Not/" - "A)Brand\";v=\"8.0.0.0\", \"Chromium\";v=\"126.0.6478.127\", \"Google " - "Chrome\";v=\"126.0.6478.127\""); - - EXPECT_FALSE(h.find("sec-ch-ua") != h.end()); - EXPECT_FALSE(h.find("sec-ch-ua-bitness") != h.end()); -} - -TEST_F(Transform, emptycases) { - FiftyoneDegrees::Common::Transform t; - auto result = t.fromJsonGHEV("{}"); - EXPECT_EQ(result.size(), 0); - bool thrown = false; - try { - t.fromJsonGHEV(""); - } catch (const FiftyoneDegrees::Common::FatalException &e) { - EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); - thrown = true; - } - - EXPECT_TRUE(thrown); -} - -TEST_F(Transform, CPPWrapperBase64Corrupt) { - FiftyoneDegrees::Common::Transform t; - bool thrown = false; - try { - auto result = t.fromBase64GHEV("base64 invalid string"); - EXPECT_EQ(result.size(), 0); - } catch (const FiftyoneDegrees::Common::FatalException &e) { - EXPECT_EQ(e.getCode(), FIFTYONE_DEGREES_STATUS_CORRUPT_DATA); - thrown = true; - } - EXPECT_TRUE(thrown); -} - -TEST_F(Transform, CPPWrapper0Size) { - FiftyoneDegrees::Common::Transform t(0); - bool thrown = false; - try { - auto result = t.fromJsonGHEV - ("{\"architecture\":\"x86\", \"bitness\":\"32\", \"new\":\"ignored\"}"); - EXPECT_EQ(result.size(), 2); - } catch (const FiftyoneDegrees::Common::FatalException &) { - thrown = true; - } - EXPECT_FALSE(thrown); -} diff --git a/transform.c b/transform.c deleted file mode 100644 index 15eafc86..00000000 --- a/transform.c +++ /dev/null @@ -1,1301 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#include "transform.h" -#include "fiftyone.h" - -#define initStaticKey(x) {x, sizeof(x) - 1} - -#define NotExpectSymbol(json, ch, exit) \ -if (*json == ch) { \ -EXCEPTION_SET(CORRUPT_DATA); \ -exit; \ -} - -#define ExpectSymbol(json, ch, exit) \ -if (*json != ch) { \ -EXCEPTION_SET(CORRUPT_DATA); \ -exit; \ -} - -#define ExpectKeySymbol(json, ch) \ -if (*json != ch) { \ -json = skipToNextChar(json, '"'); \ -return KEY_UNDEFINED; \ -} - -#define ValuePtr \ -(EXCEPTION_CHECK(INSUFFICIENT_MEMORY) ? NULL \ -: begin) - -#define GET_SEXTET(str, i) \ -(str[i] == '=' ? 0 & i++ : base64CharToValue(str[i++], exception)) - -typedef enum { - ARCHITECTURE, // sec-ch-ua-arch - BRANDS, // sec-ch-ua - BITNESS, // sec-ch-ua-bitness - FULLVERSIONLIST, // sec-ch-ua-full-version-list - MOBILE, // sec-ch-ua-mobile - MODEL, // sec-ch-ua-model - PLATFORM, // sec-ch-ua-platform - PLATFORMVERSION, // sec-ch-ua-platform-version - KEY_UNDEFINED, // -} Key; - -typedef Key (*readKeyCallback)(const char**, Exception* const); -typedef char* (*readValueCallback)(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception); - -static struct { - const char* key; - size_t len; -} key_map[] = { - initStaticKey("sec-ch-ua-arch"), - initStaticKey("sec-ch-ua"), - initStaticKey("sec-ch-ua-bitness"), - initStaticKey("sec-ch-ua-full-version-list"), - initStaticKey("sec-ch-ua-mobile"), - initStaticKey("sec-ch-ua-model"), - initStaticKey("sec-ch-ua-platform"), - initStaticKey("sec-ch-ua-platform-version"), -}; - -// ---- - -static inline char* safeWriteToBuffer(StringBuilder *builder, - char symbol, - Exception* const exception) { - StringBuilderAddChar(builder, symbol); - if (builder->full) { - EXCEPTION_SET(INSUFFICIENT_MEMORY); - } - return builder->current; -} - -static inline uint32_t base64CharToValue( - char c, Exception* const exception) { - static const uint32_t base64_lookup_table[256] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, - 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255}; - - if (base64_lookup_table[(uint8_t)c] == 255) { - EXCEPTION_SET(CORRUPT_DATA); - } - - return base64_lookup_table[(uint8_t)c]; -} - -static size_t base64Decode(const char* base64_input, StringBuilder *builder, - Exception* const exception) { - size_t before = builder->added; - size_t input_length = strlen(base64_input); - if (input_length % 4 != 0) { - EXCEPTION_SET(CORRUPT_DATA); - return 0; // Invalid base64 input length - } - - for (size_t i = 0; i < input_length;) { - uint32_t sextet_a = GET_SEXTET(base64_input, i); - uint32_t sextet_b = GET_SEXTET(base64_input, i); - uint32_t sextet_c = GET_SEXTET(base64_input, i); - uint32_t sextet_d = GET_SEXTET(base64_input, i); - - if (EXCEPTION_CHECK(CORRUPT_DATA)) { - return 0; - } - - uint32_t triple = - (sextet_a << 18) + (sextet_b << 12) + (sextet_c << 6) + sextet_d; - - safeWriteToBuffer(builder, (triple >> 16) & 0xFF, exception); - safeWriteToBuffer(builder, (triple >> 8) & 0xFF, exception); - safeWriteToBuffer(builder, triple & 0xFF, exception); - } - - safeWriteToBuffer(builder, '\0', exception); - size_t after = builder->added; - return after - before; -} - -static inline const char* skipWhitespaces(const char* json) { - for (;; ++json) { - switch (*json) { - case ' ': - case '\t': - case '\n': - case '\v': - case '\r': - case '\f': - break; - - default: - return json; - } - } -} - -static inline const char* skipToNextChar(const char* json, - const char target) { - for (; *json != target; ++json) { - switch (*json) { - case '\0': { - return json; - } break; - - case '\\': { - if (json[1] == target || json[1] == '\\') { - ++json; - } - } break; - } - } - - return json; -} - -static const char* skipValue(const char* json) { - json = skipToNextChar(json, ':'); - if (*json == '\0') { - return json; - } - - json = skipWhitespaces(json + 1); - - switch (*json) { - case '\0': { - return json; - } break; - - case '{': { // skip nested object - ++json; - - for (int nesting_level = 1; nesting_level > 0; ++json) { - switch (*json) { - case '\0': { - return json; - } break; - - case '{': { - ++nesting_level; - } break; - - case '}': { - --nesting_level; - } break; - - case '"': { - json = skipToNextChar(json + 1, '"'); - if (*json == '\0') { - return json; - } - } break; - } - } - } break; - - case '[': { - ++json; - - for (int nesting_level = 1; nesting_level > 0; ++json) { - switch (*json) { - case '\0': { - return json; - } break; - - case '[': { - ++nesting_level; - } break; - - case ']': { - --nesting_level; - } break; - - case '"': { - json = skipToNextChar(json + 1, '"'); - if (*json == '\0') { - return json; - } - } break; - } - } - } break; - - case '"': { - json = skipToNextChar(json + 1, '"'); - } break; - - default: { - for (int flag = 1; flag;) { - switch (*json) { - case '\0': { - return json; - } break; - - case ',': - case '}': - case ']': { - flag = 0; - } break; - - default: { - ++json; - } break; - } - } - } break; - } - - if (*json == '\0') { - return json; - } - - return skipToNextChar(json + 1, '"'); -} - -static inline void initKeys(StringBuilder *builder, - KeyValuePair* cache, - Exception* const exception) { - for (size_t k = 0; k < KEY_UNDEFINED; ++k) { - cache[k].key = builder->current; - cache[k].keyLength = key_map[k].len; - - for (size_t i = 0; i < key_map[k].len; ++i) { - safeWriteToBuffer(builder, key_map[k].key[i], exception); - } - } -} - -static const char* initParsing(const char* json, - StringBuilder *builder, - KeyValuePair* cache, - Exception* const exception) { - - initKeys(builder, cache, exception); - - json = skipWhitespaces(json); - ExpectSymbol(json, '{', return json); - return skipToNextChar(json, '"'); -} - -static Key readGhevKey(const char** json, - Exception* const exception) { - enum ReadKeyState { - READ_KEY_INIT, - ARCH, - BRANDS_OR_BITNESS, - FULL_VERSION_LIST, - MOBILE_OR_MODEL, - PLATFORM_OR_VERSION, - READ_KEY_BRANDS, - READ_KEY_BITNESS, - READ_KEY_MOBILE, - READ_KEY_MODEL, - READ_KEY_PLATFORMVERSION, - }; - - ++*json; - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { - switch (state) { - case READ_KEY_INIT: { - switch (**json) { - case 'a': { - state = ARCH; - } break; - - case 'f': { - state = FULL_VERSION_LIST; - } break; - - case 'b': { - state = BRANDS_OR_BITNESS; - } break; - - case 'm': { - state = MOBILE_OR_MODEL; - } break; - - case 'p': { - state = PLATFORM_OR_VERSION; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - - case '"': { - return KEY_UNDEFINED; - } break; - } - } break; - - case ARCH: { - for (const char* i = "rchitecture"; *i != '\0'; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return ARCHITECTURE; - } break; - - case FULL_VERSION_LIST: { - for (const char* i = "ullVersionList"; *i != '\0'; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return FULLVERSIONLIST; - } break; - - case BRANDS_OR_BITNESS: { - switch (**json) { - case 'r': { - state = READ_KEY_BRANDS; - } break; - - case 'i': { - state = READ_KEY_BITNESS; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case MOBILE_OR_MODEL: { - ExpectKeySymbol(*json, 'o'); - - ++(*json); - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - switch (**json) { - case 'b': { - state = READ_KEY_MOBILE; - } break; - - case 'd': { - state = READ_KEY_MODEL; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case PLATFORM_OR_VERSION: { - for (const char* i = "latform"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - switch (**json) { - case '"': { - return PLATFORM; - } break; - - case 'V': { - state = READ_KEY_PLATFORMVERSION; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case READ_KEY_BRANDS: { - for (const char* i = "ands"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return BRANDS; - } break; - - case READ_KEY_BITNESS: { - for (const char* i = "tness"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return BITNESS; - } break; - - case READ_KEY_MOBILE: { - for (const char* i = "ile"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MOBILE; - } break; - - case READ_KEY_MODEL: { - for (const char* i = "el"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MODEL; - } break; - - case READ_KEY_PLATFORMVERSION: { - for (const char* i = "ersion"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return PLATFORMVERSION; - } break; - } - } - - return KEY_UNDEFINED; -} - -static Key readSuaKey(const char** json, - Exception* const exception) { - enum ReadKeyState { - READ_KEY_INIT, - BROWSERS_OR_BITNESS, - MOBILE_OR_MODEL, - READ_KEY_BITNESS, - ARCH, - READ_KEY_MOBILE, - READ_KEY_MODEL, - READ_KEY_PLATFORM, - READ_KEY_BROWSERS, - }; - - ++*json; - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - for (enum ReadKeyState state = READ_KEY_INIT; **json != '\0'; ++(*json)) { - switch (state) { - case READ_KEY_INIT: { - switch (**json) { - case 'a': { - state = ARCH; - } break; - - case 'b': { - state = BROWSERS_OR_BITNESS; - } break; - - case 'm': { - state = MOBILE_OR_MODEL; - } break; - - case 'p': { - state = READ_KEY_PLATFORM; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - - case '"': { - return KEY_UNDEFINED; - } break; - } - } break; - - case ARCH: { - for (const char* i = "rchitecture"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return ARCHITECTURE; - } break; - - case BROWSERS_OR_BITNESS: { - switch (**json) { - case 'r': { - state = READ_KEY_BROWSERS; - } break; - - case 'i': { - state = READ_KEY_BITNESS; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case MOBILE_OR_MODEL: { - ExpectKeySymbol(*json, 'o'); - - ++(*json); - NotExpectSymbol(*json, '\0', return KEY_UNDEFINED); - - switch (**json) { - case 'b': { - state = READ_KEY_MOBILE; - } break; - - case 'd': { - state = READ_KEY_MODEL; - } break; - - default: { - *json = skipToNextChar(*json, '"'); - return KEY_UNDEFINED; - } break; - } - } break; - - case READ_KEY_PLATFORM: { - for (const char* i = "latform"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - ExpectKeySymbol(*json, '"'); - - return PLATFORM; - } break; - - case READ_KEY_BROWSERS: { - for (const char* i = "owsers"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return FULLVERSIONLIST; - } break; - - case READ_KEY_BITNESS: { - for (const char* i = "tness"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return BITNESS; - } break; - - case READ_KEY_MOBILE: { - for (const char* i = "ile"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MOBILE; - } break; - - case READ_KEY_MODEL: { - for (const char* i = "el"; *i; ++(*json), ++i) { - ExpectKeySymbol(*json, *i); - } - - return MODEL; - } break; - } - } - - return KEY_UNDEFINED; -} - -static char* readStringValue(const char** json, StringBuilder *builder, - Exception* const exception) { - char *begin = builder->current; - *json = skipWhitespaces(*json); - if (**json == 'n') { - ++(*json); - NotExpectSymbol(*json, '\0', return begin); - - for (const char* i = "ull"; *i; ++(*json), ++i) { - ExpectSymbol(*json, *i, return begin); - } - - return NULL; - } - - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (begin = safeWriteToBuffer(builder, '"', exception);; ++(*json)) { - NotExpectSymbol(*json, '\0', return begin); - - begin = safeWriteToBuffer(builder, **json, exception); - - switch (**json) { - case '\"': { - ++(*json); - return begin; - } - - case '\\': { - if ((*json)[1] == '\\' || (*json)[1] == '"') { - ++(*json); - - safeWriteToBuffer(builder, **json, exception); - } - } break; - } - } -} - -static char* readBoolGhevValue(const char** json, - StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - char *ptr = begin; - size_t before = builder->added; - switch (**json) { - case 't': { - ++(*json); - for (const char* i = "rue"; *i != '\0'; ++(*json), ++i) { - ExpectSymbol(*json, *i, return begin); - } - - ptr = safeWriteToBuffer(builder, '?', exception); - ptr = safeWriteToBuffer(builder, '1', exception); - } break; - - case 'f': { - ++(*json); - for (const char* i = "alse"; *i != '\0'; ++(*json), ++i) { - ExpectSymbol(*json, *i, return begin); - } - - ptr = safeWriteToBuffer(builder, '?', exception); - ptr = safeWriteToBuffer(builder, '0', exception); - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - - size_t after = builder->added; - cache[key].value = ValuePtr; - cache[key].valueLength = after - before; - return ptr; -} - -static char* readBoolSuaValue(const char** json, - StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - size_t before = builder->added; - switch (**json) { - case '0': - case '1': { - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - - char* ptr = safeWriteToBuffer(builder, '?', exception); - ptr = safeWriteToBuffer(builder, **json, exception); - - ++(*json); - - size_t after = builder->added; - cache[key].value = ValuePtr; - cache[key].valueLength = after - before; - - return ptr; -} - -static char* readVersionSua(const char** json, - StringBuilder *builder, - Exception* const exception) { - enum version_state { - version_read, - version_skip, - version_exit, - } state = version_skip; - - char *begin = builder->current; - for (char* ptr = begin;; ++(*json)) { - NotExpectSymbol(*json, '\0', return begin); //rollback - - switch (state) { - case version_read: { - switch (**json) { - case '\"': { - state = version_skip; - } break; - - case '\\': { - ptr = safeWriteToBuffer(builder, **json, exception); - - if ((*json)[1] == '\\' || (*json)[1] == '"') { - ++(*json); - ptr = safeWriteToBuffer(builder, **json, exception); - } - } break; - - default: { - ptr = safeWriteToBuffer(builder, **json, exception); - } break; - } - } break; - - case version_skip: { - switch (**json) { - case '"': { - state = version_read; - } break; - - case ',': { - ptr = safeWriteToBuffer(builder, '.', exception); - } break; - - case ']': { - state = version_exit; - } break; - } - } break; - - case version_exit: { - ptr = safeWriteToBuffer(builder, '"', exception); - return ptr; - } break; - } - } -} - -static char* readBrandsGhevValue(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - *json = skipToNextChar(*json, '['); - ExpectSymbol(*json, '[', return begin); - - ++*json; - - for (char* ptr = begin;; ++*json) { - *json = skipToNextChar(*json, '{'); - ExpectSymbol(*json, '{', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - ++*json; - - char* ptr2 = readStringValue(json, builder, exception); - if (ptr2 != NULL) { - ptr = safeWriteToBuffer(builder, ';', exception); - ptr = safeWriteToBuffer(builder, 'v', exception); - ptr = safeWriteToBuffer(builder, '=', exception); - - *json = skipToNextChar(*json, ','); - ExpectSymbol(*json, ',', return begin); //rollback - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); //rollback - - ++*json; - - for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); //rollback - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); //rollback - - ++*json; - - ptr2 = readStringValue(json, builder, exception); - if (ptr2 == NULL) { - ptr2 = ptr; - - ptr = safeWriteToBuffer(builder, 'n', exception); - ptr = safeWriteToBuffer(builder, 'u', exception); - ptr = safeWriteToBuffer(builder, 'l', exception); - ptr = safeWriteToBuffer(builder, 'l', exception); - } else { - ptr = ptr2; - } - } - - *json = skipToNextChar(*json, '}'); - ExpectSymbol(*json, '}', return begin); - - *json = skipWhitespaces(*json + 1); - NotExpectSymbol(*json, '\0', return begin); - - switch (**json) { - case ']': { - if (ptr != begin) { - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - return ptr; - } else { - return NULL; - } - - } break; - - case ',': { - if (ptr2 != NULL) { - ptr = safeWriteToBuffer(builder, ',', exception); - ptr = safeWriteToBuffer(builder, ' ', exception); - } - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - } -} - -static char* readBrandsSuaValue(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - *json = skipToNextChar(*json, '['); - char *begin = builder->current; - ExpectSymbol(*json, '[', return begin); - - for (char* ptr = begin;; ++*json) { - NotExpectSymbol(*json, '\0', return begin); - - *json = skipToNextChar(*json, '{'); - ExpectSymbol(*json, '{', return begin); - - *json = skipToNextChar(*json, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - ++*json; - - char* ptr2 = readStringValue(json, builder, exception); - if (ptr2 != NULL) { - ptr = safeWriteToBuffer(builder, ';', exception); - ptr = safeWriteToBuffer(builder, 'v', exception); - ptr = safeWriteToBuffer(builder, '=', exception); - - *json = skipToNextChar(*json, ','); - ExpectSymbol(*json, ',', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - *json = skipToNextChar(*json, '['); - ExpectSymbol(*json, '[', return begin); - - ++*json; - - ptr = safeWriteToBuffer(builder, '"', exception); - ptr = readVersionSua(json, builder, exception); - } - - *json = skipToNextChar(*json, '}'); - ExpectSymbol(*json, '}', return begin); - - *json = skipWhitespaces(*json + 1); - NotExpectSymbol(*json, '\0', return begin); - - switch (**json) { - case ']': { - if (ptr != begin) { - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - return ptr; - } else { - return NULL; - } - } break; - - case ',': { - if (ptr != begin) { - ptr = safeWriteToBuffer(builder, ',', exception); - ptr = safeWriteToBuffer(builder, ' ', exception); - } - } break; - - default: { - EXCEPTION_SET(CORRUPT_DATA); - return begin; - } break; - } - } -} - -static char* readPureStringValue(const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - char* ptr = readStringValue(json, builder, exception); - - if (ptr != NULL) { - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - } - - return ptr; -} - -static char* readPlatformSuaValue( - const char** json, StringBuilder *builder, - KeyValuePair* cache, Key key, - Exception* const exception) { - char *begin = builder->current; - *json = skipToNextChar(*json, '{'); - ExpectSymbol(*json, '{', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "brand\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - ++*json; - - char* ptr = readStringValue(json, builder, exception); - if (ptr == NULL) { - return NULL; - } - - cache[key].value = ValuePtr; - cache[key].valueLength = ptr - begin; - - cache[PLATFORMVERSION].value = NULL; - cache[PLATFORMVERSION].valueLength = 0; - - begin = ptr; - - *json = skipWhitespaces(*json); - - if (**json == '}') { - return begin; - } - - ExpectSymbol(*json, ',', return begin); - - *json = skipToNextChar(*json + 1, '"'); - ExpectSymbol(*json, '"', return begin); - - ++*json; - - for (const char* k = "version\""; *k != '\0'; ++k, ++*json) { - ExpectSymbol(*json, *k, return begin); - } - - *json = skipToNextChar(*json, ':'); - ExpectSymbol(*json, ':', return begin); - - *json = skipToNextChar(*json + 1, '['); - ExpectSymbol(*json, '[', return begin); - - ++*json; - NotExpectSymbol(*json, '\0', return begin); - - ptr = safeWriteToBuffer(builder, '"', exception); - ptr = readVersionSua(json, builder, exception); - - cache[PLATFORMVERSION].value = ValuePtr; - cache[PLATFORMVERSION].valueLength = ptr - begin; - - return ptr; -} - -static inline readValueCallback readValueSwitch(Key key, int isSua) { - readValueCallback res = NULL; - - switch (key) { - case ARCHITECTURE: { - res = readPureStringValue; - } break; - - case BITNESS: { - res = readPureStringValue; - } break; - - case BRANDS: { - res = isSua ? NULL : readBrandsGhevValue; - } break; - - case FULLVERSIONLIST: { - res = isSua ? readBrandsSuaValue : readBrandsGhevValue; - } break; - - case MOBILE: { - res = isSua ? readBoolSuaValue : readBoolGhevValue; - } break; - - case MODEL: { - res = readPureStringValue; - } break; - - case PLATFORM: { - res = isSua ? readPlatformSuaValue : readPureStringValue; - } break; - - case PLATFORMVERSION: { - res = isSua ? NULL : readPureStringValue; - } break; - - case KEY_UNDEFINED: { - res = NULL; - } break; - } - - return res; -} - -static bool pushToHeaders(void* ctx, KeyValuePair header) { - KeyValuePairArray* const headers = (KeyValuePairArray* const)ctx; - - if (headers->count < headers->capacity) { - KeyValuePair* pair = headers->items + headers->count++; - - pair->key = header.key; - pair->keyLength = header.keyLength; - pair->value = header.value; - pair->valueLength = header.valueLength; - } - return (headers->count < headers->capacity); -} -// ---------------------------------------------------------------------------- -static uint32_t mainParsingBody(const char* json, - StringBuilder *builder, - Exception* const exception, - int isSua, - TransformCallback callback, - void* ctx) { - KeyValuePair cache[KEY_UNDEFINED]; - - char *begin = builder->current; - // define buffer range - - // write keys to buffer, init cache and skip to the first key - json = initParsing(json, builder, cache, exception); - if (EXCEPTION_CHECK(CORRUPT_DATA)) { - return 0; - } - - uint32_t iterations = 0; // total number of parsed key-value pairs - - // main reading loop - readKeyCallback read_key = isSua ? readSuaKey : readGhevKey; - while (*json != '\0') { - Key key = read_key(&json, exception); - ExpectSymbol(json, '"', break); - - readValueCallback read_value = readValueSwitch(key, isSua); - if (key == KEY_UNDEFINED || read_value == NULL) { - json = skipValue(json + 1); - continue; - } - - json = skipToNextChar(json, ':'); - ExpectSymbol(json, ':', break); - - json = skipWhitespaces(json + 1); - NotExpectSymbol(json, '\0', break); - - char* ptr = read_value(&json, builder, cache, key, exception); - if (EXCEPTION_CHECK(CORRUPT_DATA)) { - break; - } - - if (ptr != NULL) { - begin = ptr; - - ++iterations; - if (!callback(ctx, cache[key])) { - break; - } - - if (key == PLATFORM && isSua && cache[PLATFORMVERSION].valueLength != 0) { - ++iterations; - if (!callback(ctx, cache[PLATFORMVERSION])) { - break; - } - } - } - - json = skipToNextChar(json, '"'); - } - - return iterations; -} - -// The difference of this function is that it does not initialize the builder - and assumes -// it has been initialized outside - useful for base64 and then JSON from the same buffer - -uint32_t -TransformIterateGhevFromJsonPrivate - (const char* json, StringBuilder *builder, - fiftyoneDegreesTransformCallback callback, - void* ctx, - fiftyoneDegreesException* const exception) { - return mainParsingBody(json, builder, exception, 0, callback, ctx); -} -// ------------------------------------------------------------------------------------------------ -fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformIterateGhevFromJson - (const char* json, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { - StringBuilder builder = {buffer, length}; - StringBuilderInit(&builder); - uint32_t iterations = TransformIterateGhevFromJsonPrivate(json, &builder, callback, ctx, exception); - StringBuilderComplete(&builder); - fiftyoneDegreesTransformIterateResult result = {iterations, builder.added, builder.added > builder.length}; - return result; -} - -fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformIterateGhevFromBase64 - (const char* base64, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { - StringBuilder builder = {buffer, length}; - StringBuilderInit(&builder); - base64Decode(base64, &builder, exception); - fiftyoneDegreesTransformIterateResult result = {0, builder.added, - builder.added > builder.length}; - if (EXCEPTION_CHECK(CORRUPT_DATA) || EXCEPTION_CHECK(INSUFFICIENT_MEMORY)) { - return result; - } - char *json = builder.ptr; - //note we are calling a private function to reuse the initialized stringbuilder - uint32_t iterations = TransformIterateGhevFromJsonPrivate(json, &builder, callback, ctx, exception); - StringBuilderComplete(&builder); - result.iterations = iterations; - result.written = builder.added; - result.bufferTooSmall = builder.added > builder.length; - return result; -} - -fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformIterateSua - (const char* json, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void* ctx, - fiftyoneDegreesException* const exception) { - StringBuilder builder = {buffer, length}; - StringBuilderInit(&builder); - uint32_t iterations = mainParsingBody(json, &builder, exception, 1, callback, ctx); - StringBuilderComplete(&builder); - fiftyoneDegreesTransformIterateResult result = {iterations, builder.added, builder.added > builder.length}; - return result; -} - -// Array methods internally relay on iterative methods -fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformGhevFromJson - (const char* json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception) { - uint32_t initial = headers->count; - fiftyoneDegreesTransformIterateResult result = - TransformIterateGhevFromJson(json, buffer, length, pushToHeaders, - headers, exception); - - if (result.iterations != headers->count - initial) { - EXCEPTION_SET(INSUFFICIENT_CAPACITY); - } - return result; -} - -fiftyoneDegreesTransformIterateResult fiftyoneDegreesTransformGhevFromBase64 - (const char* base64, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception) { - uint32_t initial = headers->count; - fiftyoneDegreesTransformIterateResult result = - TransformIterateGhevFromBase64(base64, buffer, length, pushToHeaders, headers, exception); - - if (result.iterations != headers->count - initial) { - EXCEPTION_SET(INSUFFICIENT_CAPACITY); - } - - return result; -} - -fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformSua - (const char* json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray* const headers, - fiftyoneDegreesException* const exception) { - uint32_t initial = headers->count; - fiftyoneDegreesTransformIterateResult result = - TransformIterateSua(json, buffer, length, pushToHeaders, headers, exception); - - if (result.iterations != headers->count - initial) { - EXCEPTION_SET(INSUFFICIENT_CAPACITY); - } - - return result; -} diff --git a/transform.h b/transform.h deleted file mode 100644 index 1b2c8bf4..00000000 --- a/transform.h +++ /dev/null @@ -1,344 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -#ifndef FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED -#define FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED - -#include -#include "pair.h" -#include "exceptions.h" - -/** - * User-Agent Client Hints (UACH) Representation Conversion Routines - * - * 3 common ways to represent UACH are: - * - [HTTP header map](https://wicg.github.io/ua-client-hints/) - * - getHighEntropyValues() JS API call result in JSON format - * - Structured User Agent Object from OpenRTB 2.6 - * - * Links: - * - - * [getHighEntropyValues()](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues) - * - - * [device.sua](https://51degrees.com/blog/openrtb-structured-user-agent-and-user-agent-client-hints) - * - [OpenRTB 2.6 - * spec](https://iabtechlab.com/wp-content/uploads/2022/04/OpenRTB-2-6_FINAL.pdf) - * - * 51Degrees uses HTTP header map to represent UACH and expects the evidence to - * be provided as HTTP headers (or same name query parameters). The header - * names in question are: - * - Sec-CH-UA - * - Sec-CH-UA-Platform - * - Sec-CH-UA-Mobile - * - Sec-CH-UA-Model - * - Sec-CH-UA-Full-Version-List - * - Sec-CH-UA-Platform-Version - * - Sec-CH-UA-Arch - * - Sec-CH-UA-Bitness - * - * The conversion routines transform the GetHighEntropyValues (GHEV) or - * Structured User Agent (SUA) SUA input into the HTTP header maps. - * - * Routines are provided in 2 styles: iterative (for potentially lazy - * consumption) and array-results (for eager consumption). The former uses - * callback to iteratively provide header name-value pairs to the caller, the - * latter provides the whole header map array as output. In addition 2 variants - * of GHEV routine is provided: one that accepts a raw JSON string and one that - * accepts a base64 encoded JSON string as input parameter. - * - * Both styles use an externally preallocated memory buffer to write the formed - * http header values to. The output callback or headermap will have pointers to - * the null-terminated strings stored in that buffer. Thus the buffer should - * outlive the last output evidence use. - */ - -/** - * Used as a return type from the conversion routines to carry information about - * the operation results to the caller, allows the caller to f.e. judge about the buffer utilization, - * and whether the buffer was of sufficient size - */ -typedef struct { - /** - * number of pairs of evidence extracted or would have been extracted and correspondingly calls - * to the callback made - */ - uint32_t iterations; - - /** - * number of characters written or that would have been written to the buffer, reflects required buffer size - */ - size_t written; - - /** - * the caller should check this flag and reallocate the buffer to be of at least `written` size - * if this flag is set - */ - bool bufferTooSmall; // -} fiftyoneDegreesTransformIterateResult; - -/** - * A callback function type definition that is called every time a header - * name-value pair is formed and allows the caller to decide how to handle the - * output. The callback function must be provided as a param to the - * Iterate-style conversion routines. - * @param state - arbitrary context object - f.e. external state or a structure - * to accumulate output - * @param header - a header key value pair containing the pointer to the header - * name and value - * @return the implementer returns true to continue the iteration or false to - * stop - */ -EXTERNAL typedef bool (*fiftyoneDegreesTransformCallback) -(void *state, fiftyoneDegreesKeyValuePair header); - -/** - * Iteratively convert getHighEntropyValue() API result JSON string to HTTP - * header representation. - * @param json a JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values. The lifetime of this buffer is managed by the - * caller - * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object - * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` - * if the conversion was successful, or will indicate error otherwise, s.a. - * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of - * insufficient size, in that case the callback will still be called, but value - * will be NULL and valueLength will indicate the length necessary to store the - * value in the buffer - this info can be then used by the caller to allocate - * the buffer of sufficient size and execute another call - essentially - * resulting in a dry run before allocation. - * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that - * case callback will likely not be called, or will be called a limited number - * of times until the corruption becomes obvious to the iterator as no lookahead - * logic is implemented - * @param callback a function that is called whenever a header key value is - * extracted header key value pair is passed as a param; if callback returns - * true, iteration continues, otherwise halts - * @param state an external context state to pass to be used by the callback - * function - * @return the number of iterations / header pairs detected (callback calls - * made) and buffer utilization information - */ -EXTERNAL fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformIterateGhevFromJson -(const char *json, char *const buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); - -/** - * Iteratively convert getHighEntropyValue() API result base64 encoded JSON - * string to HTTP header representation. - * @param base64 a base64 encoded JSON string with the getHighEntropyValue() API - * result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object - * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` - * if the conversion was successful, or will indicate error otherwise, s.a. - * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of - * insufficient size, in that case the callback will still be called, but value - * will be NULL and valueLength will indicate the length necessary to store the - * value in the buffer - this info can be then used by the caller to allocate - * the buffer of sufficient size and execute another call - essentially - * resulting in a dry run before allocation... - * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that - * case callback will likely not be called, or will be called a limited number - * of times until the corruption becomes obvious to the iterator as no lookahead - * logic is intended - * @param callback a function that is called whenever a header is extracted with - * header name and value passed as params if the function returns true, - * iteration continues, otherwise halts - * @param state an external context state to pass to be used by the callback - * function - * @return the number of iterations / header pairs detected (callback calls - * made) and buffer utilization information - */ -EXTERNAL fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformIterateGhevFromBase64 -(const char *base64, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); - -/** - * Iteratively convert device.sua JSON string to HTTP header representation. - * @param json a JSON string with the device.sua raw representation - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object - * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` - * if the conversion was successful, or will indicate error otherwise, s.a. - * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of - * insufficient size, in that case the callback will still be called, but value - * will be NULL and valueLength will indicate the length necessary to store the - * value in the buffer - this info can be then used by the caller to allocate - * the buffer of sufficient size and execute another call - essentially - * resulting in a dry run before allocation... - * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that - * case callback will likely not be called, or will be called a limited number - * of times until the corruption becomes obvious to the iterator as no lookahead - * logic is intended - * @param callback a function that is called whenever a header is extracted with - * header name and value passed as params if the function returns true, - * iteration continues, otherwise halts - * @param state an external context state to pass to be used by the callback - * function - * @return the number of iterations / header pairs detected (callback calls - * made) and buffer utilization information - */ -EXTERNAL fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformIterateSua -(const char *json, char *buffer, size_t length, - fiftyoneDegreesTransformCallback callback, void *state, - fiftyoneDegreesException *const exception); - -/** - * Eagerly convert getHighEntropyValue() API result JSON string to HTTP header - * representation. - * @param json a JSON string with the getHighEntropyValue() API result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object - * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` - * if the conversion was successful, or will indicate error otherwise, s.a. - * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of - * insufficient size, in that case the callback will still be called, but value - * will be NULL and valueLength will indicate the length necessary to store the - * value in the buffer - this info can be then used by the caller to allocate - * the buffer of sufficient size and execute another call - essentially - * resulting in a dry run before allocation... - * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that - * case callback will likely not be called, or will be called a limited number - * of times until the corruption becomes obvious to the iterator as no lookahead - * logic is intended - * @param headers a preallocated (via `FIFTYONE_DEGREES_ARRAY_CREATE` macro) array - * of capacity enough to hold up to 8 UACH headers; upon function return will - * contain the output http header names and value const char * pointers either - * to the DATA segment allocated (names) string constants or preallocated buffer - * on the heap. must not be NULL and has to be memory managed outside of the - * function, its lifetime should be long enough to survive the last use of the - * returned headers - * @return result.iterations specifies the number of headers that was written or - * should have been written to the array. in case this number is higher than headers->capacity - * case the exception will have `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY` - * status and the returned capacity will signal the array size that needs to be - * allocated. result.written and result.bufferTooSmall provide buffer utilization information - */ -EXTERNAL fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformGhevFromJson -(const char *json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception); - -/** - * Eagerly convert getHighEntropyValue() API result from base64-encoded JSON - * string to HTTP header representation. - * @param base64 a base64-encoded JSON string with the getHighEntropyValue() API - * result - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object - * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be `FIFTYONE_DEGREES_STATUS_SUCCESS` - * if the conversion was successful, or will indicate error otherwise, s.a. - * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of - * insufficient size, in that case the callback will still be called, but value - * will be NULL and valueLength will indicate the length necessary to store the - * value in the buffer - this info can be then used by the caller to allocate - * the buffer of sufficient size and execute another call - essentially - * resulting in a dry run before allocation... - * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that - * case callback will likely not be called, or will be called a limited number - * of times until the corruption becomes obvious to the iterator as no lookahead - * logic is intended - * @param headers a preallocated (via `FIFTYONE_DEGREES_ARRAY_CREATE` macro) array - * of capacity enough to hold up to 8 UACH headers; upon function return will - * contain the output http header names and value const char * pointers either - * to the DATA segment allocated (names) string constants or preallocated buffer - * on the heap. must not be NULL and has to be memory managed outside of the - * function, its lifetime should be long enough to survive the last use of the - * returned headers - * @return result.iterations specifies the number of headers that was written or - * should have been written to the array. in case this number is higher than headers->capacity - * case the exception will have `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY` - * status and the returned capacity will signal the array size that needs to be - * allocated. result.written and result.bufferTooSmall provide buffer utilization information - */ -EXTERNAL fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformGhevFromBase64 -(const char *base64, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception); - -/** - * Eagerly convert device.sua JSON string to HTTP header representation. - * @param json a raw JSON string with device.sua field contents - * @param buffer preallocated working memory buffer used to store the converted - * HTTP header names and values with a new line separator (\n) between each - * header key-value pair. The lifetime of this buffer is managed by the caller - * @param length length of the buffer - * @param exception - a constant pointer to a (preallocated) exception object - * that is filled in case any errors occurred. must be checked by the caller - * upon routine exit. `exception.status` will be FIFTYONE_DEGREES_STATUS_SUCCESS - * if the conversion was successful, or will indicate error otherwise, s.a. - * - `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_MEMORY` if provided buffer was of - * insufficient size, in that case the callback will still be called, but value - * will be NULL and valueLength will indicate the length necessary to store the - * value in the buffer - this info can be then used by the caller to allocate - * the buffer of sufficient size and execute another call - essentially - * resulting in a dry run before allocation... - * - `FIFTYONE_DEGREES_STATUS_CORRUPT_DATA` if f.e. JSON was malformed - in that - * case callback will likely not be called, or will be called a limited number - * of times until the corruption becomes obvious to the iterator as no lookahead - * logic is intended - * @param headers a preallocated (via `FIFTYONE_DEGREES_ARRAY_CREATE` macro) array - * of capacity enough to hold up to 8 UACH headers; upon function return will - * contain the output http header names and value const char * pointers either - * to the DATA segment allocated (names) string constants or preallocated buffer - * on the heap. must not be NULL and has to be memory managed outside of the - * function, its lifetime should be long enough to survive the last use of the - * returned headers - * @return result.iterations specifies the number of headers that was written or - * should have been written to the array. in case this number is higher than headers->capacity - * case the exception will have `FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY` - * status and the returned capacity will signal the array size that needs to be - * allocated. result.written and result.bufferTooSmall provide buffer utilization information - */ -EXTERNAL fiftyoneDegreesTransformIterateResult -fiftyoneDegreesTransformSua -(const char *json, char *buffer, size_t length, - fiftyoneDegreesKeyValuePairArray *const headers, - fiftyoneDegreesException *const exception); - -#endif /* FIFTYONE_DEGREES_TRANSFORM_H_INCLUDED */ From 7f334e0dff586fa7d61cb72f21e1c0523ccb1f08 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Wed, 11 Sep 2024 13:44:51 +0200 Subject: [PATCH 68/73] FIX: remove transform from VS projects --- VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj | 2 -- VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj | 2 -- .../Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj | 1 - 3 files changed, 5 deletions(-) diff --git a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj index a21583b9..de228a36 100644 --- a/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj +++ b/VisualStudio/FiftyOne.Common.C/FiftyOne.Common.C.vcxproj @@ -37,7 +37,6 @@ - @@ -72,7 +71,6 @@ - diff --git a/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj b/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj index 30a7aee5..418756f8 100644 --- a/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj +++ b/VisualStudio/FiftyOne.Common.CPP/FiftyOne.Common.CPP.vcxproj @@ -15,7 +15,6 @@ - @@ -35,7 +34,6 @@ - diff --git a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj index 5b61549c..b3edce53 100644 --- a/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj +++ b/VisualStudio/Fiftyone.Common.Tests/Fiftyone.Common.Tests.vcxproj @@ -50,7 +50,6 @@ - From 37c3a8bb8030ba9759a82d6313da32ea21c00b72 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Wed, 11 Sep 2024 13:47:07 +0200 Subject: [PATCH 69/73] FIX: removed Transform routines interop --- README.md | 29 - common.i | 32 - commonCSHARP_wrap.cxx | 1288 ----------------- interop/dotnet/CMakeLists.txt | 94 -- .../dotnet/FiftyoneDegrees.Common/Common.cs | 16 - .../FiftyoneDegrees.Common/CommonPINVOKE.cs | 338 ----- .../FiftyoneDegrees.Common.csproj | 17 - .../FiftyoneDegrees.Common.sln | 25 - .../MapStringStringSwig.cs | 313 ---- .../FiftyoneDegrees.Common/Transform.cs | 86 -- .../VectorStringSwig.cs | 374 ----- .../example/TransformExample/Program.cs | 45 - .../TransformExample/TransformExample.csproj | 14 - .../TransformExample/TransformExample.sln | 31 - 14 files changed, 2702 deletions(-) delete mode 100644 common.i delete mode 100644 commonCSHARP_wrap.cxx delete mode 100644 interop/dotnet/CMakeLists.txt delete mode 100644 interop/dotnet/FiftyoneDegrees.Common/Common.cs delete mode 100644 interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs delete mode 100644 interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj delete mode 100644 interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln delete mode 100644 interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs delete mode 100644 interop/dotnet/FiftyoneDegrees.Common/Transform.cs delete mode 100644 interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs delete mode 100644 interop/dotnet/example/TransformExample/Program.cs delete mode 100644 interop/dotnet/example/TransformExample/TransformExample.csproj delete mode 100644 interop/dotnet/example/TransformExample/TransformExample.sln diff --git a/README.md b/README.md index ed177329..2b8e00b5 100644 --- a/README.md +++ b/README.md @@ -155,32 +155,3 @@ To check the summary run: ``` gcovr -r . --print-summary ``` - -# Interoperability - -Under interoperability we understand the ability to use (explicitly) exposed and wrapped `common-cxx` code directly from higher-level languages. For now C# is the primary target. - -## Approach - -- SWIG interface definitions are in the `common.i` (and included files s.a. `Transform.i`). -- There is an `interop/dotnet` directory containing: - - `CMakeLists.txt` file that would update SWIG generated wrapper files if SWIG is installed and build the native shared library exposing SWIG generated native interfaces - - The generated SWIG .cs (C#) wrapper files are output into the `interop/dotnet/FiftyoneDegrees.Common` directory that contains a project that one can reference in their C# project to use the native common-cxx routines - - The usage example is provided as part of the `examples/TransformExample` - -## Integration - -C# integration is as simple as adding `interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj` into your solution - so it becomes a project reference dependency. Then you can refer to the exposed APIs in C# -by first `using FiftyoneDegrees.Common` namespace and then calling APIs s.a. f.e.: - -- `Transform` class -- `MapStringStringSwig` - a simple Dictionary like container - -The `.csproj` includes a call to cmake to pre-build a native DLL and then copies that artifact into the output directory as needed. This integration is demonstrated as parrt of the `interop/dotnet/examples/TransformExample`. - -You can simply navigate into that directoy and run `dotnet run`. - -**Note:** if you are using Visual Studio - sometimes it runs the pre-build targets behind the scenes - so it will invoke -`cmake` and your manual build will invoke it again - and concurrent cmake builds may cause some errors in the -build process. They usually are resolved by closing Visual Studio, running `git clean -fdx` in the `common-cxx` -directory (so all CMake caches are removed), then doing a `dotnet run` once, so cmake creates correct artifacts. diff --git a/common.i b/common.i deleted file mode 100644 index 4169b06e..00000000 --- a/common.i +++ /dev/null @@ -1,32 +0,0 @@ -/* ********************************************************************* - * This Original Work is copyright of 51 Degrees Mobile Experts Limited. - * Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House, - * Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU. - * - * This Original Work is licensed under the European Union Public Licence - * (EUPL) v.1.2 and is subject to its terms as set out below. - * - * If a copy of the EUPL was not distributed with this file, You can obtain - * one at https://opensource.org/licenses/EUPL-1.2. - * - * The 'Compatible Licences' set out in the Appendix to the EUPL (as may be - * amended by the European Commission) shall be deemed incompatible for - * the purposes of the Work and the provisions of the compatibility - * clause in Article 5 of the EUPL shall not apply. - * - * If using the Work as, or as part of, a network application, by - * including the attribution notice(s) required under Article 5 of the EUPL - * in the end user terms of the application under an appropriate heading, - * such notice(s) shall fulfill the requirements of that article. - * ********************************************************************* */ - -%module "Common" - -%{ -#include "Transform.hpp" - -using namespace FiftyoneDegrees::Common; - -%} - -%include "Transform.i" \ No newline at end of file diff --git a/commonCSHARP_wrap.cxx b/commonCSHARP_wrap.cxx deleted file mode 100644 index 7f3df2b9..00000000 --- a/commonCSHARP_wrap.cxx +++ /dev/null @@ -1,1288 +0,0 @@ -/* ---------------------------------------------------------------------------- - * This file was automatically generated by SWIG (https://www.swig.org). - * Version 4.2.1 - * - * Do not make changes to this file unless you know what you are doing - modify - * the SWIG interface file instead. - * ----------------------------------------------------------------------------- */ - - -#define SWIG_VERSION 0x040201 -#define SWIGCSHARP - -/* ----------------------------------------------------------------------------- - * This section contains generic SWIG labels for method/variable - * declarations/attributes, and other compiler dependent labels. - * ----------------------------------------------------------------------------- */ - -/* template workaround for compilers that cannot correctly implement the C++ standard */ -#ifndef SWIGTEMPLATEDISAMBIGUATOR -# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) -# define SWIGTEMPLATEDISAMBIGUATOR template -# elif defined(__HP_aCC) -/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ -/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ -# define SWIGTEMPLATEDISAMBIGUATOR template -# else -# define SWIGTEMPLATEDISAMBIGUATOR -# endif -#endif - -/* inline attribute */ -#ifndef SWIGINLINE -# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) -# define SWIGINLINE inline -# else -# define SWIGINLINE -# endif -#endif - -/* attribute recognised by some compilers to avoid 'unused' warnings */ -#ifndef SWIGUNUSED -# if defined(__GNUC__) -# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -# elif defined(__ICC) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -#endif - -#ifndef SWIG_MSC_UNSUPPRESS_4505 -# if defined(_MSC_VER) -# pragma warning(disable : 4505) /* unreferenced local function has been removed */ -# endif -#endif - -#ifndef SWIGUNUSEDPARM -# ifdef __cplusplus -# define SWIGUNUSEDPARM(p) -# else -# define SWIGUNUSEDPARM(p) p SWIGUNUSED -# endif -#endif - -/* internal SWIG method */ -#ifndef SWIGINTERN -# define SWIGINTERN static SWIGUNUSED -#endif - -/* internal inline SWIG method */ -#ifndef SWIGINTERNINLINE -# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE -#endif - -/* exporting methods */ -#if defined(__GNUC__) -# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -# ifndef GCC_HASCLASSVISIBILITY -# define GCC_HASCLASSVISIBILITY -# endif -# endif -#endif - -#ifndef SWIGEXPORT -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# if defined(STATIC_LINKED) -# define SWIGEXPORT -# else -# define SWIGEXPORT __declspec(dllexport) -# endif -# else -# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) -# define SWIGEXPORT __attribute__ ((visibility("default"))) -# else -# define SWIGEXPORT -# endif -# endif -#endif - -/* calling conventions for Windows */ -#ifndef SWIGSTDCALL -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# define SWIGSTDCALL __stdcall -# else -# define SWIGSTDCALL -# endif -#endif - -/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ -#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) -# define _CRT_SECURE_NO_DEPRECATE -#endif - -/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ -#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) -# define _SCL_SECURE_NO_DEPRECATE -#endif - -/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ -#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) -# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 -#endif - -/* Intel's compiler complains if a variable which was never initialised is - * cast to void, which is a common idiom which we use to indicate that we - * are aware a variable isn't used. So we just silence that warning. - * See: https://github.com/swig/swig/issues/192 for more discussion. - */ -#ifdef __INTEL_COMPILER -# pragma warning disable 592 -#endif - -#if defined(__cplusplus) && __cplusplus >=201103L -# define SWIG_NULLPTR nullptr -#else -# define SWIG_NULLPTR NULL -#endif - -/* ----------------------------------------------------------------------------- - * swigcompat.swg - * - * Macros to provide support compatibility with older C and C++ standards. - * ----------------------------------------------------------------------------- */ - -/* C99 and C++11 should provide snprintf, but define SWIG_NO_SNPRINTF - * if you're missing it. - */ -#if ((defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \ - (defined __cplusplus && __cplusplus >= 201103L) || \ - defined SWIG_HAVE_SNPRINTF) && \ - !defined SWIG_NO_SNPRINTF -# define SWIG_snprintf(O,S,F,A) snprintf(O,S,F,A) -# define SWIG_snprintf2(O,S,F,A,B) snprintf(O,S,F,A,B) -#else -/* Fallback versions ignore the buffer size, but most of our uses either have a - * fixed maximum possible size or dynamically allocate a buffer that's large - * enough. - */ -# define SWIG_snprintf(O,S,F,A) sprintf(O,F,A) -# define SWIG_snprintf2(O,S,F,A,B) sprintf(O,F,A,B) -#endif - - -#include -#include -#include - - -/* Support for throwing C# exceptions from C/C++. There are two types: - * Exceptions that take a message and ArgumentExceptions that take a message and a parameter name. */ -typedef enum { - SWIG_CSharpApplicationException, - SWIG_CSharpArithmeticException, - SWIG_CSharpDivideByZeroException, - SWIG_CSharpIndexOutOfRangeException, - SWIG_CSharpInvalidCastException, - SWIG_CSharpInvalidOperationException, - SWIG_CSharpIOException, - SWIG_CSharpNullReferenceException, - SWIG_CSharpOutOfMemoryException, - SWIG_CSharpOverflowException, - SWIG_CSharpSystemException -} SWIG_CSharpExceptionCodes; - -typedef enum { - SWIG_CSharpArgumentException, - SWIG_CSharpArgumentNullException, - SWIG_CSharpArgumentOutOfRangeException -} SWIG_CSharpExceptionArgumentCodes; - -typedef void (SWIGSTDCALL* SWIG_CSharpExceptionCallback_t)(const char *); -typedef void (SWIGSTDCALL* SWIG_CSharpExceptionArgumentCallback_t)(const char *, const char *); - -typedef struct { - SWIG_CSharpExceptionCodes code; - SWIG_CSharpExceptionCallback_t callback; -} SWIG_CSharpException_t; - -typedef struct { - SWIG_CSharpExceptionArgumentCodes code; - SWIG_CSharpExceptionArgumentCallback_t callback; -} SWIG_CSharpExceptionArgument_t; - -static SWIG_CSharpException_t SWIG_csharp_exceptions[] = { - { SWIG_CSharpApplicationException, NULL }, - { SWIG_CSharpArithmeticException, NULL }, - { SWIG_CSharpDivideByZeroException, NULL }, - { SWIG_CSharpIndexOutOfRangeException, NULL }, - { SWIG_CSharpInvalidCastException, NULL }, - { SWIG_CSharpInvalidOperationException, NULL }, - { SWIG_CSharpIOException, NULL }, - { SWIG_CSharpNullReferenceException, NULL }, - { SWIG_CSharpOutOfMemoryException, NULL }, - { SWIG_CSharpOverflowException, NULL }, - { SWIG_CSharpSystemException, NULL } -}; - -static SWIG_CSharpExceptionArgument_t SWIG_csharp_exceptions_argument[] = { - { SWIG_CSharpArgumentException, NULL }, - { SWIG_CSharpArgumentNullException, NULL }, - { SWIG_CSharpArgumentOutOfRangeException, NULL } -}; - -static void SWIGUNUSED SWIG_CSharpSetPendingException(SWIG_CSharpExceptionCodes code, const char *msg) { - SWIG_CSharpExceptionCallback_t callback = SWIG_csharp_exceptions[SWIG_CSharpApplicationException].callback; - if ((size_t)code < sizeof(SWIG_csharp_exceptions)/sizeof(SWIG_CSharpException_t)) { - callback = SWIG_csharp_exceptions[code].callback; - } - callback(msg); -} - -static void SWIGUNUSED SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpExceptionArgumentCodes code, const char *msg, const char *param_name) { - SWIG_CSharpExceptionArgumentCallback_t callback = SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentException].callback; - if ((size_t)code < sizeof(SWIG_csharp_exceptions_argument)/sizeof(SWIG_CSharpExceptionArgument_t)) { - callback = SWIG_csharp_exceptions_argument[code].callback; - } - callback(msg, param_name); -} - - -#ifdef __cplusplus -extern "C" -#endif -SWIGEXPORT void SWIGSTDCALL SWIGRegisterExceptionCallbacks_Common( - SWIG_CSharpExceptionCallback_t applicationCallback, - SWIG_CSharpExceptionCallback_t arithmeticCallback, - SWIG_CSharpExceptionCallback_t divideByZeroCallback, - SWIG_CSharpExceptionCallback_t indexOutOfRangeCallback, - SWIG_CSharpExceptionCallback_t invalidCastCallback, - SWIG_CSharpExceptionCallback_t invalidOperationCallback, - SWIG_CSharpExceptionCallback_t ioCallback, - SWIG_CSharpExceptionCallback_t nullReferenceCallback, - SWIG_CSharpExceptionCallback_t outOfMemoryCallback, - SWIG_CSharpExceptionCallback_t overflowCallback, - SWIG_CSharpExceptionCallback_t systemCallback) { - SWIG_csharp_exceptions[SWIG_CSharpApplicationException].callback = applicationCallback; - SWIG_csharp_exceptions[SWIG_CSharpArithmeticException].callback = arithmeticCallback; - SWIG_csharp_exceptions[SWIG_CSharpDivideByZeroException].callback = divideByZeroCallback; - SWIG_csharp_exceptions[SWIG_CSharpIndexOutOfRangeException].callback = indexOutOfRangeCallback; - SWIG_csharp_exceptions[SWIG_CSharpInvalidCastException].callback = invalidCastCallback; - SWIG_csharp_exceptions[SWIG_CSharpInvalidOperationException].callback = invalidOperationCallback; - SWIG_csharp_exceptions[SWIG_CSharpIOException].callback = ioCallback; - SWIG_csharp_exceptions[SWIG_CSharpNullReferenceException].callback = nullReferenceCallback; - SWIG_csharp_exceptions[SWIG_CSharpOutOfMemoryException].callback = outOfMemoryCallback; - SWIG_csharp_exceptions[SWIG_CSharpOverflowException].callback = overflowCallback; - SWIG_csharp_exceptions[SWIG_CSharpSystemException].callback = systemCallback; -} - -#ifdef __cplusplus -extern "C" -#endif -SWIGEXPORT void SWIGSTDCALL SWIGRegisterExceptionArgumentCallbacks_Common( - SWIG_CSharpExceptionArgumentCallback_t argumentCallback, - SWIG_CSharpExceptionArgumentCallback_t argumentNullCallback, - SWIG_CSharpExceptionArgumentCallback_t argumentOutOfRangeCallback) { - SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentException].callback = argumentCallback; - SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentNullException].callback = argumentNullCallback; - SWIG_csharp_exceptions_argument[SWIG_CSharpArgumentOutOfRangeException].callback = argumentOutOfRangeCallback; -} - - -/* Callback for returning strings to C# without leaking memory */ -typedef char * (SWIGSTDCALL* SWIG_CSharpStringHelperCallback)(const char *); -static SWIG_CSharpStringHelperCallback SWIG_csharp_string_callback = NULL; - - -#ifdef __cplusplus -extern "C" -#endif -SWIGEXPORT void SWIGSTDCALL SWIGRegisterStringCallback_Common(SWIG_CSharpStringHelperCallback callback) { - SWIG_csharp_string_callback = callback; -} - - -/* Contract support */ - -#define SWIG_contract_assert(nullreturn, expr, msg) do { if (!(expr)) {SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, msg, ""); return nullreturn; } } while (0) - - -#ifdef __cplusplus -#include -/* SwigValueWrapper is described in swig.swg */ -template class SwigValueWrapper { - struct SwigSmartPointer { - T *ptr; - SwigSmartPointer(T *p) : ptr(p) { } - ~SwigSmartPointer() { delete ptr; } - SwigSmartPointer& operator=(SwigSmartPointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; } - void reset(T *p) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = p; } - } pointer; - SwigValueWrapper& operator=(const SwigValueWrapper& rhs); - SwigValueWrapper(const SwigValueWrapper& rhs); -public: - SwigValueWrapper() : pointer(0) { } - SwigValueWrapper& operator=(const T& t) { SwigSmartPointer tmp(new T(t)); pointer = tmp; return *this; } -#if __cplusplus >=201103L - SwigValueWrapper& operator=(T&& t) { SwigSmartPointer tmp(new T(std::move(t))); pointer = tmp; return *this; } - operator T&&() const { return std::move(*pointer.ptr); } -#else - operator T&() const { return *pointer.ptr; } -#endif - T *operator&() const { return pointer.ptr; } - static void reset(SwigValueWrapper& t, T *p) { t.pointer.reset(p); } -}; - -/* - * SwigValueInit() is a generic initialisation solution as the following approach: - * - * T c_result = T(); - * - * doesn't compile for all types for example: - * - * unsigned int c_result = unsigned int(); - */ -template T SwigValueInit() { - return T(); -} - -#if __cplusplus >=201103L -# define SWIG_STD_MOVE(OBJ) std::move(OBJ) -#else -# define SWIG_STD_MOVE(OBJ) OBJ -#endif - -#endif - - -#include "Transform.hpp" - -using namespace FiftyoneDegrees::Common; - - - -#include - - -#include -#include - - -#include -#include -#include - - -#include -#include -#include - -SWIGINTERN std::map< std::string,std::string >::mapped_type const &std_map_Sl_std_string_Sc_std_string_Sg__getitem(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key){ - std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); - if (iter != self->end()) - return iter->second; - else - throw std::out_of_range("key not found"); - } -SWIGINTERN void std_map_Sl_std_string_Sc_std_string_Sg__setitem(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key,std::map< std::string,std::string >::mapped_type const &x){ -#ifdef __cpp_lib_map_try_emplace - (*self).insert_or_assign(key, x); -#else - (*self)[key] = x; -#endif - } -SWIGINTERN bool std_map_Sl_std_string_Sc_std_string_Sg__ContainsKey(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key){ - std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); - return iter != self->end(); - } -SWIGINTERN void std_map_Sl_std_string_Sc_std_string_Sg__Add(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key,std::map< std::string,std::string >::mapped_type const &value){ - std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); - if (iter != self->end()) - throw std::out_of_range("key already exists"); - self->insert(std::pair< std::string, std::string >(key, value)); - } -SWIGINTERN bool std_map_Sl_std_string_Sc_std_string_Sg__Remove(std::map< std::string,std::string > *self,std::map< std::string,std::string >::key_type const &key){ - std::map< std::string, std::string, std::less< std::string > >::iterator iter = self->find(key); - if (iter != self->end()) { - self->erase(iter); - return true; - } - return false; - } -SWIGINTERN std::map< std::string,std::string,std::less< std::string > >::iterator *std_map_Sl_std_string_Sc_std_string_Sg__create_iterator_begin(std::map< std::string,std::string > *self){ - return new std::map< std::string, std::string, std::less< std::string > >::iterator(self->begin()); - } -SWIGINTERN std::map< std::string,std::string >::key_type const &std_map_Sl_std_string_Sc_std_string_Sg__get_next_key(std::map< std::string,std::string > *self,std::map< std::string,std::string,std::less< std::string > >::iterator *swigiterator){ - (void)self; - std::map< std::string, std::string, std::less< std::string > >::iterator iter = *swigiterator; - (*swigiterator)++; - return (*iter).first; - } -SWIGINTERN void std_map_Sl_std_string_Sc_std_string_Sg__destroy_iterator(std::map< std::string,std::string > *self,std::map< std::string,std::string,std::less< std::string > >::iterator *swigiterator){ - (void)self; - delete swigiterator; - } -SWIGINTERN std::vector< std::string > *new_std_vector_Sl_std_string_Sg___SWIG_2(int capacity){ - std::vector< std::string >* pv = 0; - if (capacity >= 0) { - pv = new std::vector< std::string >(); - pv->reserve(capacity); - } else { - throw std::out_of_range("capacity"); - } - return pv; - } -SWIGINTERN std::string std_vector_Sl_std_string_Sg__getitemcopy(std::vector< std::string > *self,int index){ - if (index>=0 && index<(int)self->size()) - return (*self)[index]; - else - throw std::out_of_range("index"); - } -SWIGINTERN std::vector< std::string >::value_type const &std_vector_Sl_std_string_Sg__getitem(std::vector< std::string > *self,int index){ - if (index>=0 && index<(int)self->size()) - return (*self)[index]; - else - throw std::out_of_range("index"); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__setitem(std::vector< std::string > *self,int index,std::string const &val){ - if (index>=0 && index<(int)self->size()) - (*self)[index] = val; - else - throw std::out_of_range("index"); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__AddRange(std::vector< std::string > *self,std::vector< std::string > const &values){ - self->insert(self->end(), values.begin(), values.end()); - } -SWIGINTERN std::vector< std::string > *std_vector_Sl_std_string_Sg__GetRange(std::vector< std::string > *self,int index,int count){ - if (index < 0) - throw std::out_of_range("index"); - if (count < 0) - throw std::out_of_range("count"); - if (index >= (int)self->size()+1 || index+count > (int)self->size()) - throw std::invalid_argument("invalid range"); - return new std::vector< std::string >(self->begin()+index, self->begin()+index+count); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__Insert(std::vector< std::string > *self,int index,std::string const &x){ - if (index>=0 && index<(int)self->size()+1) - self->insert(self->begin()+index, x); - else - throw std::out_of_range("index"); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__InsertRange(std::vector< std::string > *self,int index,std::vector< std::string > const &values){ - if (index>=0 && index<(int)self->size()+1) - self->insert(self->begin()+index, values.begin(), values.end()); - else - throw std::out_of_range("index"); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__RemoveAt(std::vector< std::string > *self,int index){ - if (index>=0 && index<(int)self->size()) - self->erase(self->begin() + index); - else - throw std::out_of_range("index"); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__RemoveRange(std::vector< std::string > *self,int index,int count){ - if (index < 0) - throw std::out_of_range("index"); - if (count < 0) - throw std::out_of_range("count"); - if (index >= (int)self->size()+1 || index+count > (int)self->size()) - throw std::invalid_argument("invalid range"); - self->erase(self->begin()+index, self->begin()+index+count); - } -SWIGINTERN std::vector< std::string > *std_vector_Sl_std_string_Sg__Repeat(std::string const &value,int count){ - if (count < 0) - throw std::out_of_range("count"); - return new std::vector< std::string >(count, value); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__Reverse__SWIG_0(std::vector< std::string > *self){ - std::reverse(self->begin(), self->end()); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__Reverse__SWIG_1(std::vector< std::string > *self,int index,int count){ - if (index < 0) - throw std::out_of_range("index"); - if (count < 0) - throw std::out_of_range("count"); - if (index >= (int)self->size()+1 || index+count > (int)self->size()) - throw std::invalid_argument("invalid range"); - std::reverse(self->begin()+index, self->begin()+index+count); - } -SWIGINTERN void std_vector_Sl_std_string_Sg__SetRange(std::vector< std::string > *self,int index,std::vector< std::string > const &values){ - if (index < 0) - throw std::out_of_range("index"); - if (index+values.size() > self->size()) - throw std::out_of_range("index"); - std::copy(values.begin(), values.end(), self->begin()+index); - } -SWIGINTERN bool std_vector_Sl_std_string_Sg__Contains(std::vector< std::string > *self,std::string const &value){ - return std::find(self->begin(), self->end(), value) != self->end(); - } -SWIGINTERN int std_vector_Sl_std_string_Sg__IndexOf(std::vector< std::string > *self,std::string const &value){ - int index = -1; - std::vector< std::string >::iterator it = std::find(self->begin(), self->end(), value); - if (it != self->end()) - index = (int)(it - self->begin()); - return index; - } -SWIGINTERN int std_vector_Sl_std_string_Sg__LastIndexOf(std::vector< std::string > *self,std::string const &value){ - int index = -1; - std::vector< std::string >::reverse_iterator rit = std::find(self->rbegin(), self->rend(), value); - if (rit != self->rend()) - index = (int)(self->rend() - 1 - rit); - return index; - } -SWIGINTERN bool std_vector_Sl_std_string_Sg__Remove(std::vector< std::string > *self,std::string const &value){ - std::vector< std::string >::iterator it = std::find(self->begin(), self->end(), value); - if (it != self->end()) { - self->erase(it); - return true; - } - return false; - } - -#ifdef __cplusplus -extern "C" { -#endif - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_0___() { - void * jresult ; - std::map< std::string,std::string > *result = 0 ; - - result = (std::map< std::string,std::string > *)new std::map< std::string,std::string >(); - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_1___(void * jarg1) { - void * jresult ; - std::map< std::string,std::string > *arg1 = 0 ; - std::map< std::string,std::string > *result = 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - if (!arg1) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::map< std::string,std::string > const & is null", 0); - return 0; - } - result = (std::map< std::string,std::string > *)new std::map< std::string,std::string >((std::map< std::string,std::string > const &)*arg1); - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_size___(void * jarg1) { - unsigned int jresult ; - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string >::size_type result; - - arg1 = (std::map< std::string,std::string > *)jarg1; - result = ((std::map< std::string,std::string > const *)arg1)->size(); - jresult = (unsigned int)result; - return jresult; -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_empty___(void * jarg1) { - unsigned int jresult ; - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - bool result; - - arg1 = (std::map< std::string,std::string > *)jarg1; - result = (bool)((std::map< std::string,std::string > const *)arg1)->empty(); - jresult = result; - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Clear___(void * jarg1) { - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - (arg1)->clear(); -} - - -SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_getitem___(void * jarg1, const char * jarg2) { - const char * jresult ; - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string >::key_type *arg2 = 0 ; - std::map< std::string,std::string >::mapped_type *result = 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::map< std::string,std::string >::key_type arg2_str(jarg2); - arg2 = &arg2_str; - try { - result = (std::map< std::string,std::string >::mapped_type *) &std_map_Sl_std_string_Sc_std_string_Sg__getitem(arg1,(std::string const &)*arg2); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return 0; - } - jresult = SWIG_csharp_string_callback(result->c_str()); - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_setitem___(void * jarg1, const char * jarg2, const char * jarg3) { - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string >::key_type *arg2 = 0 ; - std::map< std::string,std::string >::mapped_type *arg3 = 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return ; - } - std::map< std::string,std::string >::key_type arg2_str(jarg2); - arg2 = &arg2_str; - if (!jarg3) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return ; - } - std::map< std::string,std::string >::mapped_type arg3_str(jarg3); - arg3 = &arg3_str; - std_map_Sl_std_string_Sc_std_string_Sg__setitem(arg1,(std::string const &)*arg2,(std::string const &)*arg3); -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_ContainsKey___(void * jarg1, const char * jarg2) { - unsigned int jresult ; - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string >::key_type *arg2 = 0 ; - bool result; - - arg1 = (std::map< std::string,std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::map< std::string,std::string >::key_type arg2_str(jarg2); - arg2 = &arg2_str; - result = (bool)std_map_Sl_std_string_Sc_std_string_Sg__ContainsKey(arg1,(std::string const &)*arg2); - jresult = result; - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Add___(void * jarg1, const char * jarg2, const char * jarg3) { - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string >::key_type *arg2 = 0 ; - std::map< std::string,std::string >::mapped_type *arg3 = 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return ; - } - std::map< std::string,std::string >::key_type arg2_str(jarg2); - arg2 = &arg2_str; - if (!jarg3) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return ; - } - std::map< std::string,std::string >::mapped_type arg3_str(jarg3); - arg3 = &arg3_str; - try { - std_map_Sl_std_string_Sc_std_string_Sg__Add(arg1,(std::string const &)*arg2,(std::string const &)*arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Remove___(void * jarg1, const char * jarg2) { - unsigned int jresult ; - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string >::key_type *arg2 = 0 ; - bool result; - - arg1 = (std::map< std::string,std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::map< std::string,std::string >::key_type arg2_str(jarg2); - arg2 = &arg2_str; - result = (bool)std_map_Sl_std_string_Sc_std_string_Sg__Remove(arg1,(std::string const &)*arg2); - jresult = result; - return jresult; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_create_iterator_begin___(void * jarg1) { - void * jresult ; - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string,std::less< std::string > >::iterator *result = 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - result = (std::map< std::string,std::string,std::less< std::string > >::iterator *)std_map_Sl_std_string_Sc_std_string_Sg__create_iterator_begin(arg1); - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_get_next_key___(void * jarg1, void * jarg2) { - const char * jresult ; - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string,std::less< std::string > >::iterator *arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *) 0 ; - std::map< std::string,std::string >::key_type *result = 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *)jarg2; - result = (std::map< std::string,std::string >::key_type *) &std_map_Sl_std_string_Sc_std_string_Sg__get_next_key(arg1,arg2); - jresult = SWIG_csharp_string_callback(result->c_str()); - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_destroy_iterator___(void * jarg1, void * jarg2) { - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - std::map< std::string,std::string,std::less< std::string > >::iterator *arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *) 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - arg2 = (std::map< std::string,std::string,std::less< std::string > >::iterator *)jarg2; - std_map_Sl_std_string_Sc_std_string_Sg__destroy_iterator(arg1,arg2); -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_delete_MapStringStringSwig___(void * jarg1) { - std::map< std::string,std::string > *arg1 = (std::map< std::string,std::string > *) 0 ; - - arg1 = (std::map< std::string,std::string > *)jarg1; - delete arg1; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_0___() { - void * jresult ; - std::vector< std::string > *result = 0 ; - - result = (std::vector< std::string > *)new std::vector< std::string >(); - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_1___(void * jarg1) { - void * jresult ; - std::vector< std::string > *arg1 = 0 ; - std::vector< std::string > *result = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - if (!arg1) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); - return 0; - } - result = (std::vector< std::string > *)new std::vector< std::string >((std::vector< std::string > const &)*arg1); - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Clear___(void * jarg1) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - (arg1)->clear(); -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Add___(void * jarg1, const char * jarg2) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::string *arg2 = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return ; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - (arg1)->push_back((std::string const &)*arg2); -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_size___(void * jarg1) { - unsigned int jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::vector< std::string >::size_type result; - - arg1 = (std::vector< std::string > *)jarg1; - result = ((std::vector< std::string > const *)arg1)->size(); - jresult = (unsigned int)result; - return jresult; -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_empty___(void * jarg1) { - unsigned int jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - bool result; - - arg1 = (std::vector< std::string > *)jarg1; - result = (bool)((std::vector< std::string > const *)arg1)->empty(); - jresult = result; - return jresult; -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_capacity___(void * jarg1) { - unsigned int jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::vector< std::string >::size_type result; - - arg1 = (std::vector< std::string > *)jarg1; - result = ((std::vector< std::string > const *)arg1)->capacity(); - jresult = (unsigned int)result; - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_reserve___(void * jarg1, unsigned int jarg2) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::vector< std::string >::size_type arg2 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (std::vector< std::string >::size_type)jarg2; - (arg1)->reserve(arg2); -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_2___(int jarg1) { - void * jresult ; - int arg1 ; - std::vector< std::string > *result = 0 ; - - arg1 = (int)jarg1; - try { - result = (std::vector< std::string > *)new_std_vector_Sl_std_string_Sg___SWIG_2(arg1); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return 0; - } - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitemcopy___(void * jarg1, int jarg2) { - const char * jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - std::string result; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - try { - result = std_vector_Sl_std_string_Sg__getitemcopy(arg1,arg2); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return 0; - } - jresult = SWIG_csharp_string_callback((&result)->c_str()); - return jresult; -} - - -SWIGEXPORT const char * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitem___(void * jarg1, int jarg2) { - const char * jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - std::vector< std::string >::value_type *result = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - try { - result = (std::vector< std::string >::value_type *) &std_vector_Sl_std_string_Sg__getitem(arg1,arg2); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return 0; - } - jresult = SWIG_csharp_string_callback(result->c_str()); - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_setitem___(void * jarg1, int jarg2, const char * jarg3) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - std::string *arg3 = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - if (!jarg3) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return ; - } - std::string arg3_str(jarg3); - arg3 = &arg3_str; - try { - std_vector_Sl_std_string_Sg__setitem(arg1,arg2,(std::string const &)*arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_AddRange___(void * jarg1, void * jarg2) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::vector< std::string > *arg2 = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (std::vector< std::string > *)jarg2; - if (!arg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); - return ; - } - std_vector_Sl_std_string_Sg__AddRange(arg1,(std::vector< std::string > const &)*arg2); -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_GetRange___(void * jarg1, int jarg2, int jarg3) { - void * jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - int arg3 ; - std::vector< std::string > *result = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - arg3 = (int)jarg3; - try { - result = (std::vector< std::string > *)std_vector_Sl_std_string_Sg__GetRange(arg1,arg2,arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return 0; - } catch(std::invalid_argument &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentException, (&_e)->what(), ""); - return 0; - } - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Insert___(void * jarg1, int jarg2, const char * jarg3) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - std::string *arg3 = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - if (!jarg3) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return ; - } - std::string arg3_str(jarg3); - arg3 = &arg3_str; - try { - std_vector_Sl_std_string_Sg__Insert(arg1,arg2,(std::string const &)*arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_InsertRange___(void * jarg1, int jarg2, void * jarg3) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - std::vector< std::string > *arg3 = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - arg3 = (std::vector< std::string > *)jarg3; - if (!arg3) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); - return ; - } - try { - std_vector_Sl_std_string_Sg__InsertRange(arg1,arg2,(std::vector< std::string > const &)*arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveAt___(void * jarg1, int jarg2) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - try { - std_vector_Sl_std_string_Sg__RemoveAt(arg1,arg2); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveRange___(void * jarg1, int jarg2, int jarg3) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - int arg3 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - arg3 = (int)jarg3; - try { - std_vector_Sl_std_string_Sg__RemoveRange(arg1,arg2,arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } catch(std::invalid_argument &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentException, (&_e)->what(), ""); - return ; - } -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Repeat___(const char * jarg1, int jarg2) { - void * jresult ; - std::string *arg1 = 0 ; - int arg2 ; - std::vector< std::string > *result = 0 ; - - if (!jarg1) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg1_str(jarg1); - arg1 = &arg1_str; - arg2 = (int)jarg2; - try { - result = (std::vector< std::string > *)std_vector_Sl_std_string_Sg__Repeat((std::string const &)*arg1,arg2); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return 0; - } - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_0___(void * jarg1) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - std_vector_Sl_std_string_Sg__Reverse__SWIG_0(arg1); -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_1___(void * jarg1, int jarg2, int jarg3) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - int arg3 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - arg3 = (int)jarg3; - try { - std_vector_Sl_std_string_Sg__Reverse__SWIG_1(arg1,arg2,arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } catch(std::invalid_argument &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentException, (&_e)->what(), ""); - return ; - } -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_SetRange___(void * jarg1, int jarg2, void * jarg3) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - int arg2 ; - std::vector< std::string > *arg3 = 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - arg2 = (int)jarg2; - arg3 = (std::vector< std::string > *)jarg3; - if (!arg3) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "std::vector< std::string > const & is null", 0); - return ; - } - try { - std_vector_Sl_std_string_Sg__SetRange(arg1,arg2,(std::vector< std::string > const &)*arg3); - } catch(std::out_of_range &_e) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentOutOfRangeException, 0, (&_e)->what()); - return ; - } -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Contains___(void * jarg1, const char * jarg2) { - unsigned int jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::string *arg2 = 0 ; - bool result; - - arg1 = (std::vector< std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - result = (bool)std_vector_Sl_std_string_Sg__Contains(arg1,(std::string const &)*arg2); - jresult = result; - return jresult; -} - - -SWIGEXPORT int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_IndexOf___(void * jarg1, const char * jarg2) { - int jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::string *arg2 = 0 ; - int result; - - arg1 = (std::vector< std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - result = (int)std_vector_Sl_std_string_Sg__IndexOf(arg1,(std::string const &)*arg2); - jresult = result; - return jresult; -} - - -SWIGEXPORT int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_LastIndexOf___(void * jarg1, const char * jarg2) { - int jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::string *arg2 = 0 ; - int result; - - arg1 = (std::vector< std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - result = (int)std_vector_Sl_std_string_Sg__LastIndexOf(arg1,(std::string const &)*arg2); - jresult = result; - return jresult; -} - - -SWIGEXPORT unsigned int SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Remove___(void * jarg1, const char * jarg2) { - unsigned int jresult ; - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - std::string *arg2 = 0 ; - bool result; - - arg1 = (std::vector< std::string > *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - result = (bool)std_vector_Sl_std_string_Sg__Remove(arg1,(std::string const &)*arg2); - jresult = result; - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_delete_VectorStringSwig___(void * jarg1) { - std::vector< std::string > *arg1 = (std::vector< std::string > *) 0 ; - - arg1 = (std::vector< std::string > *)jarg1; - delete arg1; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_0___() { - void * jresult ; - Transform *result = 0 ; - - result = (Transform *)new Transform(); - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_1___(unsigned int jarg1) { - void * jresult ; - size_t arg1 ; - Transform *result = 0 ; - - arg1 = (size_t)jarg1; - result = (Transform *)new Transform(arg1); - jresult = (void *)result; - return jresult; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_Transform_fromJsonGHEV___(void * jarg1, const char * jarg2) { - void * jresult ; - Transform *arg1 = (Transform *) 0 ; - std::string *arg2 = 0 ; - std::map< std::string,std::string,std::less< std::string > > result; - - arg1 = (Transform *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - result = (arg1)->fromJsonGHEV((std::string const &)*arg2); - jresult = new std::map< std::string,std::string,std::less< std::string > >(result); - return jresult; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_Transform_fromBase64GHEV___(void * jarg1, const char * jarg2) { - void * jresult ; - Transform *arg1 = (Transform *) 0 ; - std::string *arg2 = 0 ; - std::map< std::string,std::string,std::less< std::string > > result; - - arg1 = (Transform *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - result = (arg1)->fromBase64GHEV((std::string const &)*arg2); - jresult = new std::map< std::string,std::string,std::less< std::string > >(result); - return jresult; -} - - -SWIGEXPORT void * SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_Transform_fromSUA___(void * jarg1, const char * jarg2) { - void * jresult ; - Transform *arg1 = (Transform *) 0 ; - std::string *arg2 = 0 ; - std::map< std::string,std::string,std::less< std::string > > result; - - arg1 = (Transform *)jarg1; - if (!jarg2) { - SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "null string", 0); - return 0; - } - std::string arg2_str(jarg2); - arg2 = &arg2_str; - result = (arg1)->fromSUA((std::string const &)*arg2); - jresult = new std::map< std::string,std::string,std::less< std::string > >(result); - return jresult; -} - - -SWIGEXPORT void SWIGSTDCALL CSharp_FiftyoneDegreesfCommon_delete_Transform___(void * jarg1) { - Transform *arg1 = (Transform *) 0 ; - - arg1 = (Transform *)jarg1; - delete arg1; -} - - -#ifdef __cplusplus -} -#endif - diff --git a/interop/dotnet/CMakeLists.txt b/interop/dotnet/CMakeLists.txt deleted file mode 100644 index ee9a3f5c..00000000 --- a/interop/dotnet/CMakeLists.txt +++ /dev/null @@ -1,94 +0,0 @@ -cmake_minimum_required(VERSION 3.24) -set (CMAKE_C_STANDARD 11) -set (CMAKE_CXX_STANDARD 17) - -project(CommonCXX LANGUAGES CXX C) - -if (NOT MSVC) - add_compile_options(-fPIC) -endif() - -if (MSVC) - if (CMAKE_GENERATOR_PLATFORM) - set (GEN_PLATFORM ${CMAKE_GENERATOR_PLATFORM}) - else() - set (GEN_PLATFORM ${CMAKE_VS_PLATFORM_NAME_DEFAULT}) - endif() - - if (${GEN_PLATFORM} MATCHES .*64.* AND NOT 32bit) - set(ARCH x64) - else() - set(ARCH x86) - endif() - -else() - message(STATUS "System Processor: ${CMAKE_SYSTEM_PROCESSOR}") - if (${CMAKE_SYSTEM_PROCESSOR} MATCHES ^armhf) - set(ARCH armhf) - elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES ^aarch64) - set(ARCH aarch64) - elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES ^arm64) - set(ARCH aarch64) - elseif (${CMAKE_SYSTEM_PROCESSOR} MATCHES 64) - if (32bit) - message(STATUS "32bit: ${32bit}") - set (ARCH x86) - else() - message(STATUS "32bit: ${32bit}") - set (ARCH x64) - endif() - else() - set (ARCH x86) - endif() -endif() -message(STATUS "Arch name: ${ARCH}") -message(STATUS "Generator Platform: ${CMAKE_GENERATOR_PLATFORM}") -message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") - -if (MSVC) - set (OS windows) -elseif (UNIX AND NOT APPLE) - set (OS linux) -elseif (APPLE) - set (OS macos) -endif() -message(STATUS "OS name: ${OS}") - -# include CMakeLists from common-cxx library -option(BUILD_TESTING "" OFF) -include("${CMAKE_CURRENT_LIST_DIR}/../../CMakeLists.txt" NO_POLICY_SCOPE) - -set(NAMESPACE "FiftyoneDegrees.Common") -set(DLLNAME "${NAMESPACE}.Native") -set(CXXWRAP "${CMAKE_CURRENT_LIST_DIR}/../../commonCSHARP_wrap.cxx") - -find_package(SWIG 4.0 COMPONENTS csharp) -if(SWIG_FOUND) - message("-- Found SWIG ${SWIG_EXECUTABLE}, running codegen of the wrappers:") - execute_process(COMMAND ${SWIG_EXECUTABLE} -c++ -csharp -namespace "${NAMESPACE}" -dllimport "${DLLNAME}.dll" -outdir ${CMAKE_CURRENT_LIST_DIR}/FiftyoneDegrees.Common -o "${CXXWRAP}" ${CMAKE_CURRENT_LIST_DIR}/../../common.i - COMMAND_ECHO STDOUT) -endif() - -if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio") - set(OUTDIR ".") -else() - set(OUTDIR "${CMAKE_BUILD_TYPE}") -endif() - -add_library(common-cxx-dotnet SHARED "${CXXWRAP}") -target_link_libraries(common-cxx-dotnet fiftyone-common-cxx) -set_target_properties(common-cxx-dotnet PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${OUTDIR}" - LIBRARY_OUTPUT_DIRECTORY "${OUTDIR}" - OUTPUT_NAME "${DLLNAME}" - PREFIX "" - SUFFIX ".dll") - -if (MSVC) - # /wd4100 needed to disable "unreferenced formal parameter" which comes from the SWIG file - target_compile_options(common-cxx-dotnet PRIVATE "/D_CRT_SECURE_NO_WARNINGS" "/W4" "/WX" "/wd4100" "/EHsc") -else() - add_compile_options(-fPIC) - target_compile_options(common-cxx-dotnet INTERFACE "-static-libgcc -static-libstdc++") - target_link_options(common-cxx-dotnet INTERFACE "-static-libgcc -static-libstdc++") -endif() \ No newline at end of file diff --git a/interop/dotnet/FiftyoneDegrees.Common/Common.cs b/interop/dotnet/FiftyoneDegrees.Common/Common.cs deleted file mode 100644 index 791efebb..00000000 --- a/interop/dotnet/FiftyoneDegrees.Common/Common.cs +++ /dev/null @@ -1,16 +0,0 @@ -//------------------------------------------------------------------------------ -// -// -// This file was automatically generated by SWIG (https://www.swig.org). -// Version 4.2.1 -// -// Do not make changes to this file unless you know what you are doing - modify -// the SWIG interface file instead. -//------------------------------------------------------------------------------ - -namespace FiftyoneDegrees.Common { - -public class Common { -} - -} diff --git a/interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs b/interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs deleted file mode 100644 index 02644501..00000000 --- a/interop/dotnet/FiftyoneDegrees.Common/CommonPINVOKE.cs +++ /dev/null @@ -1,338 +0,0 @@ -//------------------------------------------------------------------------------ -// -// -// This file was automatically generated by SWIG (https://www.swig.org). -// Version 4.2.1 -// -// Do not make changes to this file unless you know what you are doing - modify -// the SWIG interface file instead. -//------------------------------------------------------------------------------ - -namespace FiftyoneDegrees.Common { - -class CommonPINVOKE { - - protected class SWIGExceptionHelper { - - public delegate void ExceptionDelegate(string message); - public delegate void ExceptionArgumentDelegate(string message, string paramName); - - static ExceptionDelegate applicationDelegate = new ExceptionDelegate(SetPendingApplicationException); - static ExceptionDelegate arithmeticDelegate = new ExceptionDelegate(SetPendingArithmeticException); - static ExceptionDelegate divideByZeroDelegate = new ExceptionDelegate(SetPendingDivideByZeroException); - static ExceptionDelegate indexOutOfRangeDelegate = new ExceptionDelegate(SetPendingIndexOutOfRangeException); - static ExceptionDelegate invalidCastDelegate = new ExceptionDelegate(SetPendingInvalidCastException); - static ExceptionDelegate invalidOperationDelegate = new ExceptionDelegate(SetPendingInvalidOperationException); - static ExceptionDelegate ioDelegate = new ExceptionDelegate(SetPendingIOException); - static ExceptionDelegate nullReferenceDelegate = new ExceptionDelegate(SetPendingNullReferenceException); - static ExceptionDelegate outOfMemoryDelegate = new ExceptionDelegate(SetPendingOutOfMemoryException); - static ExceptionDelegate overflowDelegate = new ExceptionDelegate(SetPendingOverflowException); - static ExceptionDelegate systemDelegate = new ExceptionDelegate(SetPendingSystemException); - - static ExceptionArgumentDelegate argumentDelegate = new ExceptionArgumentDelegate(SetPendingArgumentException); - static ExceptionArgumentDelegate argumentNullDelegate = new ExceptionArgumentDelegate(SetPendingArgumentNullException); - static ExceptionArgumentDelegate argumentOutOfRangeDelegate = new ExceptionArgumentDelegate(SetPendingArgumentOutOfRangeException); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="SWIGRegisterExceptionCallbacks_Common")] - public static extern void SWIGRegisterExceptionCallbacks_Common( - ExceptionDelegate applicationDelegate, - ExceptionDelegate arithmeticDelegate, - ExceptionDelegate divideByZeroDelegate, - ExceptionDelegate indexOutOfRangeDelegate, - ExceptionDelegate invalidCastDelegate, - ExceptionDelegate invalidOperationDelegate, - ExceptionDelegate ioDelegate, - ExceptionDelegate nullReferenceDelegate, - ExceptionDelegate outOfMemoryDelegate, - ExceptionDelegate overflowDelegate, - ExceptionDelegate systemExceptionDelegate); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="SWIGRegisterExceptionArgumentCallbacks_Common")] - public static extern void SWIGRegisterExceptionCallbacksArgument_Common( - ExceptionArgumentDelegate argumentDelegate, - ExceptionArgumentDelegate argumentNullDelegate, - ExceptionArgumentDelegate argumentOutOfRangeDelegate); - - static void SetPendingApplicationException(string message) { - SWIGPendingException.Set(new global::System.ApplicationException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingArithmeticException(string message) { - SWIGPendingException.Set(new global::System.ArithmeticException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingDivideByZeroException(string message) { - SWIGPendingException.Set(new global::System.DivideByZeroException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingIndexOutOfRangeException(string message) { - SWIGPendingException.Set(new global::System.IndexOutOfRangeException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingInvalidCastException(string message) { - SWIGPendingException.Set(new global::System.InvalidCastException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingInvalidOperationException(string message) { - SWIGPendingException.Set(new global::System.InvalidOperationException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingIOException(string message) { - SWIGPendingException.Set(new global::System.IO.IOException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingNullReferenceException(string message) { - SWIGPendingException.Set(new global::System.NullReferenceException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingOutOfMemoryException(string message) { - SWIGPendingException.Set(new global::System.OutOfMemoryException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingOverflowException(string message) { - SWIGPendingException.Set(new global::System.OverflowException(message, SWIGPendingException.Retrieve())); - } - static void SetPendingSystemException(string message) { - SWIGPendingException.Set(new global::System.SystemException(message, SWIGPendingException.Retrieve())); - } - - static void SetPendingArgumentException(string message, string paramName) { - SWIGPendingException.Set(new global::System.ArgumentException(message, paramName, SWIGPendingException.Retrieve())); - } - static void SetPendingArgumentNullException(string message, string paramName) { - global::System.Exception e = SWIGPendingException.Retrieve(); - if (e != null) message = message + " Inner Exception: " + e.Message; - SWIGPendingException.Set(new global::System.ArgumentNullException(paramName, message)); - } - static void SetPendingArgumentOutOfRangeException(string message, string paramName) { - global::System.Exception e = SWIGPendingException.Retrieve(); - if (e != null) message = message + " Inner Exception: " + e.Message; - SWIGPendingException.Set(new global::System.ArgumentOutOfRangeException(paramName, message)); - } - - static SWIGExceptionHelper() { - SWIGRegisterExceptionCallbacks_Common( - applicationDelegate, - arithmeticDelegate, - divideByZeroDelegate, - indexOutOfRangeDelegate, - invalidCastDelegate, - invalidOperationDelegate, - ioDelegate, - nullReferenceDelegate, - outOfMemoryDelegate, - overflowDelegate, - systemDelegate); - - SWIGRegisterExceptionCallbacksArgument_Common( - argumentDelegate, - argumentNullDelegate, - argumentOutOfRangeDelegate); - } - } - - protected static SWIGExceptionHelper swigExceptionHelper = new SWIGExceptionHelper(); - - public class SWIGPendingException { - [global::System.ThreadStatic] - private static global::System.Exception pendingException = null; - private static int numExceptionsPending = 0; - private static global::System.Object exceptionsLock = null; - - public static bool Pending { - get { - bool pending = false; - if (numExceptionsPending > 0) - if (pendingException != null) - pending = true; - return pending; - } - } - - public static void Set(global::System.Exception e) { - if (pendingException != null) - throw new global::System.ApplicationException("FATAL: An earlier pending exception from unmanaged code was missed and thus not thrown (" + pendingException.ToString() + ")", e); - pendingException = e; - lock(exceptionsLock) { - numExceptionsPending++; - } - } - - public static global::System.Exception Retrieve() { - global::System.Exception e = null; - if (numExceptionsPending > 0) { - if (pendingException != null) { - e = pendingException; - pendingException = null; - lock(exceptionsLock) { - numExceptionsPending--; - } - } - } - return e; - } - - static SWIGPendingException() { - exceptionsLock = new global::System.Object(); - } - } - - - protected class SWIGStringHelper { - - public delegate string SWIGStringDelegate(string message); - static SWIGStringDelegate stringDelegate = new SWIGStringDelegate(CreateString); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="SWIGRegisterStringCallback_Common")] - public static extern void SWIGRegisterStringCallback_Common(SWIGStringDelegate stringDelegate); - - static string CreateString(string cString) { - return cString; - } - - static SWIGStringHelper() { - SWIGRegisterStringCallback_Common(stringDelegate); - } - } - - static protected SWIGStringHelper swigStringHelper = new SWIGStringHelper(); - - - static CommonPINVOKE() { - } - - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_0___")] - public static extern global::System.IntPtr new_MapStringStringSwig__SWIG_0(); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_MapStringStringSwig__SWIG_1___")] - public static extern global::System.IntPtr new_MapStringStringSwig__SWIG_1(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_size___")] - public static extern uint MapStringStringSwig_size(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_empty___")] - public static extern bool MapStringStringSwig_empty(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Clear___")] - public static extern void MapStringStringSwig_Clear(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_getitem___")] - public static extern string MapStringStringSwig_getitem(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_setitem___")] - public static extern void MapStringStringSwig_setitem(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2, string jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_ContainsKey___")] - public static extern bool MapStringStringSwig_ContainsKey(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Add___")] - public static extern void MapStringStringSwig_Add(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2, string jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_Remove___")] - public static extern bool MapStringStringSwig_Remove(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_create_iterator_begin___")] - public static extern global::System.IntPtr MapStringStringSwig_create_iterator_begin(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_get_next_key___")] - public static extern string MapStringStringSwig_get_next_key(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.IntPtr jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_MapStringStringSwig_destroy_iterator___")] - public static extern void MapStringStringSwig_destroy_iterator(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.IntPtr jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_delete_MapStringStringSwig___")] - public static extern void delete_MapStringStringSwig(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_0___")] - public static extern global::System.IntPtr new_VectorStringSwig__SWIG_0(); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_1___")] - public static extern global::System.IntPtr new_VectorStringSwig__SWIG_1(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Clear___")] - public static extern void VectorStringSwig_Clear(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Add___")] - public static extern void VectorStringSwig_Add(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_size___")] - public static extern uint VectorStringSwig_size(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_empty___")] - public static extern bool VectorStringSwig_empty(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_capacity___")] - public static extern uint VectorStringSwig_capacity(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_reserve___")] - public static extern void VectorStringSwig_reserve(global::System.Runtime.InteropServices.HandleRef jarg1, uint jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_VectorStringSwig__SWIG_2___")] - public static extern global::System.IntPtr new_VectorStringSwig__SWIG_2(int jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitemcopy___")] - public static extern string VectorStringSwig_getitemcopy(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_getitem___")] - public static extern string VectorStringSwig_getitem(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_setitem___")] - public static extern void VectorStringSwig_setitem(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, string jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_AddRange___")] - public static extern void VectorStringSwig_AddRange(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_GetRange___")] - public static extern global::System.IntPtr VectorStringSwig_GetRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, int jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Insert___")] - public static extern void VectorStringSwig_Insert(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, string jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_InsertRange___")] - public static extern void VectorStringSwig_InsertRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, global::System.Runtime.InteropServices.HandleRef jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveAt___")] - public static extern void VectorStringSwig_RemoveAt(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_RemoveRange___")] - public static extern void VectorStringSwig_RemoveRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, int jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Repeat___")] - public static extern global::System.IntPtr VectorStringSwig_Repeat(string jarg1, int jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_0___")] - public static extern void VectorStringSwig_Reverse__SWIG_0(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Reverse__SWIG_1___")] - public static extern void VectorStringSwig_Reverse__SWIG_1(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, int jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_SetRange___")] - public static extern void VectorStringSwig_SetRange(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2, global::System.Runtime.InteropServices.HandleRef jarg3); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Contains___")] - public static extern bool VectorStringSwig_Contains(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_IndexOf___")] - public static extern int VectorStringSwig_IndexOf(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_LastIndexOf___")] - public static extern int VectorStringSwig_LastIndexOf(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_VectorStringSwig_Remove___")] - public static extern bool VectorStringSwig_Remove(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_delete_VectorStringSwig___")] - public static extern void delete_VectorStringSwig(global::System.Runtime.InteropServices.HandleRef jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_0___")] - public static extern global::System.IntPtr new_Transform__SWIG_0(); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_new_Transform__SWIG_1___")] - public static extern global::System.IntPtr new_Transform__SWIG_1(uint jarg1); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_Transform_fromJsonGHEV___")] - public static extern global::System.IntPtr Transform_fromJsonGHEV(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_Transform_fromBase64GHEV___")] - public static extern global::System.IntPtr Transform_fromBase64GHEV(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_Transform_fromSUA___")] - public static extern global::System.IntPtr Transform_fromSUA(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2); - - [global::System.Runtime.InteropServices.DllImport("FiftyoneDegrees.Common.Native.dll", EntryPoint="CSharp_FiftyoneDegreesfCommon_delete_Transform___")] - public static extern void delete_Transform(global::System.Runtime.InteropServices.HandleRef jarg1); -} - -} diff --git a/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj b/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj deleted file mode 100644 index 6cd7ebf4..00000000 --- a/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - net8.0 - enable - enable - - - - - - - - - PreserveNewest - - - diff --git a/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln b/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln deleted file mode 100644 index c0981995..00000000 --- a/interop/dotnet/FiftyoneDegrees.Common/FiftyoneDegrees.Common.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.10.35122.118 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiftyoneDegrees.Common", "FiftyoneDegrees.Common.csproj", "{D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D232EBC0-4C23-44AC-B03B-366FFBA1CCFA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {955F46EF-0512-44E7-BF66-D40079362FBE} - EndGlobalSection -EndGlobal diff --git a/interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs b/interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs deleted file mode 100644 index 3795e6fc..00000000 --- a/interop/dotnet/FiftyoneDegrees.Common/MapStringStringSwig.cs +++ /dev/null @@ -1,313 +0,0 @@ -//------------------------------------------------------------------------------ -// -// -// This file was automatically generated by SWIG (https://www.swig.org). -// Version 4.2.1 -// -// Do not make changes to this file unless you know what you are doing - modify -// the SWIG interface file instead. -//------------------------------------------------------------------------------ - -namespace FiftyoneDegrees.Common { - -public class MapStringStringSwig : global::System.IDisposable - , global::System.Collections.Generic.IDictionary - { - private global::System.Runtime.InteropServices.HandleRef swigCPtr; - protected bool swigCMemOwn; - - internal MapStringStringSwig(global::System.IntPtr cPtr, bool cMemoryOwn) { - swigCMemOwn = cMemoryOwn; - swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); - } - - internal static global::System.Runtime.InteropServices.HandleRef getCPtr(MapStringStringSwig obj) { - return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; - } - - internal static global::System.Runtime.InteropServices.HandleRef swigRelease(MapStringStringSwig obj) { - if (obj != null) { - if (!obj.swigCMemOwn) - throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); - global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; - obj.swigCMemOwn = false; - obj.Dispose(); - return ptr; - } else { - return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); - } - } - - ~MapStringStringSwig() { - Dispose(false); - } - - public void Dispose() { - Dispose(true); - global::System.GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) { - lock(this) { - if (swigCPtr.Handle != global::System.IntPtr.Zero) { - if (swigCMemOwn) { - swigCMemOwn = false; - CommonPINVOKE.delete_MapStringStringSwig(swigCPtr); - } - swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); - } - } - } - - - public string this[string key] { - get { - return getitem(key); - } - - set { - setitem(key, value); - } - } - - public bool TryGetValue(string key, out string value) { - if (this.ContainsKey(key)) { - value = this[key]; - return true; - } - value = default(string); - return false; - } - - public bool IsEmpty { - get { - return empty(); - } - } - - public int Count { - get { - return (int)size(); - } - } - - public bool IsReadOnly { - get { - return false; - } - } - - public global::System.Collections.Generic.ICollection Keys { - get { - global::System.Collections.Generic.ICollection keys = new global::System.Collections.Generic.List(); - int size = this.Count; - if (size > 0) { - global::System.IntPtr iter = create_iterator_begin(); - for (int i = 0; i < size; i++) { - keys.Add(get_next_key(iter)); - } - destroy_iterator(iter); - } - return keys; - } - } - - public global::System.Collections.Generic.ICollection Values { - get { - global::System.Collections.Generic.ICollection vals = new global::System.Collections.Generic.List(); - foreach (global::System.Collections.Generic.KeyValuePair pair in this) { - vals.Add(pair.Value); - } - return vals; - } - } - - public void Add(global::System.Collections.Generic.KeyValuePair item) { - Add(item.Key, item.Value); - } - - public bool Remove(global::System.Collections.Generic.KeyValuePair item) { - if (Contains(item)) { - return Remove(item.Key); - } else { - return false; - } - } - - public bool Contains(global::System.Collections.Generic.KeyValuePair item) { - if (this[item.Key] == item.Value) { - return true; - } else { - return false; - } - } - - public void CopyTo(global::System.Collections.Generic.KeyValuePair[] array) { - CopyTo(array, 0); - } - - public void CopyTo(global::System.Collections.Generic.KeyValuePair[] array, int arrayIndex) { - if (array == null) - throw new global::System.ArgumentNullException("array"); - if (arrayIndex < 0) - throw new global::System.ArgumentOutOfRangeException("arrayIndex", "Value is less than zero"); - if (array.Rank > 1) - throw new global::System.ArgumentException("Multi dimensional array.", "array"); - if (arrayIndex+this.Count > array.Length) - throw new global::System.ArgumentException("Number of elements to copy is too large."); - - global::System.Collections.Generic.IList keyList = new global::System.Collections.Generic.List(this.Keys); - for (int i = 0; i < keyList.Count; i++) { - string currentKey = keyList[i]; - array.SetValue(new global::System.Collections.Generic.KeyValuePair(currentKey, this[currentKey]), arrayIndex+i); - } - } - - global::System.Collections.Generic.IEnumerator> global::System.Collections.Generic.IEnumerable>.GetEnumerator() { - return new MapStringStringSwigEnumerator(this); - } - - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() { - return new MapStringStringSwigEnumerator(this); - } - - public MapStringStringSwigEnumerator GetEnumerator() { - return new MapStringStringSwigEnumerator(this); - } - - // Type-safe enumerator - /// Note that the IEnumerator documentation requires an InvalidOperationException to be thrown - /// whenever the collection is modified. This has been done for changes in the size of the - /// collection but not when one of the elements of the collection is modified as it is a bit - /// tricky to detect unmanaged code that modifies the collection under our feet. - public sealed class MapStringStringSwigEnumerator : global::System.Collections.IEnumerator, - global::System.Collections.Generic.IEnumerator> - { - private MapStringStringSwig collectionRef; - private global::System.Collections.Generic.IList keyCollection; - private int currentIndex; - private object currentObject; - private int currentSize; - - public MapStringStringSwigEnumerator(MapStringStringSwig collection) { - collectionRef = collection; - keyCollection = new global::System.Collections.Generic.List(collection.Keys); - currentIndex = -1; - currentObject = null; - currentSize = collectionRef.Count; - } - - // Type-safe iterator Current - public global::System.Collections.Generic.KeyValuePair Current { - get { - if (currentIndex == -1) - throw new global::System.InvalidOperationException("Enumeration not started."); - if (currentIndex > currentSize - 1) - throw new global::System.InvalidOperationException("Enumeration finished."); - if (currentObject == null) - throw new global::System.InvalidOperationException("Collection modified."); - return (global::System.Collections.Generic.KeyValuePair)currentObject; - } - } - - // Type-unsafe IEnumerator.Current - object global::System.Collections.IEnumerator.Current { - get { - return Current; - } - } - - public bool MoveNext() { - int size = collectionRef.Count; - bool moveOkay = (currentIndex+1 < size) && (size == currentSize); - if (moveOkay) { - currentIndex++; - string currentKey = keyCollection[currentIndex]; - currentObject = new global::System.Collections.Generic.KeyValuePair(currentKey, collectionRef[currentKey]); - } else { - currentObject = null; - } - return moveOkay; - } - - public void Reset() { - currentIndex = -1; - currentObject = null; - if (collectionRef.Count != currentSize) { - throw new global::System.InvalidOperationException("Collection modified."); - } - } - - public void Dispose() { - currentIndex = -1; - currentObject = null; - } - } - - - public MapStringStringSwig() : this(CommonPINVOKE.new_MapStringStringSwig__SWIG_0(), true) { - } - - public MapStringStringSwig(MapStringStringSwig other) : this(CommonPINVOKE.new_MapStringStringSwig__SWIG_1(MapStringStringSwig.getCPtr(other)), true) { - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - private uint size() { - uint ret = CommonPINVOKE.MapStringStringSwig_size(swigCPtr); - return ret; - } - - private bool empty() { - bool ret = CommonPINVOKE.MapStringStringSwig_empty(swigCPtr); - return ret; - } - - public void Clear() { - CommonPINVOKE.MapStringStringSwig_Clear(swigCPtr); - } - - private string getitem(string key) { - string ret = CommonPINVOKE.MapStringStringSwig_getitem(swigCPtr, key); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - private void setitem(string key, string x) { - CommonPINVOKE.MapStringStringSwig_setitem(swigCPtr, key, x); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public bool ContainsKey(string key) { - bool ret = CommonPINVOKE.MapStringStringSwig_ContainsKey(swigCPtr, key); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public void Add(string key, string value) { - CommonPINVOKE.MapStringStringSwig_Add(swigCPtr, key, value); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public bool Remove(string key) { - bool ret = CommonPINVOKE.MapStringStringSwig_Remove(swigCPtr, key); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - private global::System.IntPtr create_iterator_begin() { - global::System.IntPtr ret = CommonPINVOKE.MapStringStringSwig_create_iterator_begin(swigCPtr); - return ret; - } - - private string get_next_key(global::System.IntPtr swigiterator) { - string ret = CommonPINVOKE.MapStringStringSwig_get_next_key(swigCPtr, swigiterator); - return ret; - } - - private void destroy_iterator(global::System.IntPtr swigiterator) { - CommonPINVOKE.MapStringStringSwig_destroy_iterator(swigCPtr, swigiterator); - } - -} - -} diff --git a/interop/dotnet/FiftyoneDegrees.Common/Transform.cs b/interop/dotnet/FiftyoneDegrees.Common/Transform.cs deleted file mode 100644 index 3e753963..00000000 --- a/interop/dotnet/FiftyoneDegrees.Common/Transform.cs +++ /dev/null @@ -1,86 +0,0 @@ -//------------------------------------------------------------------------------ -// -// -// This file was automatically generated by SWIG (https://www.swig.org). -// Version 4.2.1 -// -// Do not make changes to this file unless you know what you are doing - modify -// the SWIG interface file instead. -//------------------------------------------------------------------------------ - -namespace FiftyoneDegrees.Common { - -public class Transform : global::System.IDisposable { - private global::System.Runtime.InteropServices.HandleRef swigCPtr; - protected bool swigCMemOwn; - - internal Transform(global::System.IntPtr cPtr, bool cMemoryOwn) { - swigCMemOwn = cMemoryOwn; - swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); - } - - internal static global::System.Runtime.InteropServices.HandleRef getCPtr(Transform obj) { - return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; - } - - internal static global::System.Runtime.InteropServices.HandleRef swigRelease(Transform obj) { - if (obj != null) { - if (!obj.swigCMemOwn) - throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); - global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; - obj.swigCMemOwn = false; - obj.Dispose(); - return ptr; - } else { - return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); - } - } - - ~Transform() { - Dispose(false); - } - - public void Dispose() { - Dispose(true); - global::System.GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) { - lock(this) { - if (swigCPtr.Handle != global::System.IntPtr.Zero) { - if (swigCMemOwn) { - swigCMemOwn = false; - CommonPINVOKE.delete_Transform(swigCPtr); - } - swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); - } - } - } - - public Transform() : this(CommonPINVOKE.new_Transform__SWIG_0(), true) { - } - - public Transform(uint capacity) : this(CommonPINVOKE.new_Transform__SWIG_1(capacity), true) { - } - - public MapStringStringSwig fromJsonGHEV(string json) { - MapStringStringSwig ret = new MapStringStringSwig(CommonPINVOKE.Transform_fromJsonGHEV(swigCPtr, json), true); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public MapStringStringSwig fromBase64GHEV(string json) { - MapStringStringSwig ret = new MapStringStringSwig(CommonPINVOKE.Transform_fromBase64GHEV(swigCPtr, json), true); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public MapStringStringSwig fromSUA(string json) { - MapStringStringSwig ret = new MapStringStringSwig(CommonPINVOKE.Transform_fromSUA(swigCPtr, json), true); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - -} - -} diff --git a/interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs b/interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs deleted file mode 100644 index 23afb1fe..00000000 --- a/interop/dotnet/FiftyoneDegrees.Common/VectorStringSwig.cs +++ /dev/null @@ -1,374 +0,0 @@ -//------------------------------------------------------------------------------ -// -// -// This file was automatically generated by SWIG (https://www.swig.org). -// Version 4.2.1 -// -// Do not make changes to this file unless you know what you are doing - modify -// the SWIG interface file instead. -//------------------------------------------------------------------------------ - -namespace FiftyoneDegrees.Common { - -public class VectorStringSwig : global::System.IDisposable, global::System.Collections.IEnumerable, global::System.Collections.Generic.IList - { - private global::System.Runtime.InteropServices.HandleRef swigCPtr; - protected bool swigCMemOwn; - - internal VectorStringSwig(global::System.IntPtr cPtr, bool cMemoryOwn) { - swigCMemOwn = cMemoryOwn; - swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr); - } - - internal static global::System.Runtime.InteropServices.HandleRef getCPtr(VectorStringSwig obj) { - return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; - } - - internal static global::System.Runtime.InteropServices.HandleRef swigRelease(VectorStringSwig obj) { - if (obj != null) { - if (!obj.swigCMemOwn) - throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); - global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; - obj.swigCMemOwn = false; - obj.Dispose(); - return ptr; - } else { - return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); - } - } - - ~VectorStringSwig() { - Dispose(false); - } - - public void Dispose() { - Dispose(true); - global::System.GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) { - lock(this) { - if (swigCPtr.Handle != global::System.IntPtr.Zero) { - if (swigCMemOwn) { - swigCMemOwn = false; - CommonPINVOKE.delete_VectorStringSwig(swigCPtr); - } - swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); - } - } - } - - public VectorStringSwig(global::System.Collections.IEnumerable c) : this() { - if (c == null) - throw new global::System.ArgumentNullException("c"); - foreach (string element in c) { - this.Add(element); - } - } - - public VectorStringSwig(global::System.Collections.Generic.IEnumerable c) : this() { - if (c == null) - throw new global::System.ArgumentNullException("c"); - foreach (string element in c) { - this.Add(element); - } - } - - public bool IsFixedSize { - get { - return false; - } - } - - public bool IsReadOnly { - get { - return false; - } - } - - public string this[int index] { - get { - return getitem(index); - } - set { - setitem(index, value); - } - } - - public int Capacity { - get { - return (int)capacity(); - } - set { - if (value < 0 || (uint)value < size()) - throw new global::System.ArgumentOutOfRangeException("Capacity"); - reserve((uint)value); - } - } - - public bool IsEmpty { - get { - return empty(); - } - } - - public int Count { - get { - return (int)size(); - } - } - - public bool IsSynchronized { - get { - return false; - } - } - - public void CopyTo(string[] array) - { - CopyTo(0, array, 0, this.Count); - } - - public void CopyTo(string[] array, int arrayIndex) - { - CopyTo(0, array, arrayIndex, this.Count); - } - - public void CopyTo(int index, string[] array, int arrayIndex, int count) - { - if (array == null) - throw new global::System.ArgumentNullException("array"); - if (index < 0) - throw new global::System.ArgumentOutOfRangeException("index", "Value is less than zero"); - if (arrayIndex < 0) - throw new global::System.ArgumentOutOfRangeException("arrayIndex", "Value is less than zero"); - if (count < 0) - throw new global::System.ArgumentOutOfRangeException("count", "Value is less than zero"); - if (array.Rank > 1) - throw new global::System.ArgumentException("Multi dimensional array.", "array"); - if (index+count > this.Count || arrayIndex+count > array.Length) - throw new global::System.ArgumentException("Number of elements to copy is too large."); - for (int i=0; i global::System.Collections.Generic.IEnumerable.GetEnumerator() { - return new VectorStringSwigEnumerator(this); - } - - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() { - return new VectorStringSwigEnumerator(this); - } - - public VectorStringSwigEnumerator GetEnumerator() { - return new VectorStringSwigEnumerator(this); - } - - // Type-safe enumerator - /// Note that the IEnumerator documentation requires an InvalidOperationException to be thrown - /// whenever the collection is modified. This has been done for changes in the size of the - /// collection but not when one of the elements of the collection is modified as it is a bit - /// tricky to detect unmanaged code that modifies the collection under our feet. - public sealed class VectorStringSwigEnumerator : global::System.Collections.IEnumerator - , global::System.Collections.Generic.IEnumerator - { - private VectorStringSwig collectionRef; - private int currentIndex; - private object currentObject; - private int currentSize; - - public VectorStringSwigEnumerator(VectorStringSwig collection) { - collectionRef = collection; - currentIndex = -1; - currentObject = null; - currentSize = collectionRef.Count; - } - - // Type-safe iterator Current - public string Current { - get { - if (currentIndex == -1) - throw new global::System.InvalidOperationException("Enumeration not started."); - if (currentIndex > currentSize - 1) - throw new global::System.InvalidOperationException("Enumeration finished."); - if (currentObject == null) - throw new global::System.InvalidOperationException("Collection modified."); - return (string)currentObject; - } - } - - // Type-unsafe IEnumerator.Current - object global::System.Collections.IEnumerator.Current { - get { - return Current; - } - } - - public bool MoveNext() { - int size = collectionRef.Count; - bool moveOkay = (currentIndex+1 < size) && (size == currentSize); - if (moveOkay) { - currentIndex++; - currentObject = collectionRef[currentIndex]; - } else { - currentObject = null; - } - return moveOkay; - } - - public void Reset() { - currentIndex = -1; - currentObject = null; - if (collectionRef.Count != currentSize) { - throw new global::System.InvalidOperationException("Collection modified."); - } - } - - public void Dispose() { - currentIndex = -1; - currentObject = null; - } - } - - public VectorStringSwig() : this(CommonPINVOKE.new_VectorStringSwig__SWIG_0(), true) { - } - - public VectorStringSwig(VectorStringSwig other) : this(CommonPINVOKE.new_VectorStringSwig__SWIG_1(VectorStringSwig.getCPtr(other)), true) { - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public void Clear() { - CommonPINVOKE.VectorStringSwig_Clear(swigCPtr); - } - - public void Add(string x) { - CommonPINVOKE.VectorStringSwig_Add(swigCPtr, x); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - private uint size() { - uint ret = CommonPINVOKE.VectorStringSwig_size(swigCPtr); - return ret; - } - - private bool empty() { - bool ret = CommonPINVOKE.VectorStringSwig_empty(swigCPtr); - return ret; - } - - private uint capacity() { - uint ret = CommonPINVOKE.VectorStringSwig_capacity(swigCPtr); - return ret; - } - - private void reserve(uint n) { - CommonPINVOKE.VectorStringSwig_reserve(swigCPtr, n); - } - - public VectorStringSwig(int capacity) : this(CommonPINVOKE.new_VectorStringSwig__SWIG_2(capacity), true) { - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - private string getitemcopy(int index) { - string ret = CommonPINVOKE.VectorStringSwig_getitemcopy(swigCPtr, index); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - private string getitem(int index) { - string ret = CommonPINVOKE.VectorStringSwig_getitem(swigCPtr, index); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - private void setitem(int index, string val) { - CommonPINVOKE.VectorStringSwig_setitem(swigCPtr, index, val); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public void AddRange(VectorStringSwig values) { - CommonPINVOKE.VectorStringSwig_AddRange(swigCPtr, VectorStringSwig.getCPtr(values)); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public VectorStringSwig GetRange(int index, int count) { - global::System.IntPtr cPtr = CommonPINVOKE.VectorStringSwig_GetRange(swigCPtr, index, count); - VectorStringSwig ret = (cPtr == global::System.IntPtr.Zero) ? null : new VectorStringSwig(cPtr, true); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public void Insert(int index, string x) { - CommonPINVOKE.VectorStringSwig_Insert(swigCPtr, index, x); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public void InsertRange(int index, VectorStringSwig values) { - CommonPINVOKE.VectorStringSwig_InsertRange(swigCPtr, index, VectorStringSwig.getCPtr(values)); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public void RemoveAt(int index) { - CommonPINVOKE.VectorStringSwig_RemoveAt(swigCPtr, index); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public void RemoveRange(int index, int count) { - CommonPINVOKE.VectorStringSwig_RemoveRange(swigCPtr, index, count); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public static VectorStringSwig Repeat(string value, int count) { - global::System.IntPtr cPtr = CommonPINVOKE.VectorStringSwig_Repeat(value, count); - VectorStringSwig ret = (cPtr == global::System.IntPtr.Zero) ? null : new VectorStringSwig(cPtr, true); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public void Reverse() { - CommonPINVOKE.VectorStringSwig_Reverse__SWIG_0(swigCPtr); - } - - public void Reverse(int index, int count) { - CommonPINVOKE.VectorStringSwig_Reverse__SWIG_1(swigCPtr, index, count); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public void SetRange(int index, VectorStringSwig values) { - CommonPINVOKE.VectorStringSwig_SetRange(swigCPtr, index, VectorStringSwig.getCPtr(values)); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - } - - public bool Contains(string value) { - bool ret = CommonPINVOKE.VectorStringSwig_Contains(swigCPtr, value); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public int IndexOf(string value) { - int ret = CommonPINVOKE.VectorStringSwig_IndexOf(swigCPtr, value); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public int LastIndexOf(string value) { - int ret = CommonPINVOKE.VectorStringSwig_LastIndexOf(swigCPtr, value); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - - public bool Remove(string value) { - bool ret = CommonPINVOKE.VectorStringSwig_Remove(swigCPtr, value); - if (CommonPINVOKE.SWIGPendingException.Pending) throw CommonPINVOKE.SWIGPendingException.Retrieve(); - return ret; - } - -} - -} diff --git a/interop/dotnet/example/TransformExample/Program.cs b/interop/dotnet/example/TransformExample/Program.cs deleted file mode 100644 index a1cef243..00000000 --- a/interop/dotnet/example/TransformExample/Program.cs +++ /dev/null @@ -1,45 +0,0 @@ -// See https://aka.ms/new-console-template for more information - -using System.Xml.Serialization; -using System; - -// Note the namespace defined as part of the SWIG-generated files -// that are referenced through the FiftyoneDegrees.Common.csproj wrapper project - -using FiftyoneDegrees.Common; - -Console.WriteLine("Current Dir: " + Directory.GetCurrentDirectory()); - -// MapStringStringSwig type is an ordinary key-value pair container -// that is corresponds to the C++ std::map type -// Can be used as Dictionary as demonstrated below -void outputResult(MapStringStringSwig result) { - foreach (var k in result.Keys) - { - Console.WriteLine(k + ": " + result[k]); - } - Console.WriteLine(""); -} - -// Instantiate the Transform object with a default buffer size of 1024 -// Optionaly set a working buffer size like `var transform = new Transform(2048);` -// It will be automatically increased if needed -// The instance is non-threadsafe, but reusable within a single thread -// i.e. can be used multiple times as in the example below -var transform = new Transform(); -{ - Console.WriteLine("From GHEV:"); - var result = transform.fromJsonGHEV("{\"architecture\":\"x86\",\"brands\":[{\"brand\":\"Chromium\",\"version\":\"128\"},{\"brand\":\"Not;A=Brand\",\"version\":\"24\"},{\"brand\":\"Google Chrome\",\"version\":\"128\"}],\"fullVersionList\":[{\"brand\":\"Chromium\",\"version\":\"128.0.6613.84\"},{\"brand\":\"Not;A=Brand\",\"version\":\"24.0.0.0\"},{\"brand\":\"Google Chrome\",\"version\":\"128.0.6613.84\"}],\"mobile\":false,\"model\":\"\",\"platform\":\"macOS\",\"platformVersion\":\"14.6.1\"}"); - outputResult(result); -} -{ - Console.WriteLine("From SUA:"); - var result = transform.fromSUA("{\"browsers\":[{\"brand\":\"Chromium\",\"version\":[\"124\",\"0\",\"6367\",\"82\"]},{\"brand\":\"Google Chrome\",\"version\":[\"124\",\"0\",\"6367\",\"82\"]},{\"brand\":\"Not-A.Brand\",\"version\":[\"99\",\"0\",\"0\",\"0\"]}],\"platform\":{\"brand\":\"Android\",\"version\":[\"14\",\"0\",\"0\"]},\"mobile\":1,\"model\":\"SM-G998U\",\"source\":2}"); - outputResult(result); -} -{ - Console.WriteLine("From base64 GHEV:"); - var result = transform.fromBase64GHEV("eyJhcmNoaXRlY3R1cmUiOiJ4ODYiLCJiaXRuZXNzIjoiNjQiLCJicmFuZHMiOlt7ImJyYW5kIjoiQ2hyb21pdW0iLCJ2ZXJzaW9uIjoiMTI4In0seyJicmFuZCI6Ik5vdDtBPUJyYW5kIiwidmVyc2lvbiI6IjI0In0seyJicmFuZCI6Ikdvb2dsZSBDaHJvbWUiLCJ2ZXJzaW9uIjoiMTI4In1dLCJmdWxsVmVyc2lvbkxpc3QiOlt7ImJyYW5kIjoiQ2hyb21pdW0iLCJ2ZXJzaW9uIjoiMTI4LjAuNjYxMy44NCJ9LHsiYnJhbmQiOiJOb3Q7QT1CcmFuZCIsInZlcnNpb24iOiIyNC4wLjAuMCJ9LHsiYnJhbmQiOiJHb29nbGUgQ2hyb21lIiwidmVyc2lvbiI6IjEyOC4wLjY2MTMuODQifV0sIm1vYmlsZSI6ZmFsc2UsIm1vZGVsIjoiTWFjQm9vayBQcm8iLCJwbGF0Zm9ybSI6Im1hY09TIn0="); - outputResult(result); -} - diff --git a/interop/dotnet/example/TransformExample/TransformExample.csproj b/interop/dotnet/example/TransformExample/TransformExample.csproj deleted file mode 100644 index d3328337..00000000 --- a/interop/dotnet/example/TransformExample/TransformExample.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - - - diff --git a/interop/dotnet/example/TransformExample/TransformExample.sln b/interop/dotnet/example/TransformExample/TransformExample.sln deleted file mode 100644 index 0cec6597..00000000 --- a/interop/dotnet/example/TransformExample/TransformExample.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.10.35122.118 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransformExample", "TransformExample.csproj", "{8D52CC17-D1DA-425E-B039-94A0C0A20CBC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FiftyoneDegrees.Common", "..\..\FiftyoneDegrees.Common\FiftyoneDegrees.Common.csproj", "{73678907-B1B3-4904-9467-E77A52C07AB2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8D52CC17-D1DA-425E-B039-94A0C0A20CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {73678907-B1B3-4904-9467-E77A52C07AB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {73678907-B1B3-4904-9467-E77A52C07AB2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {73678907-B1B3-4904-9467-E77A52C07AB2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {73678907-B1B3-4904-9467-E77A52C07AB2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {F0AA5F9F-E8C3-4DF2-9E9E-F7F69F9689BA} - EndGlobalSection -EndGlobal From e3be913977c1e950b4f2c9ca3fc5effcd413b5d3 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 13 Sep 2024 07:27:54 +0100 Subject: [PATCH 70/73] DOC: Improved the code comments associated with the iteration over the values associated with a profile and property. --- profile.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/profile.c b/profile.c index 2478825e..43f162de 100644 --- a/profile.c +++ b/profile.c @@ -101,17 +101,19 @@ static uint32_t* getFirstValueForProfileAndProperty( } /** - * Retrieve the values between index and max index passing the item to the - * callback method provided. The calling function is responsible for freeing - * the items passed to the callback method. + * Starting at the value index pointed to by valIndexPtr iterates over the + * value indexes checking that they relate to the property. maxValIndexPtr is + * used to prevent overrunning the memory used for values associated with the + * profile. The value items are passed to the callback method which is + * responsible for freeing these items. */ static uint32_t iterateValues( Collection *values, Property *property, void *state, ProfileIterateMethod callback, - uint32_t *valueIndex, - uint32_t *maxValueIndex, + uint32_t *valIndexPtr, + uint32_t *maxValIndexPtr, Exception *exception) { Item valueItem; uint32_t count = 0; @@ -120,10 +122,13 @@ static uint32_t iterateValues( // Loop through until the last value for the property has been returned // or the callback doesn't need to continue. while (cont == true && - //first check the address validity, before dereferencing to prevent potential segfault on deref - valueIndex < maxValueIndex && - *valueIndex <= property->lastValueIndex && - + // Check the address validity, before dereferencing to prevent + // potential memory fault on dereference. + valIndexPtr < maxValIndexPtr && + // Check that the value index could relate to the property. Saves + // having to retrieve the value item if it will never relate to the + // property. + *valIndexPtr <= property->lastValueIndex && EXCEPTION_OKAY) { // Reset the items as they should never share the same memory. @@ -131,12 +136,15 @@ static uint32_t iterateValues( // Get the value from the value index and call the callback. Do not // free the item as the calling function is responsible for this. - if (values->get(values, *valueIndex, &valueItem, exception) != NULL && + if (values->get(values, *valIndexPtr, &valueItem, exception) != NULL && EXCEPTION_OKAY) { cont = callback(state, &valueItem); count++; } - valueIndex++; + + // Move to the next value index pointer as this might relate to another + // value for the property. + valIndexPtr++; } return count; From 1db1c0d3f51e4ce325df2ccb8c944144699da046 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 13 Sep 2024 18:44:15 +0100 Subject: [PATCH 71/73] REFACT: Improved parameter naming by making consistent with other features. --- overrides.c | 37 +++++++++++++++++++++++-------------- overrides.h | 4 ++-- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/overrides.c b/overrides.c index b036aa17..aff80043 100644 --- a/overrides.c +++ b/overrides.c @@ -25,11 +25,11 @@ MAP_TYPE(Collection) -typedef struct override_profile_ids_t { - fiftyoneDegreesOverrideProfileIdMethod override; /* Method to call when +typedef struct override_profile_ids_state_t { + fiftyoneDegreesOverrideProfileIdMethod callback; /* Method to call when a profile ids is found */ - void *state; /* State to pass to the override method */ -} overrideProfileIds; + void *state; /* State to pass to the callback method */ +} overrideProfileIdsState; typedef struct add_state_t { OverridePropertyArray *properties; /* Properties available to be @@ -51,6 +51,15 @@ static void collectionRelease(Item *item) { /* Prefix to use when comparing property names. */ #define OVERRIDE_PREFIX "51D_" +/** + * Checks if the pair (p) have a field name that matches the target (t). + * The last byte of t is null where as fieldLength is the length of printable + * characters. Take 1 from the t to compare length. + */ +#define IS_HEADER_MATCH(t,p) \ + (sizeof(t) - 1 == p->fieldLength && \ + StringCompareLength(p->field, t, sizeof(t)) == 0) + static const Collection dummyCollection = { NULL, collectionRelease, @@ -403,16 +412,16 @@ void fiftyoneDegreesOverrideValuesReset( } } -static void extractProfileId(char *value, overrideProfileIds *state) { +static void extractProfileId(char *value, overrideProfileIdsState *state) { if (*value >= 0 && isdigit(*value) != 0) { int profileId = atoi(value); if (profileId >= 0) { - state->override(state->state, profileId); + state->callback(state->state, profileId); } } } -static void extractProfileIds(overrideProfileIds *state, char *value) { +static void extractProfileIds(overrideProfileIdsState *state, char *value) { char *current = value, *previous = value; while (*current != '\0') { if (*current < 0 || isdigit(*current) == 0) { @@ -425,10 +434,10 @@ static void extractProfileIds(overrideProfileIds *state, char *value) { } static bool iteratorProfileId(void *state, EvidenceKeyValuePair *pair) { - if (StringCompare( - skipPrefix(true, (char*)pair->field), - "ProfileIds") == 0) { - extractProfileIds((overrideProfileIds*)state, (char*)pair->parsedValue); + if (IS_HEADER_MATCH("ProfileIds", pair)) { + extractProfileIds( + (overrideProfileIdsState*)state, + (char*)pair->parsedValue); } return true; } @@ -436,12 +445,12 @@ static bool iteratorProfileId(void *state, EvidenceKeyValuePair *pair) { void fiftyoneDegreesOverrideProfileIds( fiftyoneDegreesEvidenceKeyValuePairArray *evidence, void *state, - fiftyoneDegreesOverrideProfileIdMethod override) { - overrideProfileIds callback = { override, state }; + fiftyoneDegreesOverrideProfileIdMethod callback) { + overrideProfileIdsState iterateState = { callback, state }; EvidenceIterate( evidence, FIFTYONE_DEGREES_EVIDENCE_COOKIE | FIFTYONE_DEGREES_EVIDENCE_QUERY, - &callback, + &iterateState, iteratorProfileId); } diff --git a/overrides.h b/overrides.h index 8f08c6d7..e2f34d12 100644 --- a/overrides.h +++ b/overrides.h @@ -264,12 +264,12 @@ EXTERNAL void fiftyoneDegreesOverrideValuesReset( * the override method supplied. * @param evidence to extract the profile ids from * @param state pointer to pass to the override method - * @param override method called to override a profile id + * @param callback method called to override a profile id */ EXTERNAL void fiftyoneDegreesOverrideProfileIds( fiftyoneDegreesEvidenceKeyValuePairArray *evidence, void *state, - fiftyoneDegreesOverrideProfileIdMethod override); + fiftyoneDegreesOverrideProfileIdMethod callback); /** * @} From a815adb7acd6b6e0d0601103a688cdee62b48de4 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Mon, 7 Oct 2024 14:24:45 +0200 Subject: [PATCH 72/73] FIX: overrides: add skipPrefix to IS_HEADER_MATCH macro --- overrides.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/overrides.c b/overrides.c index aff80043..75559292 100644 --- a/overrides.c +++ b/overrides.c @@ -51,14 +51,13 @@ static void collectionRelease(Item *item) { /* Prefix to use when comparing property names. */ #define OVERRIDE_PREFIX "51D_" -/** - * Checks if the pair (p) have a field name that matches the target (t). - * The last byte of t is null where as fieldLength is the length of printable - * characters. Take 1 from the t to compare length. - */ -#define IS_HEADER_MATCH(t,p) \ - (sizeof(t) - 1 == p->fieldLength && \ - StringCompareLength(p->field, t, sizeof(t)) == 0) +/** + * Checks if the pair (p) have a field name that matches the target (t). + * The last byte of t is null where as fieldLength is the length of printable + * characters. Take 1 from the t to compare length. + */ +#define IS_HEADER_MATCH(t,p) \ + (StringCompareLength(skipPrefix(true, (char*)pair->field), t, sizeof(t)) == 0) static const Collection dummyCollection = { NULL, From 0833f95f70e391cf64df3be77c7da787ebf9cfa5 Mon Sep 17 00:00:00 2001 From: Eugene Dorfman Date: Mon, 14 Oct 2024 13:57:40 +0200 Subject: [PATCH 73/73] FEAT: introduce invalid input value status code --- fiftyone.h | 2 ++ status.c | 5 +++-- status.h | 7 +++++-- tests/StatusTests.cpp | 16 +++++++++++++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/fiftyone.h b/fiftyone.h index d7cffebc..92ec4a16 100644 --- a/fiftyone.h +++ b/fiftyone.h @@ -435,6 +435,8 @@ MAP_TYPE(KeyValuePairArray) #define TEMP_FILE_ERROR FIFTYONE_DEGREES_STATUS_TEMP_FILE_ERROR /**< Synonym for #FIFTYONE_DEGREES_STATUS_INCORRECT_IP_ADDRESS_FORMAT status code. */ #define DATA_FILE_NEEDS_UPDATED FIFTYONE_DEGREES_STATUS_DATA_FILE_NEEDS_UPDATED /**< Synonym for #FIFTYONE_DEGREES_STATUS_DATA_FILE_NEEDS_UPDATED status code. */ #define INSUFFICIENT_CAPACITY FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY /**< Synonym for #FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY status code. */ +#define INVALID_INPUT FIFTYONE_DEGREES_STATUS_INVALID_INPUT /**< Synonym for + #FIFTYONE_DEGREES_STATUS_INVALID_INPUT status code.*/ /** * @} */ diff --git a/status.c b/status.c index cf27a3d4..f91fdcd0 100644 --- a/status.c +++ b/status.c @@ -120,7 +120,8 @@ static StatusMessage messages[] = { "string has correct format. If passing a byte array, verify the " "associated input data is also consistent." }, { TEMP_FILE_ERROR, - "Error occurs during the creation of a temporary file."} + "Error occurs during the creation of a temporary file."}, + { INVALID_INPUT, "The input value is invalid: misformatted or semantically inconsistent."}, }; static char defaultMessage[] = "Status code %i does not have any message text."; @@ -157,4 +158,4 @@ const char* fiftyoneDegreesStatusGetMessage( Snprintf(message, messageSize, defaultMessage, (int)status); } return message; -} \ No newline at end of file +} diff --git a/status.h b/status.h index edb8bdd8..485e35eb 100644 --- a/status.h +++ b/status.h @@ -138,7 +138,10 @@ typedef enum e_fiftyone_degrees_status_code { FIFTYONE_DEGREES_STATUS_INCORRECT_IP_ADDRESS_FORMAT, /**< IP address format is incorrect */ FIFTYONE_DEGREES_STATUS_TEMP_FILE_ERROR, /**< Error creating temp file */ - FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY, + FIFTYONE_DEGREES_STATUS_INSUFFICIENT_CAPACITY, /**< Insufficient capacity of + the array to hold all the items*/ + FIFTYONE_DEGREES_STATUS_INVALID_INPUT, /**< Invalid input data (f.e. base64 / JSON + misformat or semantic inconsistency) */ } fiftyoneDegreesStatusCode; /** @@ -158,4 +161,4 @@ EXTERNAL const char* fiftyoneDegreesStatusGetMessage( * @} */ -#endif \ No newline at end of file +#endif diff --git a/tests/StatusTests.cpp b/tests/StatusTests.cpp index 6d1b44f0..85c4ea55 100644 --- a/tests/StatusTests.cpp +++ b/tests/StatusTests.cpp @@ -274,4 +274,18 @@ TEST(Status, Get_NegativeCode) { Snprintf(code, 3, "%d", invalidStatus); assertContains(message, code); free((void*)message); -} \ No newline at end of file +} + +/** + * Check that a message is still returned for a negative status code which does + * not (and cannot) exist and the message contains the code as a string. + */ +TEST(Status, Get_InvalidInput) { + const char *message = fiftyoneDegreesStatusGetMessage( + FIFTYONE_DEGREES_STATUS_INVALID_INPUT, + NULL); + assertValidMessage(message); + assertContains(message, "invalid"); + assertContains(message, "input value"); + free((void*)message); +}