1 /**
2 * libdmtx - Data Matrix Encoding/Decoding Library
3 * Copyright 2011 Mike Laughton. All rights reserved.
4 * Copyright 2012-2016 Vadim A. Misbakh-Soloviov. All rights reserved.
5 *
6 * See LICENSE file in the main project directory for full
7 * terms of use and distribution.
8 *
9 * Contact:
10 * Vadim A. Misbakh-Soloviov <dmtx@mva.name>
11 * Mike Laughton <mike@dragonflylogic.com>
12 *
13 * \file dmtxencodebase256.c
14 * \brief Base 256 encoding rules
15 */
16
17 /**
18 *
19 *
20 */
21 static void
EncodeNextChunkBase256(DmtxEncodeStream * stream)22 EncodeNextChunkBase256(DmtxEncodeStream *stream)
23 {
24 DmtxByte value;
25
26 if(StreamInputHasNext(stream))
27 {
28 /* Check for FNC1 character, which needs to be sent in ASCII */
29 value = StreamInputPeekNext(stream); CHKERR;
30 if(stream->fnc1 != DmtxUndefined && (int)value == stream->fnc1) {
31 EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit);
32
33 StreamInputAdvanceNext(stream); CHKERR;
34 AppendValueAscii(stream, DmtxValueFNC1); CHKERR;
35 return;
36 }
37
38 value = StreamInputAdvanceNext(stream); CHKERR;
39 AppendValueBase256(stream, value); CHKERR;
40 }
41 }
42
43 /**
44 *
45 *
46 */
47 static void
AppendValueBase256(DmtxEncodeStream * stream,DmtxByte value)48 AppendValueBase256(DmtxEncodeStream *stream, DmtxByte value)
49 {
50 CHKSCHEME(DmtxSchemeBase256);
51
52 StreamOutputChainAppend(stream, Randomize255State(value, stream->output->length + 1)); CHKERR;
53 stream->outputChainValueCount++;
54
55 UpdateBase256ChainHeader(stream, DmtxUndefined); CHKERR;
56 }
57
58 /**
59 * check remaining symbol capacity and remaining codewords
60 * if the chain can finish perfectly at the end of symbol data words there is a
61 * special one-byte length header value that can be used (i think ... read the
62 * spec again before commiting to anything)
63 */
64 static void
CompleteIfDoneBase256(DmtxEncodeStream * stream,int sizeIdxRequest)65 CompleteIfDoneBase256(DmtxEncodeStream *stream, int sizeIdxRequest)
66 {
67 int sizeIdx;
68 int headerByteCount, outputLength, symbolRemaining;
69
70 if(stream->status == DmtxStatusComplete)
71 return;
72
73 if(!StreamInputHasNext(stream))
74 {
75 headerByteCount = stream->outputChainWordCount - stream->outputChainValueCount;
76 assert(headerByteCount == 1 || headerByteCount == 2);
77
78 /* Check for special case where every last symbol word is used */
79 if(headerByteCount == 2)
80 {
81 /* Find symbol size as if headerByteCount was only 1 */
82 outputLength = stream->output->length - 1;
83 sizeIdx = FindSymbolSize(outputLength, sizeIdxRequest); /* No CHKSIZE */
84 if(sizeIdx != DmtxUndefined)
85 {
86 symbolRemaining = GetRemainingSymbolCapacity(outputLength, sizeIdx);
87
88 if(symbolRemaining == 0)
89 {
90 /* Perfect fit -- complete encoding */
91 UpdateBase256ChainHeader(stream, sizeIdx); CHKERR;
92 StreamMarkComplete(stream, sizeIdx);
93 return;
94 }
95 }
96 }
97
98 /* Normal case */
99 sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); CHKSIZE;
100 EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit);
101 PadRemainingInAscii(stream, sizeIdx);
102 StreamMarkComplete(stream, sizeIdx);
103 }
104 }
105
106 /**
107 *
108 *
109 */
110 static void
UpdateBase256ChainHeader(DmtxEncodeStream * stream,int perfectSizeIdx)111 UpdateBase256ChainHeader(DmtxEncodeStream *stream, int perfectSizeIdx)
112 {
113 int headerIndex;
114 int outputLength;
115 int headerByteCount;
116 int symbolDataWords;
117 DmtxBoolean perfectFit;
118 DmtxByte headerValue0;
119 DmtxByte headerValue1;
120
121 outputLength = stream->outputChainValueCount;
122 headerIndex = stream->output->length - stream->outputChainWordCount;
123 headerByteCount = stream->outputChainWordCount - stream->outputChainValueCount;
124 perfectFit = (perfectSizeIdx == DmtxUndefined) ? DmtxFalse : DmtxTrue;
125
126 /*
127 * If requested perfect fit verify symbol capacity against final length
128 */
129
130 if(perfectFit)
131 {
132 symbolDataWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, perfectSizeIdx);
133 if(symbolDataWords != stream->output->length - 1)
134 {
135 StreamMarkFatal(stream, DmtxErrorUnknown);
136 return;
137 }
138 }
139
140 /*
141 * Adjust header to hold correct number of bytes, not worrying about the
142 * values held there until below. Note: Header bytes are not considered
143 * scheme "values" so we can insert or remove them without updating the
144 * outputChainValueCount.
145 */
146
147 if(headerByteCount == 0 && stream->outputChainWordCount == 0)
148 {
149 /* No output words written yet -- insert single header byte */
150 StreamOutputChainAppend(stream, 0); CHKERR;
151 headerByteCount++;
152 }
153 else if(!perfectFit && headerByteCount == 1 && outputLength > 249)
154 {
155 /* Beyond 249 bytes requires a second header byte */
156 Base256OutputChainInsertFirst(stream); CHKERR;
157 headerByteCount++;
158 }
159 else if(perfectFit && headerByteCount == 2)
160 {
161 /* Encoding to exact end of symbol only requires single byte */
162 Base256OutputChainRemoveFirst(stream); CHKERR;
163 headerByteCount--;
164 }
165
166 /*
167 * Encode header byte(s) with current length
168 */
169
170 if(!perfectFit && headerByteCount == 1 && outputLength <= 249)
171 {
172 /* Normal condition for chain length < 250 bytes */
173 headerValue0 = Randomize255State(outputLength, headerIndex + 1);
174 StreamOutputSet(stream, headerIndex, headerValue0); CHKERR;
175 }
176 else if(!perfectFit && headerByteCount == 2 && outputLength > 249)
177 {
178 /* Normal condition for chain length >= 250 bytes */
179 headerValue0 = Randomize255State(outputLength/250 + 249, headerIndex + 1);
180 StreamOutputSet(stream, headerIndex, headerValue0); CHKERR;
181
182 headerValue1 = Randomize255State(outputLength%250, headerIndex + 2);
183 StreamOutputSet(stream, headerIndex + 1, headerValue1); CHKERR;
184 }
185 else if(perfectFit && headerByteCount == 1)
186 {
187 /* Special condition when Base 256 stays in effect to end of symbol */
188 headerValue0 = Randomize255State(0, headerIndex + 1); /* XXX replace magic value 0? */
189 StreamOutputSet(stream, headerIndex, headerValue0); CHKERR;
190 }
191 else
192 {
193 StreamMarkFatal(stream, DmtxErrorUnknown);
194 return;
195 }
196 }
197
198 /**
199 * insert element at beginning of chain, shifting all following elements forward by one
200 * used for binary length changes
201 */
202 static void
Base256OutputChainInsertFirst(DmtxEncodeStream * stream)203 Base256OutputChainInsertFirst(DmtxEncodeStream *stream)
204 {
205 DmtxByte value;
206 DmtxPassFail passFail;
207 int i, chainStart;
208
209 chainStart = stream->output->length - stream->outputChainWordCount;
210 dmtxByteListPush(stream->output, 0, &passFail);
211 if(passFail == DmtxPass)
212 {
213 for(i = stream->output->length - 1; i > chainStart; i--)
214 {
215 value = UnRandomize255State(stream->output->b[i-1], i);
216 stream->output->b[i] = Randomize255State(value, i + 1);
217 }
218
219 stream->outputChainWordCount++;
220 }
221 else
222 {
223 StreamMarkFatal(stream, DmtxErrorUnknown);
224 }
225 }
226
227 /**
228 * remove first element from chain, shifting all following elements back by one
229 * used for binary length changes end condition
230 */
231 static void
Base256OutputChainRemoveFirst(DmtxEncodeStream * stream)232 Base256OutputChainRemoveFirst(DmtxEncodeStream *stream)
233 {
234 DmtxByte value;
235 DmtxPassFail passFail;
236 int i, chainStart;
237
238 chainStart = stream->output->length - stream->outputChainWordCount;
239
240 for(i = chainStart; i < stream->output->length - 1; i++)
241 {
242 value = UnRandomize255State(stream->output->b[i+1], i+2);
243 stream->output->b[i] = Randomize255State(value, i + 1);
244 }
245
246 dmtxByteListPop(stream->output, &passFail);
247 if(passFail == DmtxPass)
248 stream->outputChainWordCount--;
249 else
250 StreamMarkFatal(stream, DmtxErrorUnknown);
251 }
252
253 /**
254 * \brief Randomize 255 state
255 * \param value
256 * \param position
257 * \return Randomized value
258 */
259 static DmtxByte
Randomize255State(DmtxByte value,int position)260 Randomize255State(DmtxByte value, int position)
261 {
262 int pseudoRandom, tmp;
263
264 pseudoRandom = ((149 * position) % 255) + 1;
265 tmp = value + pseudoRandom;
266
267 return (tmp <= 255) ? tmp : tmp - 256;
268 }
269
270 /**
271 * \brief Unrandomize 255 state
272 * \param value
273 * \param idx
274 * \return Unrandomized value
275 */
276 static unsigned char
UnRandomize255State(unsigned char value,int idx)277 UnRandomize255State(unsigned char value, int idx)
278 {
279 int pseudoRandom;
280 int tmp;
281
282 pseudoRandom = ((149 * idx) % 255) + 1;
283 tmp = value - pseudoRandom;
284 if(tmp < 0)
285 tmp += 256;
286
287 assert(tmp >= 0 && tmp < 256);
288
289 return (unsigned char)tmp;
290 }
291