Ported over the BitVector bug fix and new unit test from Satoru.
authordswitkin <dswitkin@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Thu, 20 Nov 2008 17:08:30 +0000 (17:08 +0000)
committerdswitkin <dswitkin@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Thu, 20 Nov 2008 17:08:30 +0000 (17:08 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@742 59b500cc-1b3d-0410-9834-0bbf25fbcc57

core/src/com/google/zxing/qrcode/encoder/BitVector.java
core/test/src/com/google/zxing/qrcode/encoder/BitVectorTestCase.java
core/test/src/com/google/zxing/qrcode/encoder/EncoderTestCase.java

index e4938af..82512bc 100644 (file)
@@ -52,10 +52,8 @@ public final class BitVector {
   }
 
   // Return the number of bytes in the bit vector.
-  // JAVAPORT: I would have made this ((sizeInBits + 7) >> 3), but apparently users of this class
-  // depend on the number of bytes being rounded down. I don't see how that works though.
   public int num_bytes() {
-    return sizeInBits >> 3;
+    return (sizeInBits + 7) >> 3;
   }
 
   // Append one bit to the bit vector.
index 0639788..5534280 100644 (file)
@@ -101,11 +101,15 @@ public class BitVectorTestCase extends TestCase {
     BitVector v = new BitVector();
     assertEquals(0, v.num_bytes());
     v.AppendBit(0);
-    assertEquals(0, v.num_bytes());
+    // 1 bit was added in the vector, so 1 byte should be consumed.
+    assertEquals(1, v.num_bytes());
     v.AppendBits(0, 7);
     assertEquals(1, v.num_bytes());
     v.AppendBits(0, 8);
     assertEquals(2, v.num_bytes());
+    v.AppendBits(0, 1);
+    // We now have 17 bits, so 3 bytes should be consumed.
+    assertEquals(3, v.num_bytes());
   }
 
   public void testAppendBitVector() {
index 9a13c98..9353d8b 100644 (file)
@@ -607,4 +607,42 @@ public final class EncoderTestCase extends TestCase {
     assertFalse(Encoder.IsValidKanjiSequence(new ByteArray("0123")));
     assertFalse(Encoder.IsValidKanjiSequence(new ByteArray("ABC")));
   }
+
+  public void testBugInBitVectorNumBytes() throws WriterException {
+    // There was a bug in BitVector::num_bytes() that caused it to return a
+    // smaller-by-one value (ex. 1465 instead of 1466) if the number of bits
+    // in the vector is not 8-bit aligned.  In QRCodeEncoder::InitQRCode(),
+    // BitVector::num_bytes() is used for finding the smallest QR Code
+    // version that can fit the given data.  Hence there were corner cases
+    // where we chose a wrong QR Code version that cannot fit the given
+    // data.  Note that the issue did not occur with MODE_8BIT_BYTE, as the
+    // bits in the bit vector are always 8-bit aligned.
+    //
+    // Before the bug was fixed, the following test didn't pass, because:
+    //
+    // - MODE_NUMERIC is chosen as all bytes in the data are '0'
+    // - The 3518-byte numeric data needs 1466 bytes
+    //   - 3518 / 3 * 10 + 7 = 11727 bits = 1465.875 bytes
+    //   - 3 numeric bytes are encoded in 10 bits, hence the first
+    //     3516 bytes are encoded in 3516 / 3 * 10 = 11720 bits.
+    //   - 2 numeric bytes can be encoded in 7 bits, hence the last
+    //     2 bytes are encoded in 7 bits.
+    // - The version 27 QR Code with the EC level L has 1468 bytes for data.
+    //   - 1828 - 360 = 1468
+    // - In InitQRCode(), 3 bytes are reserved for a header.  Hence 1465 bytes
+    //   (1468 -3) are left for data.
+    // - Because of the bug in BitVector::num_bytes(), InitQRCode() determines
+    //   the given data can fit in 1465 bytes, despite it needs 1466 bytes.
+    // - Hence QRCodeEncoder::Encode() failed and returned false.
+    //   - To be precise, it needs 11727 + 4 (mode info) + 14 (length info) =
+    //     11745 bits = 1468.125 bytes are needed (i.e. cannot fit in 1468
+    //     bytes).
+    final int arraySize = 3518;
+    byte[] data_bytes = new byte[arraySize];
+    for (int x = 0; x < arraySize; x++) {
+      data_bytes[x] = '0';
+    }
+    QRCode qr_code = new QRCode();
+    Encoder.Encode(new ByteArray(data_bytes), QRCode.EC_LEVEL_L, qr_code);
+  }
 }