static void EncodeNextChunkCTX(DmtxEncodeStream *stream, int sizeIdxRequest) { DmtxPassFail passFail; DmtxByte inputValue; DmtxByte valueListStorage[6]; DmtxByteList valueList = dmtxByteListBuild(valueListStorage, sizeof(valueListStorage)); while(StreamInputHasNext(stream)) { inputValue = StreamInputAdvanceNext(stream); CHKERR; /* Expand next input value into up to 4 CTX values and add to valueList */ PushCTXValues(&valueList, inputValue, stream->currentScheme, &passFail); if(passFail == DmtxFail) { /* XXX Perhaps PushCTXValues should return this error code */ StreamMarkInvalid(stream, DmtxErrorUnsupportedCharacter); return; } /* If there at least 3 CTX values available encode them to output */ while(valueList.length >= 3) { AppendValuesCTX(stream, &valueList); CHKERR; ShiftValueListBy3(&valueList, &passFail); CHKPASS; } /* Finished on byte boundary -- done with current chunk */ if(valueList.length == 0) break; } /* * Special case: If all input values have been consumed and 1 or 2 unwritten * C40/Text/X12 values remain, finish encoding the symbol according to the * established end-of-symbol conditions. */ if(!StreamInputHasNext(stream) && valueList.length > 0) { if(stream->currentScheme == DmtxSchemeX12) { CompletePartialX12(stream, &valueList, sizeIdxRequest); CHKERR; } else { CompletePartialC40Text(stream, &valueList, sizeIdxRequest); CHKERR; } } }
/** * Return DmtxTrue 1 or 2 X12 values remain, otherwise DmtxFalse */ static DmtxBoolean PartialX12ChunkRemains(DmtxEncodeStream *stream) { DmtxEncodeStream streamTmp; DmtxByte inputValue; DmtxByte valueListStorage[6]; DmtxByteList valueList = dmtxByteListBuild(valueListStorage, sizeof(valueListStorage)); DmtxPassFail passFail; /* Create temporary copy of stream to track test input progress */ streamTmp = *stream; streamTmp.currentScheme = DmtxSchemeX12; streamTmp.outputChainValueCount = 0; streamTmp.outputChainWordCount = 0; streamTmp.reason = NULL; streamTmp.sizeIdx = DmtxUndefined; streamTmp.status = DmtxStatusEncoding; streamTmp.output = NULL; while(StreamInputHasNext(&streamTmp)) { inputValue = StreamInputAdvanceNext(&streamTmp); if(stream->status != DmtxStatusEncoding) { StreamMarkInvalid(stream, DmtxErrorUnknown); return DmtxFalse; } /* Expand next input value into up to 4 CTX values and add to valueList */ PushCTXValues(&valueList, inputValue, streamTmp.currentScheme, &passFail); if(passFail == DmtxFail) { StreamMarkInvalid(stream, DmtxErrorUnknown); return DmtxFalse; } /* Not a final partial chunk */ if(valueList.length >= 3) return DmtxFalse; } return (valueList.length == 0) ? DmtxFalse : DmtxTrue; }
/** * 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); } } }
static int EncodeOptimizeBest(DmtxByteList *input, DmtxByteList *output, int sizeIdxRequest, int fnc1) { enum SchemeState state; int inputNext, c40ValueCount, textValueCount, x12ValueCount; int sizeIdx; DmtxEncodeStream *winner; DmtxPassFail passFail; DmtxEncodeStream streamsBest[SchemeStateCount]; DmtxEncodeStream streamsTemp[SchemeStateCount]; DmtxByte outputsBestStorage[SchemeStateCount][4096]; DmtxByte outputsTempStorage[SchemeStateCount][4096]; DmtxByte ctxTempStorage[4]; DmtxByteList outputsBest[SchemeStateCount]; DmtxByteList outputsTemp[SchemeStateCount]; DmtxByteList ctxTemp = dmtxByteListBuild(ctxTempStorage, sizeof(ctxTempStorage)); /* Initialize all streams with their own output storage */ for(state = 0; state < SchemeStateCount; state++) { outputsBest[state] = dmtxByteListBuild(outputsBestStorage[state], sizeof(outputsBestStorage[state])); outputsTemp[state] = dmtxByteListBuild(outputsTempStorage[state], sizeof(outputsTempStorage[state])); streamsBest[state] = StreamInit(input, &(outputsBest[state])); streamsTemp[state] = StreamInit(input, &(outputsTemp[state])); streamsBest[state].fnc1 = fnc1; streamsTemp[state].fnc1 = fnc1; } c40ValueCount = textValueCount = x12ValueCount = 0; for(inputNext = 0; inputNext < input->length; inputNext++) { StreamAdvanceFromBest(streamsTemp, streamsBest, AsciiFull, sizeIdxRequest); AdvanceAsciiCompact(streamsTemp, streamsBest, AsciiCompactOffset0, inputNext, sizeIdxRequest); AdvanceAsciiCompact(streamsTemp, streamsBest, AsciiCompactOffset1, inputNext, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, C40Offset0, inputNext, c40ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, C40Offset1, inputNext, c40ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, C40Offset2, inputNext, c40ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, TextOffset0, inputNext, textValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, TextOffset1, inputNext, textValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, TextOffset2, inputNext, textValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, X12Offset0, inputNext, x12ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, X12Offset1, inputNext, x12ValueCount, sizeIdxRequest); AdvanceCTX(streamsTemp, streamsBest, X12Offset2, inputNext, x12ValueCount, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset0, inputNext, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset1, inputNext, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset2, inputNext, sizeIdxRequest); AdvanceEdifact(streamsTemp, streamsBest, EdifactOffset3, inputNext, sizeIdxRequest); StreamAdvanceFromBest(streamsTemp, streamsBest, Base256, sizeIdxRequest); /* Overwrite best streams with new results */ for(state = 0; state < SchemeStateCount; state++) { if(streamsBest[state].status != DmtxStatusComplete) StreamCopy(&(streamsBest[state]), &(streamsTemp[state])); } dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeC40, &passFail, fnc1); c40ValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeText, &passFail, fnc1); textValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); dmtxByteListClear(&ctxTemp); PushCTXValues(&ctxTemp, input->b[inputNext], DmtxSchemeX12, &passFail, fnc1); x12ValueCount += ((passFail == DmtxPass) ? ctxTemp.length : 1); #if DUMPSTREAMS DumpStreams(streamsBest); #endif } /* Choose the overall winner */ winner = NULL; for(state = 0; state < SchemeStateCount; state++) { if(streamsBest[state].status == DmtxStatusComplete) { if(winner == NULL || streamsBest[state].output->length < winner->output->length) winner = &(streamsBest[state]); } } /* Copy winner to output */ if(winner == NULL) { sizeIdx = DmtxUndefined; } else { dmtxByteListCopy(output, winner->output, &passFail); sizeIdx = (passFail == DmtxPass) ? winner->sizeIdx : DmtxUndefined; } return sizeIdx; }