int_dict: Replace with Map.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
This commit is contained in:
Anders Kaseorg 2020-02-03 00:42:50 -08:00 committed by Tim Abbott
parent 2868b7c3e3
commit 5ba593f124
20 changed files with 30 additions and 284 deletions

View File

@ -1,152 +0,0 @@
set_global('blueslip', global.make_zblueslip());
const IntDict = zrequire('int_dict').IntDict;
run_test('basic', () => {
const d = new IntDict();
assert.equal(d.size, 0);
assert.deepEqual(Array.from(d.keys()), []);
d.set(101, 'bar');
assert.equal(d.get(101), 'bar');
assert.notEqual(d.size, 0);
d.set(101, 'baz');
assert.equal(d.get(101), 'baz');
assert.equal(d.size, 1);
d.set(102, 'qux');
assert.equal(d.get(101), 'baz');
assert.equal(d.get(102), 'qux');
assert.equal(d.size, 2);
assert.equal(d.has(102), true);
assert.equal(d.has(999), false);
assert.deepEqual(Array.from(d.keys()), [101, 102]);
assert.deepEqual(Array.from(d.values()), ['baz', 'qux']);
d.delete(102);
assert.equal(d.has(102), false);
assert.strictEqual(d.get(102), undefined);
assert.deepEqual(Array.from(d.keys()), [101]);
const val = ['fred'];
const res = d.set(103, val);
assert.strictEqual(res, d);
});
run_test('each', () => {
const d = new IntDict();
d.set(4, 40);
d.set(5, 50);
d.set(6, 60);
let unseen_keys = Array.from(d.keys());
let cnt = 0;
for (const [k, v] of d) {
assert.equal(v, d.get(k));
unseen_keys = _.without(unseen_keys, k);
cnt += 1;
}
assert.equal(cnt, d.size);
assert.equal(unseen_keys.length, 0);
});
/*
run_test('benchmark', () => {
const d = new IntDict();
const n = 5000;
const t1 = new Date().getTime();
_.each(_.range(n), (i) => {
d.set(i, i);
});
_.each(_.range(n), (i) => {
d.get(i, i);
});
const t2 = new Date().getTime();
const elapsed = t2 - t1;
console.log('elapsed (milli)', elapsed);
console.log('per (micro)', 1000 * elapsed / n);
});
*/
run_test('undefined_keys', () => {
blueslip.clear_test_data();
blueslip.set_test_data('error', 'Tried to call a IntDict method with an undefined key.');
const d = new IntDict();
assert.equal(d.has(undefined), false);
assert.strictEqual(d.get(undefined), undefined);
assert.equal(blueslip.get_test_logs('error').length, 2);
});
run_test('non integers', () => {
blueslip.clear_test_data();
blueslip.set_test_data('error', 'Tried to call a IntDict method with a non-integer.');
const d = new IntDict();
assert.equal(d.has('some-string'), false);
assert.equal(blueslip.get_test_logs('error').length, 1);
// verify stringified ints still work
blueslip.clear_test_data();
blueslip.set_test_data('error', 'Tried to call a IntDict method with a non-integer.');
d.set('5', 'five');
assert.equal(d.has(5), true);
assert.equal(d.has('5'), true);
assert.equal(d.get(5), 'five');
assert.equal(d.get('5'), 'five');
assert.equal(blueslip.get_test_logs('error').length, 3);
});
run_test('num_items', () => {
const d = new IntDict();
assert.equal(d.size, 0);
d.set(101, 1);
assert.equal(d.size, 1);
d.set(101, 2);
assert.equal(d.size, 1);
d.set(102, 1);
assert.equal(d.size, 2);
d.delete(101);
assert.equal(d.size, 1);
});
run_test('clear', () => {
const d = new IntDict();
function populate() {
d.set(101, 1);
assert.equal(d.get(101), 1);
d.set(102, 2);
assert.equal(d.get(102), 2);
}
populate();
assert.equal(d.size, 2);
d.clear();
assert.equal(d.get(101), undefined);
assert.equal(d.get(102), undefined);
assert.equal(d.size, 0);
// make sure it still works after clearing
populate();
assert.equal(d.size, 2);
});

View File

