From 5ba593f1246cb8061fc621cd3e3fb94b943d8b89 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 3 Feb 2020 00:42:50 -0800 Subject: [PATCH] int_dict: Replace with Map. Signed-off-by: Anders Kaseorg --- frontend_tests/node_tests/int_dict.js | 152 ----------------------- frontend_tests/node_tests/stream_list.js | 8 +- frontend_tests/node_tests/unread.js | 3 +- static/js/bot_data.js | 6 +- static/js/bundles/app.js | 1 - static/js/condense.js | 3 +- static/js/int_dict.ts | 80 ------------ static/js/muting.js | 3 +- static/js/people.js | 11 +- static/js/recent_senders.js | 9 +- static/js/settings_account.js | 4 +- static/js/settings_streams.js | 3 +- static/js/stream_data.js | 3 +- static/js/stream_list.js | 3 +- static/js/topic_data.js | 3 +- static/js/topic_list.js | 5 +- static/js/typeahead_helper.js | 3 +- static/js/unread.js | 7 +- static/js/user_groups.js | 3 +- static/js/user_status.js | 4 +- 20 files changed, 30 insertions(+), 284 deletions(-) delete mode 100644 frontend_tests/node_tests/int_dict.js delete mode 100644 static/js/int_dict.ts diff --git a/frontend_tests/node_tests/int_dict.js b/frontend_tests/node_tests/int_dict.js deleted file mode 100644 index 074210bbd1..0000000000 --- a/frontend_tests/node_tests/int_dict.js +++ /dev/null @@ -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); -}); diff --git a/frontend_tests/node_tests/stream_list.js b/frontend_tests/node_tests/stream_list.js index edb24f8092..afd9e7ba81 100644 --- a/frontend_tests/node_tests/stream_list.js +++ b/frontend_tests/node_tests/stream_list.js @@ -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, diff --git a/frontend_tests/node_tests/unread.js b/frontend_tests/node_tests/unread.js index 6fad8aece7..441f794a2a 100644 --- a/frontend_tests/node_tests/unread.js +++ b/frontend_tests/node_tests/unread.js @@ -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); diff --git a/static/js/bot_data.js b/static/js/bot_data.js index 8a116ace23..050f0cc39b 100644 --- a/static/js/bot_data.js +++ b/static/js/bot_data.js @@ -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']; diff --git a/static/js/bundles/app.js b/static/js/bundles/app.js index b4e102470e..5682898827 100644 --- a/static/js/bundles/app.js +++ b/static/js/bundles/app.js @@ -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"; diff --git a/static/js/condense.js b/static/js/condense.js index cb84bdb96a..4b2437850f 100644 --- a/static/js/condense.js +++ b/static/js/condense.js @@ -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(); diff --git a/static/js/int_dict.ts b/static/js/int_dict.ts deleted file mode 100644 index 9bbda0f079..0000000000 --- a/static/js/int_dict.ts +++ /dev/null @@ -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 { - private _map = new Map(); - - get(key: number): V | undefined { - key = this._convert(key); - return this._map.get(key); - } - - set(key: number, value: V): IntDict { - 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 { - return this._map.keys(); - } - - values(): Iterator { - 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; - } -} diff --git a/static/js/muting.js b/static/js/muting.js index 5181c64550..d52f63f0a5 100644 --- a/static/js/muting.js +++ b/static/js/muting.js @@ -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); diff --git a/static/js/people.js b/static/js/people.js index 6bec2f3aa9..a44b9b0dc1 100644 --- a/static/js/people.js +++ b/static/js/people.js @@ -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. diff --git a/static/js/recent_senders.js b/static/js/recent_senders.js index f0f0d65023..e09b230b81 100644 --- a/static/js/recent_senders.js +++ b/static/js/recent_senders.js @@ -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) { diff --git a/static/js/settings_account.js b/static/js/settings_account.js index 9750cf86a2..a0951c2ff8 100644 --- a/static/js/settings_account.js +++ b/static/js/settings_account.js @@ -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) { diff --git a/static/js/settings_streams.js b/static/js/settings_streams.js index 940fab56e9..5b212ee4f8 100644 --- a/static/js/settings_streams.js +++ b/static/js/settings_streams.js @@ -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(); diff --git a/static/js/stream_data.js b/static/js/stream_data.js index 4c25ca8675..567b72408a 100644 --- a/static/js/stream_data.js +++ b/static/js/stream_data.js @@ -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(); diff --git a/static/js/stream_list.js b/static/js/stream_list.js index f9770e1202..d2e19fcb33 100644 --- a/static/js/stream_list.js +++ b/static/js/stream_list.js @@ -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); diff --git a/static/js/topic_data.js b/static/js/topic_data.js index 04cf4c542f..78bf33a990 100644 --- a/static/js/topic_data.js +++ b/static/js/topic_data.js @@ -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) => { diff --git a/static/js/topic_list.js b/static/js/topic_list.js index 1d143e8f26..e1a77c580a 100644 --- a/static/js/topic_list.js +++ b/static/js/topic_list.js @@ -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; diff --git a/static/js/typeahead_helper.js b/static/js/typeahead_helper.js index deb6c0ff85..21fc9ddd6a 100644 --- a/static/js/typeahead_helper.js +++ b/static/js/typeahead_helper.js @@ -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) { diff --git a/static/js/unread.js b/static/js/unread.js index 467a3ecafe..3d04ba7424 100644 --- a/static/js/unread.js +++ b/static/js/unread.js @@ -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 diff --git a/static/js/user_groups.js b/static/js/user_groups.js index 46793609d4..89e1952cb5 100644 --- a/static/js/user_groups.js +++ b/static/js/user_groups.js @@ -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! diff --git a/static/js/user_status.js b/static/js/user_status.js index a3113f5ead..4e2d366315 100644 --- a/static/js/user_status.js +++ b/static/js/user_status.js @@ -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({