hash based motion estimation for screen data

Change-Id: Iec7969ffd8f53ca2f4eefd1d757cfec7b3bde131
diff --git a/av1/av1.cmake b/av1/av1.cmake
index 7ac17c4..5295a1a 100644
--- a/av1/av1.cmake
+++ b/av1/av1.cmake
@@ -476,6 +476,15 @@
   endif ()
 endif ()
 
+if (CONFIG_HASH_ME)
+  set(AOM_AV1_ENCODER_SOURCES
+      ${AOM_AV1_ENCODER_SOURCES}
+      "${AOM_ROOT}/av1/encoder/hash_motion.h"
+      "${AOM_ROOT}/av1/encoder/hash_motion.c"
+      "${AOM_ROOT}/third_party/vector/vector.h"
+      "${AOM_ROOT}/third_party/vector/vector.c")
+endif ()
+
 # Setup AV1 common/decoder/encoder targets. The libaom target must exist before
 # this function is called.
 function (setup_av1_targets)
diff --git a/av1/av1_cx.mk b/av1/av1_cx.mk
index bbe82e6..eca4920 100644
--- a/av1/av1_cx.mk
+++ b/av1/av1_cx.mk
@@ -108,6 +108,12 @@
 AV1_CX_SRCS-yes += encoder/temporal_filter.h
 AV1_CX_SRCS-yes += encoder/mbgraph.c
 AV1_CX_SRCS-yes += encoder/mbgraph.h
+ifeq ($(CONFIG_HASH_ME),yes)
+AV1_CX_SRCS-yes += ../third_party/vector/vector.h
+AV1_CX_SRCS-yes += ../third_party/vector/vector.c
+AV1_CX_SRCS-yes += encoder/hash_motion.c
+AV1_CX_SRCS-yes += encoder/hash_motion.h
+endif
 ifeq ($(CONFIG_CDEF),yes)
 AV1_CX_SRCS-yes += encoder/pickcdef.c
 endif
diff --git a/av1/common/alloccommon.c b/av1/common/alloccommon.c
index c37f1ea..69b2197 100644
--- a/av1/common/alloccommon.c
+++ b/av1/common/alloccommon.c
@@ -86,6 +86,9 @@
     aom_free(pool->frame_bufs[i].mvs);
     pool->frame_bufs[i].mvs = NULL;
     aom_free_frame_buffer(&pool->frame_bufs[i].buf);
+#if CONFIG_HASH_ME
+    av1_hash_table_destroy(&pool->frame_bufs[i].hash_table);
+#endif
   }
 }
 
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index 66f7181..b101422 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -38,6 +38,10 @@
 #if CONFIG_CFL
 #include "av1/common/cfl.h"
 #endif
+#if CONFIG_HASH_ME
+// TODO(youzhou@microsoft.com): Encoder only. Move it out of common
+#include "av1/encoder/hash_motion.h"
+#endif
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -127,6 +131,9 @@
 #endif  // CONFIG_GLOBAL_MOTION
   aom_codec_frame_buffer_t raw_frame_buffer;
   YV12_BUFFER_CONFIG buf;
+#if CONFIG_HASH_ME
+  hash_table hash_table;
+#endif
 #if CONFIG_TEMPMV_SIGNALING
   uint8_t intra_only;
 #endif
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index d995484..2dadc97 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -45,6 +45,9 @@
 #endif
 #include "av1/encoder/ethread.h"
 #include "av1/encoder/firstpass.h"
+#if CONFIG_HASH_ME
+#include "av1/encoder/hash_motion.h"
+#endif
 #include "av1/encoder/mbgraph.h"
 #include "av1/encoder/picklpf.h"
 #if CONFIG_LOOP_RESTORATION
@@ -5362,6 +5365,11 @@
     cm->ref_frame_map[i] = INVALID_IDX;
     pool->frame_bufs[i].ref_count = 0;
   }
+#if CONFIG_HASH_ME
+  for (i = 0; i < FRAME_BUFFERS; ++i) {
+    av1_hash_table_init(&pool->frame_bufs[i].hash_table);
+  }
+#endif
 }
 
 static void check_initial_width(AV1_COMP *cpi,
@@ -6085,6 +6093,71 @@
     Pass0Encode(cpi, size, dest, 0, frame_flags);
   }
 #endif
+#if CONFIG_HASH_ME
+  if (oxcf->pass != 1 && cpi->common.allow_screen_content_tools) {
+    // add to hash table
+    const int pic_width = cpi->source->y_crop_width;
+    const int pic_height = cpi->source->y_crop_height;
+    uint32_t *block_hash_values[2][2];
+    int8_t *is_block_same[2][3];
+    int k, j;
+
+    for (k = 0; k < 2; k++) {
+      for (j = 0; j < 2; j++) {
+        CHECK_MEM_ERROR(cm, block_hash_values[k][j],
+                        aom_malloc(sizeof(uint32_t) * pic_width * pic_height));
+      }
+
+      for (j = 0; j < 3; j++) {
+        CHECK_MEM_ERROR(cm, is_block_same[k][j],
+                        aom_malloc(sizeof(int8_t) * pic_width * pic_height));
+      }
+    }
+
+    av1_hash_table_create(&cm->cur_frame->hash_table);
+    av1_generate_block_2x2_hash_value(cpi->source, block_hash_values[0],
+                                      is_block_same[0]);
+    av1_generate_block_hash_value(cpi->source, 4, block_hash_values[0],
+                                  block_hash_values[1], is_block_same[0],
+                                  is_block_same[1]);
+    av1_generate_block_hash_value(cpi->source, 8, block_hash_values[1],
+                                  block_hash_values[0], is_block_same[1],
+                                  is_block_same[0]);
+    av1_add_to_hash_map_by_row_with_precal_data(
+        &cm->cur_frame->hash_table, block_hash_values[0], is_block_same[0][2],
+        pic_width, pic_height, 8);
+    av1_generate_block_hash_value(cpi->source, 16, block_hash_values[0],
+                                  block_hash_values[1], is_block_same[0],
+                                  is_block_same[1]);
+    av1_add_to_hash_map_by_row_with_precal_data(
+        &cm->cur_frame->hash_table, block_hash_values[1], is_block_same[1][2],
+        pic_width, pic_height, 16);
+    av1_generate_block_hash_value(cpi->source, 32, block_hash_values[1],
+                                  block_hash_values[0], is_block_same[1],
+                                  is_block_same[0]);
+    av1_add_to_hash_map_by_row_with_precal_data(
+        &cm->cur_frame->hash_table, block_hash_values[0], is_block_same[0][2],
+        pic_width, pic_height, 32);
+    av1_generate_block_hash_value(cpi->source, 64, block_hash_values[0],
+                                  block_hash_values[1], is_block_same[0],
+                                  is_block_same[1]);
+    av1_add_to_hash_map_by_row_with_precal_data(
+        &cm->cur_frame->hash_table, block_hash_values[1], is_block_same[1][2],
+        pic_width, pic_height, 64);
+
+    for (k = 0; k < 2; k++) {
+      for (j = 0; j < 2; j++) {
+        aom_free(block_hash_values[k][j]);
+      }
+
+      for (j = 0; j < 3; j++) {
+        aom_free(is_block_same[k][j]);
+      }
+    }
+  }
+
+#endif
+
 #if CONFIG_NO_FRAME_CONTEXT_SIGNALING
   cm->frame_contexts[cm->new_fb_idx] = *cm->fc;
 #else
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 66e509f..e71c91c 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -692,6 +692,17 @@
   return (map_idx != INVALID_IDX) ? cm->ref_frame_map[map_idx] : INVALID_IDX;
 }
 