@ -3,8 +3,6 @@ set_global('$', global.make_zjquery());
set_global('blueslip', global.make_zblueslip());
set_global('i18n', global.stub_i18n);
const IntDict = zrequire('int_dict').IntDict;
zrequire('unread_ui');
zrequire('Filter', 'js/filter');
zrequire('topic_data');
@ -637,7 +635,7 @@ run_test('update_count_in_dom', () => {
stream_li.addClass('stream-with-count');
assert(stream_li.hasClass('stream-with-count'));
const stream_count = new IntDict();
const stream_count = new Map();
const stream_id = 11;
const stream_row = {
@ -649,7 +647,7 @@ run_test('update_count_in_dom', () => {
stream_count.set(stream_id, 0);
const counts = {
stream_count: stream_count,
topic_count: new IntDict(),
topic_count: new Map(),
};
stream_list.update_dom_with_unread_counts(counts);
@ -742,7 +740,7 @@ run_test('refresh_pin', () => {
run_test('create_initial_sidebar_rows', () => {
initialize_stream_data();
const html_dict = new IntDict();
const html_dict = new Map();
stream_list.stream_sidebar = {
has_row_for: return_false,

View File

@ -6,7 +6,6 @@ zrequire('stream_data');
zrequire('unread');
zrequire('settings_notifications');
const FoldDict = zrequire('fold_dict').FoldDict;
const IntDict = zrequire('int_dict').IntDict;
set_global('page_params', {});
set_global('blueslip', {});
@ -168,7 +167,7 @@ run_test('changing_topics', () => {
unread: true,
};
const message_dict = new IntDict();
const message_dict = new Map();
message_dict.set(message.id, message);
message_dict.set(other_message.id, other_message);
message_dict.set(sticky_message.id, sticky_message);

View File

@ -1,10 +1,8 @@
const IntDict = require('./int_dict').IntDict;
const bots = new IntDict();
const bots = new Map();
const bot_fields = ['api_key', 'avatar_url', 'default_all_public_streams',
'default_events_register_stream', 'default_sending_stream',
'email', 'full_name', 'is_active', 'owner', 'bot_type', 'user_id'];
const services = new IntDict();
const services = new Map();
const services_fields = ['base_url', 'interface',
'config_data', 'service_name', 'token'];

View File

@ -28,7 +28,6 @@ import "../keydown_util.js";
import "../lightbox_canvas.js";
import "../rtl.js";
import "../lazy_set.js";
import "../int_dict.ts";
import "../fold_dict.ts";
import "../scroll_util.js";
import "../components.js";

View File

@ -10,9 +10,8 @@ This library implements two related, similar concepts:
*/
const IntDict = require('./int_dict').IntDict;
const _message_content_height_cache = new IntDict();
const _message_content_height_cache = new Map();
function show_more_link(row) {
row.find(".message_condenser").hide();

View File

@ -1,80 +0,0 @@
import * as _ from 'underscore';
/*
If we know our keys are ints, the
map-based implementation is about
20% faster than if we have to normalize
keys as strings. Of course, this
requires us to be a bit careful in the
calling code. We validate ints, which
is cheap, but we don't handle them; we
just report errors.
This has a subset of methods from our old
Dict class, so it's not quite a drop-in
replacement. For things like setdefault,
it's easier to just use a two-liner in the
calling code. If your Dict uses from_array,
convert it to a Set, not an IntDict.
*/
export class IntDict<V> {
private _map = new Map();
get(key: number): V | undefined {
key = this._convert(key);
return this._map.get(key);
}
set(key: number, value: V): IntDict<V> {
key = this._convert(key);
this._map.set(key, value);
return this;
}
has(key: number): boolean {
key = this._convert(key);
return this._map.has(key);
}
delete(key: number): boolean {
key = this._convert(key);
return this._map.delete(key);
}
keys(): Iterator<number> {
return this._map.keys();
}
values(): Iterator<V> {
return this._map.values();
}
[Symbol.iterator](): Iterator<[number, V]> {
return this._map.entries();
}
get size(): number {
return this._map.size;
}
clear(): void {
this._map.clear();
}
private _convert(key: number): number {
// These checks are cheap! (at least on node.js)
if (key === undefined) {
blueslip.error("Tried to call a IntDict method with an undefined key.");
return key;
}
if (typeof key !== 'number') {
blueslip.error("Tried to call a IntDict method with a non-integer.");
// @ts-ignore
return parseInt(key, 10);
}
return key;
}
}

View File

@ -1,7 +1,6 @@
const FoldDict = require('./fold_dict').FoldDict;
const IntDict = require('./int_dict').IntDict;
const muted_topics = new IntDict();
const muted_topics = new Map();
exports.add_muted_topic = function (stream_id, topic) {
let sub_dict = muted_topics.get(stream_id);

View File

@ -1,6 +1,5 @@
const util = require("./util");
require("unorm"); // String.prototype.normalize polyfill for IE11
const IntDict = require('./int_dict').IntDict;
const FoldDict = require('./fold_dict').FoldDict;
const typeahead = require("../shared/js/typeahead");
@ -22,14 +21,14 @@ exports.init = function () {
// people_dict over time and always do lookups by user_id.
people_dict = new FoldDict();
people_by_name_dict = new FoldDict();
people_by_user_id_dict = new IntDict();
people_by_user_id_dict = new Map();
// The next dictionary includes all active users (human/user)
// in our realm, but it excludes non-active users and
// cross-realm bots.
active_user_dict = new IntDict();
cross_realm_dict = new IntDict(); // keyed by user_id
pm_recipient_count_dict = new IntDict();
active_user_dict = new Map();
cross_realm_dict = new Map(); // keyed by user_id
pm_recipient_count_dict = new Map();
// This maintains a set of ids of people with same full names.
duplicate_full_name_data = new FoldDict();
@ -816,7 +815,7 @@ exports.build_person_matcher = function (query) {
};
exports.filter_people_by_search_terms = function (users, search_terms) {
const filtered_users = new IntDict();
const filtered_users = new Map();
// Build our matchers outside the loop to avoid some
// search overhead that is not user-specific.

View File

@ -1,8 +1,7 @@
const FoldDict = require('./fold_dict').FoldDict;
const IntDict = require('./int_dict').IntDict;
const topic_senders = new IntDict(); // key is stream-id, value is Map
const stream_senders = new IntDict(); // key is stream-id, value is Map
const topic_senders = new Map(); // key is stream-id, value is Map
const stream_senders = new Map(); // key is stream-id, value is Map
exports.process_message_for_senders = function (message) {
const stream_id = message.stream_id;
@ -10,7 +9,7 @@ exports.process_message_for_senders = function (message) {
// Process most recent sender to topic
const topic_dict = topic_senders.get(stream_id) || new FoldDict();
let sender_message_ids = topic_dict.get(topic) || new IntDict();
let sender_message_ids = topic_dict.get(topic) || new Map();
let old_message_id = sender_message_ids.get(message.sender_id);
if (old_message_id === undefined || old_message_id < message.id) {
@ -21,7 +20,7 @@ exports.process_message_for_senders = function (message) {
topic_senders.set(stream_id, topic_dict);
// Process most recent sender to whole stream
sender_message_ids = stream_senders.get(stream_id) || new IntDict();
sender_message_ids = stream_senders.get(stream_id) || new Map();
old_message_id = sender_message_ids.get(message.sender_id);
if (old_message_id === undefined || old_message_id < message.id) {

View File

@ -1,5 +1,3 @@
const IntDict = require("./int_dict").IntDict;
const render_settings_custom_user_profile_field = require("../templates/settings/custom_user_profile_field.hbs");
const render_settings_dev_env_email_access = require('../templates/settings/dev_env_email_access.hbs');
const render_settings_api_key_modal = require('../templates/settings/api_key_modal.hbs');
@ -180,7 +178,7 @@ exports.initialize_custom_date_type_fields = function (element_id) {
exports.initialize_custom_user_type_fields = function (element_id, user_id, is_editable,
set_handler_on_update) {
const field_types = page_params.custom_profile_field_types;
const user_pills = new IntDict();
const user_pills = new Map();
const person = people.get_by_user_id(user_id);
if (person.is_bot) {

View File

@ -1,5 +1,4 @@
const render_admin_default_streams_list = require("../templates/admin_default_streams_list.hbs");
const IntDict = require('./int_dict').IntDict;
const meta = {
loaded: false,
@ -21,7 +20,7 @@ exports.maybe_disable_widgets = function () {
exports.build_default_stream_table = function (streams_data) {
const self = {};
self.row_dict = new IntDict();
self.row_dict = new Map();
const table = $("#admin_default_streams_table").expectOne();

View File

@ -1,5 +1,4 @@
const util = require("./util");
const IntDict = require('./int_dict').IntDict;
const FoldDict = require('./fold_dict').FoldDict;
const LazySet = require('./lazy_set').LazySet;
const settings_config = require("./settings_config");
@ -110,7 +109,7 @@ exports.clear_subscriptions = function () {
stream_info = new BinaryDict(function (sub) {
return sub.subscribed;
});
subs_by_stream_id = new IntDict();
subs_by_stream_id = new Map();
};
exports.clear_subscriptions();

View File

@ -1,6 +1,5 @@
const render_stream_privacy = require('../templates/stream_privacy.hbs');
const render_stream_sidebar_row = require('../templates/stream_sidebar_row.hbs');
const IntDict = require('./int_dict').IntDict;
let has_scrolled = false;
@ -29,7 +28,7 @@ exports.update_count_in_dom = function (unread_count_elem, count) {
exports.stream_sidebar = (function () {
const self = {};
self.rows = new IntDict(); // stream id -> row widget
self.rows = new Map(); // stream id -> row widget
self.set_row = function (stream_id, widget) {
self.rows.set(stream_id, widget);

View File

@ -1,7 +1,6 @@
const IntDict = require('./int_dict').IntDict;
const FoldDict = require('./fold_dict').FoldDict;
const stream_dict = new IntDict(); // stream_id -> topic_history object
const stream_dict = new Map(); // stream_id -> topic_history object
const fetched_stream_ids = new Set();
exports.is_complete_for_stream_id = (stream_id) => {

View File

@ -1,18 +1,17 @@
const render_more_topics = require('../templates/more_topics.hbs');
const render_more_topics_spinner = require('../templates/more_topics_spinner.hbs');
const render_topic_list_item = require('../templates/topic_list_item.hbs');
const IntDict = require('./int_dict').IntDict;
const topic_list_data = require('./topic_list_data');
/*
Track all active widgets with an IntDict.
Track all active widgets with a Map.
(We have at max one for now, but we may
eventually allow multiple streams to be
expanded.)
*/
const active_widgets = new IntDict();
const active_widgets = new Map();
// We know whether we're zoomed or not.
let zoomed = false;

View File

@ -2,7 +2,6 @@ const util = require("./util");
const pygments_data = require("../generated/pygments_data.json");
const typeahead = require("../shared/js/typeahead");
const render_typeahead_list_item = require('../templates/typeahead_list_item.hbs');
const IntDict = require('./int_dict').IntDict;
// Returns an array of private message recipients, removing empty elements.
// For example, "a,,b, " => ["a", "b"]
@ -64,7 +63,7 @@ exports.render_typeahead_item = function (args) {
return render_typeahead_list_item(args);
};
const rendered = { persons: new IntDict(), streams: new IntDict(), user_groups: new IntDict() };
const rendered = { persons: new Map(), streams: new Map(), user_groups: new Map() };
exports.render_person = function (person) {
if (person.special_item_text) {

View File

@ -1,6 +1,5 @@
const util = require("./util");
const FoldDict = require('./fold_dict').FoldDict;
const IntDict = require('./int_dict').IntDict;
// The unread module tracks the message IDs and locations of the
// user's unread messages. The tracking is initialized with
@ -28,7 +27,7 @@ const unread_messages = new Set();
function make_bucketer(options) {
const self = {};
const key_to_bucket = new options.KeyDict();
const reverse_lookup = new IntDict();
const reverse_lookup = new Map();
self.clear = function () {
key_to_bucket.clear();
@ -195,7 +194,7 @@ exports.unread_topic_counter = (function () {
const self = {};
const bucketer = make_bucketer({
KeyDict: IntDict, // bucket keys are stream_ids
KeyDict: Map, // bucket keys are stream_ids
make_bucket: make_per_stream_bucketer,
});
@ -236,7 +235,7 @@ exports.unread_topic_counter = (function () {
self.get_counts = function () {
const res = {};
res.stream_unread_messages = 0;
res.stream_count = new IntDict(); // hash by stream_id -> count
res.stream_count = new Map(); // hash by stream_id -> count
for (const [stream_id, per_stream_bucketer] of bucketer) {
// We track unread counts for streams that may be currently

View File

@ -1,5 +1,4 @@
const FoldDict = require('./fold_dict').FoldDict;
const IntDict = require('./int_dict').IntDict;
let user_group_name_dict;
let user_group_by_id_dict;
@ -8,7 +7,7 @@ let user_group_by_id_dict;
// can easily clear data.
exports.init = function () {
user_group_name_dict = new FoldDict();
user_group_by_id_dict = new IntDict();
user_group_by_id_dict = new Map();
};
// WE INITIALIZE DATA STRUCTURES HERE!

View File

@ -1,7 +1,5 @@
const IntDict = require('./int_dict').IntDict;
const away_user_ids = new Set();
const user_info = new IntDict();
const user_info = new Map();
exports.server_update = function (opts) {
channel.post({