diff --git a/src/common/MinimalECIInput.java b/src/common/MinimalECIInput.java deleted file mode 100755 index 5955c0b..0000000 --- a/src/common/MinimalECIInput.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright 2021 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.common; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -/** - * Class that converts a character string into a sequence of ECIs and bytes - * - * The implementation uses the Dijkstra algorithm to produce minimal encodings - * - * @author Alex Geller - */ -public class MinimalECIInput implements ECIInput { - - private static final int COST_PER_ECI = 3; // approximated (latch + 2 codewords) - private final int[] bytes; - private final int fnc1; - - /** - * Constructs a minimal input - * - * @param stringToEncode the character string to encode - * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm - * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority - * charset to encode any character in the input that can be encoded by it if the charset is among the - * supported charsets. - * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not GS1 - * input. - */ - public MinimalECIInput(String stringToEncode, Charset priorityCharset, int fnc1) { - this.fnc1 = fnc1; - ECIEncoderSet encoderSet = new ECIEncoderSet(stringToEncode, priorityCharset, fnc1); - if (encoderSet.length() == 1) { //optimization for the case when all can be encoded without ECI in ISO-8859-1 - bytes = new int[stringToEncode.length()]; - for (int i = 0; i < bytes.length; i++) { - char c = stringToEncode.charAt(i); - bytes[i] = c == fnc1 ? 1000 : (int) c; - } - } else { - bytes = encodeMinimally(stringToEncode, encoderSet, fnc1); - } - } - - public int getFNC1Character() { - return fnc1; - } - - /** - * Returns the length of this input. The length is the number - * of {@code byte}s, FNC1 characters or ECIs in the sequence. - * - * @return the number of {@code char}s in this sequence - */ - public int length() { - return bytes.length; - } - - public boolean haveNCharacters(int index, int n) { - if (index + n - 1 >= bytes.length) { - return false; - } - for (int i = 0; i < n; i++) { - if (isECI(index + i)) { - return false; - } - } - return true; - } - - /** - * Returns the {@code byte} value at the specified index. An index ranges from zero - * to {@code length() - 1}. The first {@code byte} value of the sequence is at - * index zero, the next at index one, and so on, as for array - * indexing. - * - * @param index the index of the {@code byte} value to be returned - * - * @return the specified {@code byte} value as character or the FNC1 character - * - * @throws IndexOutOfBoundsException - * if the {@code index} argument is negative or not less than - * {@code length()} - * @throws IllegalArgumentException - * if the value at the {@code index} argument is an ECI (@see #isECI) - */ - public char charAt(int index) { - if (index < 0 || index >= length()) { - throw new IndexOutOfBoundsException("" + index); - } - if (isECI(index)) { - throw new IllegalArgumentException("value at " + index + " is not a character but an ECI"); - } - return isFNC1(index) ? (char) fnc1 : (char) bytes[index]; - } - - /** - * Returns a {@code CharSequence} that is a subsequence of this sequence. - * The subsequence starts with the {@code char} value at the specified index and - * ends with the {@code char} value at index {@code end - 1}. The length - * (in {@code char}s) of the - * returned sequence is {@code end - start}, so if {@code start == end} - * then an empty sequence is returned. - * - * @param start the start index, inclusive - * @param end the end index, exclusive - * - * @return the specified subsequence - * - * @throws IndexOutOfBoundsException - * if {@code start} or {@code end} are negative, - * if {@code end} is greater than {@code length()}, - * or if {@code start} is greater than {@code end} - * @throws IllegalArgumentException - * if a value in the range {@code start}-{@code end} is an ECI (@see #isECI) - */ - public CharSequence subSequence(int start, int end) { - if (start < 0 || start > end || end > length()) { - throw new IndexOutOfBoundsException("" + start); - } - StringBuilder result = new StringBuilder(); - for (int i = start; i < end; i++) { - if (isECI(i)) { - throw new IllegalArgumentException("value at " + i + " is not a character but an ECI"); - } - result.append(charAt(i)); - } - return result; - } - - /** - * Determines if a value is an ECI - * - * @param index the index of the value - * - * @return true if the value at position {@code index} is an ECI - * - * @throws IndexOutOfBoundsException - * if the {@code index} argument is negative or not less than - * {@code length()} - */ - public boolean isECI(int index) { - if (index < 0 || index >= length()) { - throw new IndexOutOfBoundsException("" + index); - } - return bytes[index] > 255 && bytes[index] <= 999; - } - - /** - * Determines if a value is the FNC1 character - * - * @param index the index of the value - * - * @return true if the value at position {@code index} is the FNC1 character - * - * @throws IndexOutOfBoundsException - * if the {@code index} argument is negative or not less than - * {@code length()} - */ - public boolean isFNC1(int index) { - if (index < 0 || index >= length()) { - throw new IndexOutOfBoundsException("" + index); - } - return bytes[index] == 1000; - } - - /** - * Returns the {@code int} ECI value at the specified index. An index ranges from zero - * to {@code length() - 1}. The first {@code byte} value of the sequence is at - * index zero, the next at index one, and so on, as for array - * indexing. - * - * @param index the index of the {@code int} value to be returned - * - * @return the specified {@code int} ECI value. - * The ECI specified the encoding of all bytes with a higher index until the - * next ECI or until the end of the input if no other ECI follows. - * - * @throws IndexOutOfBoundsException - * if the {@code index} argument is negative or not less than - * {@code length()} - * @throws IllegalArgumentException - * if the value at the {@code index} argument is not an ECI (@see #isECI) - */ - public int getECIValue(int index) { - if (index < 0 || index >= length()) { - throw new IndexOutOfBoundsException("" + index); - } - if (!isECI(index)) { - throw new IllegalArgumentException("value at " + index + " is not an ECI but a character"); - } - return bytes[index] - 256; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < length(); i++) { - if (i > 0) { - result.append(", "); - } - if (isECI(i)) { - result.append("ECI("); - result.append(getECIValue(i)); - result.append(')'); - } else if (charAt(i) < 128) { - result.append('\''); - result.append(charAt(i)); - result.append('\''); - } else { - result.append((int) charAt(i)); - } - } - return result.toString(); - } - static void addEdge(InputEdge[][] edges, int to, InputEdge edge) { - if (edges[to][edge.encoderIndex] == null || - edges[to][edge.encoderIndex].cachedTotalSize > edge.cachedTotalSize) { - edges[to][edge.encoderIndex] = edge; - } - } - - static void addEdges(String stringToEncode, - ECIEncoderSet encoderSet, - InputEdge[][] edges, - int from, - InputEdge previous, - int fnc1) { - - char ch = stringToEncode.charAt(from); - - int start = 0; - int end = encoderSet.length(); - if (encoderSet.getPriorityEncoderIndex() >= 0 && (ch == fnc1 || encoderSet.canEncode(ch, - encoderSet.getPriorityEncoderIndex()))) { - start = encoderSet.getPriorityEncoderIndex(); - end = start + 1; - } - - for (int i = start; i < end; i++) { - if (ch == fnc1 || encoderSet.canEncode(ch,i)) { - addEdge(edges, from + 1, new InputEdge(ch, encoderSet, i, previous, fnc1)); - } - } - } - - static int[] encodeMinimally(String stringToEncode, ECIEncoderSet encoderSet, int fnc1) { - int inputLength = stringToEncode.length(); - - // Array that represents vertices. There is a vertex for every character and encoding. - InputEdge[][] edges = new InputEdge[inputLength + 1][encoderSet.length()]; - addEdges(stringToEncode, encoderSet, edges, 0, null, fnc1); - - for (int i = 1; i <= inputLength; i++) { - for (int j = 0; j < encoderSet.length(); j++) { - if (edges[i][j] != null && i < inputLength) { - addEdges(stringToEncode, encoderSet, edges, i, edges[i][j], fnc1); - } - } - //optimize memory by removing edges that have been passed. - for (int j = 0; j < encoderSet.length(); j++) { - edges[i - 1][j] = null; - } - } - int minimalJ = -1; - int minimalSize = Integer.MAX_VALUE; - for (int j = 0; j < encoderSet.length(); j++) { - if (edges[inputLength][j] != null) { - InputEdge edge = edges[inputLength][j]; - if (edge.cachedTotalSize < minimalSize) { - minimalSize = edge.cachedTotalSize; - minimalJ = j; - } - } - } - if (minimalJ < 0) { - throw new RuntimeException("Internal error: failed to encode \"" + stringToEncode + "\""); - } - List intsAL = new ArrayList<>(); - InputEdge current = edges[inputLength][minimalJ]; - while (current != null) { - if (current.isFNC1()) { - intsAL.add(0, 1000); - } else { - byte[] bytes = encoderSet.encode(current.c,current.encoderIndex); - for (int i = bytes.length - 1; i >= 0; i--) { - intsAL.add(0, (bytes[i] & 0xFF)); - } - } - int previousEncoderIndex = current.previous == null ? 0 : current.previous.encoderIndex; - if (previousEncoderIndex != current.encoderIndex) { - intsAL.add(0,256 + encoderSet.getECIValue(current.encoderIndex)); - } - current = current.previous; - } - int[] ints = new int[intsAL.size()]; - for (int i = 0; i < ints.length; i++) { - ints[i] = intsAL.get(i); - } - return ints; - } - - private static final class InputEdge { - private final char c; - private final int encoderIndex; //the encoding of this edge - private final InputEdge previous; - private final int cachedTotalSize; - - private InputEdge(char c, ECIEncoderSet encoderSet, int encoderIndex, InputEdge previous, int fnc1) { - this.c = c == fnc1 ? 1000 : c; - this.encoderIndex = encoderIndex; - this.previous = previous; - - int size = this.c == 1000 ? 1 : encoderSet.encode(c, encoderIndex).length; - int previousEncoderIndex = previous == null ? 0 : previous.encoderIndex; - if (previousEncoderIndex != encoderIndex) { - size += COST_PER_ECI; - } - if (previous != null) { - size += previous.cachedTotalSize; - } - this.cachedTotalSize = size; - } - - boolean isFNC1() { - return c == 1000; - } - - } -} diff --git a/src/common/mod.rs b/src/common/mod.rs index 25670dc..fa05703 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -6,6 +6,7 @@ use std::any::Any; use std::cmp; use std::collections::HashMap; use std::fmt; +use std::rc::Rc; use crate::DecodeHintType; use crate::Exceptions; @@ -1389,7 +1390,7 @@ pub trait ECIInput { * * @return the number of {@code char}s in this sequence */ - fn length() -> usize; + fn length(&self) -> usize; /** * Returns the {@code byte} value at the specified index. An index ranges from zero @@ -1407,7 +1408,7 @@ pub trait ECIInput { * @throws IllegalArgumentException * if the value at the {@code index} argument is an ECI (@see #isECI) */ - fn charAt(index: usize) -> char; + fn charAt(&self, index: usize) -> Result; /** * Returns a {@code CharSequence} that is a subsequence of this sequence. @@ -1429,7 +1430,7 @@ pub trait ECIInput { * @throws IllegalArgumentException * if a value in the range {@code start}-{@code end} is an ECI (@see #isECI) */ - fn subSequence(start: usize, end: usize) -> Vec; + fn subSequence(&self, start: usize, end: usize) -> Result, Exceptions>; /** * Determines if a value is an ECI @@ -1442,7 +1443,7 @@ pub trait ECIInput { * if the {@code index} argument is negative or not less than * {@code length()} */ - fn isECI(index: u32) -> bool; + fn isECI(&self, index: u32) -> Result; /** * Returns the {@code int} ECI value at the specified index. An index ranges from zero @@ -1462,8 +1463,8 @@ pub trait ECIInput { * @throws IllegalArgumentException * if the value at the {@code index} argument is not an ECI (@see #isECI) */ - fn getECIValue(index: usize) -> u32; - fn haveNCharacters(index: usize, n: usize) -> bool; + fn getECIValue(&self, index: usize) -> Result; + fn haveNCharacters(&self, index: usize, n: usize) -> bool; } /* @@ -2869,7 +2870,7 @@ impl ECIEncoderSet { pub fn new( stringToEncode: &str, priorityCharset: &'static dyn encoding::Encoding, - fnc1: char, + fnc1: u16, ) -> Self { // List of encoders that potentially encode characters not in ISO-8859-1 in one byte. let mut ENCODERS = Vec::new(); @@ -2923,7 +2924,7 @@ impl ECIEncoderSet { for encoder in &neededEncoders { // for (CharsetEncoder encoder : neededEncoders) { let c = stringToEncode.chars().nth(i).unwrap(); - if c == fnc1 + if c == fnc1 as u8 as char || encoder .encode(&c.to_string(), encoding::EncoderTrap::Strict) .is_ok() @@ -3024,7 +3025,7 @@ impl ECIEncoderSet { return self.priorityEncoderIndex; } - pub fn canEncode(&self, c: char, encoderIndex: usize) -> bool { + pub fn canEncode(&self, c: u16, encoderIndex: usize) -> bool { assert!(encoderIndex < self.len()); let encoder = self.encoders[encoderIndex]; let enc_data = encoder.encode(&c.to_string(), encoding::EncoderTrap::Strict); @@ -3046,3 +3047,455 @@ impl ECIEncoderSet { encoder.encode(s, encoding::EncoderTrap::Replace).unwrap() } } + +/* + * Copyright 2021 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.common; + +// import java.nio.charset.Charset; +// import java.util.ArrayList; +// import java.util.List; + +//* approximated (latch + 2 codewords) +static COST_PER_ECI: usize = 3; + +/** + * Class that converts a character string into a sequence of ECIs and bytes + * + * The implementation uses the Dijkstra algorithm to produce minimal encodings + * + * @author Alex Geller + */ +pub struct MinimalECIInput { + bytes: Vec, + fnc1: u16, +} + +impl ECIInput for MinimalECIInput { + /** + * Returns the length of this input. The length is the number + * of {@code byte}s, FNC1 characters or ECIs in the sequence. + * + * @return the number of {@code char}s in this sequence + */ + fn length(&self) -> usize { + return self.bytes.len(); + } + + /** + * Returns the {@code byte} value at the specified index. An index ranges from zero + * to {@code length() - 1}. The first {@code byte} value of the sequence is at + * index zero, the next at index one, and so on, as for array + * indexing. + * + * @param index the index of the {@code byte} value to be returned + * + * @return the specified {@code byte} value as character or the FNC1 character + * + * @throws IndexOutOfBoundsException + * if the {@code index} argument is negative or not less than + * {@code length()} + * @throws IllegalArgumentException + * if the value at the {@code index} argument is an ECI (@see #isECI) + */ + fn charAt(&self, index: usize) -> Result { + if (index < 0 || index >= self.length()) { + return Err(Exceptions::IndexOutOfBoundsException(index.to_string())); + } + if (self.isECI(index as u32)?) { + return Err(Exceptions::IllegalArgumentException(format!( + "value at {} is not a character but an ECI", + index + ))); + } + if self.isFNC1(index)? { + Ok(self.fnc1 as u8 as char) + } else { + Ok(self.bytes[index] as u8 as char) + } + } + + /** + * Returns a {@code CharSequence} that is a subsequence of this sequence. + * The subsequence starts with the {@code char} value at the specified index and + * ends with the {@code char} value at index {@code end - 1}. The length + * (in {@code char}s) of the + * returned sequence is {@code end - start}, so if {@code start == end} + * then an empty sequence is returned. + * + * @param start the start index, inclusive + * @param end the end index, exclusive + * + * @return the specified subsequence + * + * @throws IndexOutOfBoundsException + * if {@code start} or {@code end} are negative, + * if {@code end} is greater than {@code length()}, + * or if {@code start} is greater than {@code end} + * @throws IllegalArgumentException + * if a value in the range {@code start}-{@code end} is an ECI (@see #isECI) + */ + fn subSequence(&self, start: usize, end: usize) -> Result, Exceptions> { + if start < 0 || start > end || end > self.length() { + return Err(Exceptions::IndexOutOfBoundsException(start.to_string())); + } + let mut result = String::new(); + for i in start..end { + // for (int i = start; i < end; i++) { + if self.isECI(i as u32)? { + return Err(Exceptions::IllegalArgumentException(format!( + "value at {} is not a character but an ECI", + i + ))); + } + result.push_str(&self.charAt(i)?.to_string()); + } + Ok(result.chars().collect()) + } + + /** + * Determines if a value is an ECI + * + * @param index the index of the value + * + * @return true if the value at position {@code index} is an ECI + * + * @throws IndexOutOfBoundsException + * if the {@code index} argument is negative or not less than + * {@code length()} + */ + fn isECI(&self, index: u32) -> Result { + if index < 0 || index >= self.length() as u32 { + return Err(Exceptions::IndexOutOfBoundsException(index.to_string())); + } + Ok(self.bytes[index as usize] > 255 && self.bytes[index as usize] <= u16::MAX) + } + + /** + * Returns the {@code int} ECI value at the specified index. An index ranges from zero + * to {@code length() - 1}. The first {@code byte} value of the sequence is at + * index zero, the next at index one, and so on, as for array + * indexing. + * + * @param index the index of the {@code int} value to be returned + * + * @return the specified {@code int} ECI value. + * The ECI specified the encoding of all bytes with a higher index until the + * next ECI or until the end of the input if no other ECI follows. + * + * @throws IndexOutOfBoundsException + * if the {@code index} argument is negative or not less than + * {@code length()} + * @throws IllegalArgumentException + * if the value at the {@code index} argument is not an ECI (@see #isECI) + */ + fn getECIValue(&self, index: usize) -> Result { + if index < 0 || index >= self.length() { + return Err(Exceptions::IndexOutOfBoundsException(index.to_string())); + } + if !self.isECI(index as u32)? { + return Err(Exceptions::IllegalArgumentException(format!( + "value at {} is not an ECI but a character", + index + ))); + } + Ok((self.bytes[index] as u32 - 256) as u32) + } + + fn haveNCharacters(&self, index: usize, n: usize) -> bool { + if index + n - 1 >= self.bytes.len() { + return false; + } + for i in 0..n { + // for (int i = 0; i < n; i++) { + if self.isECI(index as u32 + i as u32).unwrap() { + return false; + } + } + return true; + } +} +impl MinimalECIInput { + /** + * Constructs a minimal input + * + * @param stringToEncode the character string to encode + * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm + * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority + * charset to encode any character in the input that can be encoded by it if the charset is among the + * supported charsets. + * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not GS1 + * input. + */ + pub fn new(stringToEncode: &str, priorityCharset: &'static dyn Encoding, fnc1: u16) -> Self { + let encoderSet = ECIEncoderSet::new(stringToEncode, priorityCharset, fnc1); + let bytes = if encoderSet.len() == 1 { + //optimization for the case when all can be encoded without ECI in ISO-8859-1 + let mut bytes_hld = vec![0; stringToEncode.len()]; + for i in 0..stringToEncode.len() { + // for (int i = 0; i < bytes.length; i++) { + let c = stringToEncode.chars().nth(i).unwrap(); + bytes_hld[i] = if c as u16 == fnc1 { 1000 } else { c as u16 }; + } + bytes_hld + } else { + Self::encodeMinimally(stringToEncode, &encoderSet, fnc1) + }; + + Self { + bytes: bytes, + fnc1: fnc1, + } + } + + pub fn getFNC1Character(&self) -> u16 { + self.fnc1 + } + + /** + * Determines if a value is the FNC1 character + * + * @param index the index of the value + * + * @return true if the value at position {@code index} is the FNC1 character + * + * @throws IndexOutOfBoundsException + * if the {@code index} argument is negative or not less than + * {@code length()} + */ + pub fn isFNC1(&self, index: usize) -> Result { + if index < 0 || index >= self.length() { + return Err(Exceptions::IndexOutOfBoundsException(index.to_string())); + } + Ok(self.bytes[index] == 1000) + } + + fn addEdge(edges: &mut Vec>>>, to: usize, edge: Rc) { + if edges[to][edge.encoderIndex].is_none() + || edges[to][edge.encoderIndex].clone().unwrap().cachedTotalSize > edge.cachedTotalSize + { + edges[to][edge.encoderIndex] = Some(edge.clone()); + } + } + + fn addEdges( + stringToEncode: &str, + encoderSet: &ECIEncoderSet, + edges: &mut Vec>>>, + from: usize, + previous: Option>, + fnc1: u16, + ) { + let ch = stringToEncode.chars().nth(from).unwrap() as u16; + + let mut start = 0; + let mut end = encoderSet.len(); + if encoderSet.getPriorityEncoderIndex() >= 0 + && (ch as u16 == fnc1 || encoderSet.canEncode(ch, encoderSet.getPriorityEncoderIndex())) + { + start = encoderSet.getPriorityEncoderIndex(); + end = start + 1; + } + + for i in start..end { + // for (int i = start; i < end; i++) { + if ch as u16== fnc1 || encoderSet.canEncode(ch, i) { + Self::addEdge( + edges, + from + 1, + Rc::new(InputEdge::new(ch, encoderSet, i, previous.clone(), fnc1)), + ); + } + } + } + + pub fn encodeMinimally( + stringToEncode: &str, + encoderSet: &ECIEncoderSet, + fnc1: u16, + ) -> Vec { + let inputLength = stringToEncode.len(); + + // Array that represents vertices. There is a vertex for every character and encoding. + let mut edges = vec![vec![None; encoderSet.len()]; inputLength + 1]; //InputEdge[inputLength + 1][encoderSet.length()]; + Self::addEdges(stringToEncode, encoderSet, &mut edges, 0, None, fnc1); + + for i in 0..=inputLength { + // for (int i = 1; i <= inputLength; i++) { + for j in 0..encoderSet.len() { + // for (int j = 0; j < encoderSet.length(); j++) { + if edges[i][j].is_some() && i < inputLength { + let edg = edges[i][j].clone(); + Self::addEdges(stringToEncode, encoderSet, &mut edges, i, edg, fnc1); + } + } + //optimize memory by removing edges that have been passed. + for j in 0..encoderSet.len() { + // for (int j = 0; j < encoderSet.length(); j++) { + edges[i - 1][j] = None; + } + } + let mut minimalJ: i32 = -1; + let mut minimalSize: i32 = i32::MAX; + for j in 0..encoderSet.len() { + // for (int j = 0; j < encoderSet.length(); j++) { + if edges[inputLength][j].is_some() { + let edge = edges[inputLength][j].clone().unwrap(); + if (edge.cachedTotalSize as i32) < minimalSize { + minimalSize = edge.cachedTotalSize as i32; + minimalJ = j as i32; + } + } + } + if minimalJ < 0 { + panic!("Internal error: failed to encode \"{}\"", stringToEncode); + } + let mut intsAL:Vec = Vec::new(); + let mut current = edges[inputLength][minimalJ as usize].clone(); + while current.is_some() { + let c = current.unwrap().clone(); + if c.isFNC1() { + intsAL.splice(0..0, [1000]); + } else { + let bytes:Vec = encoderSet.encode_char(c.c as u8 as char, c.encoderIndex).iter().map(|x| *x as u16).collect(); + let mut i = bytes.len() as i32 - 1; + while i >= 0 { + // for (int i = bytes.length - 1; i >= 0; i--) { + intsAL.splice(0..0, [bytes[i as usize]]); + i = -1; + } + } + let previousEncoderIndex = if c.previous.is_none() { + 0 + } else { + c.previous.clone().unwrap().encoderIndex + }; + if previousEncoderIndex != c.encoderIndex { + intsAL.splice(0..0, [256 as u16+ encoderSet.getECIValue(c.encoderIndex) as u16]); + } + current = c.previous.clone(); + } + let mut ints = vec![0; intsAL.len()]; + for i in 0..ints.len() { + // for (int i = 0; i < ints.length; i++) { + ints[i] = *intsAL.get(i).unwrap() as u16; + } + return ints; + } +} + +struct InputEdge { + c: u16, + encoderIndex: usize, //the encoding of this edge + previous: Option>, + cachedTotalSize: usize, +} +impl InputEdge { + pub fn new( + c: u16, + encoderSet: &ECIEncoderSet, + encoderIndex: usize, + previous: Option>, + fnc1: u16, + ) -> Self { + let mut size = if c == 1000 { + 1 + } else { + encoderSet.encode_char(c as u8 as char, encoderIndex).len() + }; + + if let Some(prev) = previous { + let previousEncoderIndex = prev.encoderIndex; + if previousEncoderIndex != encoderIndex { + size += COST_PER_ECI; + } + size += prev.cachedTotalSize; + + Self { + c: if c as u16== fnc1 { 1000 } else { c as u16 }, + encoderIndex, + previous: Some(prev.clone()), + cachedTotalSize: size, + } + } else { + let previousEncoderIndex = 0; + if previousEncoderIndex != encoderIndex { + size += COST_PER_ECI; + } + + Self { + c: if c as u16 == fnc1 { 1000 } else { c as u16 }, + encoderIndex, + previous: None, + cachedTotalSize: size, + } + } + + // int size = this.c == 1000 ? 1 : encoderSet.encode(c, encoderIndex).length; + // let previousEncoderIndex = if previous.is_none() { + // 0 + // } else { + // previous.unwrap().encoderIndex + // }; + // int previousEncoderIndex = previous == null ? 0 : previous.encoderIndex; + // if previousEncoderIndex != encoderIndex { + // size += COST_PER_ECI; + // } + // if prev_is_some { + // size += previous.unwrap().cachedTotalSize; + // } + + // Self { + // c: if c == fnc1 { 1000 as char } else { c }, + // encoderIndex, + // previous: previous, + // cachedTotalSize: size, + // } + // this.c = c == fnc1 ? 1000 : c; + // this.encoderIndex = encoderIndex; + // this.previous = previous; + // this.cachedTotalSize = size; + } + + pub fn isFNC1(&self) -> bool { + self.c == 1000 + } +} + +impl fmt::Display for MinimalECIInput { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut result = String::new(); + for i in 0..self.length() { + // for (int i = 0; i < length(); i++) { + if i > 0 { + result.push_str(", "); + } + if self.isECI(i as u32).unwrap() { + result.push_str("ECI("); + result.push_str(&self.getECIValue(i).unwrap().to_string()); + result.push(')'); + } else if (self.charAt(i).unwrap() as u8) < 128 { + result.push('\''); + result.push(self.charAt(i).unwrap()); + result.push('\''); + } else { + result.push(self.charAt(i).unwrap()); + } + } + write!(f, "{}", result) + } +} diff --git a/src/exceptions.rs b/src/exceptions.rs index f194ecb..827b77a 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -12,5 +12,7 @@ pub enum Exceptions { ReaderException(String), WriterException(String), ReedSolomonException(String), + IndexOutOfBoundsException(String), + RuntimeException(String), ReaderDecodeException() } \ No newline at end of file