+#if CONFIG_HASH_ME
+static INLINE hash_table *get_ref_frame_hash_map(const AV1_COMP *cpi,
+                                                 MV_REFERENCE_FRAME ref_frame) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int buf_idx = get_ref_frame_buf_idx(cpi, ref_frame);
+  return buf_idx != INVALID_IDX
+             ? &cm->buffer_pool->frame_bufs[buf_idx].hash_table
+             : NULL;
+}
+#endif
+
 static INLINE YV12_BUFFER_CONFIG *get_ref_frame_buffer(
     const AV1_COMP *cpi, MV_REFERENCE_FRAME ref_frame) {
   const AV1_COMMON *const cm = &cpi->common;
diff --git a/av1/encoder/hash_motion.c b/av1/encoder/hash_motion.c
new file mode 100644
index 0000000..8732b82
--- /dev/null
+++ b/av1/encoder/hash_motion.c
@@ -0,0 +1,445 @@
+#include <assert.h>
+#include "av1/encoder/hash_motion.h"
+#include "./av1_rtcd.h"
+
+typedef struct _crc_calculator {
+  uint32_t remainder;
+  uint32_t trunc_poly;
+  uint32_t bits;
+  uint32_t table[256];
+  uint32_t final_result_mask;
+} crc_calculator;
+
+static void crc_calculator_process_data(crc_calculator *p_crc_calculator,
+                                        uint8_t *pData, uint32_t dataLength) {
+  for (uint32_t i = 0; i < dataLength; i++) {
+    const uint8_t index =
+        (p_crc_calculator->remainder >> (p_crc_calculator->bits - 8)) ^
+        pData[i];
+    p_crc_calculator->remainder <<= 8;
+    p_crc_calculator->remainder ^= p_crc_calculator->table[index];
+  }
+}
+
+static void crc_calculator_reset(crc_calculator *p_crc_calculator) {
+  p_crc_calculator->remainder = 0;
+}
+
+static uint32_t crc_calculator_get_crc(crc_calculator *p_crc_calculator) {
+  return p_crc_calculator->remainder & p_crc_calculator->final_result_mask;
+}
+
+static void crc_calculator_init_table(crc_calculator *p_crc_calculator) {
+  const uint32_t high_bit = 1 << (p_crc_calculator->bits - 1);
+  const uint32_t byte_high_bit = 1 << (8 - 1);
+
+  for (uint32_t value = 0; value < 256; value++) {
+    uint32_t remainder = 0;
+    for (uint8_t mask = byte_high_bit; mask != 0; mask >>= 1) {
+      if (value & mask) {
+        remainder ^= high_bit;
+      }
+
+      if (remainder & high_bit) {
+        remainder <<= 1;
+        remainder ^= p_crc_calculator->trunc_poly;
+      } else {
+        remainder <<= 1;
+      }
+    }
+    p_crc_calculator->table[value] = remainder;
+  }
+}
+
+static void crc_calculator_init(crc_calculator *p_crc_calculator, uint32_t bits,
+                                uint32_t truncPoly) {
+  p_crc_calculator->remainder = 0;
+  p_crc_calculator->bits = bits;
+  p_crc_calculator->trunc_poly = truncPoly;
+  p_crc_calculator->final_result_mask = (1 << bits) - 1;
+  crc_calculator_init_table(p_crc_calculator);
+}
+
+static const int crc_bits = 16;
+static const int block_size_bits = 2;
+
+static crc_calculator crc_calculator1;
+static crc_calculator crc_calculator2;
+static int g_crc_initialized = 0;
+
+static void hash_table_clear_all(hash_table *p_hash_table) {
+  if (p_hash_table->p_lookup_table == NULL) {
+    return;
+  }
+  int max_addr = 1 << (crc_bits + block_size_bits);
+  for (int i = 0; i < max_addr; i++) {
+    if (p_hash_table->p_lookup_table[i] != NULL) {
+      vector_destroy(p_hash_table->p_lookup_table[i]);
+      aom_free(p_hash_table->p_lookup_table[i]);
+      p_hash_table->p_lookup_table[i] = NULL;
+    }
+  }
+}
+
+static uint32_t get_crc_value1(uint8_t *p, int length) {
+  crc_calculator_reset(&crc_calculator1);
+  crc_calculator_process_data(&crc_calculator1, p, length);
+  return crc_calculator_get_crc(&crc_calculator1);
+}
+
+static uint32_t get_crc_value2(uint8_t *p, int length) {
+  crc_calculator_reset(&crc_calculator2);
+  crc_calculator_process_data(&crc_calculator2, p, length);
+  return crc_calculator_get_crc(&crc_calculator2);
+}
+
+// TODO(youzhou@microsoft.com): is higher than 8 bits screen content supported?
+// If yes, fix this function
+static void get_pixels_in_1D_char_array_by_block_2x2(uint8_t *y_src, int stride,
+                                                     uint8_t *p_pixels_in1D) {
+  uint8_t *p_pel = y_src;
+  int index = 0;
+  for (int i = 0; i < 2; i++) {
+    for (int j = 0; j < 2; j++) {
+      p_pixels_in1D[index++] = p_pel[j];
+    }
+    p_pel += stride;
+  }
+}
+
+static int is_block_2x2_row_same_value(uint8_t *p) {
+  if (p[0] != p[1] || p[2] != p[3]) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int is_block_2x2_col_same_value(uint8_t *p) {
+  if ((p[0] != p[2]) || (p[1] != p[3])) {
+    return 0;
+  }
+
+  return 1;
+}
+
+// the hash value (hash_value1 consists two parts, the first 2 bits relate to
+// the block size and the remaining 16 bits are the crc values. This fuction
+// is used to get the first 2 bits.
+static int hash_block_size_to_index(int block_size) {
+  switch (block_size) {
+    case 8: return 0;
+    case 16: return 1;
+    case 32: return 2;
+    case 64: return 3;
+    default: return -1;
+  }
+}
+
+void av1_hash_table_init(hash_table *p_hash_table) {
+  if (g_crc_initialized == 0) {
+    crc_calculator_init(&crc_calculator1, 24, 0x5D6DCB);
+    crc_calculator_init(&crc_calculator2, 24, 0x864CFB);
+    g_crc_initialized = 1;
+  }
+  p_hash_table->p_lookup_table = NULL;
+}
+
+void av1_hash_table_destroy(hash_table *p_hash_table) {
+  hash_table_clear_all(p_hash_table);
+  aom_free(p_hash_table->p_lookup_table);
+  p_hash_table->p_lookup_table = NULL;
+}
+
+void av1_hash_table_create(hash_table *p_hash_table) {
+  if (p_hash_table->p_lookup_table != NULL) {
+    hash_table_clear_all(p_hash_table);
+    return;
+  }
+  const int max_addr = 1 << (crc_bits + block_size_bits);
+  p_hash_table->p_lookup_table =
+      (Vector **)aom_malloc(sizeof(p_hash_table->p_lookup_table[0]) * max_addr);
+  memset(p_hash_table->p_lookup_table, 0,
+         sizeof(p_hash_table->p_lookup_table[0]) * max_addr);
+}
+
+static void hash_table_add_to_table(hash_table *p_hash_table,
+                                    uint32_t hash_value,
+                                    block_hash *curr_block_hash) {
+  if (p_hash_table->p_lookup_table[hash_value] == NULL) {
+    p_hash_table->p_lookup_table[hash_value] =
+        aom_malloc(sizeof(p_hash_table->p_lookup_table[0][0]));
+    vector_setup(p_hash_table->p_lookup_table[hash_value], 10,
+                 sizeof(curr_block_hash[0]));
+    vector_push_back(p_hash_table->p_lookup_table[hash_value], curr_block_hash);
+  } else {
+    vector_push_back(p_hash_table->p_lookup_table[hash_value], curr_block_hash);
+  }
+}
+
+int32_t av1_hash_table_count(hash_table *p_hash_table, uint32_t hash_value) {
+  if (p_hash_table->p_lookup_table[hash_value] == NULL) {
+    return 0;
+  } else {
+    return (int32_t)(p_hash_table->p_lookup_table[hash_value]->size);
+  }
+}
+
+Iterator av1_hash_get_first_iterator(hash_table *p_hash_table,
+                                     uint32_t hash_value) {
+  assert(av1_hash_table_count(p_hash_table, hash_value) > 0);
+  return vector_begin(p_hash_table->p_lookup_table[hash_value]);
+}
+
+int32_t av1_has_exact_match(hash_table *p_hash_table, uint32_t hash_value1,
+                            uint32_t hash_value2) {
+  if (p_hash_table->p_lookup_table[hash_value1] == NULL) {
+    return 0;
+  }
+  Iterator iterator = vector_begin(p_hash_table->p_lookup_table[hash_value1]);
+  Iterator last = vector_end(p_hash_table->p_lookup_table[hash_value1]);
+  for (; !iterator_equals(&iterator, &last); iterator_increment(&iterator)) {
+    if ((*(block_hash *)iterator_get(&iterator)).hash_value2 == hash_value2) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+void av1_generate_block_2x2_hash_value(const YV12_BUFFER_CONFIG *picture,
+                                       uint32_t *pic_block_hash[2],
+                                       int8_t *pic_block_same_info[3]) {
+  const int width = 2;
+  const int height = 2;
+  const int x_end = picture->y_crop_width - width + 1;
+  const int y_end = picture->y_crop_height - height + 1;
+
+  const int length = width * 2;
+  uint8_t p[4];
+
+  int pos = 0;
+  for (int y_pos = 0; y_pos < y_end; y_pos++) {
+    for (int x_pos = 0; x_pos < x_end; x_pos++) {
+      get_pixels_in_1D_char_array_by_block_2x2(
+          picture->y_buffer + y_pos * picture->y_stride + x_pos,
+          picture->y_stride, p);
+      pic_block_same_info[0][pos] = is_block_2x2_row_same_value(p);
+      pic_block_same_info[1][pos] = is_block_2x2_col_same_value(p);
+
+      pic_block_hash[0][pos] = get_crc_value1(p, length * sizeof(p[0]));
+      pic_block_hash[1][pos] = get_crc_value2(p, length * sizeof(p[0]));
+
+      pos++;
+    }
+    pos += width - 1;
+  }
+}
+
+void av1_generate_block_hash_value(const YV12_BUFFER_CONFIG *picture,
+                                   int block_size,
+                                   uint32_t *src_pic_block_hash[2],
+                                   uint32_t *dst_pic_block_hash[2],
+                                   int8_t *src_pic_block_same_info[3],
+                                   int8_t *dst_pic_block_same_info[3]) {
+  const int pic_width = picture->y_crop_width;
+  const int x_end = picture->y_crop_width - block_size + 1;
+  const int y_end = picture->y_crop_height - block_size + 1;
+
+  const int src_size = block_size >> 1;
+  const int quad_size = block_size >> 2;
+
+  uint32_t p[4];
+  const int length = sizeof(p);
+
+  int pos = 0;
+  for (int y_pos = 0; y_pos < y_end; y_pos++) {
+    for (int x_pos = 0; x_pos < x_end; x_pos++) {
+      p[0] = src_pic_block_hash[0][pos];
+      p[1] = src_pic_block_hash[0][pos + src_size];
+      p[2] = src_pic_block_hash[0][pos + src_size * pic_width];
+      p[3] = src_pic_block_hash[0][pos + src_size * pic_width + src_size];
+      dst_pic_block_hash[0][pos] = get_crc_value1((uint8_t *)p, length);
+
+      p[0] = src_pic_block_hash[1][pos];
+      p[1] = src_pic_block_hash[1][pos + src_size];
+      p[2] = src_pic_block_hash[1][pos + src_size * pic_width];
+      p[3] = src_pic_block_hash[1][pos + src_size * pic_width + src_size];
+      dst_pic_block_hash[1][pos] = get_crc_value2((uint8_t *)p, length);
+
+      dst_pic_block_same_info[0][pos] =
+          src_pic_block_same_info[0][pos] &&
+          src_pic_block_same_info[0][pos + quad_size] &&
+          src_pic_block_same_info[0][pos + src_size] &&
+          src_pic_block_same_info[0][pos + src_size * pic_width] &&
+          src_pic_block_same_info[0][pos + src_size * pic_width + quad_size] &&
+          src_pic_block_same_info[0][pos + src_size * pic_width + src_size];
+
+      dst_pic_block_same_info[1][pos] =
+          src_pic_block_same_info[1][pos] &&
+          src_pic_block_same_info[1][pos + src_size] &&
+          src_pic_block_same_info[1][pos + quad_size * pic_width] &&
+          src_pic_block_same_info[1][pos + quad_size * pic_width + src_size] &&
+          src_pic_block_same_info[1][pos + src_size * pic_width] &&
+          src_pic_block_same_info[1][pos + src_size * pic_width + src_size];
+      pos++;
+    }
+    pos += block_size - 1;
+  }
+
+  if (block_size >= 8) {
+    const int size_minus1 = block_size - 1;
+    pos = 0;
+    for (int y_pos = 0; y_pos < y_end; y_pos++) {
+      for (int x_pos = 0; x_pos < x_end; x_pos++) {
+        dst_pic_block_same_info[2][pos] =
+            (!dst_pic_block_same_info[0][pos] &&
+             !dst_pic_block_same_info[1][pos]) ||
+            (((x_pos & size_minus1) == 0) && ((y_pos & size_minus1) == 0));
+        pos++;
+      }
+      pos += block_size - 1;
+    }
+  }
+}
+
+void av1_add_to_hash_map_by_row_with_precal_data(hash_table *p_hash_table,
+                                                 uint32_t *pic_hash[2],
+                                                 int8_t *pic_is_same,
+                                                 int pic_width, int pic_height,
+                                                 int block_size) {
+  const int x_end = pic_width - block_size + 1;
+  const int y_end = pic_height - block_size + 1;
+
+  const int8_t *src_is_added = pic_is_same;
+  const uint32_t *src_hash[2] = { pic_hash[0], pic_hash[1] };
+
+  int add_value = hash_block_size_to_index(block_size);
+  assert(add_value >= 0);
+  add_value <<= crc_bits;
+  const int crc_mask = (1 << crc_bits) - 1;
+
+  for (int x_pos = 0; x_pos < x_end; x_pos++) {
+    for (int y_pos = 0; y_pos < y_end; y_pos++) {
+      const int pos = y_pos * pic_width + x_pos;
+      // valid data
+      if (src_is_added[pos]) {
+        block_hash curr_block_hash;
+        curr_block_hash.x = x_pos;
+        curr_block_hash.y = y_pos;
+
+        const uint32_t hash_value1 = (src_hash[0][pos] & crc_mask) + add_value;
+        curr_block_hash.hash_value2 = src_hash[1][pos];
+
+        hash_table_add_to_table(p_hash_table, hash_value1, &curr_block_hash);
+      }
+    }
+  }
+}
+
+int av1_hash_is_horizontal_perfect(const YV12_BUFFER_CONFIG *picture,
+                                   int block_size, int x_start, int y_start) {
+  const int stride = picture->y_stride;
+  const uint8_t *p = picture->y_buffer + y_start * stride + x_start;
+
+  for (int i = 0; i < block_size; i++) {
+    for (int j = 1; j < block_size; j++) {
+      if (p[j] != p[0]) {
+        return 0;
+      }
+    }
+    p += stride;
+  }
+
+  return 1;
+}
+
+int av1_hash_is_vertical_perfect(const YV12_BUFFER_CONFIG *picture,
+                                 int block_size, int x_start, int y_start) {
+  const int stride = picture->y_stride;
+  const uint8_t *p = picture->y_buffer + y_start * stride + x_start;
+
+  for (int i = 0; i < block_size; i++) {
+    for (int j = 1; j < block_size; j++) {
+      if (p[j * stride + i] != p[i]) {
+        return 0;
+      }
+    }
+  }
+
+  return 1;
+}
+
+// global buffer for hash value calculation of a block
+// used only in av1_get_block_hash_value()
+static uint32_t hash_value_buffer[2][2][1024];  // [first hash/second hash]
+                                                // [two buffers used ping-pong]
+                                                // [num of 2x2 blocks in 64x64]
+
+void av1_get_block_hash_value(uint8_t *y_src, int stride, int block_size,
+                              uint32_t *hash_value1, uint32_t *hash_value2) {
+  uint8_t pixel_to_hash[4];
+  uint32_t to_hash[4];
+  const int add_value = hash_block_size_to_index(block_size) << crc_bits;
+  assert(add_value >= 0);
+  const int crc_mask = (1 << crc_bits) - 1;
+
+  // 2x2 subblock hash values in current CU
+  int sub_block_in_width = (block_size >> 1);
+  for (int y_pos = 0; y_pos < block_size; y_pos += 2) {
+    for (int x_pos = 0; x_pos < block_size; x_pos += 2) {
+      int pos = (y_pos >> 1) * sub_block_in_width + (x_pos >> 1);
+      get_pixels_in_1D_char_array_by_block_2x2(y_src + y_pos * stride + x_pos,
+                                               stride, pixel_to_hash);
+
+      hash_value_buffer[0][0][pos] =
+          get_crc_value1(pixel_to_hash, sizeof(pixel_to_hash));
+      hash_value_buffer[1][0][pos] =
+          get_crc_value2(pixel_to_hash, sizeof(pixel_to_hash));
+    }
+  }
+
+  int src_sub_block_in_width = sub_block_in_width;
+  sub_block_in_width >>= 1;
+
+  int src_idx = 1;
+  int dst_idx = 0;
+
+  // 4x4 subblock hash values to current block hash values
+  for (int sub_width = 4; sub_width <= block_size; sub_width *= 2) {
+    src_idx = 1 - src_idx;
+    dst_idx = 1 - dst_idx;
+
+    int dst_pos = 0;
+    for (int y_pos = 0; y_pos < sub_block_in_width; y_pos++) {
+      for (int x_pos = 0; x_pos < sub_block_in_width; x_pos++) {
+        int srcPos = (y_pos << 1) * src_sub_block_in_width + (x_pos << 1);
+
+        to_hash[0] = hash_value_buffer[0][src_idx][srcPos];
+        to_hash[1] = hash_value_buffer[0][src_idx][srcPos + 1];
+        to_hash[2] =
+            hash_value_buffer[0][src_idx][srcPos + src_sub_block_in_width];
+        to_hash[3] =
+            hash_value_buffer[0][src_idx][srcPos + src_sub_block_in_width + 1];
+
+        hash_value_buffer[0][dst_idx][dst_pos] =
+            get_crc_value1((uint8_t *)to_hash, sizeof(to_hash));
+
+        to_hash[0] = hash_value_buffer[1][src_idx][srcPos];
+        to_hash[1] = hash_value_buffer[1][src_idx][srcPos + 1];
+        to_hash[2] =
+            hash_value_buffer[1][src_idx][srcPos + src_sub_block_in_width];
+        to_hash[3] =
+            hash_value_buffer[1][src_idx][srcPos + src_sub_block_in_width + 1];
+        hash_value_buffer[1][dst_idx][dst_pos] =
+            get_crc_value2((uint8_t *)to_hash, sizeof(to_hash));
+        dst_pos++;
+      }
+    }
+
+    src_sub_block_in_width = sub_block_in_width;
+    sub_block_in_width >>= 1;
+  }
+
+  *hash_value1 = (hash_value_buffer[0][dst_idx][0] & crc_mask) + add_value;
+  *hash_value2 = hash_value_buffer[1][dst_idx][0];
+}
diff --git a/av1/encoder/hash_motion.h b/av1/encoder/hash_motion.h
new file mode 100644
index 0000000..26e1ac4
--- /dev/null
+++ b/av1/encoder/hash_motion.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AV1_ENCODER_HASH_MOTION_H_
+#define AV1_ENCODER_HASH_MOTION_H_
+
+#include "./aom_config.h"
+#include "aom/aom_integer.h"
+#include "aom_scale/yv12config.h"
+#include "third_party/vector/vector.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// store a block's hash info.
+// x and y are the position from the top left of the picture
+// hash_value2 is used to store the second hash value
+typedef struct _block_hash {
+  int16_t x;
+  int16_t y;
+  uint32_t hash_value2;
+} block_hash;
+
+typedef struct _hash_table { Vector **p_lookup_table; } hash_table;
+
+void av1_hash_table_init(hash_table *p_hash_table);
+void av1_hash_table_destroy(hash_table *p_hash_table);
+void av1_hash_table_create(hash_table *p_hash_table);
+int32_t av1_hash_table_count(hash_table *p_hash_table, uint32_t hash_value);
+Iterator av1_hash_get_first_iterator(hash_table *p_hash_table,
+                                     uint32_t hash_value);
+int32_t av1_has_exact_match(hash_table *p_hash_table, uint32_t hash_value1,
+                            uint32_t hash_value2);
+void av1_generate_block_2x2_hash_value(const YV12_BUFFER_CONFIG *picture,
+                                       uint32_t *pic_block_hash[2],
+                                       int8_t *pic_block_same_info[3]);
+void av1_generate_block_hash_value(const YV12_BUFFER_CONFIG *picture,
+                                   int block_size,
+                                   uint32_t *src_pic_block_hash[2],
+                                   uint32_t *dst_pic_block_hash[2],
+                                   int8_t *src_pic_block_same_info[3],
+                                   int8_t *dst_pic_block_same_info[3]);
+void av1_add_to_hash_map_by_row_with_precal_data(hash_table *p_hash_table,
+                                                 uint32_t *pic_hash[2],
+                                                 int8_t *pic_is_same,
+                                                 int pic_width, int pic_height,
+                                                 int block_size);
+
+// check whether the block starts from (x_start, y_start) with the size of
+// block_size x block_size has the same color in all rows
+int av1_hash_is_horizontal_perfect(const YV12_BUFFER_CONFIG *picture,
+                                   int block_size, int x_start, int y_start);
+// check whether the block starts from (x_start, y_start) with the size of
+// block_size x block_size has the same color in all columns
+int av1_hash_is_vertical_perfect(const YV12_BUFFER_CONFIG *picture,
+                                 int block_size, int x_start, int y_start);
+void av1_get_block_hash_value(uint8_t *y_src, int stride, int block_size,
+                              uint32_t *hash_value1, uint32_t *hash_value2);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // AV1_ENCODER_HASH_MOTION_H_
diff --git a/av1/encoder/mcomp.c b/av1/encoder/mcomp.c
index 4efadff..02ac6ef 100644
--- a/av1/encoder/mcomp.c
+++ b/av1/encoder/mcomp.c
@@ -2562,10 +2562,45 @@
          (*x->ex_search_count_ptr <= max_ex) && !cpi->rc.is_src_frame_alt_ref;
 }
 
+#if CONFIG_HASH_ME
+#define MAX_HASH_MV_TABLE_SIZE 5
+static void add_to_sort_table(block_hash block_hashes[MAX_HASH_MV_TABLE_SIZE],
+                              int costs[MAX_HASH_MV_TABLE_SIZE], int *existing,
+                              int max_size, block_hash curr_block,
+                              int curr_cost) {
+  if (*existing < max_size) {
+    block_hashes[*existing] = curr_block;
+    costs[*existing] = curr_cost;
+    (*existing)++;
+  } else {
+    int max_cost = 0;
+    int max_cost_idx = 0;
+    for (int i = 0; i < max_size; i++) {
+      if (costs[i] > max_cost) {
+        max_cost = costs[i];
+        max_cost_idx = i;
+      }
+    }
+
+    if (curr_cost < max_cost) {
+      block_hashes[max_cost_idx] = curr_block;
+      costs[max_cost_idx] = curr_cost;
+    }
+  }
+}
+#endif
+
+#if CONFIG_HASH_ME
+int av1_full_pixel_search(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
+                          MV *mvp_full, int step_param, int error_per_bit,
+                          int *cost_list, const MV *ref_mv, int var_max, int rd,
+                          int x_pos, int y_pos) {
+#else
 int av1_full_pixel_search(const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize,
                           MV *mvp_full, int step_param, int error_per_bit,
                           int *cost_list, const MV *ref_mv, int var_max,
                           int rd) {
+#endif
   const SPEED_FEATURES *const sf = &cpi->sf;
   const SEARCH_METHODS method = sf->mv.search_method;
   const aom_variance_fn_ptr_t *fn_ptr = &cpi->fn_ptr[bsize];
@@ -2637,6 +2672,80 @@
   if (method != NSTEP && rd && var < var_max)
     var = av1_get_mvpred_var(x, &x->best_mv.as_mv, ref_mv, fn_ptr, 1);
 
+#if CONFIG_HASH_ME
+  do {
+    if (!cpi->common.allow_screen_content_tools) {
+      break;
+    }
+    // already single ME
+    // get block size and original buffer of current block
+    const int block_height = block_size_high[bsize];
+    const int block_width = block_size_wide[bsize];
+    if (block_height == block_width && x_pos >= 0 && y_pos >= 0) {
+      if (block_width == 8 || block_width == 16 || block_width == 32 ||
+          block_width == 64) {
+        uint8_t *what = x->plane[0].src.buf;
+        const int what_stride = x->plane[0].src.stride;
+        block_hash block_hashes[MAX_HASH_MV_TABLE_SIZE];
+        int costs[MAX_HASH_MV_TABLE_SIZE];
+        int existing = 0;
+        int i;
+        uint32_t hash_value1, hash_value2;
+        MV best_hash_mv;
+        int best_hash_cost = INT_MAX;
+
+        // for the hashMap
+        hash_table *ref_frame_hash =
+            get_ref_frame_hash_map(cpi, x->e_mbd.mi[0]->mbmi.ref_frame[0]);
+
+        av1_get_block_hash_value(what, what_stride, block_width, &hash_value1,
+                                 &hash_value2);
+
+        const int count = av1_hash_table_count(ref_frame_hash, hash_value1);
+        if (count == 0) {
+          break;
+        }
+
+        Iterator iterator =
+            av1_hash_get_first_iterator(ref_frame_hash, hash_value1);
+        for (i = 0; i < count; i++, iterator_increment(&iterator)) {
+          block_hash ref_block_hash = *(block_hash *)(iterator_get(&iterator));
+          if (hash_value2 == ref_block_hash.hash_value2) {
+            int refCost =
+                abs(ref_block_hash.x - x_pos) + abs(ref_block_hash.y - y_pos);
+            add_to_sort_table(block_hashes, costs, &existing,
+                              MAX_HASH_MV_TABLE_SIZE, ref_block_hash, refCost);
+          }
+        }
+
+        if (existing == 0) {
+          break;
+        }
+
+        for (i = 0; i < existing; i++) {
+          MV hash_mv;
+          hash_mv.col = block_hashes[i].x - x_pos;
+          hash_mv.row = block_hashes[i].y - y_pos;
+          if (!is_mv_in(&x->mv_limits, &hash_mv)) {
+            continue;
+          }
+          int currHashCost = av1_get_mvpred_var(x, &hash_mv, ref_mv, fn_ptr, 1);
+          if (currHashCost < best_hash_cost) {
+            best_hash_cost = currHashCost;
+            best_hash_mv = hash_mv;
+          }
+        }
+
+        if (best_hash_cost < var) {
+          x->second_best_mv = x->best_mv;
+          x->best_mv.as_mv = best_hash_mv;
+          var = best_hash_cost;
+        }
+      }
+    }
+  } while (0);
+#endif
+
   return var;
 }
 
diff --git a/av1/encoder/mcomp.h b/av1/encoder/mcomp.h
index 733e415..8de2957 100644
--- a/av1/encoder/mcomp.h
+++ b/av1/encoder/mcomp.h
@@ -131,10 +131,17 @@
 
 struct AV1_COMP;
 
+#if CONFIG_HASH_ME
+int av1_full_pixel_search(const struct AV1_COMP *cpi, MACROBLOCK *x,
+                          BLOCK_SIZE bsize, MV *mvp_full, int step_param,
+                          int error_per_bit, int *cost_list, const MV *ref_mv,
+                          int var_max, int rd, int x_pos, int y_pos);
+#else
 int av1_full_pixel_search(const struct AV1_COMP *cpi, MACROBLOCK *x,
                           BLOCK_SIZE bsize, MV *mvp_full, int step_param,
                           int error_per_bit, int *cost_list, const MV *ref_mv,
                           int var_max, int rd);
+#endif
 
 #if CONFIG_MOTION_VAR
 int av1_obmc_full_pixel_diamond(const struct AV1_COMP *cpi, MACROBLOCK *x,
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index ad2b8e5..c961884 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -7173,9 +7173,16 @@
   switch (mbmi->motion_mode) {
     case SIMPLE_TRANSLATION:
 #endif  // CONFIG_MOTION_VAR
+#if CONFIG_HASH_ME
       bestsme = av1_full_pixel_search(cpi, x, bsize, &mvp_full, step_param,
                                       sadpb, cond_cost_list(cpi, cost_list),
-                                      &ref_mv, INT_MAX, 1);
+                                      &ref_mv, INT_MAX, 1, (MI_SIZE * mi_col),
+                                      (MI_SIZE * mi_row));
+#else
+  bestsme = av1_full_pixel_search(cpi, x, bsize, &mvp_full, step_param, sadpb,
+                                  cond_cost_list(cpi, cost_list), &ref_mv,
+                                  INT_MAX, 1);
+#endif
 #if CONFIG_MOTION_VAR
       break;
     case OBMC_CAUSAL:
@@ -9793,9 +9800,15 @@
     mvp_full.row >>= 3;
     int sadpb = x->sadperbit16;
     int cost_list[5];
+#if CONFIG_HASH_ME
+    int bestsme = av1_full_pixel_search(cpi, x, bsize, &mvp_full, step_param,
+                                        sadpb, cond_cost_list(cpi, cost_list),
+                                        &dv_ref.as_mv, INT_MAX, 1, -1, -1);
+#else
     int bestsme = av1_full_pixel_search(cpi, x, bsize, &mvp_full, step_param,
                                         sadpb, cond_cost_list(cpi, cost_list),
                                         &dv_ref.as_mv, INT_MAX, 1);
+#endif
 
     x->mv_limits = tmp_mv_limits;
     if (bestsme == INT_MAX) continue;
diff --git a/configure b/configure
index ee51cba..0950048 100755
--- a/configure
+++ b/configure
@@ -340,6 +340,7 @@
     uv_lvl
     no_frame_context_signaling
     txmg
+    hash_me
 "
 CONFIG_LIST="
     dependency_tracking
diff --git a/third_party/vector/vector.c b/third_party/vector/vector.c
new file mode 100644
index 0000000..2f0a38e
--- /dev/null
+++ b/third_party/vector/vector.c
@@ -0,0 +1,543 @@
+/*
+The MIT License(MIT)
+Copyright(c) 2016 Peter Goldsborough
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files(the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions :
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#define __STDC_WANT_LIB_EXT1__ 1
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "third_party/vector/vector.h"
+
+int vector_setup(Vector *vector, size_t capacity, size_t element_size) {
+  assert(vector != NULL);
+
+  if (vector == NULL) return VECTOR_ERROR;
+
+  vector->size = 0;
+  vector->capacity = MAX(VECTOR_MINIMUM_CAPACITY, capacity);
+  vector->element_size = element_size;
+  vector->data = malloc(vector->capacity * element_size);
+
+  return vector->data == NULL ? VECTOR_ERROR : VECTOR_SUCCESS;
+}
+
+int vector_copy(Vector *destination, Vector *source) {
+  assert(destination != NULL);
+  assert(source != NULL);
+  assert(vector_is_initialized(source));
+  assert(!vector_is_initialized(destination));
+
+  if (destination == NULL) return VECTOR_ERROR;
+  if (source == NULL) return VECTOR_ERROR;
+  if (vector_is_initialized(destination)) return VECTOR_ERROR;
+  if (!vector_is_initialized(source)) return VECTOR_ERROR;
+
+  /* Copy ALL the data */
+  destination->size = source->size;
+  destination->capacity = source->size * 2;
+  destination->element_size = source->element_size;
+
+  /* Note that we are not necessarily allocating the same capacity */
+  destination->data = malloc(destination->capacity * source->element_size);
+  if (destination->data == NULL) return VECTOR_ERROR;
+
+  memcpy(destination->data, source->data, vector_byte_size(source));
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_copy_assign(Vector *destination, Vector *source) {
+  assert(destination != NULL);
+  assert(source != NULL);
+  assert(vector_is_initialized(source));
+  assert(vector_is_initialized(destination));
+
+  if (destination == NULL) return VECTOR_ERROR;
+  if (source == NULL) return VECTOR_ERROR;
+  if (!vector_is_initialized(destination)) return VECTOR_ERROR;
+  if (!vector_is_initialized(source)) return VECTOR_ERROR;
+
+  vector_destroy(destination);
+
+  return vector_copy(destination, source);
+}
+
+int vector_move(Vector *destination, Vector *source) {
+  assert(destination != NULL);
+  assert(source != NULL);
+
+  if (destination == NULL) return VECTOR_ERROR;
+  if (source == NULL) return VECTOR_ERROR;
+
+  *destination = *source;
+  source->data = NULL;
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_move_assign(Vector *destination, Vector *source) {
+  vector_swap(destination, source);
+  return vector_destroy(source);
+}
+
+int vector_swap(Vector *destination, Vector *source) {
+  void *temp;
+
+  assert(destination != NULL);
+  assert(source != NULL);
+  assert(vector_is_initialized(source));
+  assert(vector_is_initialized(destination));
+
+  if (destination == NULL) return VECTOR_ERROR;
+  if (source == NULL) return VECTOR_ERROR;
+  if (!vector_is_initialized(destination)) return VECTOR_ERROR;
+  if (!vector_is_initialized(source)) return VECTOR_ERROR;
+
+  _vector_swap(&destination->size, &source->size);
+  _vector_swap(&destination->capacity, &source->capacity);
+  _vector_swap(&destination->element_size, &source->element_size);
+
+  temp = destination->data;
+  destination->data = source->data;
+  source->data = temp;
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_destroy(Vector *vector) {
+  assert(vector != NULL);
+
+  if (vector == NULL) return VECTOR_ERROR;
+
+  free(vector->data);
+  vector->data = NULL;
+
+  return VECTOR_SUCCESS;
+}
+
+/* Insertion */
+int vector_push_back(Vector *vector, void *element) {
+  assert(vector != NULL);
+  assert(element != NULL);
+
+  if (_vector_should_grow(vector)) {
+    if (_vector_adjust_capacity(vector) == VECTOR_ERROR) {
+      return VECTOR_ERROR;
+    }
+  }
+
+  _vector_assign(vector, vector->size, element);
+
+  ++vector->size;
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_push_front(Vector *vector, void *element) {
+  return vector_insert(vector, 0, element);
+}
+
+int vector_insert(Vector *vector, size_t index, void *element) {
+  void *offset;
+
+  assert(vector != NULL);
+  assert(element != NULL);
+  assert(index <= vector->size);
+
+  if (vector == NULL) return VECTOR_ERROR;
+  if (element == NULL) return VECTOR_ERROR;
+  if (vector->element_size == 0) return VECTOR_ERROR;
+  if (index > vector->size) return VECTOR_ERROR;
+
+  if (_vector_should_grow(vector)) {
+    if (_vector_adjust_capacity(vector) == VECTOR_ERROR) {
+      return VECTOR_ERROR;
+    }
+  }
+
+  /* Move other elements to the right */
+  if (_vector_move_right(vector, index) == VECTOR_ERROR) {
+    return VECTOR_ERROR;
+  }
+
+  /* Insert the element */
+  offset = _vector_offset(vector, index);
+  memcpy(offset, element, vector->element_size);
+  ++vector->size;
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_assign(Vector *vector, size_t index, void *element) {
+  assert(vector != NULL);
+  assert(element != NULL);
+  assert(index < vector->size);
+
+  if (vector == NULL) return VECTOR_ERROR;
+  if (element == NULL) return VECTOR_ERROR;
+  if (vector->element_size == 0) return VECTOR_ERROR;
+  if (index >= vector->size) return VECTOR_ERROR;
+
+  _vector_assign(vector, index, element);
+
+  return VECTOR_SUCCESS;
+}
+
+/* Deletion */
+int vector_pop_back(Vector *vector) {
+  assert(vector != NULL);
+  assert(vector->size > 0);
+
+  if (vector == NULL) return VECTOR_ERROR;
+  if (vector->element_size == 0) return VECTOR_ERROR;
+
+  --vector->size;
+
+#ifndef VECTOR_NO_SHRINK
+  if (_vector_should_shrink(vector)) {
+    _vector_adjust_capacity(vector);
+  }
+#endif
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_pop_front(Vector *vector) { return vector_erase(vector, 0); }
+
+int vector_erase(Vector *vector, size_t index) {
+  assert(vector != NULL);
+  assert(index < vector->size);
+
+  if (vector == NULL) return VECTOR_ERROR;
+  if (vector->element_size == 0) return VECTOR_ERROR;
+  if (index >= vector->size) return VECTOR_ERROR;
+
+  /* Just overwrite */
+  _vector_move_left(vector, index);
+
+#ifndef VECTOR_NO_SHRINK
+  if (--vector->size == vector->capacity / 4) {
+    _vector_adjust_capacity(vector);
+  }
+#endif
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_clear(Vector *vector) { return vector_resize(vector, 0); }
+
+/* Lookup */
+void *vector_get(Vector *vector, size_t index) {
+  assert(vector != NULL);
+  assert(index < vector->size);
+
+  if (vector == NULL) return NULL;
+  if (vector->element_size == 0) return NULL;
+  if (index >= vector->size) return NULL;
+
+  return _vector_offset(vector, index);
+}
+
+const void *vector_const_get(const Vector *vector, size_t index) {
+  assert(vector != NULL);
+  assert(index < vector->size);
+
+  if (vector == NULL) return NULL;
+  if (vector->element_size == 0) return NULL;
+  if (index >= vector->size) return NULL;
+
+  return _vector_const_offset(vector, index);
+}
+
+void *vector_front(Vector *vector) { return vector_get(vector, 0); }
+
+void *vector_back(Vector *vector) {
+  return vector_get(vector, vector->size - 1);
+}
+
+/* Information */
+
+bool vector_is_initialized(const Vector *vector) {
+  return vector->data != NULL;
+}
+
+size_t vector_byte_size(const Vector *vector) {
+  return vector->size * vector->element_size;
+}
+
+size_t vector_free_space(const Vector *vector) {
+  return vector->capacity - vector->size;
+}
+
+bool vector_is_empty(const Vector *vector) { return vector->size == 0; }
+
+/* Memory management */
+int vector_resize(Vector *vector, size_t new_size) {
+  if (new_size <= vector->capacity * VECTOR_SHRINK_THRESHOLD) {
+    vector->size = new_size;
+    if (_vector_reallocate(vector, new_size * VECTOR_GROWTH_FACTOR) == -1) {
+      return VECTOR_ERROR;
+    }
+  } else if (new_size > vector->capacity) {
+    if (_vector_reallocate(vector, new_size * VECTOR_GROWTH_FACTOR) == -1) {
+      return VECTOR_ERROR;
+    }
+  }
+
+  vector->size = new_size;
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_reserve(Vector *vector, size_t minimum_capacity) {
+  if (minimum_capacity > vector->capacity) {
+    if (_vector_reallocate(vector, minimum_capacity) == VECTOR_ERROR) {
+      return VECTOR_ERROR;
+    }
+  }
+
+  return VECTOR_SUCCESS;
+}
+
+int vector_shrink_to_fit(Vector *vector) {
+  return _vector_reallocate(vector, vector->size);
+}
+
+/* Iterators */
+Iterator vector_begin(Vector *vector) { return vector_iterator(vector, 0); }
+
+Iterator vector_end(Vector *vector) {
+  return vector_iterator(vector, vector->size);
+}
+
+Iterator vector_iterator(Vector *vector, size_t index) {
+  Iterator iterator = { NULL, 0 };
+
+  assert(vector != NULL);
+  assert(index <= vector->size);
+
+  if (vector == NULL) return iterator;
+  if (index > vector->size) return iterator;
+  if (vector->element_size == 0) return iterator;
+
+  iterator.pointer = _vector_offset(vector, index);
+  iterator.element_size = vector->element_size;
+
+  return iterator;
+}
+
+void *iterator_get(Iterator *iterator) { return iterator->pointer; }
+
+int iterator_erase(Vector *vector, Iterator *iterator) {
+  size_t index = iterator_index(vector, iterator);
+
+  if (vector_erase(vector, index) == VECTOR_ERROR) {
+    return VECTOR_ERROR;
+  }
+
+  *iterator = vector_iterator(vector, index);
+
+  return VECTOR_SUCCESS;
+}
+
+void iterator_increment(Iterator *iterator) {
+  assert(iterator != NULL);
+  // iterator->pointer += iterator->element_size;
+  iterator->pointer =
+      (unsigned char *)iterator->pointer + iterator->element_size;
+}
+
+void iterator_decrement(Iterator *iterator) {
+  assert(iterator != NULL);
+  // iterator->pointer -= iterator->element_size;
+  iterator->pointer =
+      (unsigned char *)iterator->pointer - iterator->element_size;
+}
+
+void *iterator_next(Iterator *iterator) {
+  void *current = iterator->pointer;
+  iterator_increment(iterator);
+
+  return current;
+}
+
+void *iterator_previous(Iterator *iterator) {
+  void *current = iterator->pointer;
+  iterator_decrement(iterator);
+
+  return current;
+}
+
+bool iterator_equals(Iterator *first, Iterator *second) {
+  assert(first->element_size == second->element_size);
+  return first->pointer == second->pointer;
+}
+
+bool iterator_is_before(Iterator *first, Iterator *second) {
+  assert(first->element_size == second->element_size);
+  return first->pointer < second->pointer;
+}
+
+bool iterator_is_after(Iterator *first, Iterator *second) {
+  assert(first->element_size == second->element_size);
+  return first->pointer > second->pointer;
+}
+
+size_t iterator_index(Vector *vector, Iterator *iterator) {
+  assert(vector != NULL);
+  assert(iterator != NULL);
+  // return (iterator->pointer - vector->data) / vector->element_size;
+  return ((unsigned char *)iterator->pointer - (unsigned char *)vector->data) /
+         vector->element_size;
+}
+
+/***** PRIVATE *****/
+
+bool _vector_should_grow(Vector *vector) {
+  assert(vector->size <= vector->capacity);
+  return vector->size == vector->capacity;
+}
+
+bool _vector_should_shrink(Vector *vector) {
+  assert(vector->size <= vector->capacity);
+  return vector->size == vector->capacity * VECTOR_SHRINK_THRESHOLD;
+}
+
+size_t _vector_free_bytes(const Vector *vector) {
+  return vector_free_space(vector) * vector->element_size;
+}
+
+void *_vector_offset(Vector *vector, size_t index) {
+  // return vector->data + (index * vector->element_size);
+  return (unsigned char *)vector->data + (index * vector->element_size);
+}
+
+const void *_vector_const_offset(const Vector *vector, size_t index) {
+  // return vector->data + (index * vector->element_size);
+  return (unsigned char *)vector->data + (index * vector->element_size);
+}
+
+void _vector_assign(Vector *vector, size_t index, void *element) {
+  /* Insert the element */
+  void *offset = _vector_offset(vector, index);
+  memcpy(offset, element, vector->element_size);
+}
+
+int _vector_move_right(Vector *vector, size_t index) {
+  assert(vector->size < vector->capacity);
+
+  /* The location where to start to move from. */
+  void *offset = _vector_offset(vector, index);
+
+  /* How many to move to the right. */
+  size_t elements_in_bytes = (vector->size - index) * vector->element_size;
+
+#ifdef __STDC_LIB_EXT1__
+  size_t right_capacity_in_bytes =
+      (vector->capacity - (index + 1)) * vector->element_size;
+
+  /* clang-format off */
+    int return_code =  memmove_s(
+        offset + vector->element_size,
+        right_capacity_in_bytes,
+        offset,
+        elements_in_bytes);
+
+  /* clang-format on */
+
+  return return_code == 0 ? VECTOR_SUCCESS : VECTOR_ERROR;
+
+#else
+  // memmove(offset + vector->element_size, offset, elements_in_bytes);
+  memmove((unsigned char *)offset + vector->element_size, offset,
+          elements_in_bytes);
+  return VECTOR_SUCCESS;
+#endif
+}
+
+void _vector_move_left(Vector *vector, size_t index) {
+  size_t right_elements_in_bytes;
+  void *offset;
+
+  /* The offset into the memory */
+  offset = _vector_offset(vector, index);
+
+  /* How many to move to the left */
+  right_elements_in_bytes = (vector->size - index - 1) * vector->element_size;
+
+  // memmove(offset, offset + vector->element_size, right_elements_in_bytes);
+  memmove(offset, (unsigned char *)offset + vector->element_size,
+          right_elements_in_bytes);
+}
+
+int _vector_adjust_capacity(Vector *vector) {
+  return _vector_reallocate(vector,
+                            MAX(1, vector->size * VECTOR_GROWTH_FACTOR));
+}
+
+int _vector_reallocate(Vector *vector, size_t new_capacity) {
+  size_t new_capacity_in_bytes;
+  void *old;
+  assert(vector != NULL);
+
+  if (new_capacity < VECTOR_MINIMUM_CAPACITY) {
+    if (vector->capacity > VECTOR_MINIMUM_CAPACITY) {
+      new_capacity = VECTOR_MINIMUM_CAPACITY;
+    } else {
+      /* NO-OP */
+      return VECTOR_SUCCESS;
+    }
+  }
+
+  new_capacity_in_bytes = new_capacity * vector->element_size;
+  old = vector->data;
+
+  if ((vector->data = malloc(new_capacity_in_bytes)) == NULL) {
+    return VECTOR_ERROR;
+  }
+
+#ifdef __STDC_LIB_EXT1__
+  /* clang-format off */
+    if (memcpy_s(vector->data,
+                             new_capacity_in_bytes,
+                             old,
+                             vector_byte_size(vector)) != 0) {
+        return VECTOR_ERROR;
+    }
+/* clang-format on */
+#else
+  memcpy(vector->data, old, vector_byte_size(vector));
+#endif
+
+  vector->capacity = new_capacity;
+
+  free(old);
+
+  return VECTOR_SUCCESS;
+}
+
+void _vector_swap(size_t *first, size_t *second) {
+  size_t temp = *first;
+  *first = *second;
+  *second = temp;
+}
diff --git a/third_party/vector/vector.h b/third_party/vector/vector.h
new file mode 100644
index 0000000..2bf1a9a
--- /dev/null
+++ b/third_party/vector/vector.h
@@ -0,0 +1,159 @@
+/*
+The MIT License(MIT)
+Copyright(c) 2016 Peter Goldsborough
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files(the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions :
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef VECTOR_H
+#define VECTOR_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/***** DEFINITIONS *****/
+
+#define VECTOR_MINIMUM_CAPACITY 2
+#define VECTOR_GROWTH_FACTOR 2
+#define VECTOR_SHRINK_THRESHOLD (1 / 4)
+
+#define VECTOR_ERROR -1
+#define VECTOR_SUCCESS 0
+
+#define VECTOR_UNINITIALIZED NULL
+#define VECTOR_INITIALIZER \
+  { 0, 0, 0, VECTOR_UNINITIALIZED }
+
+/***** STRUCTURES *****/
+
+typedef struct Vector {
+  size_t size;
+  size_t capacity;
+  size_t element_size;
+
+  void *data;
+} Vector;
+
+typedef struct Iterator {
+  void *pointer;
+  size_t element_size;
+} Iterator;
+
+/***** METHODS *****/
+
+/* Constructor */
+int vector_setup(Vector *vector, size_t capacity, size_t element_size);
+
+/* Copy Constructor */
+int vector_copy(Vector *destination, Vector *source);
+
+/* Copy Assignment */
+int vector_copy_assign(Vector *destination, Vector *source);
+
+/* Move Constructor */
+int vector_move(Vector *destination, Vector *source);
+
+/* Move Assignment */
+int vector_move_assign(Vector *destination, Vector *source);
+
+int vector_swap(Vector *destination, Vector *source);
+
+/* Destructor */
+int vector_destroy(Vector *vector);
+
+/* Insertion */
+int vector_push_back(Vector *vector, void *element);
+int vector_push_front(Vector *vector, void *element);
+int vector_insert(Vector *vector, size_t index, void *element);
+int vector_assign(Vector *vector, size_t index, void *element);
+
+/* Deletion */
+int vector_pop_back(Vector *vector);
+int vector_pop_front(Vector *vector);
+int vector_erase(Vector *vector, size_t index);
+int vector_clear(Vector *vector);
+
+/* Lookup */
+void *vector_get(Vector *vector, size_t index);
+const void *vector_const_get(const Vector *vector, size_t index);
+void *vector_front(Vector *vector);
+void *vector_back(Vector *vector);
+#define VECTOR_GET_AS(type, vector_pointer, index) \
+  *((type *)vector_get((vector_pointer), (index)))
+
+/* Information */
+bool vector_is_initialized(const Vector *vector);
+size_t vector_byte_size(const Vector *vector);
+size_t vector_free_space(const Vector *vector);
+bool vector_is_empty(const Vector *vector);
+
+/* Memory management */
+int vector_resize(Vector *vector, size_t new_size);
+int vector_reserve(Vector *vector, size_t minimum_capacity);
+int vector_shrink_to_fit(Vector *vector);
+
+/* Iterators */
+Iterator vector_begin(Vector *vector);
+Iterator vector_end(Vector *vector);
+Iterator vector_iterator(Vector *vector, size_t index);
+
+void *iterator_get(Iterator *iterator);
+#define ITERATOR_GET_AS(type, iterator) *((type *)iterator_get((iterator)))
+
+int iterator_erase(Vector *vector, Iterator *iterator);
+
+void iterator_increment(Iterator *iterator);
+void iterator_decrement(Iterator *iterator);
+
+void *iterator_next(Iterator *iterator);
+void *iterator_previous(Iterator *iterator);
+
+bool iterator_equals(Iterator *first, Iterator *second);
+bool iterator_is_before(Iterator *first, Iterator *second);
+bool iterator_is_after(Iterator *first, Iterator *second);
+
+size_t iterator_index(Vector *vector, Iterator *iterator);
+
+#define VECTOR_FOR_EACH(vector_pointer, iterator_name)           \
+  for (Iterator(iterator_name) = vector_begin((vector_pointer)), \
+      end = vector_end((vector_pointer));                        \
+       !iterator_equals(&(iterator_name), &end);                 \
+       iterator_increment(&(iterator_name)))
+
+/***** PRIVATE *****/
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+bool _vector_should_grow(Vector *vector);
+bool _vector_should_shrink(Vector *vector);
+
+size_t _vector_free_bytes(const Vector *vector);
+void *_vector_offset(Vector *vector, size_t index);
+const void *_vector_const_offset(const Vector *vector, size_t index);
+
+void _vector_assign(Vector *vector, size_t index, void *element);
+
+int _vector_move_right(Vector *vector, size_t index);
+void _vector_move_left(Vector *vector, size_t index);
+
+int _vector_adjust_capacity(Vector *vector);
+int _vector_reallocate(Vector *vector, size_t new_capacity);
+
+void _vector_swap(size_t *first, size_t *second);
+
+#endif /* VECTOR_H */