+static struct ovsdb_monitor_changes *
+ovsdb_monitor_table_add_changes(struct ovsdb_monitor_table *mt,
+ uint64_t next_txn)
+{
+ struct ovsdb_monitor_changes *changes;
+
+ changes = xzalloc(sizeof *changes);
+
+ changes->transaction = next_txn;
+ changes->mt = mt;
+ changes->n_refs = 1;
+ hmap_init(&changes->rows);
+ hmap_insert(&mt->changes, &changes->hmap_node, hash_uint64(next_txn));
+
+ return changes;
+};
+
+static struct ovsdb_monitor_changes *
+ovsdb_monitor_table_find_changes(struct ovsdb_monitor_table *mt,
+ uint64_t transaction)
+{
+ struct ovsdb_monitor_changes *changes;
+ size_t hash = hash_uint64(transaction);
+
+ HMAP_FOR_EACH_WITH_HASH(changes, hmap_node, hash, &mt->changes) {
+ if (changes->transaction == transaction) {
+ return changes;
+ }
+ }
+
+ return NULL;
+}
+
+/* Stop currently tracking changes to table 'mt' since 'transaction'. */
+static void
+ovsdb_monitor_table_untrack_changes(struct ovsdb_monitor_table *mt,
+ uint64_t transaction)
+{
+ struct ovsdb_monitor_changes *changes =
+ ovsdb_monitor_table_find_changes(mt, transaction);
+ if (changes) {
+ if (--changes->n_refs == 0) {
+ hmap_remove(&mt->changes, &changes->hmap_node);
+ ovsdb_monitor_changes_destroy(changes);
+ }
+ }
+}
+
+/* Start tracking changes to table 'mt' begins from 'transaction' inclusive.
+ */
+static void
+ovsdb_monitor_table_track_changes(struct ovsdb_monitor_table *mt,
+ uint64_t transaction)
+{
+ struct ovsdb_monitor_changes *changes;
+
+ changes = ovsdb_monitor_table_find_changes(mt, transaction);
+ if (changes) {
+ changes->n_refs++;
+ } else {
+ ovsdb_monitor_table_add_changes(mt, transaction);
+ }
+}
+
+static void
+ovsdb_monitor_changes_destroy(struct ovsdb_monitor_changes *changes)
+{
+ struct ovsdb_monitor_row *row, *next;
+
+ HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) {
+ hmap_remove(&changes->rows, &row->hmap_node);
+ ovsdb_monitor_row_destroy(changes->mt, row);
+ }
+ hmap_destroy(&changes->rows);
+ free(changes);
+}
+
+static enum ovsdb_monitor_selection
+ovsdb_monitor_row_update_type(bool initial, const bool old, const bool new)
+{
+ return initial ? OJMS_INITIAL
+ : !old ? OJMS_INSERT
+ : !new ? OJMS_DELETE
+ : OJMS_MODIFY;
+}
+static bool
+ovsdb_monitor_row_skip_update(const struct ovsdb_monitor_table *mt,
+ const struct ovsdb_monitor_row *row,
+ enum ovsdb_monitor_selection type,
+ unsigned long int *changed)
+{
+ if (!(mt->select & type)) {
+ return true;
+ }
+
+ if (type == OJMS_MODIFY) {
+ size_t i, n_changes;
+
+ n_changes = 0;
+ memset(changed, 0, bitmap_n_bytes(mt->n_columns));
+ for (i = 0; i < mt->n_columns; i++) {
+ const struct ovsdb_column *c = mt->columns[i].column;
+ if (!ovsdb_datum_equals(&row->old[i], &row->new[i], &c->type)) {
+ bitmap_set1(changed, i);
+ n_changes++;
+ }
+ }
+ if (!n_changes) {
+ /* No actual changes: presumably a row changed and then
+ * changed back later. */
+ return true;
+ }
+ }
+
+ return false;
+}
+