Merge pull request #148 from eustas/master

Fixups in encoder and decoder.
diff --git a/dec/decode.c b/dec/decode.c
index 83f28f2..477804b 100644
--- a/dec/decode.c
+++ b/dec/decode.c
@@ -674,15 +674,14 @@
         if (result != BROTLI_RESULT_SUCCESS) {
           return result;
         }
-        pos &= s->ringbuffer_size;
+        pos &= s->ringbuffer_mask;
         s->max_distance = s->max_backward_distance;
         if (s->sub0_state == BROTLI_STATE_SUB0_UNCOMPRESSED_WRITE_2) {
-          s->meta_block_remaining_len--;
           s->sub0_state = BROTLI_STATE_SUB0_UNCOMPRESSED_SHORT;
           break;
         }
         if (s->sub0_state == BROTLI_STATE_SUB0_UNCOMPRESSED_WRITE_1) {
-          s->meta_block_remaining_len += s->ringbuffer_size;
+          s->meta_block_remaining_len -= s->ringbuffer_size;
           /* If we wrote past the logical end of the ringbuffer, copy the tail
              of the ringbuffer to its beginning and flush the ringbuffer to the
              output. */
@@ -899,11 +898,11 @@
         s->state = BROTLI_STATE_BITREADER_WARMUP;
         /* No break, continue to next state */
       case BROTLI_STATE_BITREADER_WARMUP:
-        if (!BrotliCheckInputAmount(&s->br, 32)) {
+        if (!BrotliCheckInputAmount(br, 32)) {
           result = BROTLI_RESULT_NEEDS_MORE_INPUT;
           break;
         }
-        BrotliWarmupBitReader(&s->br);
+        BrotliWarmupBitReader(br);
         /* Decode window size. */
         s->window_bits = DecodeWindowBits(br);
         if (s->window_bits == 9) {
@@ -993,7 +992,7 @@
         }
         BROTLI_LOG_UINT(s->meta_block_remaining_len);
         if (is_metadata) {
-          if (!BrotliJumpToByteBoundary(&s->br)) {
+          if (!BrotliJumpToByteBoundary(br)) {
             result = BROTLI_FAILURE();
             break;
           }
@@ -1005,7 +1004,7 @@
           break;
         }
         if (is_uncompressed) {
-          if (!BrotliJumpToByteBoundary(&s->br)) {
+          if (!BrotliJumpToByteBoundary(br)) {
             result = BROTLI_FAILURE();
             break;
           }
@@ -1028,12 +1027,12 @@
         break;
       case BROTLI_STATE_METADATA:
         for (; s->meta_block_remaining_len > 0; --s->meta_block_remaining_len) {
-          if (!BrotliCheckInputAmount(&s->br, 32)) {
+          if (!BrotliCheckInputAmount(br, 32)) {
             result = BROTLI_RESULT_NEEDS_MORE_INPUT;
             break;
           }
           /* Read one byte and ignore it. */
-          BrotliReadBits(&s->br, 8);
+          BrotliReadBits(br, 8);
         }
         if (result == BROTLI_RESULT_SUCCESS) {
           s->state = BROTLI_STATE_METABLOCK_DONE;
@@ -1546,7 +1545,7 @@
             break;
           }
         }
-        if (!BrotliJumpToByteBoundary(&s->br)) {
+        if (!BrotliJumpToByteBoundary(br)) {
           result = BROTLI_FAILURE();
         }
         if (BrotliGetRemainingBytes(br) < BROTLI_IMPLICIT_ZEROES) {
diff --git a/dec/huffman.c b/dec/huffman.c
index e7ae707..c68a404 100644
--- a/dec/huffman.c
+++ b/dec/huffman.c
@@ -197,7 +197,8 @@
         total_size += table_size;
         low = key & mask;
         root_table[low].bits = (uint8_t)(table_bits + root_bits);
-        root_table[low].value = (uint16_t)((table - root_table) - low);
+        root_table[low].value = (uint16_t)(
+            ((size_t)(table - root_table)) - low);
       }
       code.bits = (uint8_t)(len - root_bits);
       symbol = symbol_lists[symbol];
diff --git a/dec/port.h b/dec/port.h
index 1530e23..ce38364 100644
--- a/dec/port.h
+++ b/dec/port.h
@@ -96,10 +96,8 @@
 #if (defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || \
      defined(__PPC64__))
 #define BROTLI_64_BITS 1
-#define BROTLI_PRELOAD_SYMBOLS 1
 #else
 #define BROTLI_64_BITS 0
-#define BROTLI_PRELOAD_SYMBOLS 0
 #endif
 
 #if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
diff --git a/enc/backward_references.cc b/enc/backward_references.cc
index 16c8d84..283aee7 100644
--- a/enc/backward_references.cc
+++ b/enc/backward_references.cc
@@ -455,6 +455,7 @@
     pos += insert_length;
     if (i == 0) {
       insert_length += *last_insert_len;
+      *last_insert_len = 0;
     }
     int distance = next.distance;
     int len_code = next.length_code;
@@ -476,7 +477,7 @@
     insert_length = 0;
     pos += copy_length;
   }
-  *last_insert_len = num_bytes - pos;
+  *last_insert_len += num_bytes - pos;
   *num_commands += (commands - orig_commands);
 }
 
diff --git a/tests/testdata/bb.binast b/tests/testdata/bb.binast
new file mode 100644
index 0000000..9b7f073
--- /dev/null
+++ b/tests/testdata/bb.binast
Binary files differ
diff --git a/tests/testdata/random_chunks b/tests/testdata/random_chunks
new file mode 100644
index 0000000..c014f0b
--- /dev/null
+++ b/tests/testdata/random_chunks
Binary files differ
diff --git a/tools/bro.cc b/tools/bro.cc
index 608fade..8cec13a 100644
--- a/tools/bro.cc
+++ b/tools/bro.cc
@@ -44,10 +44,14 @@
                       char **output_path,
                       int *force,
                       int *quality,
-                      int *decompress) {
+                      int *decompress,
+                      int *repeat,
+                      int *verbose) {
   *force = 0;
   *input_path = 0;
   *output_path = 0;
+  *repeat = 1;
+  *verbose = 0;
   {
     size_t argv0_len = strlen(argv[0]);
     *decompress =
@@ -66,6 +70,13 @@
                !strcmp("-d", argv[k])) {
       *decompress = 1;
       continue;
+    } else if (!strcmp("--verbose", argv[k]) ||
+               !strcmp("-v", argv[k])) {
+      if (*verbose != 0) {
+        goto error;
+      }
+      *verbose = 1;
+      continue;
     }
     if (k < argc - 1) {
       if (!strcmp("--input", argv[k]) ||
@@ -93,6 +104,13 @@
         }
         ++k;
         continue;
+      } else if (!strcmp("--repeat", argv[k]) ||
+                 !strcmp("-r", argv[k])) {
+        if (!ParseQuality(argv[k + 1], repeat)) {
+          goto error;
+        }
+        ++k;
+        continue;
       }
     }
     goto error;
@@ -101,7 +119,8 @@
 error:
   fprintf(stderr,
           "Usage: %s [--force] [--quality n] [--decompress]"
-          " [--input filename] [--output filename]\n",
+          " [--input filename] [--output filename] [--repeat iters]"
+          " [--verbose]\n",
           argv[0]);
   exit(1);
 }
@@ -138,41 +157,74 @@
   return fdopen(fd, "wb");
 }
 
