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