continued progress on aztec, no pass

This commit is contained in:
Henry Schimke
2022-09-23 17:09:46 -05:00
parent 96d42c23a6
commit fb08ee0e34
19 changed files with 2379 additions and 1624 deletions

View File

@@ -1,404 +0,0 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.aztec.encoder;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.reedsolomon.GenericGF;
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Generates Aztec 2D barcodes.
*
* @author Rustam Abdullaev
*/
public final class Encoder {
public static final int DEFAULT_EC_PERCENT = 33; // default minimal percentage of error check words
public static final int DEFAULT_AZTEC_LAYERS = 0;
private static final int MAX_NB_BITS = 32;
private static final int MAX_NB_BITS_COMPACT = 4;
private static final int[] WORD_SIZE = {
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12
};
private Encoder() {
}
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(String data) {
return encode(data.getBytes(StandardCharsets.ISO_8859_1));
}
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(String data, int minECCPercent, int userSpecifiedLayers) {
return encode(data.getBytes(StandardCharsets.ISO_8859_1), minECCPercent, userSpecifiedLayers, null);
}
/**
* Encodes the given string content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set in which to encode string using ECI; if null, no ECI code
* will be inserted, and the string must be encodable as ISO/IEC 8859-1
* (Latin-1), the default encoding of the symbol.
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(String data, int minECCPercent, int userSpecifiedLayers, Charset charset) {
byte[] bytes = data.getBytes(null != charset ? charset : StandardCharsets.ISO_8859_1);
return encode(bytes, minECCPercent, userSpecifiedLayers, charset);
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(byte[] data) {
return encode(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS, null);
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers) {
return encode(data, minECCPercent, userSpecifiedLayers, null);
}
/**
* Encodes the given binary content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set to mark using ECI; if null, no ECI code will be inserted, and the
* default encoding of ISO/IEC 8859-1 will be assuming by readers.
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers, Charset charset) {
// High-level encode
BitArray bits = new HighLevelEncoder(data, charset).encode();
// stuff bits and choose symbol size
int eccBits = bits.getSize() * minECCPercent / 100 + 11;
int totalSizeBits = bits.getSize() + eccBits;
boolean compact;
int layers;
int totalBitsInLayer;
int wordSize;
BitArray stuffedBits;
if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS) {
compact = userSpecifiedLayers < 0;
layers = Math.abs(userSpecifiedLayers);
if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS)) {
throw new IllegalArgumentException(
String.format("Illegal value %s for layers", userSpecifiedLayers));
}
totalBitsInLayer = totalBitsInLayer(layers, compact);
wordSize = WORD_SIZE[layers];
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
stuffedBits = stuffBits(bits, wordSize);
if (stuffedBits.getSize() + eccBits > usableBitsInLayers) {
throw new IllegalArgumentException("Data to large for user specified layer");
}
if (compact && stuffedBits.getSize() > wordSize * 64) {
// Compact format only allows 64 data words, though C4 can hold more words than that
throw new IllegalArgumentException("Data to large for user specified layer");
}
} else {
wordSize = 0;
stuffedBits = null;
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
// is the same size, but has more data.
for (int i = 0; ; i++) {
if (i > MAX_NB_BITS) {
throw new IllegalArgumentException("Data too large for an Aztec code");
}
compact = i <= 3;
layers = compact ? i + 1 : i;
totalBitsInLayer = totalBitsInLayer(layers, compact);
if (totalSizeBits > totalBitsInLayer) {
continue;
}
// [Re]stuff the bits if this is the first opportunity, or if the
// wordSize has changed
if (stuffedBits == null || wordSize != WORD_SIZE[layers]) {
wordSize = WORD_SIZE[layers];
stuffedBits = stuffBits(bits, wordSize);
}
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
if (compact && stuffedBits.getSize() > wordSize * 64) {
// Compact format only allows 64 data words, though C4 can hold more words than that
continue;
}
if (stuffedBits.getSize() + eccBits <= usableBitsInLayers) {
break;
}
}
}
BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize);
// generate mode message
int messageSizeInWords = stuffedBits.getSize() / wordSize;
BitArray modeMessage = generateModeMessage(compact, layers, messageSizeInWords);
// allocate symbol
int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines
int[] alignmentMap = new int[baseMatrixSize];
int matrixSize;
if (compact) {
// no alignment marks in compact mode, alignmentMap is a no-op
matrixSize = baseMatrixSize;
for (int i = 0; i < alignmentMap.length; i++) {
alignmentMap[i] = i;
}
} else {
matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
int origCenter = baseMatrixSize / 2;
int center = matrixSize / 2;
for (int i = 0; i < origCenter; i++) {
int newOffset = i + i / 15;
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
alignmentMap[origCenter + i] = center + newOffset + 1;
}
}
BitMatrix matrix = new BitMatrix(matrixSize);
// draw data bits
for (int i = 0, rowOffset = 0; i < layers; i++) {
int rowSize = (layers - i) * 4 + (compact ? 9 : 12);
for (int j = 0; j < rowSize; j++) {
int columnOffset = j * 2;
for (int k = 0; k < 2; k++) {
if (messageBits.get(rowOffset + columnOffset + k)) {
matrix.set(alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]);
}
if (messageBits.get(rowOffset + rowSize * 2 + columnOffset + k)) {
matrix.set(alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]);
}
if (messageBits.get(rowOffset + rowSize * 4 + columnOffset + k)) {
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]);
}
if (messageBits.get(rowOffset + rowSize * 6 + columnOffset + k)) {
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]);
}
}
}
rowOffset += rowSize * 8;
}
// draw mode message
drawModeMessage(matrix, compact, matrixSize, modeMessage);
// draw alignment marks
if (compact) {
drawBullsEye(matrix, matrixSize / 2, 5);
} else {
drawBullsEye(matrix, matrixSize / 2, 7);
for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {
for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
matrix.set(matrixSize / 2 - j, k);
matrix.set(matrixSize / 2 + j, k);
matrix.set(k, matrixSize / 2 - j);
matrix.set(k, matrixSize / 2 + j);
}
}
}
AztecCode aztec = new AztecCode();
aztec.setCompact(compact);
aztec.setSize(matrixSize);
aztec.setLayers(layers);
aztec.setCodeWords(messageSizeInWords);
aztec.setMatrix(matrix);
return aztec;
}
private static void drawBullsEye(BitMatrix matrix, int center, int size) {
for (int i = 0; i < size; i += 2) {
for (int j = center - i; j <= center + i; j++) {
matrix.set(j, center - i);
matrix.set(j, center + i);
matrix.set(center - i, j);
matrix.set(center + i, j);
}
}
matrix.set(center - size, center - size);
matrix.set(center - size + 1, center - size);
matrix.set(center - size, center - size + 1);
matrix.set(center + size, center - size);
matrix.set(center + size, center - size + 1);
matrix.set(center + size, center + size - 1);
}
static BitArray generateModeMessage(boolean compact, int layers, int messageSizeInWords) {
BitArray modeMessage = new BitArray();
if (compact) {
modeMessage.appendBits(layers - 1, 2);
modeMessage.appendBits(messageSizeInWords - 1, 6);
modeMessage = generateCheckWords(modeMessage, 28, 4);
} else {
modeMessage.appendBits(layers - 1, 5);
modeMessage.appendBits(messageSizeInWords - 1, 11);
modeMessage = generateCheckWords(modeMessage, 40, 4);
}
return modeMessage;
}
private static void drawModeMessage(BitMatrix matrix, boolean compact, int matrixSize, BitArray modeMessage) {
int center = matrixSize / 2;
if (compact) {
for (int i = 0; i < 7; i++) {
int offset = center - 3 + i;
if (modeMessage.get(i)) {
matrix.set(offset, center - 5);
}
if (modeMessage.get(i + 7)) {
matrix.set(center + 5, offset);
}
if (modeMessage.get(20 - i)) {
matrix.set(offset, center + 5);
}
if (modeMessage.get(27 - i)) {
matrix.set(center - 5, offset);
}
}
} else {
for (int i = 0; i < 10; i++) {
int offset = center - 5 + i + i / 5;
if (modeMessage.get(i)) {
matrix.set(offset, center - 7);
}
if (modeMessage.get(i + 10)) {
matrix.set(center + 7, offset);
}
if (modeMessage.get(29 - i)) {
matrix.set(offset, center + 7);
}
if (modeMessage.get(39 - i)) {
matrix.set(center - 7, offset);
}
}
}
}
private static BitArray generateCheckWords(BitArray bitArray, int totalBits, int wordSize) {
// bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
int messageSizeInWords = bitArray.getSize() / wordSize;
ReedSolomonEncoder rs = new ReedSolomonEncoder(getGF(wordSize));
int totalWords = totalBits / wordSize;
int[] messageWords = bitsToWords(bitArray, wordSize, totalWords);
rs.encode(messageWords, totalWords - messageSizeInWords);
int startPad = totalBits % wordSize;
BitArray messageBits = new BitArray();
messageBits.appendBits(0, startPad);
for (int messageWord : messageWords) {
messageBits.appendBits(messageWord, wordSize);
}
return messageBits;
}
private static int[] bitsToWords(BitArray stuffedBits, int wordSize, int totalWords) {
int[] message = new int[totalWords];
int i;
int n;
for (i = 0, n = stuffedBits.getSize() / wordSize; i < n; i++) {
int value = 0;
for (int j = 0; j < wordSize; j++) {
value |= stuffedBits.get(i * wordSize + j) ? (1 << wordSize - j - 1) : 0;
}
message[i] = value;
}
return message;
}
private static GenericGF getGF(int wordSize) {
switch (wordSize) {
case 4:
return GenericGF.AZTEC_PARAM;
case 6:
return GenericGF.AZTEC_DATA_6;
case 8:
return GenericGF.AZTEC_DATA_8;
case 10:
return GenericGF.AZTEC_DATA_10;
case 12:
return GenericGF.AZTEC_DATA_12;
default:
throw new IllegalArgumentException("Unsupported word size " + wordSize);
}
}
static BitArray stuffBits(BitArray bits, int wordSize) {
BitArray out = new BitArray();
int n = bits.getSize();
int mask = (1 << wordSize) - 2;
for (int i = 0; i < n; i += wordSize) {
int word = 0;
for (int j = 0; j < wordSize; j++) {
if (i + j >= n || bits.get(i + j)) {
word |= 1 << (wordSize - 1 - j);
}
}
if ((word & mask) == mask) {
out.appendBits(word & mask, wordSize);
i--;
} else if ((word & mask) == 0) {
out.appendBits(word | 1, wordSize);
i--;
} else {
out.appendBits(word, wordSize);
}
}
return out;
}
private static int totalBitsInLayer(int layers, boolean compact) {
return ((compact ? 88 : 112) + 16 * layers) * layers;
}
}

View File

@@ -30,6 +30,16 @@ pub struct AztecCode {
}
impl AztecCode {
pub fn new(compact: bool, size: u32, layers: u32, code_words: u32, matrix: BitMatrix) -> Self {
Self {
compact,
size,
layers,
code_words,
matrix,
}
}
/**
* @return {@code true} if compact instead of full mode
*/

View File

@@ -18,7 +18,7 @@ use std::fmt;
use crate::common::BitArray;
#[derive(Debug,PartialEq, Eq,Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct BinaryShiftToken {
binaryShiftStart: u32,
binaryShiftByteCount: u32,
@@ -44,7 +44,7 @@ impl BinaryShiftToken {
bitArray.appendBits(bsbc as u32 - 31, 16);
} else if (i == 0) {
// 1 <= binaryShiftByteCode <= 62
bitArray.appendBits(bsbc.min( 31) as u32, 5);
bitArray.appendBits(bsbc.min(31) as u32, 5);
} else {
// 32 <= binaryShiftCount <= 62 and i == 31
bitArray.appendBits(bsbc as u32 - 31, 5);

View File

@@ -0,0 +1,523 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use encoding::Encoding;
use crate::{
common::{
reedsolomon::{
get_predefined_genericgf, GenericGF, PredefinedGenericGF, ReedSolomonEncoder,
},
BitArray, BitMatrix,
},
exceptions::Exceptions,
};
use super::{AztecCode, HighLevelEncoder};
/**
* Generates Aztec 2D barcodes.
*
* @author Rustam Abdullaev
*/
pub const DEFAULT_EC_PERCENT: u32 = 33; // default minimal percentage of error check words
pub const DEFAULT_AZTEC_LAYERS: u32 = 0;
pub const MAX_NB_BITS: u32 = 32;
pub const MAX_NB_BITS_COMPACT: u32 = 4;
pub const WORD_SIZE: [u32; 33] = [
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12,
];
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @return Aztec symbol matrix with metadata
*/
pub fn encode_simple(data: &str) -> Result<AztecCode, Exceptions> {
let bytes = encoding::all::ISO_8859_1
.encode(data, encoding::EncoderTrap::Replace)
.unwrap();
encode_bytes_simple(&bytes)
}
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
pub fn encode(
data: &str,
minECCPercent: u32,
userSpecifiedLayers: u32,
) -> Result<AztecCode, Exceptions> {
let bytes = encoding::all::ISO_8859_1
.encode(data, encoding::EncoderTrap::Replace)
.unwrap();
encode_bytes(&bytes, minECCPercent, userSpecifiedLayers)
}
/**
* Encodes the given string content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set in which to encode string using ECI; if null, no ECI code
* will be inserted, and the string must be encodable as ISO/IEC 8859-1
* (Latin-1), the default encoding of the symbol.
* @return Aztec symbol matrix with metadata
*/
pub fn encode_with_charset(
data: &str,
minECCPercent: u32,
userSpecifiedLayers: u32,
charset: &'static dyn encoding::Encoding,
) -> Result<AztecCode, Exceptions> {
let bytes = charset
.encode(data, encoding::EncoderTrap::Replace)
.unwrap(); //data.getBytes(null != charset ? charset : StandardCharsets.ISO_8859_1);
encode_bytes_with_charset(&bytes, minECCPercent, userSpecifiedLayers, charset)
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @return Aztec symbol matrix with metadata
*/
pub fn encode_bytes_simple(data: &[u8]) -> Result<AztecCode, Exceptions> {
encode_bytes(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS)
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
pub fn encode_bytes(
data: &[u8],
minECCPercent: u32,
userSpecifiedLayers: u32,
) -> Result<AztecCode, Exceptions> {
encode_bytes_with_charset(
data,
minECCPercent,
userSpecifiedLayers,
encoding::all::ISO_8859_1,
)
}
/**
* Encodes the given binary content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set to mark using ECI; if null, no ECI code will be inserted, and the
* default encoding of ISO/IEC 8859-1 will be assuming by readers.
* @return Aztec symbol matrix with metadata
*/
pub fn encode_bytes_with_charset(
data: &[u8],
minECCPercent: u32,
userSpecifiedLayers: u32,
charset: &'static dyn encoding::Encoding,
) -> Result<AztecCode, Exceptions> {
// High-level encode
let bits = HighLevelEncoder::with_charset(data.into(), charset).encode()?;
// stuff bits and choose symbol size
let eccBits = bits.getSize() as u32 * minECCPercent / 100 + 11;
let totalSizeBits = bits.getSize() as u32 + eccBits;
let mut compact;
let mut layers;
let mut totalBitsInLayerVar;
let mut wordSize;
let mut stuffedBits;
if userSpecifiedLayers != DEFAULT_AZTEC_LAYERS {
compact = userSpecifiedLayers < 0;
layers = userSpecifiedLayers;
if layers
> (if compact {
MAX_NB_BITS_COMPACT
} else {
MAX_NB_BITS
})
{
return Err(Exceptions::IllegalArgumentException(format!(
"Illegal value {} for layers",
userSpecifiedLayers
)));
}
totalBitsInLayerVar = totalBitsInLayer(layers, compact);
wordSize = WORD_SIZE[layers as usize];
let usableBitsInLayers = totalBitsInLayerVar - (totalBitsInLayerVar % wordSize);
stuffedBits = stuffBits(&bits, wordSize as usize);
if stuffedBits.getSize() as u32+ eccBits > usableBitsInLayers {
return Err(Exceptions::IllegalArgumentException(
"Data to large for user specified layer".to_owned(),
));
}
if compact && stuffedBits.getSize() as u32 > wordSize * 64 {
// Compact format only allows 64 data words, though C4 can hold more words than that
return Err(Exceptions::IllegalArgumentException(
"Data to large for user specified layer".to_owned(),
));
}
} else {
wordSize = 0;
stuffedBits = BitArray::new();
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
// is the same size, but has more data.
let mut i = 0;
loop {
// for (int i = 0; ; i++) {
if i > MAX_NB_BITS {
return Err(Exceptions::IllegalArgumentException(
"Data too large for an Aztec code".to_owned(),
));
}
compact = i <= 3;
layers = if compact { i + 1 } else { i };
totalBitsInLayerVar = totalBitsInLayer(layers, compact);
if totalSizeBits > totalBitsInLayerVar as u32 {
continue;
}
// [Re]stuff the bits if this is the first opportunity, or if the
// wordSize has changed
if stuffedBits.getSize() == 0 || wordSize != WORD_SIZE[layers as usize] {
wordSize = WORD_SIZE[layers as usize];
stuffedBits = stuffBits(&bits, wordSize as usize);
}
let usableBitsInLayers = totalBitsInLayerVar - (totalBitsInLayerVar % wordSize);
if compact && stuffedBits.getSize() as u32 > wordSize * 64 {
// Compact format only allows 64 data words, though C4 can hold more words than that
continue;
}
if stuffedBits.getSize()as u32 + eccBits<= usableBitsInLayers {
break;
}
i += 1;
}
}
let messageBits = generateCheckWords(
&stuffedBits,
totalBitsInLayerVar as usize,
wordSize as usize,
);
// generate mode message
let messageSizeInWords = stuffedBits.getSize() as u32 / wordSize;
let modeMessage = generateModeMessage(compact, layers as u32, messageSizeInWords);
// allocate symbol
let baseMatrixSize = (if compact { 11 } else { 14 }) + layers * 4; // not including alignment lines
let mut alignmentMap = vec![0u32; baseMatrixSize as usize];
let matrixSize;
if compact {
// no alignment marks in compact mode, alignmentMap is a no-op
matrixSize = baseMatrixSize;
for i in 0..alignmentMap.len() {
// for (int i = 0; i < alignmentMap.length; i++) {
alignmentMap[i] = i as u32;
}
} else {
matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
let origCenter = (baseMatrixSize / 2) as usize;
let center = matrixSize / 2;
for i in 0..origCenter {
// for (int i = 0; i < origCenter; i++) {
let newOffset = (i + i / 15) as u32;
alignmentMap[origCenter - i - 1] = center as u32 - newOffset - 1;
alignmentMap[origCenter + i] = center as u32 + newOffset + 1;
}
}
let mut matrix = BitMatrix::with_single_dimension(matrixSize as u32);
// draw data bits
let mut rowOffset = 0;
for i in 0..layers as usize {
// for (int i = 0, rowOffset = 0; i < layers; i++) {
let rowSize = (layers as usize - i) * 4 + (if compact { 9 } else { 12 });
for j in 0..rowSize {
// for (int j = 0; j < rowSize; j++) {
let columnOffset = j * 2;
for k in 0..2 {
// for (int k = 0; k < 2; k++) {
if messageBits.get(rowOffset + columnOffset + k) {
matrix.set(alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]);
}
if messageBits.get(rowOffset + rowSize * 2 + columnOffset + k) {
matrix.set(
alignmentMap[i * 2 + j],
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - k],
);
}
if messageBits.get(rowOffset + rowSize * 4 + columnOffset + k) {
matrix.set(
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - k],
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - j],
);
}
if messageBits.get(rowOffset + rowSize * 6 + columnOffset + k) {
matrix.set(
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - j],
alignmentMap[i * 2 + k],
);
}
}
}
rowOffset += rowSize * 8;
}
// draw mode message
drawModeMessage(&mut matrix, compact, matrixSize as u32, modeMessage);
// draw alignment marks
if compact {
drawBullsEye(&mut matrix, matrixSize as u32 / 2, 5);
} else {
drawBullsEye(&mut matrix, matrixSize as u32 / 2, 7);
let mut i = 0;
let mut j = 0;
while i < baseMatrixSize / 2 - 1 {
let mut k = (matrixSize / 2) & 1;
while k < matrixSize {
// for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
matrix.set(matrixSize as u32 / 2 - j, k as u32);
matrix.set(matrixSize as u32 / 2 + j, k as u32);
matrix.set(k as u32, matrixSize as u32 / 2 - j);
matrix.set(k as u32, matrixSize as u32 / 2 + j);
k += 2;
}
i += 15;
j += 16;
}
// for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {
// for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
// matrix.set(matrixSize / 2 - j, k);
// matrix.set(matrixSize / 2 + j, k);
// matrix.set(k, matrixSize / 2 - j);
// matrix.set(k, matrixSize / 2 + j);
// }
// }
}
let aztec = AztecCode::new(
compact,
matrixSize as u32,
layers,
messageSizeInWords as u32,
matrix,
);
// aztec.setCompact(compact);
// aztec.setSize(matrixSize);
// aztec.setLayers(layers);
// aztec.setCodeWords(messageSizeInWords);
// aztec.setMatrix(matrix);
Ok(aztec)
}
fn drawBullsEye(matrix: &mut BitMatrix, center: u32, size: u32) {
let mut i = 0;
while i < size {
// for (int i = 0; i < size; i += 2) {
for j in (center - 1)..=(center + 1) {
// for (int j = center - i; j <= center + i; j++) {
matrix.set(j, center - i);
matrix.set(j, center + i);
matrix.set(center - i, j);
matrix.set(center + i, j);
}
i += 2;
}
matrix.set(center - size, center - size);
matrix.set(center - size + 1, center - size);
matrix.set(center - size, center - size + 1);
matrix.set(center + size, center - size);
matrix.set(center + size, center - size + 1);
matrix.set(center + size, center + size - 1);
}
pub fn generateModeMessage(compact: bool, layers: u32, messageSizeInWords: u32) -> BitArray {
let mut modeMessage = BitArray::new();
if compact {
modeMessage.appendBits(layers - 1, 2);
modeMessage.appendBits(messageSizeInWords - 1, 6);
modeMessage = generateCheckWords(&modeMessage, 28, 4);
} else {
modeMessage.appendBits(layers - 1, 5);
modeMessage.appendBits(messageSizeInWords - 1, 11);
modeMessage = generateCheckWords(&modeMessage, 40, 4);
}
return modeMessage;
}
fn drawModeMessage(matrix: &mut BitMatrix, compact: bool, matrixSize: u32, modeMessage: BitArray) {
let center = matrixSize / 2;
if compact {
for i in 0..7usize {
// for (int i = 0; i < 7; i++) {
let offset = (center as usize - 3 + i) as u32;
if modeMessage.get(i) {
matrix.set(offset, center - 5);
}
if modeMessage.get(i + 7) {
matrix.set(center + 5, offset);
}
if modeMessage.get(20 - i) {
matrix.set(offset, center + 5);
}
if modeMessage.get(27 - i) {
matrix.set(center - 5, offset);
}
}
} else {
for i in 0..10usize {
// for (int i = 0; i < 10; i++) {
let offset = (center as usize - 5 + i + i / 5) as u32;
if modeMessage.get(i) {
matrix.set(offset, center - 7);
}
if modeMessage.get(i + 10) {
matrix.set(center + 7, offset);
}
if modeMessage.get(29 - i) {
matrix.set(offset, center + 7);
}
if modeMessage.get(39 - i) {
matrix.set(center - 7, offset);
}
}
}
}
fn generateCheckWords(bitArray: &BitArray, totalBits: usize, wordSize: usize) -> BitArray {
// bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
let messageSizeInWords = bitArray.getSize() / wordSize;
let mut rs = ReedSolomonEncoder::new(getGF(wordSize).expect("Should never have bad value"));
let totalWords = totalBits / wordSize;
let mut messageWords = bitsToWords(bitArray, wordSize, totalWords);
rs.encode(&mut messageWords, totalWords - messageSizeInWords);
let startPad = totalBits % wordSize;
let mut messageBits = BitArray::new();
messageBits.appendBits(0, startPad);
for messageWord in messageWords {
// for (int messageWord : messageWords) {
messageBits.appendBits(messageWord as u32, wordSize);
}
return messageBits;
}
fn bitsToWords(stuffedBits: &BitArray, wordSize: usize, totalWords: usize) -> Vec<i32> {
let mut message = vec![0i32; totalWords];
// let i=0;
// let n;
for i in 0..(stuffedBits.getSize() / wordSize) {
// for (i = 0, n = stuffedBits.getSize() / wordSize; i < n; i++) {
let mut value = 0;
for j in 0..wordSize {
// for (int j = 0; j < wordSize; j++) {
value |= if stuffedBits.get(i * wordSize + j) {
1 << wordSize - j - 1
} else {
0
};
}
message[i] = value;
}
return message;
}
fn getGF(wordSize: usize) -> Result<GenericGF, Exceptions> {
match wordSize {
4 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecParam)),
6 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData6)),
8 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData8)),
10 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData10)),
12 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData12)),
_ => Err(Exceptions::IllegalArgumentException(format!(
"Unsupported word size {}",
wordSize
))),
}
// switch (wordSize) {
// case 4:
// return GenericGF.AZTEC_PARAM;
// case 6:
// return GenericGF.AZTEC_DATA_6;
// case 8:
// return GenericGF.AZTEC_DATA_8;
// case 10:
// return GenericGF.AZTEC_DATA_10;
// case 12:
// return GenericGF.AZTEC_DATA_12;
// default:
// throw new IllegalArgumentException("Unsupported word size " + wordSize);
// }
}
pub fn stuffBits(bits: &BitArray, wordSize: usize) -> BitArray {
let mut out = BitArray::new();
let n = bits.getSize();
let mask = (1 << wordSize) - 2;
let mut i = 0;
while i < n {
// for (int i = 0; i < n; i += wordSize) {
let mut word = 0;
for j in 0..wordSize {
// for (int j = 0; j < wordSize; j++) {
if i + j >= n || bits.get(i + j) {
word |= 1 << (wordSize - 1 - j);
}
}
if (word & mask) == mask {
out.appendBits(word & mask, wordSize);
i -= 1;
} else if (word & mask) == 0 {
out.appendBits(word | 1, wordSize);
i -= 1;
} else {
out.appendBits(word, wordSize);
}
i += wordSize;
}
return out;
}
fn totalBitsInLayer(layers: u32, compact: bool) -> u32 {
((if compact { 88 } else { 112 }) + 16 * layers) * layers
// return ((compact ? 88 : 112) + 16 * layers) * layers;
}

