The example illustrates a "clock-time" benchmark for assessing detection speed.
The example illustrates a "clock-time" benchmark for assessing detection speed.Using a YAML formatted evidence file - "20000 Evidence Records.yml" - from the data repository on GitHub, which the example downloads when it is missing.
It's important to understand the trade-offs between performance, memory usage and accuracy, that the 51Degrees pipeline configuration makes available, and this example shows a range of different configurations to illustrate the difference in performance.
Requesting properties from a single component reduces detection time compared with requesting properties from multiple components. If you don't specify any properties to detect, then all properties are detected.
#include <stdio.h>
#include <time.h>
#include "ExampleBase.h"
#define DEFAULT_NUMBER_OF_THREADS 2
#define DEFAULT_ITERATIONS 10000
static bool detectionOnly = false;
#define SIZE_OF_KEY 500
#define SIZE_OF_VALUE 1000
#define MAX_EVIDENCE 20
static const char* dataDir = "data/device-detection";
static const char* dataFileName = "51Degrees-LiteV4.1.hash";
static const char* evidenceFileName = "20000 Evidence Records.yml";
static int exitCode = 0;
static const char* userAgent = "user-agent";
typedef struct performanceConfig_t {
bool allProperties;
};
typedef struct evidence_node_t {
evidenceNode* next;
typedef struct shared_string_node_t {
const char* value;
size_t length;
sharedStringNode* next;
sharedStringNode* bucketNext;
typedef struct threadState_t {
performanceState* mainState;
evidenceNode* evidenceFirst;
evidenceNode* evidenceLast;
unsigned long long checkSum;
unsigned long long iterations;
typedef struct performanceState_t {
uint16_t numberOfThreads;
sharedStringNode* sharedStringFirst;
sharedStringNode* sharedStringLast;
sharedStringNode** sharedStringBuckets;
size_t sharedStringBucketCount;
int evidenceCount;
int iterations;
const char* dataFileLocation;
FILE* output;
FILE* resultsOutput;
double startUpMillis;
int availableProperties;
int maxEvidence;
double elapsedMilliSeconds;
threadState* threadStates;
int threadIndex;
static size_t sharedStringBucket(
const char* s) {
uint64_t h = 1469598103934665603ULL;
for (const unsigned char* p = (const unsigned char*)s; *p != '\0'; p++) {
h ^= (uint64_t)*p;
h *= 1099511628211ULL;
}
return (
size_t)h & (perfState->
sharedStringBucketCount - 1);
}
static const char* getOrAddSharedString(
const char* target) {
int haveTable = perfState->
sharedStringBuckets != NULL;
size_t bucket = 0;
if (haveTable) {
bucket = sharedStringBucket(perfState, target);
for (node = perfState->sharedStringBuckets[bucket];
node != NULL; node = node->
bucketNext) {
if (strcmp(target, node->
value) == 0) {
return node->value;
}
}
}
else {
for (node = perfState->
sharedStringFirst;
node != NULL; node = node->
next) {
if (strcmp(target, node->value) == 0) {
return node->value;
}
}
}
size_t length = strlen(target) + 1;
if (node == NULL) {
return NULL;
}
node->next = NULL;
node->bucketNext = NULL;
node->value = (
const char*)
Malloc(
sizeof(
char) * length);
if (node->value == NULL) {
return NULL;
}
if (strncpy((char*)node->value, target, length) == NULL) {
return NULL;
}
if (perfState->sharedStringFirst == NULL) {
perfState->sharedStringFirst = node;
perfState->
sharedStringLast = node;
}
else {
perfState->sharedStringLast->next = node;
perfState->sharedStringLast = node;
}
if (haveTable) {
node->bucketNext = perfState->sharedStringBuckets[bucket];
perfState->sharedStringBuckets[bucket] = node;
}
return node->value;
}
static void storeEvidence(
KeyValuePair* pairs, uint16_t size,
void* state) {
char* ptr;
}
else {
}
for (uint32_t i = 0; i < size; i++) {
if (prefix != NULL) {
evidence->items[i].item.
key = getOrAddSharedString(
perfState,
if (
evidence->items[i].item.key == NULL) {
}
}
else {
}
if (
evidence->items[i].item.key == NULL ||
strcmp(
evidence->items[i].item.key, userAgent) == 0) {
strlen(pairs[i].value);
sizeof(
char) * (
evidence->items[i].item.valueLength + 1));
ptr = strncpy(
pairs[i].value,
if (ptr == NULL) {
}
*(ptr +
evidence->items[i].item.valueLength) =
'\0';
}
else {
evidence->items[i].parsedValue = getOrAddSharedString(
perfState,
pairs[i].value);
if (
evidence->items[i].parsedValue == NULL) {
}
evidence->items[i].parsedLength = strlen(
}
}
if (size > perfState->
maxEvidence) {
perfState->maxEvidence = size;
}
perfState->
evidenceCount++;
perfState->threadIndex++;
if (perfState->threadIndex == perfState->
numberOfThreads) {
perfState->threadIndex = 0;
}
}
void runPerformanceThread(void* state) {
&thisState->
mainState->
manager,
0);
thisState->mainState->maxEvidence);
while(node != NULL) {
for (uint32_t i = 0; i <
evidence->count; i++) {
}
for (uint32_t j = 0; detectionOnly == false &&
j++) {
results,
j,
value = (
String*)results->values.items[0].data.ptr;
if (results->values.count > 0 && value != NULL) {
thisState->checkSum += value->size;
}
}
}
for (uint32_t i = 0; i < results->count; i++) {
thisState->iterations +=
(unsigned long long)results->items[i].iterations;
}
node = node->next;
}
}
}
for (int i = 0; i < state->numberOfThreads; i++) {
state->threadStates[i].
checkSum = 0;
state->threadStates[i].
iterations = 0;
}
int thread;
TIMER_CREATE;
TIMER_START;
for (thread = 0; thread < state->numberOfThreads; thread++) {
&state->threadStates[thread]);
}
for (thread = 0; thread < state->numberOfThreads; thread++) {
}
}
else {
fprintf(state->
output,
"Example not build with multi threading support.\n");
runPerformanceThread(&state->threadStates[0]);
}
TIMER_END;
return TIMER_ELAPSED;
}
unsigned long long checksum = 0;
unsigned long long iterations = 0;
for (int i = 0; i < state->numberOfThreads; i++) {
checksum += state->threadStates[i].checkSum;
iterations += state->threadStates[i].iterations;
}
double millisPerTest = state->
elapsedMilliSeconds / (double)state->evidenceCount;
fprintf(state->output,
"%d detections, Average ms per detection: %f, Detections per second: %.0lf\n",
state->evidenceCount,
millisPerTest,
round(1000.0 / millisPerTest));
fprintf(state->output,
"Concurrent threads: %d, Checksum: %llu, Iterations: %llu\n",
state->numberOfThreads,
checksum,
iterations);
fprintf(state->output,
"Startup ms %.0lf\n",
fprintf(state->output,
"Properties retrieved %d\n",
detectionOnly ? 0 : state->
availableProperties);
fprintf(state->output, "Detection only: %s\n\n",
detectionOnly ? "true" : "false");
if (state->
resultsOutput != NULL) {
fprintf(state->resultsOutput, " \"DetectionsPerSecond\": %.2f,\n", round(1000.0 / millisPerTest));
fprintf(state->resultsOutput, " \"StartupMs\": %.0lf,\n", state->startUpMillis);
fprintf(state->resultsOutput, " \"DetectionOnly\": %s\n",
detectionOnly ? "true" : "false");
}
}
void executeBenchmark(
fprintf(state->output,
"Benchmarking with profile: %s AllProperties: %s\n",
fiftyoneDegreesExampleGetConfigName(dataSetConfig),
config.
allProperties ?
"True" :
"False");
if (config.allProperties == false) {
properties.
string =
"IsMobile";
}
fprintf(state->output, "Initialize device detection\n");
TIMER_CREATE;
TIMER_START;
&state->manager,
&dataSetConfig,
&properties,
exception);
fprintf(state->output, "%s\n", message);
exitCode = 1;
return;
}
TIMER_END;
state->startUpMillis = TIMER_ELAPSED;
fiftyoneDegreesExampleCheckDataFile(dataset);
fprintf(state->output, "Warming up\n");
runTests(state);
}
fprintf(state->output, "Running\n");
state->elapsedMilliSeconds = runTests(state);
fprintf(state->output,
"Finished - Execution time was %lf ms\n",
state->elapsedMilliSeconds);
doReport(state);
}
for(int i = 0; i < state->numberOfThreads; i++) {
while (node != NULL) {
for (uint32_t j = 0; j <
evidence->count; j++) {
if (
evidence->items[j].item.value != NULL) {
}
}
node = next;
}
}
}
while (node != NULL) {
Free((
void*)node->value);
node = next;
}
if (state->sharedStringBuckets != NULL) {
Free((
void*)state->sharedStringBuckets);
state->sharedStringBuckets = NULL;
state->sharedStringBucketCount = 0;
}
}
void fiftyoneDegreesHashPerformance(
const char* dataFilePath,
const char* evidenceFilePath,
uint16_t numberOfThreads,
int iterations,
FILE* output,
FILE* resultsOutput) {
fprintf(output, "Running Performance example - ");
fprintf(output, "optimised build\n");
}
else {
fprintf(output, "standard build\n");
printf("\033[0;33m");
fprintf(
output,
"Use FIFTYONE_DEGREES_MEMORY_ONLY directive for optimum " \
"performance\n");
printf("\033[0m");
}
state.dataFileLocation = dataFilePath;
state.output = output;
state.resultsOutput = resultsOutput;
state.evidenceCount = 0;
state.numberOfThreads = numberOfThreads;
}
else {
state.numberOfThreads = 1;
}
for(int i = 0; i < numberOfThreads; i++) {
state.threadStates[i].evidenceFirst = NULL;
state.threadStates[i].evidenceLast = NULL;
state.threadStates[i].mainState = &state;
}
state.
iterations = iterations;
char buffer[MAX_EVIDENCE * (SIZE_OF_KEY + SIZE_OF_VALUE)];
char key[MAX_EVIDENCE][SIZE_OF_KEY];
char value[MAX_EVIDENCE][SIZE_OF_VALUE];
for (int i = 0; i < MAX_EVIDENCE; i++) {
pair[i].key = key[i];
pair[i].keyLength = SIZE_OF_KEY;
pair[i].value = value[i];
pair[i].valueLength = SIZE_OF_VALUE;
}
fprintf(
state.output,
"Reading '%i' evidence records into memory.\n",
state.iterations);
state.evidenceCount = 0;
state.maxEvidence = 0;
state.threadIndex = 0;
state.sharedStringFirst = NULL;
state.sharedStringLast = NULL;
state.sharedStringBucketCount = (size_t)1 << 20;
if (state.sharedStringBuckets != NULL) {
memset(state.sharedStringBuckets, 0,
}
else {
state.sharedStringBucketCount = 0;
}
evidenceFilePath,
buffer,
sizeof(buffer),
pair,
MAX_EVIDENCE,
state.iterations,
&state,
storeEvidence);
fprintf(
state.output,
"Read '%i' evidence records into memory.\n",
state.evidenceCount);
if (state.resultsOutput != NULL) {
fprintf(state.resultsOutput, "{");
}
for (int i = 0;
i++) {
if (state.resultsOutput != NULL) {
fprintf(state.resultsOutput, "%s\n\"%s%s\": {\n",
i > 0 ? "," : "",
fiftyoneDegreesExampleGetConfigName(*(config.config)),
config.allProperties ? "_All" : "");
}
executeBenchmark(&state, config);
if (state.resultsOutput != NULL) {
fprintf(state.resultsOutput, "}");
}
}
}
if (state.resultsOutput != NULL) {
fprintf(state.resultsOutput, "}\n");
}
freeSharedStrings(&state);
freeEvidence(&state);
Free(state.threadStates);
fprintf(output, "Finished Performance example\n");
}
void fiftyoneDegreesExampleCPerformanceRun(ExampleParameters* params) {
fiftyoneDegreesHashPerformance(
params->dataFilePath,
params->evidenceFilePath,
params->numberOfThreads,
params->iterations,
params->output,
params->resultsOutput);
}
#ifndef TEST
#define DATA_OPTION "--data-file"
#define DATA_OPTION_SHORT "-d"
#define UA_OPTION "--user-agent-file"
#define UA_OPTION_SHORT "-u"
#define THREAD_OPTION "--threads"
#define THREAD_OPTION_SHORT "-t"
#define JSON_OPTION "--json-output"
#define JSON_OPTION_SHORT "-j"
#define ITERATIONS_OPTION "--iterations"
#define ITERATIONS_OPTION_SHORT "-i"
#define DETECTION_ONLY_OPTION "--detection-only"
#define HELP_OPTION "--help"
#define HELP_OPTION_SHORT "-h"
#define OPTION_PADDING(o) ((int)(30 - strlen(o)))
#define OPTION_MESSAGE(m, o, s) printf(" %s, %s%*s: %s\n", o, s, OPTION_PADDING(o), " ", m);
void printHelp() {
printf("Available options are:\n");
printf(" --detection-only: Skip property retrieval, keeping component selection unchanged\n");
OPTION_MESSAGE("Path to a 51Degrees Hash data file", DATA_OPTION, DATA_OPTION_SHORT);
OPTION_MESSAGE("Path to a User-Agents YAML file", UA_OPTION, UA_OPTION_SHORT);
OPTION_MESSAGE("Number of threads to run in parallel", THREAD_OPTION, THREAD_OPTION_SHORT);
OPTION_MESSAGE("Number of iterations", ITERATIONS_OPTION, ITERATIONS_OPTION_SHORT);
OPTION_MESSAGE("Path to a file to output JSON format results to", JSON_OPTION, JSON_OPTION_SHORT);
OPTION_MESSAGE("Print this help", HELP_OPTION, HELP_OPTION_SHORT);
}
int main(int argc, char* argv[]) {
uint16_t numberOfThreads = DEFAULT_NUMBER_OF_THREADS;
int iterations = DEFAULT_ITERATIONS;
char *outFile = NULL;
dataFilePath[0] = '\0';
evidenceFilePath[0] = '\0';
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], DATA_OPTION) == 0 ||
strcmp(argv[i], DATA_OPTION_SHORT) == 0) {
strcpy(dataFilePath, argv[i + 1]);
}
else if (strcmp(argv[i], UA_OPTION) == 0 ||
strcmp(argv[i], UA_OPTION_SHORT) == 0) {
strcpy(evidenceFilePath, argv[i + 1]);
}
else if (strcmp(argv[i], THREAD_OPTION) == 0 ||
strcmp(argv[i], THREAD_OPTION_SHORT) == 0) {
numberOfThreads = (uint16_t)atoi(argv[i + 1]);
}
else if (strcmp(argv[i], JSON_OPTION) == 0 ||
strcmp(argv[i], JSON_OPTION_SHORT) == 0) {
outFile = argv[i + 1];
}
else if (strcmp(argv[i], ITERATIONS_OPTION) == 0 ||
strcmp(argv[i], ITERATIONS_OPTION_SHORT) == 0) {
iterations = atoi(argv[i + 1]);
}
else if (strcmp(argv[i], DETECTION_ONLY_OPTION) == 0) {
detectionOnly = true;
}
else if (strcmp(argv[i], HELP_OPTION) == 0 ||
strcmp(argv[i], HELP_OPTION_SHORT) == 0) {
printHelp();
return 0;
}
else if (argv[i][0] == '-') {
printf(
"The option '%s' is not recognized. Use %s (%s) to list options.",
argv[i],
HELP_OPTION,
HELP_OPTION_SHORT);
return 1;
}
else {
}
}
if (strlen(dataFilePath) == 0) {
status = fiftyoneDegreesExampleGetFilePath(
dataDir,
dataFileName,
dataFilePath,
sizeof(dataFilePath));
printf(("Failed to find a device detection "
"data file. Make sure the data submodule is there, "
"by running `git submodule update --init data`, and "
"run this example again to download the file.\n"));
fgetc(stdin);
return 1;
}
}
if (strlen(evidenceFilePath) == 0) {
status = fiftyoneDegreesExampleGetFilePath(
dataDir,
evidenceFileName,
evidenceFilePath,
sizeof(evidenceFilePath));
printf(("Failed to find a device detection "
"evidence file. Make sure the data submodule is there, "
"by running `git submodule update --init data`, and "
"run this example again to download the file.\n"));
fgetc(stdin);
return 1;
}
}
ExampleParameters params;
params.dataFilePath = dataFilePath;
params.evidenceFilePath = evidenceFilePath;
params.numberOfThreads = numberOfThreads;
params.iterations = iterations;
params.output = stdout;
if (outFile != NULL) {
params.resultsOutput = fopen(outFile, "w");
}
else {
params.resultsOutput = NULL;
}
fiftyoneDegreesExampleMemCheck(
¶ms,
fiftyoneDegreesExampleCPerformanceRun);
if (outFile != NULL) {
fclose(params.resultsOutput);
}
return exitCode;
}
#endif
@ FIFTYONE_DEGREES_EVIDENCE_IGNORE
The evidence is invalid and should be ignored.
Definition evidence.h:151
#define DataSetHashRelease
Synonym for fiftyoneDegreesDataSetHashRelease function.
Definition dd/hash/fiftyone.h:72
#define ResultsHashFromEvidence
Synonym for fiftyoneDegreesResultsHashFromEvidence function.
Definition dd/hash/fiftyone.h:70
#define ResultsHashGetValues
Synonym for fiftyoneDegreesResultsHashGetValues function.
Definition dd/hash/fiftyone.h:58
fiftyoneDegreesConfigHash ConfigHash
Synonym for fiftyoneDegreesConfigHash type.
Definition dd/hash/fiftyone.h:51
fiftyoneDegreesResultsHash ResultsHash
Synonym for fiftyoneDegreesResultsHash type.
Definition dd/hash/fiftyone.h:50
#define HashInMemoryConfig
Synonym for fiftyoneDegreesHashInMemoryConfig config.
Definition dd/hash/fiftyone.h:86
fiftyoneDegreesDataSetHash DataSetHash
Synonym for fiftyoneDegreesDataSetHash type.
Definition dd/hash/fiftyone.h:48
#define ResultsHashCreate(manager, overridesCapacity)
Two argument convenience form of fiftyoneDegreesResultsHashCreate that injects the deprecated userAge...
Definition dd/hash/fiftyone.h:66
#define ResultsHashFree
Synonym for fiftyoneDegreesResultsHashFree function.
Definition dd/hash/fiftyone.h:67
#define HashInitManagerFromFile
Synonym for fiftyoneDegreesHashInitManagerFromFile function.
Definition dd/hash/fiftyone.h:75
#define DataSetHashGet
Synonym for fiftyoneDegreesDataSetHashGet function.
Definition dd/hash/fiftyone.h:71
#define ThreadingGetIsThreadSafe
Synonym for fiftyoneDegreesThreadingGetIsThreadSafe function.
Definition common/fiftyone.h:339
#define EXCEPTION_THROW
Synonym for FIFTYONE_DEGREES_EXCEPTION_THROW macro.
Definition common/fiftyone.h:446
#define CollectionGetIsMemoryOnly
Synonym for fiftyoneDegreesCollectionGetIsMemoryOnly function.
Definition common/fiftyone.h:342
fiftyoneDegreesEvidenceKeyValuePairArray EvidenceKeyValuePairArray
Synonym for fiftyoneDegreesEvidenceKeyValuePairArray type.
Definition common/fiftyone.h:172
#define PropertiesDefault
Synonym for fiftyoneDegreesPropertiesDefault function.
Definition common/fiftyone.h:268
#define EXCEPTION_CREATE
Synonym for FIFTYONE_DEGREES_EXCEPTION_CREATE macro.
Definition common/fiftyone.h:443
fiftyoneDegreesEvidenceKeyValuePair EvidenceKeyValuePair
Synonym for fiftyoneDegreesEvidenceKeyValuePair type.
Definition common/fiftyone.h:138
#define StatusGetMessage
Synonym for fiftyoneDegreesStatusGetMessage function.
Definition common/fiftyone.h:293
#define EvidenceCreate
Synonym for fiftyoneDegreesEvidenceCreate function.
Definition common/fiftyone.h:240
#define YamlFileIterateWithLimit
Synonym for fiftyoneDegreesYamlFileIterateWithLimit.
Definition common/fiftyone.h:396
#define PropertiesGetRequiredPropertyCount
Synonym for fiftyoneDegreesPropertiesGetRequiredPropertyCount function.
Definition common/fiftyone.h:327
fiftyoneDegreesStatusCode StatusCode
Synonym for fiftyoneDegreesStatusCode type.
Definition common/fiftyone.h:134
#define ResultsEvaluateAll
Synonym for fiftyoneDegreesResultsEvaluateAll function.
Definition common/fiftyone.h:354
#define EXCEPTION_OKAY
Synonym for FIFTYONE_DEGREES_EXCEPTION_OKAY macro.
Definition common/fiftyone.h:444
#define THREAD_JOIN
Synonym for FIFTYONE_DEGREES_THREAD_JOIN macro.
Definition common/fiftyone.h:454
fiftyoneDegreesString String
Synonym for fiftyoneDegreesString type.
Definition common/fiftyone.h:158
fiftyoneDegreesKeyValuePair KeyValuePair
Synonym for fiftyoneDegreesKeyValuePair type.
Definition common/fiftyone.h:193
#define THREAD_CREATE
Synonym for FIFTYONE_DEGREES_THREAD_CREATE macro.
Definition common/fiftyone.h:451
fiftyoneDegreesEvidencePrefixMap EvidencePrefixMap
Synonym for fiftyoneDegreesEvidencePrefixMap type.
Definition common/fiftyone.h:139
#define SUCCESS
Synonym for FIFTYONE_DEGREES_STATUS_SUCCESS status code.
Definition common/fiftyone.h:478
#define EvidenceFree
Synonym for fiftyoneDegreesEvidenceFree function.
Definition common/fiftyone.h:241
#define Malloc
Synonym for fiftyoneDegreesMalloc function.
Definition common/fiftyone.h:263
#define THREAD_ROUTINE
Synonym for FIFTYONE_DEGREES_THREAD_ROUTINE macro.
Definition common/fiftyone.h:455
#define ResourceManagerFree
Synonym for fiftyoneDegreesResourceManagerFree function.
Definition common/fiftyone.h:310
fiftyoneDegreesResourceManager ResourceManager
Synonym for fiftyoneDegreesResourceManager type.
Definition common/fiftyone.h:137
#define THREAD_CLOSE
Synonym for FIFTYONE_DEGREES_THREAD_CLOSE macro.
Definition common/fiftyone.h:452
fiftyoneDegreesPropertiesRequired PropertiesRequired
Synonym for fiftyoneDegreesPropertiesRequired type.
Definition common/fiftyone.h:135
#define Free
Synonym for fiftyoneDegreesFree function.
Definition common/fiftyone.h:265
#define THREAD_EXIT
Synonym for FIFTYONE_DEGREES_THREAD_EXIT macro.
Definition common/fiftyone.h:453
#define EvidenceMapPrefix
Synonym for fiftyoneDegreesEvidenceMapPrefix function.
Definition common/fiftyone.h:321
#define FILE_MAX_PATH
Synonym for FIFTYONE_DEGREES_FILE_MAX_PATH macro.
Definition common/fiftyone.h:450
#define FIFTYONE_DEGREES_THREAD
A thread created with the FIFTYONE_DEGREES_THREAD_CREATE macro.
Definition threading.h:180
Definition GettingStarted.c:67
Definition Performance.c:129
uint16_t concurrency
Expected number of concurrent requests, 1 or greater.
Definition collection.h:299
bool allInMemory
True if the data file should be loaded entirely into continuous memory.
Definition config.h:54
fiftyoneDegreesConfigBase b
Base structure members.
Definition config-dd.h:53
bool processSpecialEvidence
Some evidence requires additional processing that doesn't need to be checked for if being used in an ...
Definition config-dd.h:73
bool updateMatchedUserAgent
True if the detection should record the matched characters from the target User-Agent.
Definition config-dd.h:54
fiftyoneDegreesCollectionConfig maps
Maps collection config.
Definition hash.h:192
bool usePerformanceGraph
True if the performance optimized graph should be used for processing.
Definition hash.h:209
bool usePredictiveGraph
True if the predictive optimized graph should be used for processing.
Definition hash.h:211
fiftyoneDegreesCollectionConfig properties
Properties collection config.
Definition hash.h:193
fiftyoneDegreesCollectionConfig nodes
Nodes collection config.
Definition hash.h:199
fiftyoneDegreesCollectionConfig values
Values collection config.
Definition hash.h:195
fiftyoneDegreesConfigDeviceDetection b
Base configuration.
Definition hash.h:188
fiftyoneDegreesCollectionConfig strings
Strings collection config.
Definition hash.h:189
fiftyoneDegreesCollectionConfig profiles
Profiles collection config.
Definition hash.h:196
fiftyoneDegreesCollectionConfig components
Components collection config.
Definition hash.h:190
fiftyoneDegreesCollectionConfig profileOffsets
Profile offsets collection config.
Definition hash.h:200
fiftyoneDegreesPropertiesAvailable * available
Available properties.
Definition dataset.h:129
fiftyoneDegreesDataSetBase b
Base structure members.
Definition dataset-dd.h:53
fiftyoneDegreesDataSetDeviceDetection b
Base data set.
Definition hash.h:271
fiftyoneDegreesEvidencePrefix prefixEnum
Enum value of prefix name.
Definition evidence.h:159
size_t prefixLength
Length of the prefix string.
Definition evidence.h:158
const char * string
Separated list of required properties or NULL if all properties are required.
Definition properties.h:234
Definition Performance.c:140
State specific to a single thread.
Definition Performance.c:157