+int64_t FileSize(char *path) {
+  FILE *f = fopen(path, "rb");
+  if (f == NULL) {
+    return -1;
+  }
+  fseek(f, 0L, SEEK_END);
+  int64_t retval = ftell(f);
+  fclose(f);
+  return retval;
+}
+
 int main(int argc, char** argv) {
   char *input_path = 0;
   char *output_path = 0;
   int force = 0;
   int quality = 11;
   int decompress = 0;
+  int repeat = 1;
+  int verbose = 0;
   ParseArgv(argc, argv, &input_path, &output_path, &force,
-            &quality, &decompress);
-  FILE* fin = OpenInputFile(input_path);
-  FILE* fout = OpenOutputFile(output_path, force);
-  if (decompress) {
-    BrotliInput in = BrotliFileInput(fin);
-    BrotliOutput out = BrotliFileOutput(fout);
-    if (!BrotliDecompress(in, out)) {
-      fprintf(stderr, "corrupt input\n");
+            &quality, &decompress, &repeat, &verbose);
+  const clock_t clock_start = clock();
+  for (int i = 0; i < repeat; ++i) {
+    FILE* fin = OpenInputFile(input_path);
+    FILE* fout = OpenOutputFile(output_path, force);
+    if (decompress) {
+      BrotliInput in = BrotliFileInput(fin);
+      BrotliOutput out = BrotliFileOutput(fout);
+      if (!BrotliDecompress(in, out)) {
+        fprintf(stderr, "corrupt input\n");
+        exit(1);
+      }
+    } else {
+      brotli::BrotliParams params;
+      params.quality = quality;
+      brotli::BrotliFileIn in(fin, 1 << 16);
+      brotli::BrotliFileOut out(fout);
+      if (!BrotliCompress(params, &in, &out)) {
+        fprintf(stderr, "compression failed\n");
+        unlink(output_path);
+        exit(1);
+      }
+    }
+    if (fclose(fin) != 0) {
+      perror("fclose");
       exit(1);
     }
-  } else {
-    brotli::BrotliParams params;
-    params.quality = quality;
-    brotli::BrotliFileIn in(fin, 1 << 16);
-    brotli::BrotliFileOut out(fout);
-    if (!BrotliCompress(params, &in, &out)) {
-      fprintf(stderr, "compression failed\n");
-      unlink(output_path);
+    if (fclose(fout) != 0) {
+      perror("fclose");
       exit(1);
     }
   }
-  if (fclose(fin) != 0) {
-    perror("fclose");
-    exit(1);
-  }
-  if (fclose(fout) != 0) {
-    perror("fclose");
-    exit(1);
+  if (verbose) {
+    const clock_t clock_end = clock();
+    double duration =
+        static_cast<double>(clock_end - clock_start) / CLOCKS_PER_SEC;
+    if (duration < 1e-9) {
+      duration = 1e-9;
+    }
+    int64_t uncompressed_bytes = repeat *
+        FileSize(decompress ? output_path : input_path);
+    double uncompressed_bytes_in_MB = uncompressed_bytes / (1024.0 * 1024.0);
+    if (decompress) {
+      printf("Brotli decompression speed: ");
+    } else {
+      printf("Brotli compression speed: ");
+    }
+    printf("%g MB/s\n", uncompressed_bytes_in_MB / duration);
   }
   return 0;
 }