Skip to content

Commit

Permalink
Merge pull request #5232 from open62541/1.3
Browse files Browse the repository at this point in the history
Merge 1.3 to master
  • Loading branch information
jpfr committed Jul 1, 2022
2 parents 671a7f4 + 327c9c4 commit 8c64fab
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 68 deletions.
138 changes: 87 additions & 51 deletions src/ua_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -617,68 +617,81 @@ UA_Variant_setArrayCopy(UA_Variant *v, const void * UA_RESTRICT array,
return UA_STATUSCODE_GOOD;
}

/* Test if a range is compatible with a variant. If yes, the following values
* are set:
* - total: how many elements are in the range
* - block: how big is each contiguous block of elements in the variant that
* maps into the range
* - stride: how many elements are between the blocks (beginning to beginning)
* - first: where does the first block begin */
/* Test if a range is compatible with a variant. This may adjust the upper bound
* (max) in order to fit the variant. */
static UA_StatusCode
computeStrides(const UA_Variant *v, const UA_NumericRange range,
size_t *total, size_t *block, size_t *stride, size_t *first) {
checkAdjustRange(const UA_Variant *v, UA_NumericRange *range) {
/* Test for max array size (64bit only) */
#if (SIZE_MAX > 0xffffffff)
if(v->arrayLength > UA_UINT32_MAX)
return UA_STATUSCODE_BADINTERNALERROR;
#endif

/* Test the integrity of the source variant dimensions, make dimensions
* vector of one dimension if none defined */
u32 arrayLength = (u32)v->arrayLength;
const u32 *dims = &arrayLength;
size_t dims_count = 1;
if(v->arrayDimensionsSize > 0) {
size_t elements = 1;
dims_count = v->arrayDimensionsSize;
dims = (u32*)v->arrayDimensions;
for(size_t i = 0; i < dims_count; ++i)
elements *= dims[i];
if(elements != v->arrayLength)
return UA_STATUSCODE_BADINTERNALERROR;

/* Assume one array dimension if none defined */
const u32 *dims = v->arrayDimensions;
size_t dims_count = v->arrayDimensionsSize;
if(v->arrayDimensionsSize == 0) {
dims_count = 1;
dims = &arrayLength;
}
UA_assert(dims_count > 0);

/* Upper bound of the dimensions for stack-allocation */
if(dims_count > UA_MAX_ARRAY_DIMS)
/* Does the range match the dimension of the variant? */
if(range->dimensionsSize != dims_count)
return UA_STATUSCODE_BADINDEXRANGENODATA;

/* Check that the number of elements in the variant matches the array
* dimensions */
size_t elements = 1;
for(size_t i = 0; i < dims_count; ++i)
elements *= dims[i];
if(elements != v->arrayLength)
return UA_STATUSCODE_BADINTERNALERROR;
UA_UInt32 realmax[UA_MAX_ARRAY_DIMS];

/* Test the integrity of the range and compute the max index used for every
* dimension. The standard says in Part 4, Section 7.22:
*
* When reading a value, the indexes may not specify a range that is within
* the bounds of the array. The Server shall return a partial result if some
* elements exist within the range. */
size_t count = 1;
if(range.dimensionsSize != dims_count)
return UA_STATUSCODE_BADINDEXRANGENODATA;
for(size_t i = 0; i < dims_count; ++i) {
if(range.dimensions[i].min > range.dimensions[i].max)
if(range->dimensions[i].min > range->dimensions[i].max)
return UA_STATUSCODE_BADINDEXRANGEINVALID;
if(range.dimensions[i].min >= dims[i])
if(range->dimensions[i].min >= dims[i])
return UA_STATUSCODE_BADINDEXRANGENODATA;

if(range.dimensions[i].max < dims[i])
realmax[i] = range.dimensions[i].max;
else
realmax[i] = dims[i] - 1;

count *= (realmax[i] - range.dimensions[i].min) + 1;
/* Reduce the max to fit the variant */
if(range->dimensions[i].max >= dims[i])
range->dimensions[i].max = dims[i] - 1;
}

return UA_STATUSCODE_GOOD;
}

/* Computes the stride for copying the range elements.
* - total: how many elements are in the range
* - block: how big is each contiguous block of elements in the variant that
* maps into the range
* - stride: how many elements are between the blocks (beginning to beginning)
* - first: where does the first block begin */
static void
computeStrides(const UA_Variant *v, const UA_NumericRange range,
size_t *total, size_t *block, size_t *stride, size_t *first) {
/* Number of total elements to be copied */
size_t count = 1;
for(size_t i = 0; i < range.dimensionsSize; ++i)
count *= (range.dimensions[i].max - range.dimensions[i].min) + 1;
*total = count;

/* Assume one array dimension if none defined */
u32 arrayLength = (u32)v->arrayLength;
const u32 *dims = v->arrayDimensions;
size_t dims_count = v->arrayDimensionsSize;
if(v->arrayDimensionsSize == 0) {
dims_count = 1;
dims = &arrayLength;
}

/* Compute the stride length and the position of the first element */
*block = count; /* Assume the range describes the entire array. */
*stride = v->arrayLength; /* So it can be copied as a contiguous block. */
Expand All @@ -687,7 +700,7 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
UA_Boolean found_contiguous = false;
for(size_t k = dims_count; k > 0;) {
--k;
size_t dimrange = 1 + realmax[k] - range.dimensions[k].min;
size_t dimrange = 1 + range.dimensions[k].max - range.dimensions[k].min;
if(!found_contiguous && dimrange != dims[k]) {
/* Found the maximum block that can be copied contiguously */
found_contiguous = true;
Expand All @@ -697,7 +710,6 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
*first += running_dimssize * range.dimensions[k].min;
running_dimssize *= dims[k];
}
return UA_STATUSCODE_GOOD;
}

/* Is the type string-like? */
Expand Down Expand Up @@ -738,15 +750,26 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant * UA_RESTRICT dst,
const UA_NumericRange range) {
if(!src->type)
return UA_STATUSCODE_BADINVALIDARGUMENT;

UA_Boolean isScalar = UA_Variant_isScalar(src);
UA_Boolean stringLike = isStringLike(src->type);
UA_Variant arraySrc;

/* Upper bound of the dimensions for stack-allocation */
if(range.dimensionsSize > UA_MAX_ARRAY_DIMS)
return UA_STATUSCODE_BADINTERNALERROR;

/* Copy the const range to a mutable stack location */
UA_NumericRangeDimension thisrangedims[UA_MAX_ARRAY_DIMS];
memcpy(thisrangedims, range.dimensions, sizeof(UA_NumericRangeDimension) * range.dimensionsSize);
UA_NumericRange thisrange = {range.dimensionsSize, thisrangedims};

UA_NumericRangeDimension scalarThisDimension = {0,0}; /* a single entry */
UA_NumericRange nextrange = {0, NULL};

/* Extract the range for copying at this level. The remaining range is dealt
* with in the "scalar" type that may define an array by itself (string,
* variant, ...). */
UA_NumericRange thisrange, nextrange;
UA_NumericRangeDimension scalarThisDimension = {0,0}; /* a single entry */
UA_Variant arraySrc;
if(isScalar) {
/* Replace scalar src with array of length 1 */
arraySrc = *src;
Expand All @@ -763,19 +786,19 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant * UA_RESTRICT dst,
dims = 1;
if(dims > range.dimensionsSize)
return UA_STATUSCODE_BADINDEXRANGEINVALID;
thisrange = range;
thisrange.dimensionsSize = dims;
nextrange.dimensions = &range.dimensions[dims];
nextrange.dimensionsSize = range.dimensionsSize - dims;
}

/* Compute the strides */
size_t count, block, stride, first;
UA_StatusCode retval = computeStrides(src, thisrange, &count,
&block, &stride, &first);
UA_StatusCode retval = checkAdjustRange(src, &thisrange);
if(retval != UA_STATUSCODE_GOOD)
return retval;

/* Compute the strides */
size_t count, block, stride, first;
computeStrides(src, thisrange, &count, &block, &stride, &first);

/* Allocate the array */
UA_Variant_init(dst);
dst->data = UA_Array_new(count, src->type);
Expand Down Expand Up @@ -868,12 +891,25 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant * UA_RESTRICT dst,
static UA_StatusCode
Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
const UA_NumericRange range, UA_Boolean copy) {
/* Compute the strides */
size_t count, block, stride, first;
UA_StatusCode retval = computeStrides(v, range, &count,
&block, &stride, &first);
if(!v->type)
return UA_STATUSCODE_BADINVALIDARGUMENT;

/* Upper bound of the dimensions for stack-allocation */
if(range.dimensionsSize > UA_MAX_ARRAY_DIMS)
return UA_STATUSCODE_BADINTERNALERROR;

/* Copy the const range to a mutable stack location */
UA_NumericRangeDimension thisrangedims[UA_MAX_ARRAY_DIMS];
memcpy(thisrangedims, range.dimensions, sizeof(UA_NumericRangeDimension) * range.dimensionsSize);
UA_NumericRange thisrange = {range.dimensionsSize, thisrangedims};

UA_StatusCode retval = checkAdjustRange(v, &thisrange);
if(retval != UA_STATUSCODE_GOOD)
return retval;

/* Compute the strides */
size_t count, block, stride, first;
computeStrides(v, range, &count, &block, &stride, &first);
if(count != arraySize)
return UA_STATUSCODE_BADINDEXRANGEINVALID;

Expand Down
63 changes: 46 additions & 17 deletions tests/check_types_range.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,50 +38,78 @@ START_TEST(parseRangeMinEqualMax) {
} END_TEST

START_TEST(copySimpleArrayRange) {
UA_Variant v, v2;
UA_Variant_init(&v);
UA_Variant_init(&v2);
UA_UInt32 arr[5] = {1,2,3,4,5};
UA_Variant_setArray(&v, arr, 5, &UA_TYPES[UA_TYPES_UINT32]);

UA_NumericRange r;
UA_String sr = UA_STRING("1:3");
UA_StatusCode retval = UA_NumericRange_parse(&r, sr);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);

retval = UA_Variant_copyRange(&v, &v2, r);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(3, v2.arrayLength);
ck_assert_uint_eq(2, *(UA_UInt32*)v2.data);

UA_Variant_clear(&v2);
UA_free(r.dimensions);
}
END_TEST

START_TEST(copyIntoStringArrayRange) {
UA_Variant v, v2;
UA_Variant_init(&v);
UA_Variant_init(&v2);
UA_UInt32 arr[5] = {1,2,3,4,5};
UA_Variant_setArray(&v, arr, 5, &UA_TYPES[UA_TYPES_UINT32]);
UA_String arr[2];
arr[0] = UA_STRING("abcd");
arr[1] = UA_STRING("wxyz");
UA_Variant_setArray(&v, arr, 5, &UA_TYPES[UA_TYPES_STRING]);

UA_NumericRange r;
UA_String sr = UA_STRING("1:3");
UA_String sr = UA_STRING("0:1,1:2");
UA_StatusCode retval = UA_NumericRange_parse(&r, sr);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);

retval = UA_Variant_copyRange(&v, &v2, r);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(3, v2.arrayLength);
ck_assert_uint_eq(2, *(UA_UInt32*)v2.data);
ck_assert_uint_eq(2, v2.arrayLength);

UA_String s1 = UA_STRING("bc");
UA_String s2 = UA_STRING("xy");
UA_String *arr2 = (UA_String*)v2.data;
ck_assert(UA_String_equal(&arr2[0], &s1));
ck_assert(UA_String_equal(&arr2[1], &s2));

UA_Variant_clear(&v2);
UA_free(r.dimensions);
}
END_TEST

START_TEST(copyIntoStringArrayRange) {
START_TEST(copyArrayRangeUpperBoundOutOfRange) {
UA_Variant v, v2;
UA_Variant_init(&v);
UA_Variant_init(&v2);
UA_String arr[2];
arr[0] = UA_STRING("abcd");
arr[1] = UA_STRING("wxyz");
UA_Variant_setArray(&v, arr, 5, &UA_TYPES[UA_TYPES_STRING]);
UA_UInt32 arr[5] = {1,2,3,4,5};
UA_UInt32 arraySize = 5;
UA_Variant_setArray(&v, arr, 5, &UA_TYPES[UA_TYPES_UINT32]);
v.arrayDimensionsSize = 1;
v.arrayDimensions = &arraySize;

UA_NumericRange r;
UA_String sr = UA_STRING("0:1,1:2");
UA_String sr = UA_STRING("3:5");
UA_StatusCode retval = UA_NumericRange_parse(&r, sr);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);

retval = UA_Variant_copyRange(&v, &v2, r);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(2, v2.arrayLength);

UA_String s1 = UA_STRING("bc");
UA_String s2 = UA_STRING("xy");
UA_String *arr2 = (UA_String*)v2.data;
ck_assert(UA_String_equal(&arr2[0], &s1));
ck_assert(UA_String_equal(&arr2[1], &s2));
ck_assert_uint_eq(4, *(UA_UInt32*)v2.data);
ck_assert_uint_eq(5, *((UA_UInt32*)v2.data+1));
ck_assert_uint_eq(1, v2.arrayDimensionsSize);
ck_assert_uint_eq(2, v2.arrayDimensions[0]);

UA_Variant_clear(&v2);
UA_free(r.dimensions);
Expand All @@ -95,6 +123,7 @@ int main(void) {
tcase_add_test(tc, parseRangeMinEqualMax);
tcase_add_test(tc, copySimpleArrayRange);
tcase_add_test(tc, copyIntoStringArrayRange);
tcase_add_test(tc, copyArrayRangeUpperBoundOutOfRange);
suite_add_tcase(s, tc);

SRunner *sr = srunner_create(s);
Expand Down

0 comments on commit 8c64fab

Please sign in to comment.