static void AppendValuesCTX(DmtxEncodeStream *stream, DmtxByteList *valueList) { int pairValue; DmtxByte cw0, cw1; if(!IsCTX(stream->currentScheme)) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } if(valueList->length < 3) { StreamMarkFatal(stream, DmtxErrorIncompleteValueList); return; } /* Build codewords from computed value */ pairValue = (1600 * valueList->b[0]) + (40 * valueList->b[1]) + valueList->b[2] + 1; cw0 = pairValue / 256; cw1 = pairValue % 256; /* Append 2 codewords */ StreamOutputChainAppend(stream, cw0); CHKERR; StreamOutputChainAppend(stream, cw1); CHKERR; /* Update count for 3 encoded values */ stream->outputChainValueCount += 3; }
/** * used as each input cw is processed * * \param value Value to populate, can be null (for blind dequeues) * \param stream */ static void StreamInputAdvancePrev(DmtxEncodeStream *stream) { if(stream->inputNext > 0) stream->inputNext--; else StreamMarkFatal(stream, DmtxErrorOutOfBounds); }
/** * overwrite arbitrary element * used for binary length changes */ static void StreamOutputSet(DmtxEncodeStream *stream, int index, DmtxByte value) { if(index < 0 || index >= stream->output->length) StreamMarkFatal(stream, DmtxErrorOutOfBounds); else stream->output->b[index] = value; }
/** * Partial chunks are not valid in X12. Encode using ASCII instead, using * an implied unlatch if there is exactly one ascii codeword and one symbol * codeword remaining. Otherwise use explicit unlatch. */ static void CompletePartialX12(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest) { int i; int sizeIdx; int symbolRemaining; DmtxPassFail passFail; DmtxByte outputTmpStorage[2]; DmtxByteList outputTmp; if(stream->currentScheme != DmtxSchemeX12) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Should have exactly one or two input values left */ assert(valueList->length == 1 || valueList->length == 2); /* Roll back input progress */ for(i = 0; i < valueList->length; i++) { StreamInputAdvancePrev(stream); CHKERR; } /* Encode up to 2 codewords to a temporary stream */ outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage, sizeof(outputTmpStorage), &passFail); sizeIdx = FindSymbolSize(stream->output->length + 1, sizeIdxRequest); symbolRemaining = GetRemainingSymbolCapacity(stream->output->length, sizeIdx); if(outputTmp.length == 1 && symbolRemaining == 1) { /* End of symbol condition (XXX) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR; AppendValueAscii(stream, outputTmp.b[0]); CHKERR; /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx); } else { /* Finish in ASCII (XXX) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; for(i = 0; i < outputTmp.length; i++) AppendValueAscii(stream, outputTmp.b[i]); CHKERR; sizeIdx = FindSymbolSize(stream->output->length, sizeIdxRequest); PadRemainingInAscii(stream, sizeIdx); /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx); } }
/** * push on newest/last append * used for encoding each output cw */ static void StreamOutputChainAppend(DmtxEncodeStream *stream, DmtxByte value) { DmtxPassFail passFail; dmtxByteListPush(stream->output, value, &passFail); if(passFail == DmtxPass) stream->outputChainWordCount++; else StreamMarkFatal(stream, DmtxErrorOutOfBounds); }
/** * peek at first/oldest * used for ascii double digit */ static DmtxByte StreamInputPeekNext(DmtxEncodeStream *stream) { DmtxByte value = 0; if(StreamInputHasNext(stream)) value = stream->input->b[stream->inputNext]; else StreamMarkFatal(stream, DmtxErrorOutOfBounds); return value; }
static void AdvanceCTX(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int ctxValueCount, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; /* we won't actually use inputNext here */ switch(targetState) { case C40Offset0: case TextOffset0: case X12Offset0: isStartState = (ctxValueCount % 3 == 0) ? DmtxTrue : DmtxFalse; break; case C40Offset1: case TextOffset1: case X12Offset1: isStartState = (ctxValueCount % 3 == 1) ? DmtxTrue : DmtxFalse; break; case C40Offset2: case TextOffset2: case X12Offset2: isStartState = (ctxValueCount % 3 == 2) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(inputNext < currentStream->inputNext) { StreamCopy(targetStream, currentStream); } else if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); StreamMarkInvalid(targetStream, DmtxErrorUnknown); } }
static void AdvanceEdifact(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; switch(targetState) { case EdifactOffset0: isStartState = (inputNext % 4 == 0) ? DmtxTrue : DmtxFalse; break; case EdifactOffset1: isStartState = (inputNext % 4 == 1) ? DmtxTrue : DmtxFalse; break; case EdifactOffset2: isStartState = (inputNext % 4 == 2) ? DmtxTrue : DmtxFalse; break; case EdifactOffset3: isStartState = (inputNext % 4 == 3) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); if(currentStream->status == DmtxStatusEncoding && currentStream->currentScheme == DmtxSchemeEdifact) EncodeNextChunk(targetStream, DmtxSchemeEdifact, DmtxEncodeNormal, sizeIdxRequest); else StreamMarkInvalid(targetStream, DmtxErrorUnknown); } }
/** * pop off newest/last * used for edifact */ static DmtxByte StreamOutputChainRemoveLast(DmtxEncodeStream *stream) { DmtxByte value; DmtxPassFail passFail; if(stream->outputChainWordCount > 0) { value = dmtxByteListPop(stream->output, &passFail); stream->outputChainWordCount--; } else { value = 0; StreamMarkFatal(stream, DmtxErrorEmptyList); } return value; }
static void AppendUnlatchCTX(DmtxEncodeStream *stream) { if(!IsCTX(stream->currentScheme)) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Verify we are on byte boundary */ if(stream->outputChainValueCount % 3 != 0) { StreamMarkInvalid(stream, DmtxErrorNotOnByteBoundary); return; } StreamOutputChainAppend(stream, DmtxValueCTXUnlatch); CHKERR; stream->outputChainValueCount++; }
/** * remove first element from chain, shifting all following elements back by one * used for binary length changes end condition */ static void Base256OutputChainRemoveFirst(DmtxEncodeStream *stream) { DmtxByte value; DmtxPassFail passFail; int i, chainStart; chainStart = stream->output->length - stream->outputChainWordCount; for(i = chainStart; i < stream->output->length - 1; i++) { value = UnRandomize255State(stream->output->b[i+1], i+2); stream->output->b[i] = Randomize255State(value, i + 1); } dmtxByteListPop(stream->output, &passFail); if(passFail == DmtxPass) stream->outputChainWordCount--; else StreamMarkFatal(stream, DmtxErrorUnknown); }
static void AdvanceAsciiCompact(DmtxEncodeStream *streamsNext, DmtxEncodeStream *streamsBest, int targetState, int inputNext, int sizeIdxRequest) { DmtxEncodeStream *currentStream = &(streamsBest[targetState]); DmtxEncodeStream *targetStream = &(streamsNext[targetState]); DmtxBoolean isStartState; switch(targetState) { case AsciiCompactOffset0: isStartState = (inputNext % 2 == 0) ? DmtxTrue : DmtxFalse; break; case AsciiCompactOffset1: isStartState = (inputNext % 2 == 1) ? DmtxTrue : DmtxFalse; break; default: StreamMarkFatal(targetStream, DmtxErrorIllegalParameterValue); return; } if(inputNext < currentStream->inputNext) { StreamCopy(targetStream, currentStream); } else if(isStartState == DmtxTrue) { StreamAdvanceFromBest(streamsNext, streamsBest, targetState, sizeIdxRequest); } else { StreamCopy(targetStream, currentStream); StreamMarkInvalid(targetStream, DmtxErrorUnknown); } }
/** * insert element at beginning of chain, shifting all following elements forward by one * used for binary length changes */ static void Base256OutputChainInsertFirst(DmtxEncodeStream *stream) { DmtxByte value; DmtxPassFail passFail; int i, chainStart; chainStart = stream->output->length - stream->outputChainWordCount; dmtxByteListPush(stream->output, 0, &passFail); if(passFail == DmtxPass) { for(i = stream->output->length - 1; i > chainStart; i--) { value = UnRandomize255State(stream->output->b[i-1], i); stream->output->b[i] = Randomize255State(value, i + 1); } stream->outputChainWordCount++; } else { StreamMarkFatal(stream, DmtxErrorUnknown); } }
static void UpdateBase256ChainHeader(DmtxEncodeStream *stream, int perfectSizeIdx) { int headerIndex; int outputLength; int headerByteCount; int symbolDataWords; DmtxBoolean perfectFit; DmtxByte headerValue0; DmtxByte headerValue1; outputLength = stream->outputChainValueCount; headerIndex = stream->output->length - stream->outputChainWordCount; headerByteCount = stream->outputChainWordCount - stream->outputChainValueCount; perfectFit = (perfectSizeIdx == DmtxUndefined) ? DmtxFalse : DmtxTrue; /* * If requested perfect fit verify symbol capacity against final length */ if(perfectFit) { symbolDataWords = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, perfectSizeIdx); if(symbolDataWords != stream->output->length - 1) { StreamMarkFatal(stream, DmtxErrorUnknown); return; } } /* * Adjust header to hold correct number of bytes, not worrying about the * values held there until below. Note: Header bytes are not considered * scheme "values" so we can insert or remove them without updating the * outputChainValueCount. */ if(headerByteCount == 0 && stream->outputChainWordCount == 0) { /* No output words written yet -- insert single header byte */ StreamOutputChainAppend(stream, 0); CHKERR; headerByteCount++; } else if(!perfectFit && headerByteCount == 1 && outputLength > 249) { /* Beyond 249 bytes requires a second header byte */ Base256OutputChainInsertFirst(stream); CHKERR; headerByteCount++; } else if(perfectFit && headerByteCount == 2) { /* Encoding to exact end of symbol only requires single byte */ Base256OutputChainRemoveFirst(stream); CHKERR; headerByteCount--; } /* * Encode header byte(s) with current length */ if(!perfectFit && headerByteCount == 1 && outputLength <= 249) { /* Normal condition for chain length < 250 bytes */ headerValue0 = Randomize255State(outputLength, headerIndex + 1); StreamOutputSet(stream, headerIndex, headerValue0); CHKERR; } else if(!perfectFit && headerByteCount == 2 && outputLength > 249) { /* Normal condition for chain length >= 250 bytes */ headerValue0 = Randomize255State(outputLength/250 + 249, headerIndex + 1); StreamOutputSet(stream, headerIndex, headerValue0); CHKERR; headerValue1 = Randomize255State(outputLength%250, headerIndex + 2); StreamOutputSet(stream, headerIndex + 1, headerValue1); CHKERR; } else if(perfectFit && headerByteCount == 1) { /* Special condition when Base 256 stays in effect to end of symbol */ headerValue0 = Randomize255State(0, headerIndex + 1); /* XXX replace magic value 0? */ StreamOutputSet(stream, headerIndex, headerValue0); CHKERR; } else { StreamMarkFatal(stream, DmtxErrorUnknown); return; } }
/** * The remaining values can exist in 3 possible cases: * * a) 1 C40/Text/X12 remaining == 1 data * b) 2 C40/Text/X12 remaining == 1 shift + 1 data * c) 2 C40/Text/X12 remaining == 1 data + 1 data * * To distinguish between cases (b) and (c), encode the final input value to * C40/Text/X12 in a temporary location and check the resulting length. If * it expands to multiple values it represents (b); otherwise it is (c). This * accounts for both shift and upper shift conditions. * * Note that in cases (a) and (c) the final C40/Text/X12 value encoded in the * previous chunk may have been a shift value, but this will be ignored by * the decoder due to the implicit shift to ASCII. <-- what if symbol is much * larger though? * * Term Value Symbol Codeword * Cond Count Remain Sequence * ---- ------- ------ ------------------------ * (b) C40 2 2 C40+C40+0 * (d) ASCII 1 1 ASCII (implicit unlatch) * (c) ASCII 1 2 UNLATCH ASCII * - - UNLATCH (finish ASCII) */ static void CompletePartialC40Text(DmtxEncodeStream *stream, DmtxByteList *valueList, int sizeIdxRequest) { int i; int sizeIdx1, sizeIdx2; int symbolRemaining1, symbolRemaining2; DmtxPassFail passFail; DmtxByte inputValue; DmtxByte outputTmpStorage[4]; DmtxByteList outputTmp = dmtxByteListBuild(outputTmpStorage, sizeof(outputTmpStorage)); if(stream->currentScheme != DmtxSchemeC40 && stream->currentScheme != DmtxSchemeText) { StreamMarkFatal(stream, DmtxErrorUnexpectedScheme); return; } /* Should have exactly one or two input values left */ assert(valueList->length == 1 || valueList->length == 2); sizeIdx1 = FindSymbolSize(stream->output->length + 1, sizeIdxRequest); sizeIdx2 = FindSymbolSize(stream->output->length + 2, sizeIdxRequest); symbolRemaining1 = GetRemainingSymbolCapacity(stream->output->length, sizeIdx1); symbolRemaining2 = GetRemainingSymbolCapacity(stream->output->length, sizeIdx2); if(valueList->length == 2 && symbolRemaining2 == 2) { /* End of symbol condition (b) -- Use Shift1 to pad final list value */ dmtxByteListPush(valueList, DmtxValueCTXShift1, &passFail); CHKPASS; AppendValuesCTX(stream, valueList); CHKERR; StreamMarkComplete(stream, sizeIdx2); } else { /* * Rollback progress of previously consumed input value(s) since ASCII * encoder will be used to finish the symbol. 2 rollbacks are needed if * valueList holds 2 data words (i.e., not shifts or upper shifts). */ StreamInputAdvancePrev(stream); CHKERR; inputValue = StreamInputPeekNext(stream); CHKERR; /* Test-encode most recently consumed input value to C40/Text/X12 */ PushCTXValues(&outputTmp, inputValue, stream->currentScheme, &passFail); if(valueList->length == 2 && outputTmp.length == 1) StreamInputAdvancePrev(stream); CHKERR; /* Re-use outputTmp to hold ASCII representation of 1-2 input values */ /* XXX Refactor how the DmtxByteList is passed back here */ outputTmp = EncodeTmpRemainingInAscii(stream, outputTmpStorage, sizeof(outputTmpStorage), &passFail); if(passFail == DmtxFail) { StreamMarkFatal(stream, DmtxErrorUnknown); return; } if(outputTmp.length == 1 && symbolRemaining1 == 1) { /* End of symbol condition (d) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchImplicit); CHKERR; AppendValueAscii(stream, outputTmp.b[0]); CHKERR; /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx1); } else { /* Finish in ASCII (c) */ EncodeChangeScheme(stream, DmtxSchemeAscii, DmtxUnlatchExplicit); CHKERR; for(i = 0; i < outputTmp.length; i++) AppendValueAscii(stream, outputTmp.b[i]); CHKERR; sizeIdx1 = FindSymbolSize(stream->output->length, sizeIdxRequest); PadRemainingInAscii(stream, sizeIdx1); /* Register progress since encoding happened outside normal path */ stream->inputNext = stream->input->length; StreamMarkComplete(stream, sizeIdx1); } } }