-
Notifications
You must be signed in to change notification settings - Fork 8
/
dmtxencodeascii.c
204 lines (180 loc) · 5.41 KB
/
dmtxencodeascii.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/**
* libdmtx - Data Matrix Encoding/Decoding Library
* Copyright 2011 Mike Laughton. All rights reserved.
*
* See LICENSE file in the main project directory for full
* terms of use and distribution.
*
* Contact: Mike Laughton <mike@dragonflylogic.com>
*
* \file dmtxencodeascii.c
* \brief ASCII encoding rules
*/
/**
* Simple single scheme encoding uses "Normal"
* The optimizer needs to track "Expanded" and "Compact" streams separately, so they
* are called explicitly.
*
* Normal: Automatically collapses 2 consecutive digits into one codeword
* Expanded: Uses a whole codeword to represent a digit (never collapses)
* Compact: Collapses 2 digits into a single codeword or marks the stream
* invalid if either values are not digits
*
* \param stream
* \param option [Expanded|Compact|Normal]
*/
static void
EncodeNextChunkAscii(DmtxEncodeStream *stream, int option)
{
DmtxByte v0, v1;
DmtxBoolean compactDigits;
if(StreamInputHasNext(stream))
{
v0 = StreamInputAdvanceNext(stream); CHKERR;
if((option == DmtxEncodeCompact || option == DmtxEncodeNormal) &&
StreamInputHasNext(stream))
{
v1 = StreamInputPeekNext(stream); CHKERR;
compactDigits = (ISDIGIT(v0) && ISDIGIT(v1)) ? DmtxTrue : DmtxFalse;
}
else /* option == DmtxEncodeFull */
{
v1 = 0;
compactDigits = DmtxFalse;
}
if(compactDigits == DmtxTrue)
{
/* Two adjacent digit chars: Make peek progress official and encode */
StreamInputAdvanceNext(stream); CHKERR;
AppendValueAscii(stream, 10 * (v0-'0') + (v1-'0') + 130); CHKERR;
}
else if(option == DmtxEncodeCompact)
{
/* Can't compact non-digits */
StreamMarkInvalid(stream, DmtxErrorCantCompactNonDigits);
}
else
{
/* Encode single ASCII value */
if(v0 < 128)
{
/* Regular ASCII */
AppendValueAscii(stream, v0 + 1); CHKERR;
}
else
{
/* Extended ASCII */
AppendValueAscii(stream, DmtxValueAsciiUpperShift); CHKERR;
AppendValueAscii(stream, v0 - 127); CHKERR;
}
}
}
}
/**
* this code is separated from EncodeNextChunkAscii() because it needs to be
* called directly elsewhere
*/
static void
AppendValueAscii(DmtxEncodeStream *stream, DmtxByte value)
{
CHKSCHEME(DmtxSchemeAscii);
StreamOutputChainAppend(stream, value); CHKERR;
stream->outputChainValueCount++;
}
/**
*
*
*/
static void
CompleteIfDoneAscii(DmtxEncodeStream *stream, int sizeIdxRequest)
{
int sizeIdx;
if(stream->status == DmtxStatusComplete)
return;
if(!StreamInputHasNext(stream))
{
sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE;
PadRemainingInAscii(stream, sizeIdx); CHKERR;
StreamMarkComplete(stream, sizeIdx);
}
}
/**
* Can we just receive a length to pad here? I don't like receiving
* sizeIdxRequest (or sizeIdx) this late in the game
*/
static void
PadRemainingInAscii(DmtxEncodeStream *stream, int sizeIdx)
{
int symbolRemaining;
DmtxByte padValue;
CHKSCHEME(DmtxSchemeAscii);
CHKSIZE;
symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx);
/* First pad character is not randomized */
if(symbolRemaining > 0)
{
padValue = DmtxValueAsciiPad;
StreamOutputChainAppend(stream, padValue); CHKERR;
symbolRemaining--;
}
/* All remaining pad characters are randomized based on character position */
while(symbolRemaining > 0)
{
padValue = Randomize253State(DmtxValueAsciiPad, stream->output->length + 1);
StreamOutputChainAppend(stream, padValue); CHKERR;
symbolRemaining--;
}
}
/**
* consider receiving instantiated DmtxByteList instead of the output components
*/
static DmtxByteList
EncodeTmpRemainingInAscii(DmtxEncodeStream *stream, DmtxByte *storage,
int capacity, DmtxPassFail *passFail)
{
DmtxEncodeStream streamAscii;
DmtxByteList output = dmtxByteListBuild(storage, capacity);
/* Create temporary copy of stream that writes to storage */
streamAscii = *stream;
streamAscii.currentScheme = DmtxSchemeAscii;
streamAscii.outputChainValueCount = 0;
streamAscii.outputChainWordCount = 0;
streamAscii.reason = NULL;
streamAscii.sizeIdx = DmtxUndefined;
streamAscii.status = DmtxStatusEncoding;
streamAscii.output = &output;
while(dmtxByteListHasCapacity(streamAscii.output))
{
if(StreamInputHasNext(&streamAscii))
EncodeNextChunkAscii(&streamAscii, DmtxEncodeNormal); /* No CHKERR */
else
break;
}
/*
* We stopped encoding before attempting to write beyond output boundary so
* any stream errors are truly unexpected. The passFail status indicates
* whether output.length can be trusted by the calling function.
*/
if(streamAscii.status == DmtxStatusInvalid || streamAscii.status == DmtxStatusFatal)
*passFail = DmtxFail;
else
*passFail = DmtxPass;
return output;
}
/**
* \brief Randomize 253 state
* \param codewordValue
* \param codewordPosition
* \return Randomized value
*/
static DmtxByte
Randomize253State(DmtxByte cwValue, int cwPosition)
{
int pseudoRandom, tmp;
pseudoRandom = ((149 * cwPosition) % 253) + 1;
tmp = cwValue + pseudoRandom;
if(tmp > 254)
tmp -= 254;
assert(tmp >= 0 && tmp < 256);
return (DmtxByte)tmp;
}