Wrote a new ByteArray class to replace StringPiece and fixed all uses of it. Also converted all uses of stl::string (which was being used as vector<unsigned char>) to ByteArray. Everything in the Encoder but the Reed Solomon related code compiles now.

ByteArray could certainly move up to the common package, although it currently has a dependency on BitVector. We'll have to figure out what to do with the latter first.

This is the first set of changes I've made which are error prone. They involved a lot of pointer conversion, signed/unsigned semantics, etc. These diffs may have clues for later bugs.

git-svn-id: https://zxing.googlecode.com/svn/trunk@705 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
dswitkin 2008-11-14 21:51:26 +00:00
parent fabdd40edf
commit aea12b5286
5 changed files with 236 additions and 159 deletions

View file

@ -95,15 +95,6 @@ public final class BitVector {
} }
} }
// Append "bytes".
//
// JAVAPORT: Uncomment and implement when a substitute for StringPiece is chosen.
// public void AppendBytes(final StringPiece stringPiece) {
// for (int i = 0; i < stringPiece.size(); ++i) {
// AppendBits(stringPiece[i], 8);
// }
// }
// Append "bits". // Append "bits".
public void AppendBitVector(final BitVector bits) { public void AppendBitVector(final BitVector bits) {
int size = bits.size(); int size = bits.size();

View file

@ -0,0 +1,91 @@
/*
* Copyright 2008 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.qrcode.encoder;
/**
* This class implements an array of unsigned bytes.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ByteArray {
private static final int INITIAL_SIZE = 32;
private byte[] bytes;
private int size;
public ByteArray() {
bytes = null;
size = 0;
}
public ByteArray(String string) {
bytes = string.getBytes();
size = bytes.length;
}
public ByteArray(byte[] byteArray) {
bytes = byteArray;
size = bytes.length;
}
/**
* Access an unsigned byte at location index.
* @param index The index in the array to access.
* @return The unsigned value of the byte as an int.
*/
public int at(int index) {
return bytes[index] & 0xff;
}
public int size() {
return size;
}
public boolean empty() {
return size == 0;
}
public void appendByte(int value) {
if (size == 0 || size >= bytes.length) {
int newSize = Math.max(INITIAL_SIZE, size * 2);
reserve(newSize);
}
bytes[size] = (byte) value;
size++;
}
public void reserve(int capacity) {
if (bytes == null || bytes.length < capacity) {
byte[] newArray = new byte[capacity];
if (bytes != null) {
System.arraycopy(bytes, 0, newArray, 0, bytes.length);
}
bytes = newArray;
}
}
// This could probably be generalized to take a byte[] instead of a BitVector.
public void set(BitVector bits, int offset, int count) {
bytes = new byte[count];
size = count;
for (int x = 0; x < count; x++) {
bytes[x] = (byte) bits.at(x + offset);
}
}
}

View file

@ -16,8 +16,9 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import java.util.Vector;
// class GF_Poly; // class GF_Poly;
// #include "strings/stringpiece.h"
// #include "util/reedsolomon/galois_field.h" // #include "util/reedsolomon/galois_field.h"
// #include "util/reedsolomon/galois_poly.h" // #include "util/reedsolomon/galois_poly.h"
@ -280,18 +281,35 @@ private static final ECPolyInfo kECPolynomials[] = {
private static final int kFieldSize = 8; private static final int kFieldSize = 8;
private static GF_Poly[] g_ec_polynomials = new GF_Poly[kMaxNumECBytes + 1]; private static GF_Poly[] g_ec_polynomials = new GF_Poly[kMaxNumECBytes + 1];
// Encode "bytes" with the error correction level "ec_level". The private static final class BlockPair {
// encoding mode will be chosen internally by ChooseMode().
// On success, store the result in "qr_code" and return true. On private ByteArray dataBytes;
// error, return false. We recommend you to use QRCode.EC_LEVEL_L private ByteArray errorCorrectionBytes;
// (the lowest level) for "ec_level" since our primary use is to
// show QR code on desktop screens. We don't need very strong error public BlockPair(ByteArray data, ByteArray errorCorrection) {
// correction for this purpose. dataBytes = data;
errorCorrectionBytes = errorCorrection;
}
public ByteArray getDataBytes() {
return dataBytes;
}
public ByteArray getErrorCorrectionBytes() {
return errorCorrectionBytes;
}
}
// Encode "bytes" with the error correction level "ec_level". The encoding mode will be chosen
// internally by ChooseMode(). On success, store the result in "qr_code" and return true. On
// error, return false. We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
// "ec_level" since our primary use is to show QR code on desktop screens. We don't need very
// strong error correction for this purpose.
// //
// Note that there is no way to encode bytes in MODE_KANJI. We might // Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
// want to add EncodeWithMode() with which clients can specify the // with which clients can specify the encoding mode. For now, we don't need the functionality.
// encoding mode. For now, we don't need the functionality. public static boolean Encode(final ByteArray bytes, int ec_level, QRCode qr_code) {
public static boolean Encode(final StringPiece bytes, int ec_level, QRCode qr_code) {
// Step 1: Choose the mode (encoding). // Step 1: Choose the mode (encoding).
final int mode = ChooseMode(bytes); final int mode = ChooseMode(bytes);
@ -311,8 +329,7 @@ private static final ECPolyInfo kECPolynomials[] = {
if (!AppendModeInfo(qr_code.mode(), header_and_data_bits)) { if (!AppendModeInfo(qr_code.mode(), header_and_data_bits)) {
return false; return false;
} }
if (!AppendLengthInfo(bytes.size(), qr_code.version(), qr_code.mode(), if (!AppendLengthInfo(bytes.size(), qr_code.version(), qr_code.mode(), header_and_data_bits)) {
header_and_data_bits)) {
return false; return false;
} }
header_and_data_bits.AppendBitVector(data_bits); header_and_data_bits.AppendBitVector(data_bits);
@ -324,17 +341,12 @@ private static final ECPolyInfo kECPolynomials[] = {
// Step 6: Interleave data bits with error correction code. // Step 6: Interleave data bits with error correction code.
BitVector final_bits = new BitVector(); BitVector final_bits = new BitVector();
InterleaveWithECBytes(header_and_data_bits, InterleaveWithECBytes(header_and_data_bits, qr_code.num_total_bytes(), qr_code.num_data_bytes(),
qr_code.num_total_bytes(), qr_code.num_rs_blocks(), final_bits);
qr_code.num_data_bytes(),
qr_code.num_rs_blocks(),
final_bits);
// Step 7: Choose the mask pattern and set to "qr_code". // Step 7: Choose the mask pattern and set to "qr_code".
Matrix matrix = new Matrix(qr_code.matrix_width(), qr_code.matrix_width()); Matrix matrix = new Matrix(qr_code.matrix_width(), qr_code.matrix_width());
qr_code.set_mask_pattern(ChooseMaskPattern(final_bits, qr_code.set_mask_pattern(ChooseMaskPattern(final_bits, qr_code.ec_level(), qr_code.version(),
qr_code.ec_level(),
qr_code.version(),
matrix)); matrix));
if (qr_code.mask_pattern() == -1) { if (qr_code.mask_pattern() == -1) {
// There was an error. // There was an error.
@ -342,14 +354,12 @@ private static final ECPolyInfo kECPolynomials[] = {
} }
// Step 8. Build the matrix and set it to "qr_code". // Step 8. Build the matrix and set it to "qr_code".
MatrixUtil.BuildMatrix(final_bits, MatrixUtil.BuildMatrix(final_bits, qr_code.ec_level(), qr_code.version(),
qr_code.ec_level(),
qr_code.version(),
qr_code.mask_pattern(), matrix); qr_code.mask_pattern(), matrix);
qr_code.set_matrix(matrix); qr_code.set_matrix(matrix);
// Step 9. Make sure we have a valid QR Code. // Step 9. Make sure we have a valid QR Code.
if (!qr_code.IsValid()) { if (!qr_code.IsValid()) {
Debug.LOG_ERROR("Invalid QR code: " + qr_code.DebugString()); Debug.LOG_ERROR("Invalid QR code: " + qr_code.toString());
return false; return false;
} }
return true; return true;
@ -364,26 +374,27 @@ private static final ECPolyInfo kECPolynomials[] = {
return -1; return -1;
} }
// Choose the best mode from the content of "bytes". // Choose the best mode by examining the content of "bytes". The function is guaranteed to return
// The function is guaranteed to return valid mode. // a valid mode.
// //
// Note that the function does not return MODE_KANJI, as we cannot // Note that this function does not return MODE_KANJI, as we cannot distinguish Shift_JIS from
// distinguish Shift_JIS from other encodings such as ISO-8859-1, from // other encodings such as ISO-8859-1, from data bytes alone. For example "\xE0\xE0" can be
// data bytes alone. For example "\xE0\xE0" can be interpreted as one // interpreted as one character in Shift_JIS, but also two characters in ISO-8859-1.
// character in Shift_JIS, but also two characters in ISO-8859-1. //
public static int ChooseMode(final StringPiece bytes) { // JAVAPORT: This MODE_KANJI limitation sounds like a problem for us.
public static int ChooseMode(final ByteArray bytes) {
boolean has_numeric = false; boolean has_numeric = false;
boolean has_alphanumeric = false; boolean has_alphanumeric = false;
boolean has_other = false; boolean has_other = false;
for (int i = 0; i < bytes.size(); ++i) { for (int i = 0; i < bytes.size(); ++i) {
final int byte = bytes[i]; final int oneByte = bytes.at(i);
if (byte >= '0' && byte <= '9') { if (oneByte >= '0' && oneByte <= '9') {
has_numeric = true; has_numeric = true;
} else if (GetAlphanumericCode(byte) != -1) { } else if (GetAlphanumericCode(oneByte) != -1) {
has_alphanumeric = true; has_alphanumeric = true;
} else { } else {
has_other = true; has_other = true;
} }
} }
if (has_other) { if (has_other) {
return QRCode.MODE_8BIT_BYTE; return QRCode.MODE_8BIT_BYTE;
@ -430,9 +441,9 @@ private static final ECPolyInfo kECPolynomials[] = {
qr_code.set_mode(mode); qr_code.set_mode(mode);
if (!QRCode.IsValidECLevel(ec_level)) { if (!QRCode.IsValidECLevel(ec_level)) {
Debug.LOG_ERROR("Invalid EC level: " + ec_level); Debug.LOG_ERROR("Invalid EC level: " + ec_level);
return false; return false;
} }
// In the following comments, we use numbers of Version 7-H. // In the following comments, we use numbers of Version 7-H.
for (int i = 0; i < kRSBlockTable.length; ++i) { for (int i = 0; i < kRSBlockTable.length; ++i) {
@ -445,10 +456,9 @@ private static final ECPolyInfo kECPolynomials[] = {
final int num_rs_blocks = row.block_info[ec_level][1]; final int num_rs_blocks = row.block_info[ec_level][1];
// num_data_bytes = 196 - 130 = 66 // num_data_bytes = 196 - 130 = 66
final int num_data_bytes = num_bytes - num_ec_bytes; final int num_data_bytes = num_bytes - num_ec_bytes;
// We want to choose the smallest version which can contain data // We want to choose the smallest version which can contain data of "num_input_bytes" + some
// of "num_input_bytes" + some extra bits for the header (mode // extra bits for the header (mode info and length info). The header can be three bytes
// info and length info). The header can be three bytes // (precisely 4 + 16 bits) at most. Hence we do +3 here.
// (precisely 4 + 16 bits) at most. Hence we do +3 here.
if (num_data_bytes >= num_input_bytes + 3) { if (num_data_bytes >= num_input_bytes + 3) {
// Yay, we found the proper rs block info! // Yay, we found the proper rs block info!
qr_code.set_version(i + 1); qr_code.set_version(i + 1);
@ -546,50 +556,40 @@ private static final ECPolyInfo kECPolynomials[] = {
} }
} }
// Interleave "bits" with corresponding error correction bytes. On // Interleave "bits" with corresponding error correction bytes. On success, store the result in
// success, store the result in "result" and return true. On error, // "result" and return true. On error, return false. The interleave rule is complicated. See 8.6
// return false. // of JISX0510:2004 (p.37) for details.
// The interleave rule is complicated. See 8.6 of JISX0510:2004 static boolean InterleaveWithECBytes(final BitVector bits, int num_total_bytes,
// (p.37) for details. int num_data_bytes, int num_rs_blocks, BitVector result) {
static boolean InterleaveWithECBytes(final BitVector bits,
int num_total_bytes,
int num_data_bytes,
int num_rc_blocks,
BitVector result) {
// "bits" must have "num_data_bytes" bytes of data. // "bits" must have "num_data_bytes" bytes of data.
Debug.DCHECK(bits.num_bytes() == num_data_bytes); Debug.DCHECK(bits.num_bytes() == num_data_bytes);
// Step 1. Divide data bytes into blocks and generate error // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
// correction bytes for them. We'll store the divided data bytes // store the divided data bytes blocks and error correction bytes blocks into "blocks".
// blocks and error correction bytes blocks into "blocks".
typedef pair<StringPiece, String> BlockPair;
int data_bytes_offset = 0; int data_bytes_offset = 0;
// JAVAPORT: This is not a String, it's really a byte[] int max_num_data_bytes = 0;
final String &encoded_bytes = bits.ToString(); int max_num_ec_bytes = 0;
int max_num_data_bytes = 0; // StringPiece's size is "int".
size_t max_num_ec_bytes = 0; // STL String's size is "size_t". // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
vector<BlockPair> blocks; Vector<BlockPair> blocks = new Vector<BlockPair>(num_rs_blocks);
// Since, we know the number of reedsolmon blocks, we can initialize
// the vector with the number.
blocks.resize(num_rs_blocks);
for (int i = 0; i < num_rs_blocks; ++i) { for (int i = 0; i < num_rs_blocks; ++i) {
int num_data_bytes_in_block, num_ec_bytes_in_block; Integer num_data_bytes_in_block = new Integer(0);
Integer num_ec_bytes_in_block = new Integer(0);
GetNumDataBytesAndNumECBytesForBlockID( GetNumDataBytesAndNumECBytesForBlockID(
num_total_bytes, num_data_bytes, num_rs_blocks, i, num_total_bytes, num_data_bytes, num_rs_blocks, i,
&num_data_bytes_in_block, &num_ec_bytes_in_block); num_data_bytes_in_block, num_ec_bytes_in_block);
// We modify the objects in the vector instead of copying new
// objects to the vector. In particular, we want to avoid String
// copies.
StringPiece *data_bytes = &(blocks[i].first);
String *ec_bytes = &(blocks[i].second);
data_bytes.set(encoded_bytes.data() + data_bytes_offset, ByteArray data_bytes = new ByteArray();
num_data_bytes_in_block); ByteArray ec_bytes = new ByteArray();
GenerateECBytes(*data_bytes, num_ec_bytes_in_block, ec_bytes); blocks.addElement(new BlockPair(data_bytes, ec_bytes));
max_num_data_bytes = max(max_num_data_bytes, data_bytes.size()); data_bytes.set(bits, data_bytes_offset, num_data_bytes_in_block);
max_num_ec_bytes = max(max_num_ec_bytes, ec_bytes.size()); GenerateECBytes(data_bytes, num_ec_bytes_in_block, ec_bytes);
max_num_data_bytes = Math.max(max_num_data_bytes, data_bytes.size());
max_num_ec_bytes = Math.max(max_num_ec_bytes, ec_bytes.size());
data_bytes_offset += num_data_bytes_in_block; data_bytes_offset += num_data_bytes_in_block;
} }
Debug.DCHECK_EQ(num_data_bytes, data_bytes_offset); Debug.DCHECK_EQ(num_data_bytes, data_bytes_offset);
@ -597,18 +597,18 @@ private static final ECPolyInfo kECPolynomials[] = {
// First, place data blocks. // First, place data blocks.
for (int i = 0; i < max_num_data_bytes; ++i) { for (int i = 0; i < max_num_data_bytes; ++i) {
for (int j = 0; j < blocks.size(); ++j) { for (int j = 0; j < blocks.size(); ++j) {
final StringPiece &data_bytes = blocks[j].first; final ByteArray data_bytes = blocks.elementAt(j).getDataBytes();
if (i < data_bytes.size()) { if (i < data_bytes.size()) {
result.AppendBits(data_bytes[i], 8); result.AppendBits(data_bytes.at(i), 8);
} }
} }
} }
// Then, place error correction blocks. // Then, place error correction blocks.
for (int i = 0; i < max_num_ec_bytes; ++i) { for (int i = 0; i < max_num_ec_bytes; ++i) {
for (int j = 0; j < blocks.size(); ++j) { for (int j = 0; j < blocks.size(); ++j) {
final String &ec_bytes = blocks[j].second; final ByteArray ec_bytes = blocks.elementAt(j).getErrorCorrectionBytes();
if (i < ec_bytes.size()) { if (i < ec_bytes.size()) {
result.AppendBits(ec_bytes[i], 8); result.AppendBits(ec_bytes.at(i), 8);
} }
} }
} }
@ -620,8 +620,8 @@ private static final ECPolyInfo kECPolynomials[] = {
return false; return false;
} }
// Append mode info. On success, store the result in "bits" and // Append mode info. On success, store the result in "bits" and return true. On error, return
// return true. On error, return false. // false.
static boolean AppendModeInfo(int mode, BitVector bits) { static boolean AppendModeInfo(int mode, BitVector bits) {
final int code = QRCode.GetModeCode(mode); final int code = QRCode.GetModeCode(mode);
if (code == -1) { if (code == -1) {
@ -633,8 +633,8 @@ private static final ECPolyInfo kECPolynomials[] = {
} }
// Append length info. On success, store the result in "bits" and // Append length info. On success, store the result in "bits" and return true. On error, return
// return true. On error, return false. // false.
static boolean AppendLengthInfo(int num_bytes, int version, int mode, BitVector bits) { static boolean AppendLengthInfo(int num_bytes, int version, int mode, BitVector bits) {
int num_letters = num_bytes; int num_letters = num_bytes;
// In Kanji mode, a letter is represented in two bytes. // In Kanji mode, a letter is represented in two bytes.
@ -658,16 +658,16 @@ private static final ECPolyInfo kECPolynomials[] = {
// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits" // Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits"
// and return true. On error, return false. // and return true. On error, return false.
static boolean AppendBytes(final StringPiece bytes, int mode, BitVector bits) { static boolean AppendBytes(final ByteArray bytes, int mode, BitVector bits) {
switch (mode) { switch (mode) {
case QRCode.MODE_NUMERIC: case QRCode.MODE_NUMERIC:
return AppendNumericBytes(bytes, bits); return AppendNumericBytes(bytes, bits);
case QRCode.MODE_ALPHANUMERIC: case QRCode.MODE_ALPHANUMERIC:
return AppendAlphanumericBytes(bytes, bits); return AppendAlphanumericBytes(bytes, bits);
case QRCode.MODE_8BIT_BYTE: case QRCode.MODE_8BIT_BYTE:
return Append8BitBytes(bytes, bits); return Append8BitBytes(bytes, bits);
case QRCode.MODE_KANJI: case QRCode.MODE_KANJI:
return AppendKanjiBytes(bytes, bits); return AppendKanjiBytes(bytes, bits);
default: default:
break; break;
} }
@ -677,24 +677,25 @@ private static final ECPolyInfo kECPolynomials[] = {
// Append "bytes" to "bits" using QRCode.MODE_NUMERIC mode. On success, store the result in "bits" // Append "bytes" to "bits" using QRCode.MODE_NUMERIC mode. On success, store the result in "bits"
// and return true. On error, return false. // and return true. On error, return false.
static boolean AppendNumericBytes(final StringPiece bytes, BitVector bits) { static boolean AppendNumericBytes(final ByteArray bytes, BitVector bits) {
// Validate all the bytes first. // Validate all the bytes first.
for (int i = 0; i < bytes.size(); ++i) { for (int i = 0; i < bytes.size(); ++i) {
if (!isdigit(bytes[i])) { int oneByte = bytes.at(i);
if (oneByte < '0' || oneByte > '9') {
return false; return false;
} }
} }
for (int i = 0; i < bytes.size();) { for (int i = 0; i < bytes.size();) {
final int num1 = bytes[i] - '0'; final int num1 = bytes.at(i) - '0';
if (i + 2 < bytes.size()) { if (i + 2 < bytes.size()) {
// Encode three numeric letters in ten bits. // Encode three numeric letters in ten bits.
final int num2 = bytes[i + 1] - '0'; final int num2 = bytes.at(i + 1) - '0';
final int num3 = bytes[i + 2] - '0'; final int num3 = bytes.at(i + 2) - '0';
bits.AppendBits(num1 * 100 + num2 * 10 + num3, 10); bits.AppendBits(num1 * 100 + num2 * 10 + num3, 10);
i += 3; i += 3;
} else if (i + 1 < bytes.size()) { } else if (i + 1 < bytes.size()) {
// Encode two numeric letters in seven bits. // Encode two numeric letters in seven bits.
final int num2 = bytes[i + 1] - '0'; final int num2 = bytes.at(i + 1) - '0';
bits.AppendBits(num1 * 10 + num2, 7); bits.AppendBits(num1 * 10 + num2, 7);
i += 2; i += 2;
} else { } else {
@ -706,17 +707,16 @@ private static final ECPolyInfo kECPolynomials[] = {
return true; return true;
} }
// Append "bytes" to "bits" using QRCode.MODE_ALPHANUMERIC mode. // Append "bytes" to "bits" using QRCode.MODE_ALPHANUMERIC mode. On success, store the result in
// On success, store the result in "bits" and return true. On error, // "bits" and return true. On error, return false.
// return false. static boolean AppendAlphanumericBytes(final ByteArray bytes, BitVector bits) {
static boolean AppendAlphanumericBytes(final StringPiece bytes, BitVector bits) {
for (int i = 0; i < bytes.size();) { for (int i = 0; i < bytes.size();) {
final int code1 = GetAlphanumericCode(bytes[i]); final int code1 = GetAlphanumericCode(bytes.at(i));
if (code1 == -1) { if (code1 == -1) {
return false; return false;
} }
if (i + 1 < bytes.size()) { if (i + 1 < bytes.size()) {
final int code2 = GetAlphanumericCode(bytes[i + 1]); final int code2 = GetAlphanumericCode(bytes.at(i + 1));
if (code2 == -1) { if (code2 == -1) {
return false; return false;
} }
@ -732,28 +732,26 @@ private static final ECPolyInfo kECPolynomials[] = {
return true; return true;
} }
// Append "bytes" to "bits" using QRCode.MODE_8BIT_BYTE mode. // Append "bytes" to "bits" using QRCode.MODE_8BIT_BYTE mode. On success, store the result in
// On success, store the result in "bits" and return true. On error, // "bits" and return true. On error, return false.
// return false. static boolean Append8BitBytes(final ByteArray bytes, BitVector bits) {
static boolean Append8BitBytes(final StringPiece bytes, BitVector bits) {
for (int i = 0; i < bytes.size(); ++i) { for (int i = 0; i < bytes.size(); ++i) {
bits.AppendBits(bytes[i], 8); bits.AppendBits(bytes.at(i), 8);
} }
return true; return true;
} }
// Append "bytes" to "bits" using QRCode.MODE_KANJI mode. // Append "bytes" to "bits" using QRCode.MODE_KANJI mode. On success, store the result in "bits"
// On success, store the result in "bits" and return true. On error, // and return true. On error, return false. See 8.4.5 of JISX0510:2004 (p.21) for how to encode
// return false. // Kanji bytes.
// See 8.4.5 of JISX0510:2004 (p.21) for how to encode Kanji bytes. static boolean AppendKanjiBytes(final ByteArray bytes, BitVector bits) {
static boolean AppendKanjiBytes(final StringPiece bytes, BitVector bits) {
if (bytes.size() % 2 != 0) { if (bytes.size() % 2 != 0) {
Debug.LOG_ERROR("Invalid byte sequence: " + bytes); Debug.LOG_ERROR("Invalid byte sequence: " + bytes);
return false; return false;
} }
for (int i = 0; i < bytes.size(); i += 2) { for (int i = 0; i < bytes.size(); i += 2) {
Debug.DCHECK(IsValidKanji(bytes[i], bytes[i + 1])); Debug.DCHECK(IsValidKanji(bytes.at(i), bytes.at(i + 1)));
final int code = (static_cast<int>(bytes[i]) << 8 | bytes[i + 1]); final int code = (bytes.at(i) << 8) | bytes.at(i + 1);
int subtracted = -1; int subtracted = -1;
if (code >= 0x8140 && code <= 0x9ffc) { if (code >= 0x8140 && code <= 0x9ffc) {
subtracted = code - 0x8140; subtracted = code - 0x8140;
@ -777,32 +775,31 @@ private static final ECPolyInfo kECPolynomials[] = {
// Initialize "g_ec_polynomials" with numbers in kECPolynomials. // Initialize "g_ec_polynomials" with numbers in kECPolynomials.
private static void InitECPolynomials() { private static void InitECPolynomials() {
final GaloisField &field = GaloisField.GetField(kFieldSize); final GaloisField field = GaloisField.GetField(kFieldSize);
for (int i = 0; i < arraysize(kECPolynomials); ++i) { for (int i = 0; i < kECPolynomials.length; ++i) {
final ECPolyInfo& ec_poly_info = kECPolynomials[i]; final ECPolyInfo ec_poly_info = kECPolynomials[i];
final int ec_length = ec_poly_info.ec_length; final int ec_length = ec_poly_info.ec_length;
vector<GF_Element> *coeffs = new vector<GF_Element>; vector<GF_Element> *coeffs = new vector<GF_Element>;
// The number of coefficients is one more than "ec_length". // The number of coefficients is one more than "ec_length". That's why the termination
// That's why the termination condition is <= instead of <. // condition is <= instead of <.
for (int j = 0; j <= ec_length; ++j) { for (int j = 0; j <= ec_length; ++j) {
// We need exp'ed numbers for later use. // We need exp'ed numbers for later use.
final int coeff = field.Exp(ec_poly_info.coeffs[j]); final int coeff = field.Exp(ec_poly_info.coeffs[j]);
coeffs.push_back(coeff); coeffs.push_back(coeff);
} }
// Reverse the coefficients since the numbers in kECPolynomials // Reverse the coefficients since the numbers in kECPolynomials are ordered in reverse order
// are ordered in reverse order to the order GF_Poly expects. // to the order GF_Poly expects.
reverse(coeffs.begin(), coeffs.end()); reverse(coeffs.begin(), coeffs.end());
GF_Poly *ec_poly = new GF_Poly(coeffs, GaloisField.GetField(kFieldSize)); GF_Poly ec_poly = new GF_Poly(coeffs, GaloisField.GetField(kFieldSize));
g_ec_polynomials[ec_length] = ec_poly; g_ec_polynomials[ec_length] = ec_poly;
} }
} }
// Get error correction polynomials. The polynomials are // Get error correction polynomials. The polynomials are defined in Appendix A of JISX0510 2004
// defined in Appendix A of JISX0510 2004 (p. 59). In the appendix, // (p. 59). In the appendix, they use exponential notations for the polynomials. We need to apply
// they use exponential notations for the polynomials. We need to // GaloisField.Log() to all coefficients generated by the function to compare numbers with the
// apply GaloisField.Log() to all coefficients generated by the // ones in the appendix.
// function to compare numbers with the ones in the appendix.
// //
// Example: // Example:
// - Input: 17 // - Input: 17
@ -822,9 +819,8 @@ private static final ECPolyInfo kECPolynomials[] = {
// Example: // Example:
// - Input: {32,65,205,69,41,220,46,128,236}, ec_length = 17 // - Input: {32,65,205,69,41,220,46,128,236}, ec_length = 17
// - Output: {42,159,74,221,244,169,239,150,138,70,237,85,224,96,74,219,61} // - Output: {42,159,74,221,244,169,239,150,138,70,237,85,224,96,74,219,61}
private static void GenerateECBytes(final StringPiece data_bytes, int ec_length, String ec_bytes) { private static void GenerateECBytes(final ByteArray data_bytes, int ec_length, ByteArray ec_bytes) {
// First, fill the vector with "ec_length" copies of 0. // First, fill the vector with "ec_length" copies of 0. They are low-order zero coefficients.
// They are low-order zero coefficients.
vector<GF_Element> *coeffs = new vector<GF_Element>(ec_length, 0); vector<GF_Element> *coeffs = new vector<GF_Element>(ec_length, 0);
// Then copy data_bytes backward. // Then copy data_bytes backward.
copy(data_bytes.rbegin(), data_bytes.rend(), back_inserter(*coeffs)); copy(data_bytes.rbegin(), data_bytes.rend(), back_inserter(*coeffs));
@ -835,27 +831,25 @@ private static final ECPolyInfo kECPolynomials[] = {
final GF_Poly &ec_poly = GetECPoly(ec_length); final GF_Poly &ec_poly = GetECPoly(ec_length);
pair<GF_Poly*, GF_Poly*> divrem = GF_Poly.DivRem(data_poly, ec_poly); pair<GF_Poly*, GF_Poly*> divrem = GF_Poly.DivRem(data_poly, ec_poly);
// Basically, the coefficients in the remainder polynomial are the // Basically, the coefficients in the remainder polynomial are the error correction bytes.
// error correction bytes.
GF_Poly *remainder = divrem.second; GF_Poly *remainder = divrem.second;
ec_bytes.reserve(ec_length); ec_bytes.reserve(ec_length);
// However, high-order zero cofficients in the remainder polynomial // However, high-order zero cofficients in the remainder polynomial are ommited. We should add
// are ommited. We should add zero by ourselvs. // zero by ourselvs.
final int num_pruned_zero_coeffs = ec_length - (remainder.degree() + 1); final int num_pruned_zero_coeffs = ec_length - (remainder.degree() + 1);
for (int i = 0; i < num_pruned_zero_coeffs; ++i) { for (int i = 0; i < num_pruned_zero_coeffs; ++i) {
ec_bytes.push_back(0); ec_bytes.appendByte(0);
} }
// Copy the remainder numbers to "ec_bytes". // Copy the remainder numbers to "ec_bytes".
for (int i = remainder.degree(); i >= 0; --i) { for (int i = remainder.degree(); i >= 0; --i) {
ec_bytes.push_back(remainder.coeff(i)); ec_bytes.appendByte(remainder.coeff(i));
} }
Debug.DCHECK_EQ(ec_length, ec_bytes.size()); Debug.DCHECK_EQ(ec_length, ec_bytes.size());
} }
// Check if "byte1" and "byte2" can compose a valid Kanji letter // Check if "byte1" and "byte2" can compose a valid Kanji letter (2-byte Shift_JIS letter). The
// (2-byte Shift_JIS letter). // numbers are from http://ja.wikipedia.org/wiki/Shift_JIS.
// The numbers are from http://ja.wikipedia.org/wiki/Shift_JIS. private static boolean IsValidKanji(final int byte1, final int byte2) {
private static boolean IsValidKanji(final char byte1, final char byte2) {
return (byte2 != 0x7f && return (byte2 != 0x7f &&
((byte1 >= 0x81 && byte1 <= 0x9f && ((byte1 >= 0x81 && byte1 <= 0x9f &&
byte2 >= 0x40 && byte2 <= 0xfc) || byte2 >= 0x40 && byte2 <= 0xfc) ||
@ -864,13 +858,15 @@ private static final ECPolyInfo kECPolynomials[] = {
} }
// Check if "bytes" is a valid Kanji sequence. // Check if "bytes" is a valid Kanji sequence.
private static boolean IsValidKanjiSequence(final StringPiece bytes) { //
// JAVAPORT - Remove if not used by the unit tests.
private static boolean IsValidKanjiSequence(final ByteArray bytes) {
if (bytes.size() % 2 != 0) { if (bytes.size() % 2 != 0) {
return false; return false;
} }
int i = 0; int i = 0;
for (; i < bytes.size(); i += 2) { for (; i < bytes.size(); i += 2) {
if (!IsValidKanji(bytes[i], bytes[i + 1])) { if (!IsValidKanji(bytes.at(i), bytes.at(i + 1))) {
break; break;
} }
} }

View file

@ -161,7 +161,7 @@ public final class QRCode {
} }
// Return debug String. // Return debug String.
public String DebugString() { public String toString() {
StringBuffer result = new StringBuffer(); StringBuffer result = new StringBuffer();
result.append("<<QRCode\n"); result.append("<<QRCode\n");
result.append(" mode: "); result.append(" mode: ");

View file

@ -16,7 +16,6 @@
package com.google.zxing.qrcode.encoder; package com.google.zxing.qrcode.encoder;
//class StringPiece;
// #include "third_party/png/png.h" // #include "third_party/png/png.h"
/** /**
@ -119,7 +118,7 @@ public final class Renderer {
// Similar to RenderAsPNG but it renders QR code from data in // Similar to RenderAsPNG but it renders QR code from data in
// "bytes" with error correction level "ec_level". This is the // "bytes" with error correction level "ec_level". This is the
// friendliest function in the QR code library. // friendliest function in the QR code library.
public static boolean RenderAsPNGFromData(final StringPiece& bytes, int ec_level, int cell_size, public static boolean RenderAsPNGFromData(final ByteArray& bytes, int ec_level, int cell_size,
String *result) { String *result) {
QRCode qr_code; QRCode qr_code;
if (!Encoder.Encode(bytes, ec_level, &qr_code)) { if (!Encoder.Encode(bytes, ec_level, &qr_code)) {