summaryrefslogtreecommitdiffstats
path: root/zbar/decoder/databar.c
diff options
context:
space:
mode:
Diffstat (limited to 'zbar/decoder/databar.c')
-rw-r--r--zbar/decoder/databar.c1240
1 files changed, 1240 insertions, 0 deletions
diff --git a/zbar/decoder/databar.c b/zbar/decoder/databar.c
new file mode 100644
index 0000000..5722ef6
--- /dev/null
+++ b/zbar/decoder/databar.c
@@ -0,0 +1,1240 @@
+/*------------------------------------------------------------------------
+ * Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+
+#include "config.h"
+#include <zbar.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef DEBUG_DATABAR
+#define DEBUG_LEVEL (DEBUG_DATABAR)
+#endif
+#include "debug.h"
+#include "decoder.h"
+
+#define GS ('\035')
+
+enum
+{
+ SCH_NUM,
+ SCH_ALNUM,
+ SCH_ISO646
+};
+
+static const signed char finder_hash[0x20] = {
+ 0x16, 0x1f, 0x02, 0x00, 0x03, 0x00, 0x06, 0x0b, 0x1f, 0x0e, 0x17,
+ 0x0c, 0x0b, 0x14, 0x11, 0x0c, 0x1f, 0x03, 0x13, 0x08, 0x00, 0x0a,
+ -1, 0x16, 0x0c, 0x09, -1, 0x1a, 0x1f, 0x1c, 0x00, -1,
+};
+
+/* DataBar character encoding groups */
+struct group_s {
+ unsigned short sum;
+ unsigned char wmax;
+ unsigned char todd;
+ unsigned char teven;
+} groups[] = {
+ /* (17,4) DataBar Expanded character groups */
+ { 0, 7, 87, 4 },
+ { 348, 5, 52, 20 },
+ { 1388, 4, 30, 52 },
+ { 2948, 3, 10, 104 },
+ { 3988, 1, 1, 204 },
+
+ /* (16,4) DataBar outer character groups */
+ { 0, 8, 161, 1 },
+ { 161, 6, 80, 10 },
+ { 961, 4, 31, 34 },
+ { 2015, 3, 10, 70 },
+ { 2715, 1, 1, 126 },
+
+ /* (15,4) DataBar inner character groups */
+ { 1516, 8, 81, 1 },
+ { 1036, 6, 48, 10 },
+ { 336, 4, 20, 35 },
+ { 0, 2, 4, 84 },
+};
+
+static const unsigned char exp_sequences[] = {
+ /* sequence Group 1 */
+ 0x01, 0x23, 0x25, 0x07, 0x29, 0x47, 0x29, 0x67, 0x0b, 0x29, 0x87, 0xab,
+ /* sequence Group 2 */
+ 0x21, 0x43, 0x65, 0x07, 0x21, 0x43, 0x65, 0x89, 0x21, 0x43, 0x65, 0xa9,
+ 0x0b, 0x21, 0x43, 0x67, 0x89, 0xab
+};
+
+/* DataBar expanded checksum multipliers */
+static const unsigned char exp_checksums[] = { 1, 189, 62, 113, 46, 43,
+ 109, 134, 6, 79, 161, 45 };
+
+static inline void append_check14(unsigned char *buf)
+{
+ unsigned char chk = 0, d;
+ int i;
+ for (i = 13; --i >= 0;) {
+ d = *(buf++) - '0';
+ chk += d;
+ if (!(i & 1))
+ chk += d << 1;
+ }
+ chk %= 10;
+ if (chk)
+ chk = 10 - chk;
+ *buf = chk + '0';
+}
+
+static inline void decode10(unsigned char *buf, unsigned long n, int i)
+{
+ buf += i;
+ while (--i >= 0) {
+ unsigned char d = n % 10;
+ n /= 10;
+ *--buf = '0' + d;
+ }
+}
+
+#define VAR_MAX(l, i) ((((l)*12 + (i)) * 2 + 6) / 7)
+
+#define FEED_BITS(b) \
+ while (i < (b) && len) { \
+ d = (d << 12) | (*(data++) & 0xfff); \
+ i += 12; \
+ len--; \
+ dbprintf(2, " %03lx", d & 0xfff); \
+ }
+
+#define PUSH_CHAR(c) *(buf++) = (c)
+
+#define PUSH_CHAR4(c0, c1, c2, c3) \
+ do { \
+ PUSH_CHAR(c0); \
+ PUSH_CHAR(c1); \
+ PUSH_CHAR(c2); \
+ PUSH_CHAR(c3); \
+ } while (0);
+
+static inline int databar_postprocess_exp(zbar_decoder_t *dcode, int *data)
+{
+ int i = 0, enc;
+ unsigned n;
+ unsigned char *buf;
+ unsigned long d = *(data++);
+ int len = d / 211 + 4, buflen;
+
+ /* grok encodation method */
+ d = *(data++);
+ dbprintf(2, "\n len=%d %03lx", len, d & 0xfff);
+ n = (d >> 4) & 0x7f;
+ if (n >= 0x40) {
+ i = 10;
+ enc = 1;
+ buflen = 2 + 14 + VAR_MAX(len, 10 - 2 - 44 + 6) + 2;
+ } else if (n >= 0x38) {
+ i = 4;
+ enc = 6 + (n & 7);
+ buflen = 2 + 14 + 4 + 6 + 2 + 6 + 2;
+ } else if (n >= 0x30) {
+ i = 6;
+ enc = 2 + ((n >> 2) & 1);
+ buflen = 2 + 14 + 4 + 3 + VAR_MAX(len, 6 - 2 - 44 - 2 - 10) + 2;
+ } else if (n >= 0x20) {
+ i = 7;
+ enc = 4 + ((n >> 3) & 1);
+ buflen = 2 + 14 + 4 + 6;
+ } else {
+ i = 9;
+ enc = 0;
+ buflen = VAR_MAX(len, 9 - 2) + 2;
+ }
+ dbprintf(2, " buflen=%d enc=%d", buflen, enc);
+ zassert(buflen > 2, -1, "buflen=%d\n", buflen);
+
+ if (enc < 4) {
+ /* grok variable length symbol bit field */
+ if ((len ^ (d >> (--i))) & 1)
+ /* even/odd length mismatch */
+ return (-1);
+ if (((d >> (--i)) & 1) != (len > 14))
+ /* size group mismatch */
+ return (-1);
+ }
+ len -= 2;
+ dbprintf(2, " [%d+%d]", i, len);
+
+ if (size_buf(dcode, buflen))
+ return (-1);
+ buf = dcode->buf;
+
+ /* handle compressed fields */
+ if (enc) {
+ PUSH_CHAR('0');
+ PUSH_CHAR('1');
+ }
+
+ if (enc == 1) {
+ i -= 4;
+ n = (d >> i) & 0xf;
+ if (i >= 10)
+ return (-1);
+ PUSH_CHAR('0' + n);
+ } else if (enc)
+ PUSH_CHAR('9');
+
+ if (enc) {
+ int j;
+ for (j = 0; j < 4; j++) {
+ FEED_BITS(10);
+ i -= 10;
+ n = (d >> i) & 0x3ff;
+ if (n >= 1000)
+ return (-1);
+ decode10(buf, n, 3);
+ buf += 3;
+ }
+ append_check14(buf - 13);
+ buf++;
+ }
+
+ switch (enc) {
+ case 2: /* 01100: AI 392x */
+ FEED_BITS(2);
+ i -= 2;
+ n = (d >> i) & 0x3;
+ PUSH_CHAR4('3', '9', '2', '0' + n);
+ break;
+
+ case 3: /* 01101: AI 393x */
+ FEED_BITS(12);
+ i -= 2;
+ n = (d >> i) & 0x3;
+ PUSH_CHAR4('3', '9', '3', '0' + n);
+ i -= 10;
+ n = (d >> i) & 0x3ff;
+ if (n >= 1000)
+ return (-1);
+ decode10(buf, n, 3);
+ buf += 3;
+ break;
+
+ case 4: /* 0100: AI 3103 */
+ FEED_BITS(15);
+ i -= 15;
+ n = (d >> i) & 0x7fff;
+ PUSH_CHAR4('3', '1', '0', '3');
+ decode10(buf, n, 6);
+ buf += 6;
+ break;
+
+ case 5: /* 0101: AI 3202/3203 */
+ FEED_BITS(15);
+ i -= 15;
+ n = (d >> i) & 0x7fff;
+ dbprintf(2, " v=%d", n);
+ PUSH_CHAR4('3', '2', '0', (n >= 10000) ? '3' : '2');
+ if (n >= 10000)
+ n -= 10000;
+ decode10(buf, n, 6);
+ buf += 6;
+ break;
+ }
+ if (enc >= 6) {
+ /* 0111000 - 0111111: AI 310x/320x + AI 11/13/15/17 */
+ PUSH_CHAR4('3', '1' + (enc & 1), '0', 'x');
+ FEED_BITS(20);
+ i -= 20;
+ n = (d >> i) & 0xfffff;
+ dbprintf(2, " [%d+%d] %d", i, len, n);
+ if (n >= 1000000)
+ return (-1);
+ decode10(buf, n, 6);
+ *(buf - 1) = *buf;
+ *buf = '0';
+ buf += 6;
+
+ FEED_BITS(16);
+ i -= 16;
+ n = (d >> i) & 0xffff;
+ if (n < 38400) {
+ int dd, mm, yy;
+ dd = n % 32;
+ n /= 32;
+ mm = n % 12 + 1;
+ n /= 12;
+ yy = n;
+ PUSH_CHAR('1');
+ PUSH_CHAR('0' + ((enc - 6) | 1));
+ decode10(buf, yy, 2);
+ buf += 2;
+ decode10(buf, mm, 2);
+ buf += 2;
+ decode10(buf, dd, 2);
+ buf += 2;
+ } else if (n > 38400)
+ return (-1);
+ }
+
+ if (enc < 4) {
+ /* remainder is general-purpose data compaction */
+ int scheme = SCH_NUM;
+ while (i > 0 || len > 0) {
+ FEED_BITS(8);
+ dbprintf(2, " [%d+%d]", i, len);
+
+ if (scheme == SCH_NUM) {
+ int n1;
+ i -= 4;
+ if (i < 0)
+ break;
+ if (!((d >> i) & 0xf)) {
+ scheme = SCH_ALNUM;
+ dbprintf(2, ">A");
+ continue;
+ }
+ if (!len && i < 3) {
+ /* special case last digit */
+ n = ((d >> i) & 0xf) - 1;
+ if (n > 9)
+ return (-1);
+ *(buf++) = '0' + n;
+ break;
+ }
+ i -= 3;
+ zassert(i >= 0, -1, "\n");
+ n = ((d >> i) & 0x7f) - 8;
+ n1 = n % 11;
+ n = n / 11;
+ dbprintf(2, "N%d%d", n, n1);
+ *(buf++) = (n < 10) ? '0' + n : GS;
+ *(buf++) = (n1 < 10) ? '0' + n1 : GS;
+ } else {
+ unsigned c = 0;
+ i -= 3;
+ if (i < 0)
+ break;
+ if (!((d >> i) & 0x7)) {
+ scheme = SCH_NUM;
+ continue;
+ }
+ i -= 2;
+ if (i < 0)
+ break;
+ n = (d >> i) & 0x1f;
+ if (n == 0x04) {
+ scheme ^= 0x3;
+ dbprintf(2, ">%d", scheme);
+ } else if (n == 0x0f)
+ c = GS;
+ else if (n < 0x0f)
+ c = 43 + n;
+ else if (scheme == SCH_ALNUM) {
+ i--;
+ if (i < 0)
+ return (-1);
+ n = (d >> i) & 0x1f;
+ if (n < 0x1a)
+ c = 'A' + n;
+ else if (n == 0x1a)
+ c = '*';
+ else if (n < 0x1f)
+ c = ',' + n - 0x1b;
+ else
+ return (-1);
+ } else if (scheme == SCH_ISO646 && n < 0x1d) {
+ i -= 2;
+ if (i < 0)
+ return (-1);
+ n = (d >> i) & 0x3f;
+ if (n < 0x1a)
+ c = 'A' + n;
+ else if (n < 0x34)
+ c = 'a' + n - 0x1a;
+ else
+ return (-1);
+ } else if (scheme == SCH_ISO646) {
+ i -= 3;
+ if (i < 0)
+ return (-1);
+ n = ((d >> i) & 0x1f);
+ dbprintf(2, "(%02x)", n);
+ if (n < 0xa)
+ c = '!' + n - 8;
+ else if (n < 0x15)
+ c = '%' + n - 0xa;
+ else if (n < 0x1b)
+ c = ':' + n - 0x15;
+ else if (n == 0x1b)
+ c = '_';
+ else if (n == 0x1c)
+ c = ' ';
+ else
+ return (-1);
+ } else
+ return (-1);
+
+ if (c) {
+ dbprintf(2, "%d%c", scheme, c);
+ *(buf++) = c;
+ }
+ }
+ }
+ /* FIXME check pad? */
+ }
+
+ i = buf - dcode->buf;
+ zassert(i < dcode->buf_alloc, -1, "i=%02x %s\n", i,
+ _zbar_decoder_buf_dump(dcode->buf, i));
+
+ *buf = 0;
+ dcode->buflen = i;
+ if (i && *--buf == GS) {
+ *buf = 0;
+ dcode->buflen--;
+ }
+
+ dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, dcode->buflen));
+ return (0);
+}
+#undef FEED_BITS
+
+/* convert from heterogeneous base {1597,2841}
+ * to base 10 character representation
+ */
+static inline void databar_postprocess(zbar_decoder_t *dcode, unsigned d[4])
+{
+ unsigned long r;
+ databar_decoder_t *db = &dcode->databar;
+ int i;
+ unsigned c, chk = 0;
+ unsigned char *buf = dcode->buf;
+ *(buf++) = '0';
+ *(buf++) = '1';
+ buf += 15;
+ *--buf = '\0';
+ *--buf = '\0';
+
+ dbprintf(2, "\n d={%d,%d,%d,%d}", d[0], d[1], d[2], d[3]);
+ r = d[0] * 1597 + d[1];
+ d[1] = r / 10000;
+ r %= 10000;
+ r = r * 2841 + d[2];
+ d[2] = r / 10000;
+ r %= 10000;
+ r = r * 1597 + d[3];
+ d[3] = r / 10000;
+ dbprintf(2, " r=%ld", r);
+
+ for (i = 4; --i >= 0;) {
+ c = r % 10;
+ chk += c;
+ if (i & 1)
+ chk += c << 1;
+ *--buf = c + '0';
+ if (i)
+ r /= 10;
+ }
+
+ dbprintf(2, " d={%d,%d,%d}", d[1], d[2], d[3]);
+ r = d[1] * 2841 + d[2];
+ d[2] = r / 10000;
+ r %= 10000;
+ r = r * 1597 + d[3];
+ d[3] = r / 10000;
+ dbprintf(2, " r=%ld", r);
+
+ for (i = 4; --i >= 0;) {
+ c = r % 10;
+ chk += c;
+ if (i & 1)
+ chk += c << 1;
+ *--buf = c + '0';
+ if (i)
+ r /= 10;
+ }
+
+ r = d[2] * 1597 + d[3];
+ dbprintf(2, " d={%d,%d} r=%ld", d[2], d[3], r);
+
+ for (i = 5; --i >= 0;) {
+ c = r % 10;
+ chk += c;
+ if (!(i & 1))
+ chk += c << 1;
+ *--buf = c + '0';
+ if (i)
+ r /= 10;
+ }
+
+ /* NB linkage flag not supported */
+ if (TEST_CFG(db->config, ZBAR_CFG_EMIT_CHECK)) {
+ chk %= 10;
+ if (chk)
+ chk = 10 - chk;
+ buf[13] = chk + '0';
+ dcode->buflen = buf - dcode->buf + 14;
+ } else
+ dcode->buflen = buf - dcode->buf + 13;
+
+ dbprintf(2, "\n %s", _zbar_decoder_buf_dump(dcode->buf, 16));
+}
+
+static inline int check_width(unsigned wf, unsigned wd, unsigned n)
+{
+ unsigned dwf = wf * 3;
+ wd *= 14;
+ wf *= n;
+ return (wf - dwf <= wd && wd <= wf + dwf);
+}
+
+static inline void merge_segment(databar_decoder_t *db, databar_segment_t *seg)
+{
+ unsigned csegs = db->csegs;
+ int i;
+ for (i = 0; i < csegs; i++) {
+ databar_segment_t *s = db->segs + i;
+ if (s != seg && s->finder == seg->finder && s->exp == seg->exp &&
+ s->color == seg->color && s->side == seg->side &&
+ s->data == seg->data && s->check == seg->check &&
+ check_width(seg->width, s->width, 14)) {
+ /* merge with existing segment */
+ unsigned cnt = s->count;
+ if (cnt < 0x7f)
+ cnt++;
+ seg->count = cnt;
+ seg->partial &= s->partial;
+ seg->width = (3 * seg->width + s->width + 2) / 4;
+ s->finder = -1;
+ dbprintf(2, " dup@%d(%d,%d)", i, cnt,
+ (db->epoch - seg->epoch) & 0xff);
+ } else if (s->finder >= 0) {
+ unsigned age = (db->epoch - s->epoch) & 0xff;
+ if (age >= 248 || (age >= 128 && s->count < 2))
+ s->finder = -1;
+ }
+ }
+}
+
+static inline zbar_symbol_type_t match_segment(zbar_decoder_t *dcode,
+ databar_segment_t *seg)
+{
+ databar_decoder_t *db = &dcode->databar;
+ unsigned csegs = db->csegs, maxage = 0xfff;
+ int i0, i1, i2, maxcnt = 0;
+ databar_segment_t *smax[3] = {
+ NULL,
+ };
+ unsigned d[4];
+
+ if (seg->partial && seg->count < 4)
+ return (ZBAR_PARTIAL);
+
+ for (i0 = 0; i0 < csegs; i0++) {
+ databar_segment_t *s0 = db->segs + i0;
+ if (s0 == seg || s0->finder != seg->finder || s0->exp ||
+ s0->color != seg->color || s0->side == seg->side ||
+ (s0->partial && s0->count < 4) ||
+ !check_width(seg->width, s0->width, 14))
+ continue;
+
+ for (i1 = 0; i1 < csegs; i1++) {
+ databar_segment_t *s1 = db->segs + i1;
+ int chkf, chks, chk;
+ unsigned age1;
+ if (i1 == i0 || s1->finder < 0 || s1->exp ||
+ s1->color == seg->color || (s1->partial && s1->count < 4) ||
+ !check_width(seg->width, s1->width, 14))
+ continue;
+ dbprintf(2, "\n\t[%d,%d] f=%d(0%xx)/%d(%x%x%x)", i0, i1,
+ seg->finder, seg->color, s1->finder, s1->exp, s1->color,
+ s1->side);
+
+ if (seg->color)
+ chkf = seg->finder + s1->finder * 9;
+ else
+ chkf = s1->finder + seg->finder * 9;
+ if (chkf > 72)
+ chkf--;
+ if (chkf > 8)
+ chkf--;
+
+ chks = (seg->check + s0->check + s1->check) % 79;
+
+ if (chkf >= chks)
+ chk = chkf - chks;
+ else
+ chk = 79 + chkf - chks;
+
+ dbprintf(2, " chk=(%d,%d) => %d", chkf, chks, chk);
+ age1 = (((db->epoch - s0->epoch) & 0xff) +
+ ((db->epoch - s1->epoch) & 0xff));
+
+ for (i2 = i1 + 1; i2 < csegs; i2++) {
+ databar_segment_t *s2 = db->segs + i2;
+ unsigned cnt, age2, age;
+ if (i2 == i0 || s2->finder != s1->finder || s2->exp ||
+ s2->color != s1->color || s2->side == s1->side ||
+ s2->check != chk || (s2->partial && s2->count < 4) ||
+ !check_width(seg->width, s2->width, 14))
+ continue;
+ age2 = (db->epoch - s2->epoch) & 0xff;
+ age = age1 + age2;
+ cnt = s0->count + s1->count + s2->count;
+ dbprintf(2, " [%d] MATCH cnt=%d age=%d", i2, cnt, age);
+ if (maxcnt < cnt || (maxcnt == cnt && maxage > age)) {
+ maxcnt = cnt;
+ maxage = age;
+ smax[0] = s0;
+ smax[1] = s1;
+ smax[2] = s2;
+ }
+ }
+ }
+ }
+
+ if (!smax[0])
+ return (ZBAR_PARTIAL);
+
+ d[(seg->color << 1) | seg->side] = seg->data;
+ for (i0 = 0; i0 < 3; i0++) {
+ d[(smax[i0]->color << 1) | smax[i0]->side] = smax[i0]->data;
+ if (!--(smax[i0]->count))
+ smax[i0]->finder = -1;
+ }
+ seg->finder = -1;
+
+ if (size_buf(dcode, 18))
+ return (ZBAR_PARTIAL);
+
+ if (acquire_lock(dcode, ZBAR_DATABAR))
+ return (ZBAR_PARTIAL);
+
+ databar_postprocess(dcode, d);
+ dcode->modifiers = MOD(ZBAR_MOD_GS1);
+ dcode->direction = 1 - 2 * (seg->side ^ seg->color ^ 1);
+ return (ZBAR_DATABAR);
+}
+
+static inline signed lookup_sequence(databar_segment_t *seg, int fixed,
+ int seq[22], const size_t maxsize)
+{
+ unsigned n = seg->data / 211, i;
+ const unsigned char *p;
+ i = (n + 1) / 2 + 1;
+ n += 4;
+ i = (i * i) / 4;
+ dbprintf(2, " {%d,%d:", i, n);
+ p = exp_sequences + i;
+
+ if (n >= maxsize-1) {
+ // The loop below checks i<n and increments i by one within the loop
+ // when accessing seq[22]. For this to be safe, n needs to be < 21.
+ // See CVE-2023-40890.
+ return -1;
+ }
+
+ fixed >>= 1;
+ seq[0] = 0;
+ seq[1] = 1;
+ for (i = 2; i < n;) {
+ int s = *p;
+ if (!(i & 2)) {
+ p++;
+ s >>= 4;
+ } else
+ s &= 0xf;
+ if (s == fixed)
+ fixed = -1;
+ s <<= 1;
+ dbprintf(2, "%x", s);
+ seq[i++] = s++;
+ seq[i++] = s;
+ }
+ dbprintf(2, "}");
+ seq[n] = -1;
+ return (fixed < 1);
+}
+
+#define IDX(s) \
+ (((s)->finder << 2) | ((s)->color << 1) | ((s)->color ^ (s)->side))
+
+static inline zbar_symbol_type_t
+match_segment_exp(zbar_decoder_t *dcode, databar_segment_t *seg, int dir)
+{
+ databar_decoder_t *db = &dcode->databar;
+ int bestsegs[22], i = 0, segs[22], seq[22];
+ int ifixed = seg - db->segs, fixed = IDX(seg), maxcnt = 0;
+ int iseg[DATABAR_MAX_SEGMENTS];
+ unsigned csegs = db->csegs, width = seg->width, maxage = 0x7fff;
+
+ bestsegs[0] = segs[0] = seq[1] = -1;
+ seq[0] = 0;
+
+ dbprintf(2, "\n fixed=%d@%d: ", fixed, ifixed);
+ for (i = csegs, seg = db->segs + csegs - 1; --i >= 0; seg--) {
+ if (seg->exp && seg->finder >= 0 && (!seg->partial || seg->count >= 4))
+ iseg[i] = IDX(seg);
+ else
+ iseg[i] = -1;
+ dbprintf(2, " %d", iseg[i]);
+ }
+
+ for (i = 0;; i--) {
+ unsigned cnt, chk, age;
+ unsigned data0, chk0;
+ if (!i)
+ dbprintf(2, "\n ");
+ for (; i >= 0 && seq[i] >= 0; i--) {
+ int j;
+ dbprintf(2, " [%d]%d", i, seq[i]);
+
+ if (seq[i] == fixed) {
+ seg = db->segs + ifixed;
+ if (segs[i] < 0 && check_width(width, seg->width, 14)) {
+ dbprintf(2, "*");
+ j = ifixed;
+ } else
+ continue;
+ } else {
+ for (j = segs[i] + 1; j < csegs; j++) {
+ if (iseg[j] == seq[i] &&
+ (!i || check_width(width, db->segs[j].width, 14))) {
+ seg = db->segs + j;
+ break;
+ }
+ }
+ if (j == csegs)
+ continue;
+ }
+
+ if (!i) {
+ signed int lu = lookup_sequence(seg, fixed, seq, sizeof(seq)/sizeof(seq[0]));
+ if(!lu) {
+ dbprintf(2, "[nf]");
+ continue;
+ }
+ if(lu < 0) {
+ dbprintf(1, " [aborted]\n");
+ goto abort;
+ }
+ width = seg->width;
+ dbprintf(2, " A00@%d", j);
+ } else {
+ width = (width + seg->width) / 2;
+ dbprintf(2, " %c%x%x@%d", 'A' + seg->finder, seg->color,
+ seg->side, j);
+ }
+ segs[i++] = j;
+ segs[i++] = -1;
+ }
+ if (i < 0)
+ break;
+
+ seg = db->segs + segs[0];
+ cnt = 0, chk = 0, age = (db->epoch - seg->epoch) & 0xff;
+ for (i = 1; segs[i] >= 0; i++) {
+ seg = db->segs + segs[i];
+ chk += seg->check;
+ cnt += seg->count;
+ age += (db->epoch - seg->epoch) & 0xff;
+ }
+
+ data0 = db->segs[segs[0]].data;
+ chk0 = data0 % 211;
+ chk %= 211;
+
+ dbprintf(2, " chk=%d ?= %d", chk, chk0);
+ if (chk != chk0)
+ continue;
+
+ dbprintf(2, " cnt=%d age=%d", cnt, age);
+ if (maxcnt > cnt || (maxcnt == cnt && maxage <= age))
+ continue;
+
+ dbprintf(2, " !");
+ maxcnt = cnt;
+ maxage = age;
+ for (i = 0; segs[i] >= 0; i++)
+ bestsegs[i] = segs[i];
+ bestsegs[i] = -1;
+ }
+
+ if (bestsegs[0] < 0)
+ return (ZBAR_PARTIAL);
+
+ if (acquire_lock(dcode, ZBAR_DATABAR_EXP))
+ return (ZBAR_PARTIAL);
+
+ for (i = 0; bestsegs[i] >= 0; i++)
+ segs[i] = db->segs[bestsegs[i]].data;
+
+ if (databar_postprocess_exp(dcode, segs)) {
+ release_lock(dcode, ZBAR_DATABAR_EXP);
+ return (ZBAR_PARTIAL);
+ }
+
+ for (i = 0; bestsegs[i] >= 0; i++)
+ if (bestsegs[i] != ifixed) {
+ seg = db->segs + bestsegs[i];
+ if (!--seg->count)
+ seg->finder = -1;
+ }
+
+ /* FIXME stacked rows are frequently reversed,
+ * so direction is impossible to determine at this level
+ */
+ dcode->direction = (1 - 2 * (seg->side ^ seg->color)) * dir;
+ dcode->modifiers = MOD(ZBAR_MOD_GS1);
+ return (ZBAR_DATABAR_EXP);
+abort:
+ return (ZBAR_NONE);
+}
+#undef IDX
+
+static inline unsigned calc_check(unsigned sig0, unsigned sig1, unsigned side,
+ unsigned mod)
+{
+ unsigned chk = 0;
+ int i;
+ for (i = 4; --i >= 0;) {
+ chk = (chk * 3 + (sig1 & 0xf) + 1) * 3 + (sig0 & 0xf) + 1;
+ sig1 >>= 4;
+ sig0 >>= 4;
+ if (!(i & 1))
+ chk %= mod;
+ }
+ dbprintf(2, " chk=%d", chk);
+
+ if (side)
+ chk = (chk * (6561 % mod)) % mod;
+ return (chk);
+}
+
+static inline int calc_value4(unsigned sig, unsigned n, unsigned wmax,
+ unsigned nonarrow)
+{
+ unsigned w0, w1, w2, w3;
+ unsigned v = 0;
+ n--;
+
+ w0 = (sig >> 12) & 0xf;
+ if (w0 > 1) {
+ unsigned n0, sk20, sk21;
+ if (w0 > wmax)
+ return (-1);
+ n0 = n - w0;
+ sk20 = (n - 1) * n * (2 * n - 1);
+ sk21 = n0 * (n0 + 1) * (2 * n0 + 1);
+ v = sk20 - sk21 - 3 * (w0 - 1) * (2 * n - w0);
+
+ if (!nonarrow && w0 > 2 && n > 4) {
+ unsigned k = (n - 2) * (n - 1) * (2 * n - 3) - sk21;
+ k -= 3 * (w0 - 2) * (14 * n - 7 * w0 - 31);
+ v -= k;
+ }
+
+ if (n - 2 > wmax) {
+ unsigned wm20 = 2 * wmax * (wmax + 1);
+ unsigned wm21 = (2 * wmax + 1);
+ unsigned k = sk20;
+ if (n0 > wmax) {
+ k -= sk21;
+ k += 3 * (w0 - 1) * (wm20 - wm21 * (2 * n - w0));
+ } else {
+ k -= (wmax + 1) * (wmax + 2) * (2 * wmax + 3);
+ k += 3 * (n - wmax - 2) * (wm20 - wm21 * (n + wmax + 1));
+ }
+ k *= 3;
+ v -= k;
+ }
+ v /= 12;
+ } else
+ nonarrow = 1;
+ n -= w0;
+
+ w1 = (sig >> 8) & 0xf;
+ if (w1 > 1) {
+ if (w1 > wmax)
+ return (-1);
+ v += (2 * n - w1) * (w1 - 1) / 2;
+ if (!nonarrow && w1 > 2 && n > 3)
+ v -= (2 * n - w1 - 5) * (w1 - 2) / 2;
+ if (n - 1 > wmax) {
+ if (n - w1 > wmax)
+ v -= (w1 - 1) * (2 * n - w1 - 2 * wmax);
+ else
+ v -= (n - wmax) * (n - wmax - 1);
+ }
+ } else
+ nonarrow = 1;
+ n -= w1;
+
+ w2 = (sig >> 4) & 0xf;
+ if (w2 > 1) {
+ if (w2 > wmax)
+ return (-1);
+ v += w2 - 1;
+ if (!nonarrow && w2 > 2 && n > 2)
+ v -= n - 2;
+ if (n > wmax)
+ v -= n - wmax;
+ } else
+ nonarrow = 1;
+
+ w3 = sig & 0xf;
+ if (w3 == 1)
+ nonarrow = 1;
+ else if (w3 > wmax)
+ return (-1);
+
+ if (!nonarrow)
+ return (-1);
+
+ return (v);
+}
+
+static inline zbar_symbol_type_t
+decode_char(zbar_decoder_t *dcode, databar_segment_t *seg, int off, int dir)
+{
+ databar_decoder_t *db = &dcode->databar;
+ unsigned s = calc_s(dcode, (dir > 0) ? off : off - 6, 8);
+ int n, i, emin[2] = { 0, }, sum = 0;
+ unsigned sig0 = 0, sig1 = 0;
+ int diff, vodd, veven, v;
+ unsigned sum0, sum1, chk;
+ struct group_s *g;
+
+ if (seg->exp)
+ n = 17;
+ else if (seg->side)
+ n = 15;
+ else
+ n = 16;
+ emin[1] = -n;
+
+ dbprintf(2,
+ "\n char[%c%d]: n=%d s=%d w=%d sig=", (dir < 0) ? '>' : '<',
+ off, n, s, seg->width);
+ if (s < 13 || !check_width(seg->width, s, n))
+ return (ZBAR_NONE);
+
+ for (i = 4; --i >= 0;) {
+ int e = decode_e(pair_width(dcode, off), s, n);
+ if (e < 0)
+ return (ZBAR_NONE);
+ dbprintf(2, "%d", e);
+ sum = e - sum;
+ off += dir;
+ sig1 <<= 4;
+ if (emin[1] < -sum)
+ emin[1] = -sum;
+ sig1 += sum;
+ if (!i)
+ break;
+
+ e = decode_e(pair_width(dcode, off), s, n);
+ if (e < 0)
+ return (ZBAR_NONE);
+ dbprintf(2, "%d", e);
+ sum = e - sum;
+ off += dir;
+ sig0 <<= 4;
+ if (emin[0] > sum)
+ emin[0] = sum;
+ sig0 += sum;
+ }
+
+ diff = emin[~n & 1];
+ diff = diff + (diff << 4);
+ diff = diff + (diff << 8);
+
+ sig0 -= diff;
+ sig1 += diff;
+
+ dbprintf(2, " emin=%d,%d el=%04x/%04x", emin[0], emin[1], sig0, sig1);
+
+ sum0 = sig0 + (sig0 >> 8);
+ sum1 = sig1 + (sig1 >> 8);
+ sum0 += sum0 >> 4;
+ sum1 += sum1 >> 4;
+ sum0 &= 0xf;
+ sum1 &= 0xf;
+
+ dbprintf(2, " sum=%d/%d", sum0, sum1);
+
+ if (sum0 + sum1 + 8 != n) {
+ dbprintf(2, " [SUM]");
+ return (ZBAR_NONE);
+ }
+
+ if (((sum0 ^ (n >> 1)) | (sum1 ^ (n >> 1) ^ n)) & 1) {
+ dbprintf(2, " [ODD]");
+ return (ZBAR_NONE);
+ }
+
+ i = ((n & 0x3) ^ 1) * 5 + (sum1 >> 1);
+ zassert(i < sizeof(groups) / sizeof(*groups), -1,
+ "n=%d sum=%d/%d sig=%04x/%04x g=%d", n, sum0, sum1, sig0, sig1, i);
+ g = groups + i;
+ dbprintf(2, "\n g=%d(%d,%d,%d/%d)", i, g->sum, g->wmax, g->todd,
+ g->teven);
+
+ vodd = calc_value4(sig0 + 0x1111, sum0 + 4, g->wmax, ~n & 1);
+ dbprintf(2, " v=%d", vodd);
+ if (vodd < 0 || vodd > g->todd)
+ return (ZBAR_NONE);
+
+ veven = calc_value4(sig1 + 0x1111, sum1 + 4, 9 - g->wmax, n & 1);
+ dbprintf(2, "/%d", veven);
+ if (veven < 0 || veven > g->teven)
+ return (ZBAR_NONE);
+
+ v = g->sum;
+ if (n & 2)
+ v += vodd + veven * g->todd;
+ else
+ v += veven + vodd * g->teven;
+
+ dbprintf(2, " f=%d(%x%x%x)", seg->finder, seg->exp, seg->color, seg->side);
+
+ chk = 0;
+ if (seg->exp) {
+ unsigned side = seg->color ^ seg->side ^ 1;
+ if (v >= 4096)
+ return (ZBAR_NONE);
+ /* skip A1 left */
+ chk = calc_check(sig0, sig1, side, 211);
+ if (seg->finder || seg->color || seg->side) {
+ i = (seg->finder << 1) - side + seg->color;
+ zassert(i >= 0 && i < 12, ZBAR_NONE, "f=%d(%x%x%x) side=%d i=%d\n",
+ seg->finder, seg->exp, seg->color, seg->side, side, i);
+ chk = (chk * exp_checksums[i]) % 211;
+ } else if (v >= 4009)
+ return (ZBAR_NONE);
+ else
+ chk = 0;
+ } else {
+ chk = calc_check(sig0, sig1, seg->side, 79);
+ if (seg->color)
+ chk = (chk * 16) % 79;
+ }
+ dbprintf(2, " => %d val=%d", chk, v);
+
+ seg->check = chk;
+ seg->data = v;
+
+ merge_segment(db, seg);
+
+ if (seg->exp)
+ return (match_segment_exp(dcode, seg, dir));
+ else if (dir > 0)
+ return (match_segment(dcode, seg));
+ return (ZBAR_PARTIAL);
+}
+
+static inline int alloc_segment(databar_decoder_t *db)
+{
+ unsigned maxage = 0, csegs = db->csegs;
+ int i, old = -1;
+ for (i = 0; i < csegs; i++) {
+ databar_segment_t *seg = db->segs + i;
+ unsigned age;
+ if (seg->finder < 0) {
+ dbprintf(2, " free@%d", i);
+ return (i);
+ }
+ age = (db->epoch - seg->epoch) & 0xff;
+ if (age >= 128 && seg->count < 2) {
+ seg->finder = -1;
+ dbprintf(2, " stale@%d (%d - %d = %d)", i, db->epoch, seg->epoch,
+ age);
+ return (i);
+ }
+
+ /* score based on both age and count */
+ if (age > seg->count)
+ age = age - seg->count + 1;
+ else
+ age = 1;
+
+ if (maxage < age) {
+ maxage = age;
+ old = i;
+ dbprintf(2, " old@%d(%u)", i, age);
+ }
+ }
+
+ if (csegs < DATABAR_MAX_SEGMENTS) {
+ dbprintf(2, " new@%d", i);
+ i = csegs;
+ csegs *= 2;
+ if (csegs > DATABAR_MAX_SEGMENTS)
+ csegs = DATABAR_MAX_SEGMENTS;
+ if (csegs != db->csegs) {
+ databar_segment_t *seg;
+ db->segs = realloc(db->segs, csegs * sizeof(*db->segs));
+ db->csegs = csegs;
+ seg = db->segs + csegs;
+ while (--seg, --csegs >= i) {
+ seg->finder = -1;
+ seg->exp = 0;
+ seg->color = 0;
+ seg->side = 0;
+ seg->partial = 0;
+ seg->count = 0;
+ seg->epoch = 0;
+ seg->check = 0;
+ }
+ return (i);
+ }
+ }
+ zassert(old >= 0, -1, "\n");
+
+ db->segs[old].finder = -1;
+ return (old);
+}
+
+static inline zbar_symbol_type_t decode_finder(zbar_decoder_t *dcode)
+{
+ databar_decoder_t *db = &dcode->databar;
+ databar_segment_t *seg;
+ unsigned e0 = pair_width(dcode, 1);
+ unsigned e2 = pair_width(dcode, 3);
+ unsigned e1, e3, s, finder, dir;
+ int sig, iseg;
+ int rc, i;
+
+ dbprintf(2, " databar: e0=%d e2=%d", e0, e2);
+ if (e0 < e2) {
+ unsigned e = e2 * 4;
+ if (e < 15 * e0 || e > 34 * e0)
+ return (ZBAR_NONE);
+ dir = 0;
+ e3 = pair_width(dcode, 4);
+ } else {
+ unsigned e = e0 * 4;
+ if (e < 15 * e2 || e > 34 * e2)
+ return (ZBAR_NONE);
+ dir = 1;
+ e2 = e0;
+ e3 = pair_width(dcode, 0);
+ }
+ e1 = pair_width(dcode, 2);
+
+ s = e1 + e3;
+ dbprintf(2, " e1=%d e3=%d dir=%d s=%d", e1, e3, dir, s);
+ if (s < 12)
+ return (ZBAR_NONE);
+
+ sig = ((decode_e(e3, s, 14) << 8) | (decode_e(e2, s, 14) << 4) |
+ decode_e(e1, s, 14));
+ dbprintf(2, " sig=%04x", sig & 0xfff);
+ if (sig < 0 || ((sig >> 4) & 0xf) < 8 || ((sig >> 4) & 0xf) > 10 ||
+ (sig & 0xf) >= 10 || ((sig >> 8) & 0xf) >= 10 ||
+ (((sig >> 8) + sig) & 0xf) != 10)
+ return (ZBAR_NONE);
+
+ finder = (finder_hash[(sig - (sig >> 5)) & 0x1f] +
+ finder_hash[(sig >> 1) & 0x1f]) &
+ 0x1f;
+ dbprintf(2, " finder=%d", finder);
+ if (finder == 0x1f ||
+ !TEST_CFG((finder < 9) ? db->config : db->config_exp, ZBAR_CFG_ENABLE))
+ return (ZBAR_NONE);
+
+ zassert(finder >= 0, ZBAR_NONE, "dir=%d sig=%04x f=%d\n", dir, sig & 0xfff,
+ finder);
+
+ iseg = alloc_segment(db);
+ if (iseg < 0)
+ return (ZBAR_NONE);
+
+ seg = db->segs + iseg;
+ seg->finder = (finder >= 9) ? finder - 9 : finder;
+ seg->exp = (finder >= 9);
+ seg->color = get_color(dcode) ^ dir ^ 1;
+ seg->side = dir;
+ seg->partial = 0;
+ seg->count = 1;
+ seg->width = s;
+ seg->epoch = db->epoch;
+
+ rc = decode_char(dcode, seg, 12 - dir, -1);
+ if (!rc)
+ seg->partial = 1;
+ else
+ db->epoch++;
+
+ i = (dcode->idx + 8 + dir) & 0xf;
+ zassert(db->chars[i] == -1, ZBAR_NONE, "\n");
+ db->chars[i] = iseg;
+ return (rc);
+}
+
+zbar_symbol_type_t _zbar_decode_databar(zbar_decoder_t *dcode)
+{
+ databar_decoder_t *db = &dcode->databar;
+ databar_segment_t *seg, *pair;
+ zbar_symbol_type_t sym;
+ int iseg, i = dcode->idx & 0xf;
+
+ sym = decode_finder(dcode);
+ dbprintf(2, "\n");
+
+ iseg = db->chars[i];
+ if (iseg < 0)
+ return (sym);
+
+ db->chars[i] = -1;
+ seg = db->segs + iseg;
+ dbprintf(2, " databar: i=%d part=%d f=%d(%x%x%x)", iseg,
+ seg->partial, seg->finder, seg->exp, seg->color, seg->side);
+ zassert(seg->finder >= 0, ZBAR_NONE, "i=%d f=%d(%x%x%x) part=%x\n", iseg,
+ seg->finder, seg->exp, seg->color, seg->side, seg->partial);
+
+ if (seg->partial) {
+ pair = NULL;
+ seg->side = !seg->side;
+ } else {
+ int jseg = alloc_segment(db);
+ pair = db->segs + iseg;
+ seg = db->segs + jseg;
+ seg->finder = pair->finder;
+ seg->exp = pair->exp;
+ seg->color = pair->color;
+ seg->side = !pair->side;
+ seg->partial = 0;
+ seg->count = 1;
+ seg->width = pair->width;
+ seg->epoch = db->epoch;
+ }
+
+ sym = decode_char(dcode, seg, 1, 1);
+ if (!sym) {
+ seg->finder = -1;
+ if (pair)
+ pair->partial = 1;
+ } else
+ db->epoch++;
+ dbprintf(2, "\n");
+
+ return (sym);
+}