Skip to content
This repository has been archived by the owner on Oct 24, 2024. It is now read-only.

Commit

Permalink
Implement linter for alpha AI check character validation
Browse files Browse the repository at this point in the history
  • Loading branch information
terryburton committed Jul 26, 2021
1 parent b9174da commit 47d132b
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 22 deletions.
156 changes: 134 additions & 22 deletions src/c-lib/ai.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,28 @@ uint8_t gs1_aiLengthByPrefix(const char *ai) {
static const char *cset82 = "!\"%&'()*+,-./0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";


/*
* Set of 32 characters used in alpha check characters
*
*/
static const char *cset32 = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";


/*
* Set of prime weights used to derive alpha check characters
*
* First 97 since that is the maximum length of any AI is currently 99.
*
*/
static const uint16_t primes[] = {
2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,
73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,
283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,
419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509
};


/* "Linter" functions
*
* Used to validate AI components
Expand Down Expand Up @@ -212,6 +234,41 @@ static bool lint_csum(gs1_encoder *ctx, const struct aiEntry *entry, char *val)
return true;
}

static bool lint_csumalpha(gs1_encoder *ctx, const struct aiEntry *entry, char *val) {
size_t i;
uint32_t sum = 0;
size_t len = strlen(val);
const uint16_t *p;

DEBUG_PRINT(" csumalpha...");
if (len < 2) {
sprintf(ctx->errMsg, "AI (%s): Alphanumeric string is too short to check", entry->ai);
goto fail;
}
if (len > SIZEOF_ARRAY(primes)) {
sprintf(ctx->errMsg, "AI (%s): Alphanumeric string is too long to check", entry->ai);
goto fail;
}

p = primes + len - 3;
for (i = 0; i < len - 2; i++)
sum += (uint32_t)((strchr(cset82, val[i]) - cset82) * *p--);
sum %= 1021;
if (val[i] != cset32[sum >> 5] || val[i+1] != cset32[sum & 31]) {
sprintf(ctx->errMsg, "AI (%s): Bad alphanumeric check characters", entry->ai);
goto fail;
}

DEBUG_PRINT(" success\n");
return true;

fail:
DEBUG_PRINT(" failed\n");
ctx->errFlag = true;
return false;

}


#define FNC1 true
#define NO_FNC1 false
Expand Down Expand Up @@ -728,7 +785,7 @@ static const struct aiEntry ai_table[] = {
AI( "8010", FNC1 , C,1,30,_, __, __, __, __, "CPID" ),
AI( "8011", FNC1 , N,1,12,_, __, __, __, __, "CPID SERIAL" ),
AI( "8012", FNC1 , X,1,20,_, __, __, __, __, "VERSION" ),
AI( "8013", FNC1 , X,1,25,_, __, __, __, __, "GMN" ),
AI( "8013", FNC1 , X,1,25,csumalpha, __, __, __, __, "GMN" ),
AI( "8017", FNC1 , N,18,18,csum, __, __, __, __, "GSRN - PROVIDER" ),
AI( "8018", FNC1 , N,18,18,csum, __, __, __, __, "GSRN - RECIPIENT" ),
AI( "8019", FNC1 , N,1,10,_, __, __, __, __, "SRIN" ),
Expand Down Expand Up @@ -1263,27 +1320,31 @@ void test_ai_parseAIdata(void) {

test_parseAIdata(ctx, true, "(01)12345678901231", "^0112345678901231");
test_parseAIdata(ctx, true, "(10)12345", "^1012345");
test_parseAIdata(ctx, true, "(01)12345678901231(10)12345", "^01123456789012311012345"); // No FNC1 after (01)
test_parseAIdata(ctx, true, "(3100)123456(10)12345", "^31001234561012345"); // No FNC1 after (3100)
test_parseAIdata(ctx, true, "(10)12345(11)991225", "^1012345^11991225"); // FNC1 after (10)
test_parseAIdata(ctx, true, "(3900)12345(11)991225", "^390012345^11991225"); // FNC1 after (3900)
test_parseAIdata(ctx, true, "(10)12345\\(11)991225", "^1012345(11)991225"); // Escaped bracket
test_parseAIdata(ctx, true, "(10)12345\\(", "^1012345("); // At end if fine

test_parseAIdata(ctx, false, "(10)(11)98765", ""); // Value must not be empty
test_parseAIdata(ctx, false, "(10)12345(11)", ""); // Value must not be empty
test_parseAIdata(ctx, false, "(1A)12345", ""); // AI must be numeric
test_parseAIdata(ctx, false, "1(12345", ""); // Must start with AI
test_parseAIdata(ctx, false, "12345", ""); // Must start with AI
test_parseAIdata(ctx, false, "()12345", ""); // AI too short
test_parseAIdata(ctx, false, "(1)12345", ""); // AI too short
test_parseAIdata(ctx, false, "(12345)12345", ""); // AI too long
test_parseAIdata(ctx, false, "(15", ""); // AI must terminate
test_parseAIdata(ctx, false, "(1", ""); // AI must terminate
test_parseAIdata(ctx, false, "(", ""); // AI must terminate
test_parseAIdata(ctx, false, "(01)123456789012312(10)12345", ""); // Fixed-length AI too long
test_parseAIdata(ctx, false, "(10)12345^", ""); // Reject "^": Conflated with FNC1
test_parseAIdata(ctx, false, "(17)9(90)217", ""); // Should not parse to ^7990217
test_parseAIdata(ctx, true, "(01)12345678901231(10)12345", "^01123456789012311012345"); // No FNC1 after (01)
test_parseAIdata(ctx, true, "(3100)123456(10)12345", "^31001234561012345"); // No FNC1 after (3100)
test_parseAIdata(ctx, true, "(10)12345(11)991225", "^1012345^11991225"); // FNC1 after (10)
test_parseAIdata(ctx, true, "(3900)12345(11)991225", "^390012345^11991225"); // FNC1 after (3900)
test_parseAIdata(ctx, true, "(10)12345\\(11)991225", "^1012345(11)991225"); // Escaped bracket
test_parseAIdata(ctx, true, "(10)12345\\(", "^1012345("); // At end if fine

test_parseAIdata(ctx, false, "(10)(11)98765", ""); // Value must not be empty
test_parseAIdata(ctx, false, "(10)12345(11)", ""); // Value must not be empty
test_parseAIdata(ctx, false, "(1A)12345", ""); // AI must be numeric
test_parseAIdata(ctx, false, "1(12345", ""); // Must start with AI
test_parseAIdata(ctx, false, "12345", ""); // Must start with AI
test_parseAIdata(ctx, false, "()12345", ""); // AI too short
test_parseAIdata(ctx, false, "(1)12345", ""); // AI too short
test_parseAIdata(ctx, false, "(12345)12345", ""); // AI too long
test_parseAIdata(ctx, false, "(15", ""); // AI must terminate
test_parseAIdata(ctx, false, "(1", ""); // AI must terminate
test_parseAIdata(ctx, false, "(", ""); // AI must terminate
test_parseAIdata(ctx, false, "(01)123456789012312(10)12345", ""); // Fixed-length AI too long
test_parseAIdata(ctx, false, "(10)12345^", ""); // Reject "^": Conflated with FNC1
test_parseAIdata(ctx, false, "(17)9(90)217", ""); // Should not parse to ^7990217

// Linter tests
test_parseAIdata(ctx, true, "(8013)1987654Ad4X4bL5ttr2310c2K", "^80131987654Ad4X4bL5ttr2310c2K"); // Valid GMN check characters
test_parseAIdata(ctx, false, "(8013)1987654Ad4X4bL5ttr2310cXK", ""); // Invalid GMN check characters

gs1_encoder_free(ctx);

Expand Down Expand Up @@ -1406,6 +1467,57 @@ void test_ai_validateParity(void) {

}

static void test_csumalpha(gs1_encoder *ctx, const struct aiEntry* entry, const bool should_succeed, char *val) {

char casename[256];

strcpy(casename, val);
TEST_CASE(casename);

TEST_CHECK(lint_csumalpha(ctx, entry, val) ^ !should_succeed);;

}

void test_ai_lint_csumalpha(void) {

gs1_encoder* ctx = gs1_encoder_init(NULL);
const struct aiEntry* entry = gs1_lookupAIentry(ctx, "8013", 4);

test_csumalpha(ctx, entry, true, "1987654Ad4X4bL5ttr2310c2K");
test_csumalpha(ctx, entry, false, "1987654Ad4X4bL5ttr2310cXK");
test_csumalpha(ctx, entry, false, "1987654Ad4X4bL5ttr2310c2X");
test_csumalpha(ctx, entry, true, "12345678901234567890123NT");
test_csumalpha(ctx, entry, true, "12345_ABCDEFGHIJKLMCP");
test_csumalpha(ctx, entry, true, "12345_NOPQRSTUVWXYZDN");
test_csumalpha(ctx, entry, true, "12345_abcdefghijklmN3");
test_csumalpha(ctx, entry, true, "12345_nopqrstuvwxyzP2");
test_csumalpha(ctx, entry, true, "12345_!\"%&'()*+,-./LC");
test_csumalpha(ctx, entry, true, "12345_0123456789:;<=>?62");
test_csumalpha(ctx, entry, true, "7907665Bm8v2AB");
test_csumalpha(ctx, entry, true, "97850l6KZm0yCD");
test_csumalpha(ctx, entry, true, "225803106GSpEF");
test_csumalpha(ctx, entry, true, "149512464PM+GH");
test_csumalpha(ctx, entry, true, "62577B8fRG7HJK");
test_csumalpha(ctx, entry, true, "515942070CYxLM");
test_csumalpha(ctx, entry, true, "390800494sP6NP");
test_csumalpha(ctx, entry, true, "386830132uO+QR");
test_csumalpha(ctx, entry, true, "53395376X1:nST");
test_csumalpha(ctx, entry, true, "957813138Sb6UV");
test_csumalpha(ctx, entry, true, "530790no0qOgWX");
test_csumalpha(ctx, entry, true, "62185314IvwmYZ");
test_csumalpha(ctx, entry, true, "23956qk1&dB!23");
test_csumalpha(ctx, entry, true, "794394895ic045");
test_csumalpha(ctx, entry, true, "57453Uq3qA<H67");
test_csumalpha(ctx, entry, true, "62185314IvwmYZ");
test_csumalpha(ctx, entry, true, "0881063PhHvY89");
test_csumalpha(ctx, entry, true, "00000!HV");
test_csumalpha(ctx, entry, true, "99999zzzzzzzzzzzzzzzzzzT2");
test_csumalpha(ctx, entry, true, "99999zzzzzzzzzzzzzzzzzzT2");

gs1_encoder_free(ctx);

}


#endif /* UNIT_TESTS */

1 change: 1 addition & 0 deletions src/c-lib/ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ void test_ai_AItableVsPrefixLength(void);
void test_ai_parseAIdata(void);
void test_ai_processAIdata(void);
void test_ai_validateParity(void);
void test_ai_lint_csumalpha(void);

#endif

Expand Down
1 change: 1 addition & 0 deletions src/c-lib/gs1encoders-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ TEST_LIST = {
{ "ai_gs1_parseAIdata", test_ai_parseAIdata },
{ "ai_gs1_processAIdata", test_ai_processAIdata },
{ "ai_validateParity", test_ai_validateParity },
{ "ai_lint_csumalpha", test_ai_lint_csumalpha },


/*
Expand Down

0 comments on commit 47d132b

Please sign in to comment.