diff --git a/include/freerdp/utils/pod_arrays.h b/include/freerdp/utils/pod_arrays.h new file mode 100644 index 000000000..949f08939 --- /dev/null +++ b/include/freerdp/utils/pod_arrays.h @@ -0,0 +1,141 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * POD arrays + * + * Copyright 2022 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FREERDP_UTILS_POD_ARRAYS_H_ +#define FREERDP_UTILS_POD_ARRAYS_H_ + +#include +#include + +#define POD_ARRAYS_IMPL(T, TLOWER) \ + typedef struct \ + { \ + T* values; \ + size_t nvalues; \ + } Array##T; \ + typedef BOOL Array##T##Cb(T* v, void* data); \ + \ + static INLINE void array_##TLOWER##_init(Array##T* a) \ + { \ + WINPR_ASSERT(a); \ + a->values = NULL; \ + a->nvalues = 0; \ + } \ + \ + static INLINE size_t array_##TLOWER##_size(const Array##T* a) \ + { \ + WINPR_ASSERT(a); \ + return a->nvalues; \ + } \ + \ + static INLINE T* array_##TLOWER##_data(const Array##T* a) \ + { \ + WINPR_ASSERT(a); \ + return a->values; \ + } \ + \ + static INLINE const T* array_##TLOWER##_cdata(const Array##T* a) \ + { \ + WINPR_ASSERT(a); \ + return (const T*)a->values; \ + } \ + \ + static INLINE T array_##TLOWER##_get(const Array##T* a, size_t idx) \ + { \ + WINPR_ASSERT(a); \ + WINPR_ASSERT(a->nvalues > idx); \ + return a->values[idx]; \ + } \ + \ + static INLINE void array_##TLOWER##_set(Array##T* a, size_t idx, T v) \ + { \ + WINPR_ASSERT(a); \ + WINPR_ASSERT(a->nvalues > idx); \ + a->values[idx] = v; \ + } \ + \ + static INLINE BOOL array_##TLOWER##_append(Array##T* a, T v) \ + { \ + WINPR_ASSERT(a); \ + T* tmp = realloc(a->values, sizeof(T) * (a->nvalues + 1)); \ + if (!tmp) \ + return FALSE; \ + \ + tmp[a->nvalues] = v; \ + a->values = tmp; \ + a->nvalues++; \ + return TRUE; \ + } \ + \ + static INLINE BOOL array_##TLOWER##_contains(const Array##T* a, T v) \ + { \ + WINPR_ASSERT(a); \ + UINT32 i; \ + \ + for (i = 0; i < a->nvalues; i++) \ + { \ + if (memcmp(&a->values[i], &v, sizeof(T)) == 0) \ + return TRUE; \ + } \ + \ + return FALSE; \ + } \ + \ + static INLINE BOOL array_##TLOWER##_foreach(Array##T* a, Array##T##Cb cb, void* data) \ + { \ + WINPR_ASSERT(a); \ + size_t i; \ + for (i = 0; i < a->nvalues; i++) \ + { \ + if (!cb(&a->values[i], data)) \ + return FALSE; \ + } \ + \ + return TRUE; \ + } \ + \ + static INLINE void array_##TLOWER##_reset(Array##T* a) \ + { \ + WINPR_ASSERT(a); \ + a->nvalues = 0; \ + } \ + \ + static INLINE void array_##TLOWER##_uninit(Array##T* a) \ + { \ + WINPR_ASSERT(a); \ + free(a->values); \ + \ + a->values = NULL; \ + a->nvalues = 0; \ + } + +#ifdef __cplusplus +extern "C" { +#endif + + +POD_ARRAYS_IMPL(UINT16, uint16); +POD_ARRAYS_IMPL(UINT32, uint32); +POD_ARRAYS_IMPL(UINT64, uint64); + +#ifdef __cplusplus +} +#endif + + +#endif /* FREERDP_UTILS_POD_ARRAYS_H_ */ diff --git a/libfreerdp/utils/test/CMakeLists.txt b/libfreerdp/utils/test/CMakeLists.txt index 5fbcf4dbf..4cdb40b73 100644 --- a/libfreerdp/utils/test/CMakeLists.txt +++ b/libfreerdp/utils/test/CMakeLists.txt @@ -5,7 +5,8 @@ set(MODULE_PREFIX "TEST_FREERDP_UTILS") set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) set(${MODULE_PREFIX}_TESTS - TestRingBuffer.c) + TestRingBuffer.c + TestPodArrays.c) create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} @@ -13,7 +14,7 @@ create_test_sourcelist(${MODULE_PREFIX}_SRCS add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) -target_link_libraries(${MODULE_NAME} freerdp) +target_link_libraries(${MODULE_NAME} freerdp winpr) set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") diff --git a/libfreerdp/utils/test/TestPodArrays.c b/libfreerdp/utils/test/TestPodArrays.c new file mode 100644 index 000000000..9af89deee --- /dev/null +++ b/libfreerdp/utils/test/TestPodArrays.c @@ -0,0 +1,126 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * TestPodArrays + * + * Copyright 2022 David Fort + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +static BOOL cb_compute_sum(UINT32 *v, void* target) +{ + UINT32* ret = (UINT32*)target; + *ret += *v; + return TRUE; +} + +static BOOL cb_stop_at_5(UINT32 *v, void* target) +{ + UINT32* ret = (UINT32*)target; + *ret += 1; + return (*ret != 5); +} + +static BOOL cb_set_to_1(UINT32* v, void* target) +{ + *v = 1; + return TRUE; +} + +static BOOL cb_reset_after_1(UINT32* v, void* target) +{ + ArrayUINT32* a = (ArrayUINT32*)target; + array_uint32_reset(a); + return TRUE; +} + +typedef struct { + UINT32 v1; + UINT16 v2; +} BasicStruct; + +static BOOL cb_basic_struct(BasicStruct *v, void* target) +{ + return (v->v1 == 1) && (v->v2 == 2); +} + + +POD_ARRAYS_IMPL(BasicStruct, basicstruct); + + +int TestPodArrays(int argc, char* argv[]) +{ + UINT32 i, sum, foreach_index; + ArrayUINT32 uint32s; + UINT32* ptr; + const UINT32* cptr; + ArrayBasicStruct basicStructs; + BasicStruct basicStruct = { 1, 2 }; + + array_uint32_init(&uint32s); + for (i = 0; i < 10; i++) + if (!array_uint32_append(&uint32s, i)) + return -1; + + sum = 0; + if (!array_uint32_foreach(&uint32s, cb_compute_sum, &sum)) + return -2; + + if (sum != 45) + return -3; + + foreach_index = 0; + if (array_uint32_foreach(&uint32s, cb_stop_at_5, &foreach_index)) + return -4; + + if (foreach_index != 5) + return -5; + + if (array_uint32_get(&uint32s, 4) != 4) + return -6; + + array_uint32_set(&uint32s, 4, 5); + if (array_uint32_get(&uint32s, 4) != 5) + return -7; + + ptr = array_uint32_data(&uint32s); + if (*ptr != 0) + return -8; + + cptr = array_uint32_cdata(&uint32s); + if (*cptr != 0) + return -9; + + /* test modifying values of the array during the foreach */ + if (!array_uint32_foreach(&uint32s, cb_set_to_1, NULL) || array_uint32_get(&uint32s, 5) != 1) + return -10; + + /* this one is to test that we can modify the array itself during the foreach and that things + * go nicely */ + if (!array_uint32_foreach(&uint32s, cb_reset_after_1, &uint32s) || array_uint32_size(&uint32s)) + return -11; + + array_uint32_uninit(&uint32s); + + /* give a try with an array of BasicStructs */ + array_basicstruct_init(&basicStructs); + if (!array_basicstruct_append(&basicStructs, basicStruct)) + return -20; + + if (!array_basicstruct_foreach(&basicStructs, cb_basic_struct, NULL)) + return -21; + + array_basicstruct_uninit(&basicStructs); + return 0; +}