GS1 Barcode Syntax Tests reference
A reference to the AI component "linter" routines referred to by the GS1 Barcode Syntax Dictionary. Copyright (c) 2022-2024 GS1 AISBL.
lint_csumalpha.c File Reference

Purpose

The csumalpha linter ensures that the data has a valid alphanumeric check character pair.

Remarks
The process for validating the GS1 alphanumeric check character pair is described in the GS1 General Specifications section "Check character calculation (for alphanumeric keys)".

Functional Description

◆ gs1_lint_csumalpha()

GS1_SYNTAX_DICTIONARY_API gs1_lint_err_t gs1_lint_csumalpha ( const char *const  data,
size_t *const  err_pos,
size_t *const  err_len 
)

Used to ensure that the AI component has a valid alphanumeric check character pair.

Parameters
[in]dataPointer to the null-terminated data to be linted. Must not be NULL.
[out]err_posTo facilitate error highlighting, the start position of the bad data is written to this pointer, if not NULL.
[out]err_lenThe length of the bad data is written to this pointer, if not NULL.
Returns
GS1_LINTER_OK if okay.
GS1_LINTER_INCORRECT_CHECK_PAIR if the data contains an incorrect check character pair.
GS1_LINTER_TOO_SHORT_FOR_CHECK_PAIR if the data is too short.
GS1_LINTER_TOO_LONG_FOR_CHECK_PAIR_IMPLEMENTATION if the data is too long for this implementation of the check character pair algorithm. GS1_LINTER_INVALID_CSET82_CHARACTER if any of the data characters are not in CSET 82.
Note
The return value GS1_LINTER_TOO_LONG_FOR_CHECK_PAIR_IMPLEMENTATION represents exceeding an implementation-specific limit. Some implmenetations may choose to support longer data inputs.
68{
69
70 /*
71 * Set of prime weights used to derive alpha check characters.
72 *
73 * Here we have the first 97 since that is the maximum length of any
74 * AI is currently 99. Implementations may choose to support longer
75 * data inputs.
76 *
77 */
78 static const unsigned int primes[] = {
79 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,
80 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89,
81 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
82 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223,
83 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
84 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
85 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433,
86 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
87 509
88 };
89
90 /*
91 * Sequence of all characters in CSET 32, ordered by weight.
92 */
93 static const char* const cset32 = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
94
95 /*
96 * CSET82 character weights lookup table (0 = invalid; weight off by 1)
97 */
98 static const unsigned char cset82_weights[256] = {
99 ['!'] = 1, ['"'] = 2, ['%'] = 3, ['&'] = 4, ['\''] = 5, ['('] = 6,
100 [')'] = 7, ['*'] = 8, ['+'] = 9, [','] = 10, ['-'] = 11, ['.'] = 12,
101 ['/'] = 13, ['0'] = 14, ['1'] = 15, ['2'] = 16, ['3'] = 17, ['4'] = 18,
102 ['5'] = 19, ['6'] = 20, ['7'] = 21, ['8'] = 22, ['9'] = 23, [':'] = 24,
103 [';'] = 25, ['<'] = 26, ['='] = 27, ['>'] = 28, ['?'] = 29, ['A'] = 30,
104 ['B'] = 31, ['C'] = 32, ['D'] = 33, ['E'] = 34, ['F'] = 35, ['G'] = 36,
105 ['H'] = 37, ['I'] = 38, ['J'] = 39, ['K'] = 40, ['L'] = 41, ['M'] = 42,
106 ['N'] = 43, ['O'] = 44, ['P'] = 45, ['Q'] = 46, ['R'] = 47, ['S'] = 48,
107 ['T'] = 49, ['U'] = 50, ['V'] = 51, ['W'] = 52, ['X'] = 53, ['Y'] = 54,
108 ['Z'] = 55, ['_'] = 56, ['a'] = 57, ['b'] = 58, ['c'] = 59, ['d'] = 60,
109 ['e'] = 61, ['f'] = 62, ['g'] = 63, ['h'] = 64, ['i'] = 65, ['j'] = 66,
110 ['k'] = 67, ['l'] = 68, ['m'] = 69, ['n'] = 70, ['o'] = 71, ['p'] = 72,
111 ['q'] = 73, ['r'] = 74, ['s'] = 75, ['t'] = 76, ['u'] = 77, ['v'] = 78,
112 ['w'] = 79, ['x'] = 80, ['y'] = 81, ['z'] = 82
113 };
114
115 size_t pos, len;
116 uint32_t sum = 0; /* Sufficient for 97-prime implementation */
117
118 assert(data);
119
120 /*
121 * Find length constraining data to the number of primes that we have.
122 *
123 */
124 for (len = 0; data[len] != '\0'; len++) {
125 if (GS1_LINTER_UNLIKELY(len >= sizeof(primes) / sizeof(primes[0]) + 2))
128 0,
129 len + 1
130 );
131 }
132
133 /*
134 * Data must include at least the check character pair.
135 *
136 */
137 if (GS1_LINTER_UNLIKELY(len < 2))
140 0,
141 len
142 );
143
144 /*
145 * Handling the two-character case now avoids conditionals later.
146 *
147 */
148 if (len == 2) {
149 if (GS1_LINTER_UNLIKELY(data[0] != '2' || data[1] != '2'))
152 len - 2,
153 2
154 );
156 }
157
158 /*
159 * Sum of data-character values weighted by increasing prime values,
160 * from right to left, modulo 1021.
161 *
162 * The check characters are valid when their values match the above
163 * data checksum with its 10-bit value split evenly into two 5-bit
164 * values.
165 *
166 * The data character values are defined by their respective positions
167 * in CSET 82.
168 *
169 * The check character pair values are defined by their respective
170 * positions in CSET 32.
171 *
172 */
173 for (pos = 0; pos < len - 2; pos++) {
174 if (GS1_LINTER_UNLIKELY(cset82_weights[(unsigned char)data[pos]] == 0))
177 pos,
178 1
179 );
180 sum += (unsigned int)(cset82_weights[(unsigned char)data[pos]] - 1) * primes[len - 3 - pos];
181 }
182 sum %= 1021; /* Overflow not possible with uint32_t */
183
184 if (GS1_LINTER_UNLIKELY(data[len-2] != cset32[sum >> 5] || data[len-1] != cset32[sum & 31]))
187 len - 2,
188 2
189 );
190
192
193}
#define GS1_LINTER_UNLIKELY(x)
Implemention may provide hint to the compiler that the expression is likely to be false.
Definition gs1syntaxdictionary-utils.h:76
#define GS1_LINTER_RETURN_ERROR(error, position, length)
Return from a linter indicating that a problem was detected with the given data.
Definition gs1syntaxdictionary-utils.h:103
#define GS1_LINTER_RETURN_OK
Return from a linter indicating that no problem was detected with the given data.
Definition gs1syntaxdictionary-utils.h:88
@ GS1_LINTER_TOO_LONG_FOR_CHECK_PAIR_IMPLEMENTATION
The component is too long to perform an alphanumeric check character pair calculation.
Definition gs1syntaxdictionary.h:86
@ GS1_LINTER_INCORRECT_CHECK_PAIR
The alphanumeric check-character pair are incorrect.
Definition gs1syntaxdictionary.h:84
@ GS1_LINTER_INVALID_CSET82_CHARACTER
A non-CSET 82 character was found where a CSET 82 character is expected.
Definition gs1syntaxdictionary.h:79
@ GS1_LINTER_TOO_SHORT_FOR_CHECK_PAIR
The component is too short to perform an alphanumeric check character pair calculation.
Definition gs1syntaxdictionary.h:85