View File

@@ -14,9 +14,14 @@
* limitations under the License.
*/
use crate::common::BitArray;
use std::cmp::Ordering;
use crate::{
common::{BitArray, CharacterSetECI},
exceptions::Exceptions,
};
use super::{State, Token};
/**
* This produces nearly optimal encodings of text into the first-level of
@@ -31,342 +36,425 @@ use crate::common::BitArray;
* @author Rustam Abdullaev
*/
pub struct HighLevelEncoder {
text:Vec<u8>,
charset: &'static dyn encoding::Encoding,
text: Vec<u8>,
charset: &'static dyn encoding::Encoding,
}
impl HighLevelEncoder {
pub const MODE_NAMES : [&str] = ["UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"];
pub const MODE_NAMES: [&'static str; 5] = ["UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"];
const MODE_UPPER :u32= 0; // 5 bits
const MODE_LOWER :u32 = 1; // 5 bits
const MODE_DIGIT :u32 = 2; // 4 bits
const MODE_MIXED :u32 = 3; // 5 bits
const MODE_PUNCT :u32 = 4; // 5 bits
pub const MODE_UPPER: usize = 0; // 5 bits
pub const MODE_LOWER: usize = 1; // 5 bits
pub const MODE_DIGIT: usize = 2; // 4 bits
pub const MODE_MIXED: usize = 3; // 5 bits
pub const MODE_PUNCT: usize = 4; // 5 bits
// The Latch Table shows, for each pair of Modes, the optimal method for
// getting from one mode to another. In the worst possible case, this can
// be up to 14 bits. In the best possible case, we are already there!
// The high half-word of each entry gives the number of bits.
// The low half-word of each entry are the actual bits necessary to change
const LATCH_TABLE : [[u32]]= [
[
0,
(5 << 16) + 28, // UPPER -> LOWER
(5 << 16) + 30, // UPPER -> DIGIT
(5 << 16) + 29, // UPPER -> MIXED
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
],
[
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
0,
(5 << 16) + 30, // LOWER -> DIGIT
(5 << 16) + 29, // LOWER -> MIXED
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
],
[
(4 << 16) + 14, // DIGIT -> UPPER
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
0,
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
(14 << 16) + (14 << 10) + (29 << 5) + 30,
] // DIGIT -> UPPER -> MIXED -> PUNCT
,
[
(5 << 16) + 29, // MIXED -> UPPER
(5 << 16) + 28, // MIXED -> LOWER
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
0,
(5 << 16) + 30, // MIXED -> PUNCT
],
[
(5 << 16) + 31, // PUNCT -> UPPER
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
0,
],
];
// A reverse mapping from [mode][char] to the encoding for that character
// in that mode. An entry of 0 indicates no mapping exists.
const CHAR_MAP : [[u32]] = {
let char_map = vec![vec![0u32;256];5];
char_map[Self::MODE_UPPER][' '] = 1;
for (int c = 'A'; c <= 'Z'; c++) {
char_map[Self::MODE_UPPER][c] = c - 'A' + 2;
}
char_map[Self::MODE_LOWER][' '] = 1;
for (int c = 'a'; c <= 'z'; c++) {
char_map[Self::MODE_LOWER][c] = c - 'a' + 2;
}
char_map[Self::MODE_DIGIT][' '] = 1;
for (int c = '0'; c <= '9'; c++) {
char_map[Self::MODE_DIGIT][c] = c - '0' + 2;
}
char_map[Self::MODE_DIGIT][','] = 12;
char_map[Self::MODE_DIGIT]['.'] = 13;
let mixedTable = [
'\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
'\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
'_', '`', '|', '~', '\177'
// The Latch Table shows, for each pair of Modes, the optimal method for
// getting from one mode to another. In the worst possible case, this can
// be up to 14 bits. In the best possible case, we are already there!
// The high half-word of each entry gives the number of bits.
// The low half-word of each entry are the actual bits necessary to change
pub const LATCH_TABLE: [[u32; 5]; 5] = [
[
0,
(5 << 16) + 28, // UPPER -> LOWER
(5 << 16) + 30, // UPPER -> DIGIT
(5 << 16) + 29, // UPPER -> MIXED
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
],
[
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
0,
(5 << 16) + 30, // LOWER -> DIGIT
(5 << 16) + 29, // LOWER -> MIXED
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
],
[
(4 << 16) + 14, // DIGIT -> UPPER
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
0,
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
(14 << 16) + (14 << 10) + (29 << 5) + 30,
], // DIGIT -> UPPER -> MIXED -> PUNCT
[
(5 << 16) + 29, // MIXED -> UPPER
(5 << 16) + 28, // MIXED -> LOWER
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
0,
(5 << 16) + 30, // MIXED -> PUNCT
],
[
(5 << 16) + 31, // PUNCT -> UPPER
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
0,
],
];
for (int i = 0; i < mixedTable.length; i++) {
CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
}
let punctTable = [
'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
'[', ']', '{', '}'
];
for (int i = 0; i < punctTable.length; i++) {
if (punctTable[i] > 0) {
CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
}
}
};
// private static final int[][] CHAR_MAP = new int[5][256];
// static {
// CHAR_MAP[MODE_UPPER][' '] = 1;
// for (int c = 'A'; c <= 'Z'; c++) {
// CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
// }
// CHAR_MAP[MODE_LOWER][' '] = 1;
// for (int c = 'a'; c <= 'z'; c++) {
// CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
// }
// CHAR_MAP[MODE_DIGIT][' '] = 1;
// for (int c = '0'; c <= '9'; c++) {
// CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
// }
// CHAR_MAP[MODE_DIGIT][','] = 12;
// CHAR_MAP[MODE_DIGIT]['.'] = 13;
// int[] mixedTable = {
// '\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
// '\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
// '_', '`', '|', '~', '\177'
// };
// for (int i = 0; i < mixedTable.length; i++) {
// CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
// }
// int[] punctTable = {
// '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
// '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
// '[', ']', '{', '}'
// };
// for (int i = 0; i < punctTable.length; i++) {
// if (punctTable[i] > 0) {
// CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
// }
// }
// }
// A map showing the available shift codes. (The shifts to BINARY are not
// shown
const SHIFT_TABLE : [[i32]]= { // mode shift codes, per table
let mut shift_table = [[-1i32;6];6];
shift_table[Self::MODE_UPPER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_UPPER] = 28;
shift_table[Self::MODE_MIXED][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_UPPER] = 15;
shift_table
};
// const SHIFT_TABLE : [[u32]]= new int[6][6]; // mode shift codes, per table
// static {
// for (int[] table : SHIFT_TABLE) {
// Arrays.fill(table, -1);
// }
// SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
// SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
// }
pub fn new(text:Vec<u8>) -> Self{
Self{
text,
charset: encoding::all::UTF_8,
}
}
pub fn with_charset(text:Vec<u8>, charset:&'static dyn encoding::Encoding) -> Self{
Self{
text,
charset,
}
}
/**
* @return text represented by this encoder encoded as a {@link BitArray}
*/
pub fn encode(&self)-> BitArray {
State initialState = State.INITIAL_STATE;
if (charset != null) {
CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(charset);
if (null == eci) {
throw new IllegalArgumentException("No ECI code for character set " + charset);
}
initialState = initialState.appendFLGn(eci.getValue());
}
Collection<State> states = Collections.singletonList(initialState);
for (int index = 0; index < text.length; index++) {
int pairCode;
int nextChar = index + 1 < text.length ? text[index + 1] : 0;
switch (text[index]) {
case '\r':
pairCode = nextChar == '\n' ? 2 : 0;
break;
case '.' :
pairCode = nextChar == ' ' ? 3 : 0;
break;
case ',' :
pairCode = nextChar == ' ' ? 4 : 0;
break;
case ':' :
pairCode = nextChar == ' ' ? 5 : 0;
break;
default:
pairCode = 0;
}
if (pairCode > 0) {
// We have one of the four special PUNCT pairs. Treat them specially.
// Get a new set of states for the two new characters.
states = updateStateListForPair(states, index, pairCode);
index++;
} else {
// Get a new set of states for the new character.
states = updateStateListForChar(states, index);
}
}
// We are left with a set of states. Find the shortest one.
State minState = Collections.min(states, new Comparator<State>() {
@Override
public int compare(State a, State b) {
return a.getBitCount() - b.getBitCount();
}
});
// Convert it to a bit array, and return.
return minState.toBitArray(text);
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
private Collection<State> updateStateListForChar(Iterable<State> states, int index) {
Collection<State> result = new LinkedList<>();
for (State state : states) {
updateStateForChar(state, index, result);
}
return simplifyStates(result);
}
// Return a set of states that represent the possible ways of updating this
// state for the next character. The resulting set of states are added to
// the "result" list.
private void updateStateForChar(State state, int index, Collection<State> result) {
char ch = (char) (text[index] & 0xFF);
boolean charInCurrentTable = CHAR_MAP[state.getMode()][ch] > 0;
State stateNoBinary = null;
for (int mode = 0; mode <= MODE_PUNCT; mode++) {
int charInMode = CHAR_MAP[mode][ch];
if (charInMode > 0) {
if (stateNoBinary == null) {
// Only create stateNoBinary the first time it's required.
stateNoBinary = state.endBinaryShift(index);
// A reverse mapping from [mode][char] to the encoding for that character
// in that mode. An entry of 0 indicates no mapping exists.
pub const CHAR_MAP: [[u8; 256]; 5] = {
let mut char_map = [[0u8; 256]; 5];
char_map[Self::MODE_UPPER][b' ' as usize] = 1;
let mut c = b'A';
while c <= b'Z' {
char_map[Self::MODE_UPPER][c as usize] = c - b'A' + 2;
c += 1;
}
// Try generating the character by latching to its mode
if (!charInCurrentTable || mode == state.getMode() || mode == MODE_DIGIT) {
// If the character is in the current table, we don't want to latch to
// any other mode except possibly digit (which uses only 4 bits). Any
// other latch would be equally successful *after* this character, and
// so wouldn't save any bits.
State latchState = stateNoBinary.latchAndAppend(mode, charInMode);
result.add(latchState);
// for (int c = 'A'; c <= 'Z'; c++) {
// char_map[Self::MODE_UPPER][c] = c - 'A' + 2;
// }
char_map[Self::MODE_LOWER][b' ' as usize] = 1;
let mut c = b'a';
while c <= b'z' {
char_map[Self::MODE_LOWER][c as usize] = c - b'a' + 2;
c += 1;
}
// Try generating the character by switching to its mode.
if (!charInCurrentTable && SHIFT_TABLE[state.getMode()][mode] >= 0) {
// It never makes sense to temporarily shift to another mode if the
// character exists in the current mode. That can never save bits.
State shiftState = stateNoBinary.shiftAndAppend(mode, charInMode);
result.add(shiftState);
// for (int c = 'a'; c <= 'z'; c++) {
// char_map[Self::MODE_LOWER][c] = c - 'a' + 2;
// }
char_map[Self::MODE_DIGIT][b' ' as usize] = 1;
let mut c = b'0';
while c <= b'9' {
char_map[Self::MODE_DIGIT][c as usize] = c - b'0' + 2;
c += 1;
}
}
}
if (state.getBinaryShiftByteCount() > 0 || CHAR_MAP[state.getMode()][ch] == 0) {
// It's never worthwhile to go into binary shift mode if you're not already
// in binary shift mode, and the character exists in your current mode.
// That can never save bits over just outputting the char in the current mode.
State binaryState = state.addBinaryShiftChar(index);
result.add(binaryState);
}
}
private static Collection<State> updateStateListForPair(Iterable<State> states, int index, int pairCode) {
Collection<State> result = new LinkedList<>();
for (State state : states) {
updateStateForPair(state, index, pairCode, result);
}
return simplifyStates(result);
}
private static void updateStateForPair(State state, int index, int pairCode, Collection<State> result) {
State stateNoBinary = state.endBinaryShift(index);
// Possibility 1. Latch to MODE_PUNCT, and then append this code
result.add(stateNoBinary.latchAndAppend(MODE_PUNCT, pairCode));
if (state.getMode() != MODE_PUNCT) {
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
// Every state except MODE_PUNCT (handled above) can shift
result.add(stateNoBinary.shiftAndAppend(MODE_PUNCT, pairCode));
}
if (pairCode == 3 || pairCode == 4) {
// both characters are in DIGITS. Sometimes better to just add two digits
State digitState = stateNoBinary
.latchAndAppend(MODE_DIGIT, 16 - pairCode) // period or comma in DIGIT
.latchAndAppend(MODE_DIGIT, 1); // space in DIGIT
result.add(digitState);
}
if (state.getBinaryShiftByteCount() > 0) {
// It only makes sense to do the characters as binary if we're already
// in binary mode.
State binaryState = state.addBinaryShiftChar(index).addBinaryShiftChar(index + 1);
result.add(binaryState);
}
}
private static Collection<State> simplifyStates(Iterable<State> states) {
Deque<State> result = new LinkedList<>();
for (State newState : states) {
boolean add = true;
for (Iterator<State> iterator = result.iterator(); iterator.hasNext();) {
State oldState = iterator.next();
if (oldState.isBetterThanOrEqualTo(newState)) {
add = false;
break;
// for (int c = '0'; c <= '9'; c++) {
// char_map[Self::MODE_DIGIT][c] = c - '0' + 2;
// }
char_map[Self::MODE_DIGIT][b',' as usize] = 12;
char_map[Self::MODE_DIGIT][b'.' as usize] = 13;
let mixedTable = [
'\0', ' ', '\u{1}', '\u{2}', '\u{3}', '\u{4}', '\u{5}', '\u{6}', '\u{7}', '\u{8}',
'\t', '\n', '\u{13}', '\u{f}', '\r', '\u{33}', '\u{34}', '\u{35}', '\u{36}', '\u{37}',
'@', '\\', '^', '_', '`', '|', '~', '\u{177}',
];
let mut i = 0;
while i < mixedTable.len() {
char_map[Self::MODE_MIXED][mixedTable[i] as u8 as usize] = i as u8;
i += 1;
}
if (newState.isBetterThanOrEqualTo(oldState)) {
iterator.remove();
// for (int i = 0; i < mixedTable.length; i++) {
// CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
// }
let punctTable = [
'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'', '(', ')', '*',
'+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', ']', '{', '}',
];
let mut i = 0;
while i < punctTable.len() {
if punctTable[i] as u8 > 0u8 {
char_map[Self::MODE_PUNCT][punctTable[i] as u8 as usize] = i as u8;
}
i += 1;
}
}
if (add) {
result.addFirst(newState);
}
}
return result;
}
// for (int i = 0; i < punctTable.length; i++) {
// if (punctTable[i] > 0) {
// CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
// }
// }
char_map
};
// private static final int[][] CHAR_MAP = new int[5][256];
// static {
// CHAR_MAP[MODE_UPPER][' '] = 1;
// for (int c = 'A'; c <= 'Z'; c++) {
// CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
// }
// CHAR_MAP[MODE_LOWER][' '] = 1;
// for (int c = 'a'; c <= 'z'; c++) {
// CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
// }
// CHAR_MAP[MODE_DIGIT][' '] = 1;
// for (int c = '0'; c <= '9'; c++) {
// CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
// }
// CHAR_MAP[MODE_DIGIT][','] = 12;
// CHAR_MAP[MODE_DIGIT]['.'] = 13;
// int[] mixedTable = {
// '\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
// '\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
// '_', '`', '|', '~', '\177'
// };
// for (int i = 0; i < mixedTable.length; i++) {
// CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
// }
// int[] punctTable = {
// '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
// '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
// '[', ']', '{', '}'
// };
// for (int i = 0; i < punctTable.length; i++) {
// if (punctTable[i] > 0) {
// CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
// }
// }
// }
// A map showing the available shift codes. (The shifts to BINARY are not
// shown
pub const SHIFT_TABLE: [[i32; 6]; 6] = {
// mode shift codes, per table
let mut shift_table = [[-1i32; 6]; 6];
shift_table[Self::MODE_UPPER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_UPPER] = 28;
shift_table[Self::MODE_MIXED][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_UPPER] = 15;
shift_table
};
// const SHIFT_TABLE : [[u32]]= new int[6][6]; // mode shift codes, per table
// static {
// for (int[] table : SHIFT_TABLE) {
// Arrays.fill(table, -1);
// }
// SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
// SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
// }
pub fn new(text: Vec<u8>) -> Self {
Self {
text,
charset: encoding::all::UTF_8,
}
}
pub fn with_charset(text: Vec<u8>, charset: &'static dyn encoding::Encoding) -> Self {
Self { text, charset }
}
/**
* @return text represented by this encoder encoded as a {@link BitArray}
*/
pub fn encode(&self) -> Result<BitArray, Exceptions> {
let mut initialState = State::new(Token::new(), Self::MODE_UPPER as u32, 0, 0);
if let Some(eci) = CharacterSetECI::getCharacterSetECI(self.charset) {
initialState = initialState.appendFLGn(CharacterSetECI::getValue(&eci))?;
} else {
return Err(Exceptions::IllegalArgumentException(
"No ECI code for character set".to_owned(),
));
}
// if self.charset != null {
// CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(charset);
// if (null == eci) {
// throw new IllegalArgumentException("No ECI code for character set " + charset);
// }
// initialState = initialState.appendFLGn(eci.getValue());
// }
let mut states = vec![initialState];
let mut index = 0;
while index < self.text.len() {
// for index in 0..self.text.len() {
// for (int index = 0; index < text.length; index++) {
let pairCode;
let nextChar = if index + 1 < self.text.len() {
self.text[index + 1]
} else {
0
};
pairCode = match self.text[index] {
b'\r' if nextChar == b'\n' => 2,
b'.' if nextChar == b' ' => 3,
b',' if nextChar == b' ' => 4,
b':' if nextChar == b' ' => 5,
_ => 0,
};
// switch (text[index]) {
// case '\r':
// pairCode = nextChar == '\n' ? 2 : 0;
// break;
// case '.' :
// pairCode = nextChar == ' ' ? 3 : 0;
// break;
// case ',' :
// pairCode = nextChar == ' ' ? 4 : 0;
// break;
// case ':' :
// pairCode = nextChar == ' ' ? 5 : 0;
// break;
// default:
// pairCode = 0;
// }
if pairCode > 0 {
// We have one of the four special PUNCT pairs. Treat them specially.
// Get a new set of states for the two new characters.
states = Self::updateStateListForPair(states, index as u32, pairCode);
index += 1;
} else {
// Get a new set of states for the new character.
states = self.updateStateListForChar(states, index as u32);
}
index += 1;
}
// We are left with a set of states. Find the shortest one.
let minState = states
.into_iter()
.min_by(|a, b| {
let diff: i64 = a.getBitCount() as i64 - b.getBitCount() as i64;
if diff < 0 {
Ordering::Less
} else if diff == 0 {
Ordering::Equal
} else {
Ordering::Greater
}
// a.getBitCount() - b.getBitCount()
})
.unwrap();
// let minState = Collections.min(states, new Comparator<State>() {
// @Override
// public int compare(State a, State b) {
// return a.getBitCount() - b.getBitCount();
// }
// });
// Convert it to a bit array, and return.
Ok(minState.toBitArray(&self.text))
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
fn updateStateListForChar(&self, states: Vec<State>, index: u32) -> Vec<State> {
let mut result = Vec::new();
for state in states {
// for (State state : states) {
self.updateStateForChar(state, index, &mut result);
}
Self::simplifyStates(result)
}
// Return a set of states that represent the possible ways of updating this
// state for the next character. The resulting set of states are added to
// the "result" list.
fn updateStateForChar(&self, state: State, index: u32, result: &mut Vec<State>) {
let ch = self.text[index as usize];
let charInCurrentTable = Self::CHAR_MAP[state.getMode() as usize][ch as usize] > 0;
let mut stateNoBinary = None;
for mode in 0..Self::MODE_PUNCT {
// for (int mode = 0; mode <= MODE_PUNCT; mode++) {
let charInMode = Self::CHAR_MAP[mode as usize][ch as usize];
if charInMode > 0 {
if stateNoBinary.is_none() {
// Only create stateNoBinary the first time it's required.
stateNoBinary = Some(state.clone().endBinaryShift(index));
}
// Try generating the character by latching to its mode
if !charInCurrentTable || mode as u32 == state.getMode() || mode == Self::MODE_DIGIT
{
// If the character is in the current table, we don't want to latch to
// any other mode except possibly digit (which uses only 4 bits). Any
// other latch would be equally successful *after* this character, and
// so wouldn't save any bits.
let latchState = stateNoBinary
.clone()
.unwrap()
.latchAndAppend(mode as u32, charInMode as u32);
result.push(latchState);
}
// Try generating the character by switching to its mode.
if !charInCurrentTable && Self::SHIFT_TABLE[state.getMode() as usize][mode] >= 0 {
// It never makes sense to temporarily shift to another mode if the
// character exists in the current mode. That can never save bits.
let shiftState = stateNoBinary
.clone()
.unwrap()
.shiftAndAppend(mode as u32, charInMode as u32);
result.push(shiftState);
}
}
}
if state.getBinaryShiftByteCount() > 0
|| Self::CHAR_MAP[state.getMode() as usize][ch as usize] == 0
{
// It's never worthwhile to go into binary shift mode if you're not already
// in binary shift mode, and the character exists in your current mode.
// That can never save bits over just outputting the char in the current mode.
let binaryState = state.addBinaryShiftChar(index);
result.push(binaryState);
}
}
fn updateStateListForPair(states: Vec<State>, index: u32, pairCode: u32) -> Vec<State> {
let mut result = Vec::new();
for state in states {
// for (State state : states) {
Self::updateStateForPair(state, index, pairCode, &mut result);
}
Self::simplifyStates(result)
}
fn updateStateForPair(state: State, index: u32, pairCode: u32, result: &mut Vec<State>) {
let stateNoBinary = state.clone().endBinaryShift(index);
// Possibility 1. Latch to MODE_PUNCT, and then append this code
result.push(
stateNoBinary
.clone()
.latchAndAppend(Self::MODE_PUNCT as u32, pairCode),
);
if state.getMode() != Self::MODE_PUNCT as u32 {
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
// Every state except MODE_PUNCT (handled above) can shift
result.push(
stateNoBinary
.clone()
.shiftAndAppend(Self::MODE_PUNCT as u32, pairCode),
);
}
if pairCode == 3 || pairCode == 4 {
// both characters are in DIGITS. Sometimes better to just add two digits
let digitState = stateNoBinary
.latchAndAppend(Self::MODE_DIGIT as u32, 16 - pairCode) // period or comma in DIGIT
.latchAndAppend(Self::MODE_DIGIT as u32, 1); // space in DIGIT
result.push(digitState);
}
if state.getBinaryShiftByteCount() > 0 {
// It only makes sense to do the characters as binary if we're already
// in binary mode.
let binaryState = state
.addBinaryShiftChar(index)
.addBinaryShiftChar(index + 1);
result.push(binaryState);
}
}
fn simplifyStates(states: Vec<State>) -> Vec<State> {
let mut result: Vec<State> = Vec::new();
for newState in states {
// for (State newState : states) {
let mut add = true;
for i in 0..result.len() {
// for st in result {
// for (Iterator<State> iterator = result.iterator(); iterator.hasNext();) {
let oldState = result.get(i).unwrap();
if oldState.isBetterThanOrEqualTo(&newState) {
add = false;
break;
}
if newState.isBetterThanOrEqualTo(&oldState) {
result.remove(i);
}
}
if add {
result.push(newState);
}
}
return result;
}
}

View File

@@ -5,6 +5,8 @@ mod binary_shift_token;
mod state;
mod high_level_encoder;
pub mod encoder;
pub use aztec_code::*;
pub use token::*;
pub use simple_token::*;

View File

@@ -18,7 +18,7 @@ use std::fmt;
use crate::common::BitArray;
#[derive(Debug,PartialEq, Eq,Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SimpleToken {
// For normal words, indicates value and bitCount
value: u16,
@@ -34,7 +34,9 @@ impl SimpleToken {
}
pub fn appendTo(&self, bitArray: &mut BitArray, text: &[u8]) {
bitArray.appendBits(self.value as u32, self.bitCount as usize).expect("append should never fail");
bitArray
.appendBits(self.value as u32, self.bitCount as usize)
.expect("append should never fail");
}
// @Override

View File

@@ -16,197 +16,241 @@
use std::fmt;
use crate::{exceptions::Exceptions, common::BitArray};
use encoding::Encoding;
use super::{Token, TokenType};
use crate::{common::BitArray, exceptions::Exceptions};
use super::{HighLevelEncoder, Token};
/**
* State represents all information about a sequence necessary to generate the current output.
* Note that a state is immutable.
*/
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct State {
// static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);
// static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);
// The current mode of the encoding (or the mode to which we'll return if
// we're in Binary Shift mode.
mode:u32,
// The list of tokens that we output. If we are in Binary Shift mode, this
// token list does *not* yet included the token for those bytes
token:Token,
// If non-zero, the number of most recent bytes that should be output
// in Binary Shift mode.
binaryShiftByteCount:u32,
// The total number of bits generated (including Binary Shift).
bitCount:u32,
binaryShiftCost:u32,
// The current mode of the encoding (or the mode to which we'll return if
// we're in Binary Shift mode.
mode: u32,
// The list of tokens that we output. If we are in Binary Shift mode, this
// token list does *not* yet included the token for those bytes
token: Token,
// If non-zero, the number of most recent bytes that should be output
// in Binary Shift mode.
binaryShiftByteCount: u32,
// The total number of bits generated (including Binary Shift).
bitCount: u32,
binaryShiftCost: u32,
}
impl State {
pub fn new( token:Token, mode:u32, binaryBytes:u32, bitCount:u32) -> Self{
Self{
mode,
token,
binaryShiftByteCount: binaryBytes,
bitCount,
binaryShiftCost: Self::calculateBinaryShiftCost(binaryBytes),
impl State {
pub fn new(token: Token, mode: u32, binaryBytes: u32, bitCount: u32) -> Self {
Self {
mode,
token,
binaryShiftByteCount: binaryBytes,
bitCount,
binaryShiftCost: Self::calculateBinaryShiftCost(binaryBytes),
}
}
}
pub fn getMode(&self) -> u32{
self.mode
}
pub fn getToken(&self) -> &Token{
&self.token
}
pub fn getBinaryShiftByteCount(&self) -> u32{
self.binaryShiftByteCount
}
pub fn getBitCount(&self) -> u32{
self.bitCount
}
pub fn appendFLGn(&self, eci:u32) -> Result<Self,Exceptions> {
let result = self.shiftAndAppend(HighLevelEncoder::MODE_PUNCT, 0); // 0: FLG(n)
let token = result.token;
let bitsAdded = 3;
if eci < 0 {
token.add(0, 3); // 0: FNC1
} else if eci > 999999 {
return Err(Exceptions::IllegalArgumentException("ECI code must be between 0 and 999999".to_owned()));
// throw new IllegalArgumentException("ECI code must be between 0 and 999999");
} else {
let eciDigits = Integer.toString(eci).getBytes(StandardCharsets.ISO_8859_1);
token.add(eciDigits.length, 3); // 1-6: number of ECI digits
for eciDigit in eciDigits {
// for (byte eciDigit : eciDigits) {
token.add(eciDigit - '0' + 2, 4);
}
bitsAdded += eciDigits.length * 4;
pub fn getMode(&self) -> u32 {
self.mode
}
Ok(State::new(token, self.mode, 0, self.bitCount + bitsAdded))
// return new State(token, mode, 0, bitCount + bitsAdded);
}
// Create a new state representing this state with a latch to a (not
// necessary different) mode, and then a code.
pub fn latchAndAppend(&self, mode:u32, value:u32) -> State{
let bitCount = self.bitCount;
let token = self.token;
if mode != self.mode {
let latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode];
token.add(latch & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
pub fn getToken(&self) -> &Token {
&self.token
}
let latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
token.add(value, latchModeBitCount);
State::new(token, mode, 0, bitCount + latchModeBitCount)
}
// Create a new state representing this state, with a temporary shift
// to a different mode to output a single value.
pub fn shiftAndAppend(&self, mode:u32, value:u32) -> State{
let token = this.token;
let thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount);
token = token.add(value, 5);
State::new(token, this.mode, 0, this.bitCount + thisModeBitCount + 5)
}
pub fn getBinaryShiftByteCount(&self) -> u32 {
self.binaryShiftByteCount
}
// Create a new state representing this state, but an additional character
// output in Binary Shift mode.
pub fn addBinaryShiftChar(&self, index:u32) -> State{
let token = this.token;
let mode = this.mode;
let bitCount = this.bitCount;
if self.mode == HighLevelEncoder.MODE_PUNCT || self.mode == HighLevelEncoder.MODE_DIGIT {
let latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER];
token.add(latch & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
mode = HighLevelEncoder.MODE_UPPER;
pub fn getBitCount(&self) -> u32 {
self.bitCount
}
let deltaBitCount =
(binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 :
(binaryShiftByteCount == 62) ? 9 : 8;
let result = State::new(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount);
if (result.binaryShiftByteCount == 2047 + 31) {
// The string is as long as it's allowed to be. We should end it.
result = result.endBinaryShift(index + 1);
}
result
}
// Create the state identical to this one, but we are no longer in
// Binary Shift mode.
pub fn endBinaryShift(self, index:u32) -> State{
if self.binaryShiftByteCount == 0 {
return self;
pub fn appendFLGn(self, eci: u32) -> Result<Self, Exceptions> {
let bit_count = self.bitCount;
let mode = self.mode;
let result = self.shiftAndAppend(HighLevelEncoder::MODE_PUNCT as u32, 0); // 0: FLG(n)
let mut token = result.token;
let mut bitsAdded = 3;
if eci < 0 {
token.add(0, 3); // 0: FNC1
} else if eci > 999999 {
return Err(Exceptions::IllegalArgumentException(
"ECI code must be between 0 and 999999".to_owned(),
));
// throw new IllegalArgumentException("ECI code must be between 0 and 999999");
} else {
let eciDigits = encoding::all::ISO_8859_1
.encode(&format!("{}", eci), encoding::EncoderTrap::Replace)
.unwrap();
// let eciDigits = Integer.toString(eci).getBytes(StandardCharsets.ISO_8859_1);
token.add(eciDigits.len() as i32, 3); // 1-6: number of ECI digits
for eciDigit in &eciDigits {
// for (byte eciDigit : eciDigits) {
token.add((eciDigit - b'0' + 2) as i32, 4);
}
bitsAdded += eciDigits.len() * 4;
}
Ok(State::new(token, mode, 0, bit_count + bitsAdded as u32))
// return new State(token, mode, 0, bitCount + bitsAdded);
}
let token = self.token;
self.token.addBinaryShift(index - self.binaryShiftByteCount, self.binaryShiftByteCount);
State::new(token, self.mode, 0, self.bitCount)
}
// Returns true if "this" state is better (or equal) to be in than "that"
// state under all possible circumstances.
pub fn isBetterThanOrEqualTo(&self, other:&State)->bool {
let newModeBitCount = self.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16);
if self.binaryShiftByteCount < other.binaryShiftByteCount {
// add additional B/S encoding cost of other, if any
newModeBitCount += other.binaryShiftCost - self.binaryShiftCost;
} else if self.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0 {
// maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)
newModeBitCount += 10;
}
newModeBitCount <= other.bitCount
}
// Create a new state representing this state with a latch to a (not
// necessary different) mode, and then a code.
pub fn latchAndAppend(self, mode: u32, value: u32) -> State {
let mut bitCount = self.bitCount;
let mut token = self.token;
if mode != self.mode {
let latch = HighLevelEncoder::LATCH_TABLE[self.mode as usize][mode as usize];
token.add(latch as i32 & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
}
let latchModeBitCount = if mode == HighLevelEncoder::MODE_DIGIT as u32 {
4
} else {
5
};
token.add(value as i32, latchModeBitCount);
pub fn toBitArray(&self, text:&[u8]) -> BitArray{
let symbols = Vec::new();
let mut tok = self.endBinaryShift(text.len() as u32).token;
let mut tkn = tok.getPrevious();
while tkn != &TokenType::Empty {
// for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
symbols.push(tkn);
tkn = tok.getPrevious();
State::new(token, mode, 0, bitCount + latchModeBitCount)
}
let bitArray = BitArray::new();
// Add each token to the result in forward order
for i in (0..symbols.len()-1).rev() {
// for (int i = symbols.size() - 1; i >= 0; i--) {
symbols.get(i).unwrap().appendTo(bitArray, text);
}
bitArray
}
// @Override
// public String toString() {
// return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
// }
// Create a new state representing this state, with a temporary shift
// to a different mode to output a single value.
pub fn shiftAndAppend(self, mode: u32, value: u32) -> State {
let mut token = self.token;
let thisModeBitCount = if self.mode == HighLevelEncoder::MODE_DIGIT as u32 {
4
} else {
5
};
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
token.add(
HighLevelEncoder::SHIFT_TABLE[self.mode as usize][mode as usize],
thisModeBitCount,
);
token.add(value as i32, 5);
State::new(token, self.mode, 0, self.bitCount + thisModeBitCount + 5)
}
fn calculateBinaryShiftCost( binaryShiftByteCount:u32) -> u32{
if binaryShiftByteCount > 62 {
return 21; // B/S with extended length
// Create a new state representing this state, but an additional character
// output in Binary Shift mode.
pub fn addBinaryShiftChar(self, index: u32) -> State {
let mut token = self.token;
let mut mode = self.mode;
let mut bitCount = self.bitCount;
if self.mode == HighLevelEncoder::MODE_PUNCT as u32
|| self.mode == HighLevelEncoder::MODE_DIGIT as u32
{
let latch = HighLevelEncoder::LATCH_TABLE[mode as usize][HighLevelEncoder::MODE_UPPER];
token.add(latch as i32 & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
mode = HighLevelEncoder::MODE_UPPER as u32;
}
let deltaBitCount = if self.binaryShiftByteCount == 0 || self.binaryShiftByteCount == 31 {
18
} else {
if self.binaryShiftByteCount == 62 {
9
} else {
8
}
};
let mut result = State::new(
token,
mode,
self.binaryShiftByteCount + 1,
bitCount + deltaBitCount,
);
if result.binaryShiftByteCount == 2047 + 31 {
// The string is as long as it's allowed to be. We should end it.
result = result.endBinaryShift(index + 1);
}
result
}
if binaryShiftByteCount > 31 {
return 20; // two B/S
}
if binaryShiftByteCount > 0 {
return 10; // one B/S
}
return 0;
}
// Create the state identical to this one, but we are no longer in
// Binary Shift mode.
pub fn endBinaryShift(self, index: u32) -> State {
if self.binaryShiftByteCount == 0 {
return self;
}
let mut token = self.token;
token.addBinaryShift(index - self.binaryShiftByteCount, self.binaryShiftByteCount);
State::new(token, self.mode, 0, self.bitCount)
}
// Returns true if "this" state is better (or equal) to be in than "that"
// state under all possible circumstances.
pub fn isBetterThanOrEqualTo(&self, other: &State) -> bool {
let mut newModeBitCount = self.bitCount
+ (HighLevelEncoder::LATCH_TABLE[self.mode as usize][other.mode as usize] >> 16);
if self.binaryShiftByteCount < other.binaryShiftByteCount {
// add additional B/S encoding cost of other, if any
newModeBitCount += other.binaryShiftCost - self.binaryShiftCost;
} else if self.binaryShiftByteCount > other.binaryShiftByteCount
&& other.binaryShiftByteCount > 0
{
// maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)
newModeBitCount += 10;
}
newModeBitCount <= other.bitCount
}
pub fn toBitArray(self, text: &[u8]) -> BitArray {
let mut symbols = Vec::new();
let tok = self.endBinaryShift(text.len() as u32).token;
for tkn in tok.into_iter() {
// for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
symbols.push(tkn);
}
// let mut tkn = tok.getPrevious();
// while tkn != &TokenType::Empty {
// // for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
// symbols.push(tkn);
// tkn = tok.getPrevious();
// }
let mut bitArray = BitArray::new();
// Add each token to the result in forward order
for i in (0..symbols.len() - 1).rev() {
// for (int i = symbols.size() - 1; i >= 0; i--) {
symbols.get(i).unwrap().appendTo(&mut bitArray, text);
}
bitArray
}
// @Override
// public String toString() {
// return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
// }
fn calculateBinaryShiftCost(binaryShiftByteCount: u32) -> u32 {
if binaryShiftByteCount > 62 {
return 21; // B/S with extended length
}
if binaryShiftByteCount > 31 {
return 20; // two B/S
}
if binaryShiftByteCount > 0 {
return 10; // one B/S
}
return 0;
}
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,"{} bits={} bytes={}", HighLevelEncoder::MODE_NAMES[mode], self.bitCount, self.binaryShiftByteCount)
write!(
f,
"{} bits={} bytes={}",
HighLevelEncoder::MODE_NAMES[self.mode as usize],
self.bitCount,
self.binaryShiftByteCount
)
}
}
}

View File

@@ -18,39 +18,83 @@ use std::rc::Rc;
use crate::common::BitArray;
use super::{SimpleToken, BinaryShiftToken};
use super::{BinaryShiftToken, SimpleToken};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TokenType {
Simple(SimpleToken), BinaryShift(BinaryShiftToken), Empty
Simple(SimpleToken),
BinaryShift(BinaryShiftToken),
Empty,
}
impl TokenType {
pub fn appendTo(&self, bit_array: &mut BitArray, text: &[u8]) {
// let token = &self.tokens[self.current_pointer];
match self {
TokenType::Simple(a) => a.appendTo(bit_array, text),
TokenType::BinaryShift(a) => a.appendTo(bit_array, text),
TokenType::Empty => panic!("cannot appendTo on Empty final item"),
}
}
}
#[derive(Debug,Clone,PartialEq, Eq)]
pub struct Token {
tokens: Vec<TokenType>,
current_pointer: usize,
tokens: Vec<TokenType>,
//current_pointer: usize,
}
impl Token {
pub fn new () -> Self {
Self {
tokens: vec![TokenType::Empty],
current_pointer: 0,
pub fn new() -> Self {
Self {
tokens: Vec::new(),
//current_pointer: 0,
}
}
// pub fn getPrevious(&mut self) -> &TokenType {
// self.current_pointer -= 1;
// &self.tokens[self.current_pointer]
// }
pub fn add(&mut self, value: i32, bit_count: u32) {
//self.current_pointer += 1;
self.tokens
.push(TokenType::Simple(SimpleToken::new(value, bit_count)));
// &self.tokens[self.current_pointer]
}
pub fn addBinaryShift(&mut self, start: u32, byte_count: u32) {
//self.current_pointer += 1;
self.tokens
.push(TokenType::BinaryShift(BinaryShiftToken::new(
start, byte_count,
)));
// &self.tokens[self.current_pointer]
}
}
pub struct TokenIter {
src: Vec<TokenType>,
// pos: usize,
}
impl Iterator for TokenIter {
type Item = TokenType;
fn next(&mut self) -> Option<Self::Item> {
self.src.pop()
}
}
impl IntoIterator for Token {
type Item = TokenType;
type IntoIter = TokenIter;
fn into_iter(self) -> Self::IntoIter {
TokenIter {
src: self.tokens,
// pos: self.current_pointer,
}
}
}
pub fn getPrevious(&mut self) -> &TokenType{
self.current_pointer -= 1;
&self.tokens[self.current_pointer]
}
pub fn add(&mut self, value:i32, bit_count: u32,) -> &TokenType {
self.current_pointer+=1;
self.tokens.push(TokenType::Simple(SimpleToken::new(value,bit_count)));
&self.tokens[self.current_pointer]
}
pub fn addBinaryShift(&mut self, start: u32, byte_count: u32) -> &TokenType {
self.current_pointer+=1;
self.tokens.push(TokenType::BinaryShift(BinaryShiftToken::new(start,byte_count)));
&self.tokens[self.current_pointer]
}
}
// pub enum Token{