Coverage Report

Created: 2025-11-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/S2OPC/src/Common/helpers/sopc_numeric_range.c
Line
Count
Source
1
/*
2
 * Licensed to Systerel under one or more contributor license
3
 * agreements. See the NOTICE file distributed with this work
4
 * for additional information regarding copyright ownership.
5
 * Systerel licenses this file to you under the Apache
6
 * License, Version 2.0 (the "License"); you may not use this
7
 * file except in compliance with the License. You may obtain
8
 * a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
#include "sopc_numeric_range.h"
21
22
#include <errno.h>
23
#include <limits.h>
24
#include <stdio.h>
25
#include <string.h>
26
27
#include "sopc_array.h"
28
#include "sopc_helper_string.h"
29
#include "sopc_mem_alloc.h"
30
31
typedef enum
32
{
33
    TOKEN_START,
34
    TOKEN_EOF,
35
    TOKEN_ERROR,
36
    TOKEN_DIGIT,
37
    TOKEN_COMMA,
38
    TOKEN_COLON,
39
} token_type_t;
40
41
struct parse_ctx_t
42
{
43
    const char* data;
44
    size_t data_len;
45
46
    size_t idx;
47
48
    token_type_t last_token;
49
    size_t token_len;
50
};
51
52
static bool is_digit(char c)
53
0
{
54
0
    return c >= '0' && c <= '9';
55
0
}
56
57
static bool lex_digit(struct parse_ctx_t* ctx)
58
0
{
59
0
    ctx->token_len = 0;
60
61
0
    while (ctx->idx < ctx->data_len && is_digit(ctx->data[ctx->idx]))
62
0
    {
63
0
        ctx->token_len++;
64
0
        ctx->idx++;
65
0
    }
66
67
0
    return true;
68
0
}
69
70
static token_type_t lex_helper(struct parse_ctx_t* ctx)
71
0
{
72
0
    if (ctx->last_token == TOKEN_EOF)
73
0
    {
74
0
        return TOKEN_EOF;
75
0
    }
76
77
0
    if (ctx->idx == ctx->data_len)
78
0
    {
79
0
        ctx->last_token = TOKEN_EOF;
80
0
        ctx->token_len = 0;
81
0
        return TOKEN_EOF;
82
0
    }
83
84
0
    const char c = ctx->data[ctx->idx];
85
86
0
    if (is_digit(c))
87
0
    {
88
0
        lex_digit(ctx);
89
0
    }
90
0
    else if (c == ':')
91
0
    {
92
0
        ctx->last_token = TOKEN_COLON;
93
0
        ctx->token_len = 1;
94
0
        ctx->idx++;
95
0
    }
96
0
    else if (c == ',')
97
0
    {
98
0
        ctx->last_token = TOKEN_COMMA;
99
0
        ctx->token_len = 1;
100
0
        ctx->idx++;
101
0
    }
102
0
    else
103
0
    {
104
        // Unknown token
105
0
        ctx->last_token = TOKEN_ERROR;
106
0
        ctx->token_len = 0;
107
0
    }
108
109
0
    return ctx->last_token;
110
0
}
111
112
static token_type_t lex(struct parse_ctx_t* ctx, bool consume)
113
0
{
114
0
    size_t idx = ctx->idx;
115
0
    token_type_t res = lex_helper(ctx);
116
117
0
    if (!consume)
118
0
    {
119
0
        ctx->idx = idx;
120
0
    }
121
122
0
    return res;
123
0
}
124
125
static bool parse_index(struct parse_ctx_t* ctx, uint32_t* val)
126
0
{
127
0
    char buf[21];
128
0
    const char* start = ctx->data + ctx->idx;
129
0
    lex_digit(ctx);
130
131
0
    if (ctx->token_len == 0 || ctx->token_len >= (sizeof(buf) / sizeof(char)))
132
0
    {
133
0
        return false;
134
0
    }
135
136
0
    memcpy(buf, start, sizeof(char) * ctx->token_len);
137
0
    buf[ctx->token_len] = '\0';
138
139
0
    return SOPC_strtouint32_t(buf, val, 10, '\0') == SOPC_STATUS_OK;
140
0
}
141
142
static bool parse_dimension(struct parse_ctx_t* ctx, SOPC_Dimension* result)
143
0
{
144
0
    if (!parse_index(ctx, &result->start))
145
0
    {
146
0
        return false;
147
0
    }
148
149
0
    if (lex(ctx, false) == TOKEN_COLON)
150
0
    {
151
0
        lex(ctx, true);
152
153
0
        if (!parse_index(ctx, &result->end) || result->end <= result->start)
154
0
        {
155
0
            return false;
156
0
        }
157
0
    }
158
0
    else
159
0
    {
160
0
        result->end = result->start;
161
0
    }
162
163
0
    return true;
164
0
}
165
166
static SOPC_ReturnStatus parse_one_dimension(struct parse_ctx_t* ctx, SOPC_Array* dimensions, bool* has_more)
167
0
{
168
0
    size_t dim_idx = SOPC_Array_Size(dimensions);
169
170
0
    if (!SOPC_Array_Append_Values(dimensions, NULL, 1))
171
0
    {
172
0
        return SOPC_STATUS_OUT_OF_MEMORY;
173
0
    }
174
175
0
    SOPC_Dimension* dim = SOPC_Array_Get_Ptr(dimensions, dim_idx);
176
177
0
    if (!parse_dimension(ctx, dim))
178
0
    {
179
0
        return SOPC_STATUS_NOK;
180
0
    }
181
182
0
    token_type_t next = lex(ctx, true);
183
184
0
    if (next == TOKEN_COMMA)
185
0
    {
186
0
        *has_more = true;
187
0
        return SOPC_STATUS_OK;
188
0
    }
189
0
    else if (next == TOKEN_EOF)
190
0
    {
191
0
        *has_more = false;
192
0
        return SOPC_STATUS_OK;
193
0
    }
194
0
    else
195
0
    {
196
0
        return SOPC_STATUS_NOK;
197
0
    }
198
0
}
199
200
static SOPC_ReturnStatus parse_dimensions(struct parse_ctx_t* ctx, SOPC_Array* dimensions)
201
0
{
202
0
    for (bool has_more = true; has_more;)
203
0
    {
204
0
        SOPC_ReturnStatus status = parse_one_dimension(ctx, dimensions, &has_more);
205
206
0
        if (status != SOPC_STATUS_OK)
207
0
        {
208
0
            return status;
209
0
        }
210
0
    }
211
212
0
    return SOPC_STATUS_OK;
213
0
}
214
215
SOPC_ReturnStatus SOPC_NumericRange_Parse(const char* text, SOPC_NumericRange** result)
216
0
{
217
0
    struct parse_ctx_t ctx = {
218
0
        .data = text,
219
0
        .data_len = strlen(text),
220
0
        .idx = 0,
221
0
        .last_token = TOKEN_START,
222
0
        .token_len = 0,
223
0
    };
224
225
0
    SOPC_Array* dimensions = SOPC_Array_Create(sizeof(SOPC_Dimension), 1, NULL);
226
0
    SOPC_NumericRange* range = SOPC_Calloc(1, sizeof(SOPC_NumericRange));
227
228
0
    if (dimensions == NULL || range == NULL)
229
0
    {
230
0
        SOPC_Array_Delete(dimensions);
231
0
        SOPC_Free(range);
232
0
        return SOPC_STATUS_OUT_OF_MEMORY;
233
0
    }
234
235
0
    SOPC_ReturnStatus status = parse_dimensions(&ctx, dimensions);
236
237
0
    range->n_dimensions = SOPC_Array_Size(dimensions);
238
239
0
    if (status == SOPC_STATUS_OK && range->n_dimensions == 0)
240
0
    {
241
0
        status = SOPC_STATUS_NOK;
242
0
    }
243
244
0
    if (status != SOPC_STATUS_OK)
245
0
    {
246
0
        SOPC_Array_Delete(dimensions);
247
0
        SOPC_Free(range);
248
0
        return status;
249
0
    }
250
251
0
    range->dimensions = SOPC_Array_Into_Raw(dimensions);
252
0
    if (NULL == range->dimensions)
253
0
    {
254
0
        return SOPC_STATUS_OUT_OF_MEMORY;
255
0
    }
256
0
    *result = range;
257
258
0
    return SOPC_STATUS_OK;
259
0
}
260
261
void SOPC_NumericRange_Delete(SOPC_NumericRange* range)
262
0
{
263
0
    if (range == NULL)
264
0
    {
265
0
        return;
266
0
    }
267
268
0
    SOPC_Free(range->dimensions);
269
0
    SOPC_Free(range);
270
0
}