Skip to content

Commit

Permalink
Improve performance of builders even further
Browse files Browse the repository at this point in the history
By negating hashing altogether.

The increase is around 1000-ish nanoseconds saved.
  • Loading branch information
arqunis committed Dec 27, 2017
1 parent 8733391 commit 3a0c890
Show file tree
Hide file tree
Showing 25 changed files with 201 additions and 66 deletions.
14 changes: 7 additions & 7 deletions src/builder/create_embed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ use chrono::{DateTime, TimeZone};
use internal::prelude::*;
use model::channel::Embed;
use serde_json::Value;
use std::collections::HashMap;
use std::default::Default;
use std::fmt::Display;
use utils;
use super::VecMap;

#[cfg(feature = "utils")]
use utils::Colour;
Expand All @@ -39,7 +39,7 @@ use utils::Colour;
/// [`Embed`]: ../model/struct.Embed.html
/// [`ExecuteWebhook::embeds`]: struct.ExecuteWebhook.html#method.embeds
#[derive(Clone, Debug)]
pub struct CreateEmbed(pub HashMap<&'static str, Value>);
pub struct CreateEmbed(pub VecMap<&'static str, Value>);

impl CreateEmbed {
/// Set the author of the embed.
Expand All @@ -50,7 +50,7 @@ impl CreateEmbed {
/// [`CreateEmbedAuthor`]: struct.CreateEmbedAuthor.html
pub fn author<F>(mut self, f: F) -> Self
where F: FnOnce(CreateEmbedAuthor) -> CreateEmbedAuthor {
let map = utils::hashmap_to_json_map(f(CreateEmbedAuthor::default()).0);
let map = utils::vecmap_to_json_map(f(CreateEmbedAuthor::default()).0);

self.0.insert("author", Value::Object(map));

Expand Down Expand Up @@ -157,7 +157,7 @@ impl CreateEmbed {
pub fn footer<F>(mut self, f: F) -> Self
where F: FnOnce(CreateEmbedFooter) -> CreateEmbedFooter {
let footer = f(CreateEmbedFooter::default()).0;
let map = utils::hashmap_to_json_map(footer);
let map = utils::vecmap_to_json_map(footer);

self.0.insert("footer", Value::Object(map));

Expand Down Expand Up @@ -309,7 +309,7 @@ impl CreateEmbed {
impl Default for CreateEmbed {
/// Creates a builder with default values, setting the `type` to `rich`.
fn default() -> CreateEmbed {
let mut map = HashMap::new();
let mut map = VecMap::new();
map.insert("type", Value::String("rich".to_string()));

CreateEmbed(map)
Expand Down Expand Up @@ -380,7 +380,7 @@ impl From<Embed> for CreateEmbed {
/// [`CreateEmbed::author`]: struct.CreateEmbed.html#method.author
/// [`name`]: #method.name
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedAuthor(pub HashMap<&'static str, Value>);
pub struct CreateEmbedAuthor(pub VecMap<&'static str, Value>);

impl CreateEmbedAuthor {
/// Set the URL of the author's icon.
Expand Down Expand Up @@ -413,7 +413,7 @@ impl CreateEmbedAuthor {
/// [`Embed`]: ../model/struct.Embed.html
/// [`CreateEmbed::footer`]: struct.CreateEmbed.html#method.footer
#[derive(Clone, Debug, Default)]
pub struct CreateEmbedFooter(pub HashMap<&'static str, Value>);
pub struct CreateEmbedFooter(pub VecMap<&'static str, Value>);

impl CreateEmbedFooter {
/// Set the icon URL's value. This only supports HTTP(S).
Expand Down
6 changes: 3 additions & 3 deletions src/builder/create_invite.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use internal::prelude::*;
use serde_json::Value;
use std::collections::HashMap;
use std::default::Default;
use super::VecMap;

/// A builder to create a [`RichInvite`] for use via [`GuildChannel::create_invite`].
///
Expand Down Expand Up @@ -62,7 +62,7 @@ use std::default::Default;
/// [`GuildChannel::create_invite`]: ../model/struct.GuildChannel.html#method.create_invite
/// [`RichInvite`]: ../model/struct.Invite.html
#[derive(Clone, Debug)]
pub struct CreateInvite(pub HashMap<&'static str, Value>);
pub struct CreateInvite(pub VecMap<&'static str, Value>);

impl CreateInvite {
/// The duration that the invite will be valid for.
Expand Down Expand Up @@ -207,7 +207,7 @@ impl Default for CreateInvite {
/// let invite_builder = CreateInvite::default();
/// ```
fn default() -> CreateInvite {
let mut map = HashMap::new();
let mut map = VecMap::new();
map.insert("validate", Value::Null);

CreateInvite(map)
Expand Down
9 changes: 4 additions & 5 deletions src/builder/create_message.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use internal::prelude::*;
use model::channel::ReactionType;
use std::collections::HashMap;
use std::fmt::Display;
use super::CreateEmbed;
use super::{CreateEmbed, VecMap};
use utils;

/// A builder to specify the contents of an [`http::send_message`] request,
Expand Down Expand Up @@ -41,7 +40,7 @@ use utils;
/// [`embed`]: #method.embed
/// [`http::send_message`]: ../http/fn.send_message.html
#[derive(Clone, Debug)]
pub struct CreateMessage(pub HashMap<&'static str, Value>, pub Option<Vec<ReactionType>>);
pub struct CreateMessage(pub VecMap<&'static str, Value>, pub Option<Vec<ReactionType>>);

impl CreateMessage {
/// Set the content of the message.
Expand All @@ -56,7 +55,7 @@ impl CreateMessage {
/// Set an embed for the message.
pub fn embed<F>(mut self, f: F) -> Self
where F: FnOnce(CreateEmbed) -> CreateEmbed {
let map = utils::hashmap_to_json_map(f(CreateEmbed::default()).0);
let map = utils::vecmap_to_json_map(f(CreateEmbed::default()).0);
let embed = Value::Object(map);

self.0.insert("embed", embed);
Expand Down Expand Up @@ -90,7 +89,7 @@ impl Default for CreateMessage {
/// [`Message`]: ../model/struct.Message.html
/// [`tts`]: #method.tts
fn default() -> CreateMessage {
let mut map = HashMap::default();
let mut map = VecMap::new();
map.insert("tts", Value::Bool(false));

CreateMessage(map, None)
Expand Down
4 changes: 2 additions & 2 deletions src/builder/edit_channel.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use internal::prelude::*;
use std::collections::HashMap;
use super::VecMap;

/// A builder to edit a [`GuildChannel`] for use via [`GuildChannel::edit`]
///
Expand All @@ -19,7 +19,7 @@ use std::collections::HashMap;
/// [`GuildChannel`]: ../model/struct.GuildChannel.html
/// [`GuildChannel::edit`]: ../model/struct.GuildChannel.html#method.edit
#[derive(Clone, Debug, Default)]
pub struct EditChannel(pub HashMap<&'static str, Value>);
pub struct EditChannel(pub VecMap<&'static str, Value>);

impl EditChannel {
/// The bitrate of the channel in bits.
Expand Down
4 changes: 2 additions & 2 deletions src/builder/edit_guild.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use internal::prelude::*;
use model::prelude::*;
use std::collections::HashMap;
use super::VecMap;

/// A builder to optionally edit certain fields of a [`Guild`]. This is meant
/// for usage with [`Guild::edit`].
Expand All @@ -12,7 +12,7 @@ use std::collections::HashMap;
/// [`Guild`]: ../model/struct.Guild.html
/// [Manage Guild]: ../model/permissions/constant.MANAGE_GUILD.html
#[derive(Clone, Debug, Default)]
pub struct EditGuild(pub HashMap<&'static str, Value>);
pub struct EditGuild(pub VecMap<&'static str, Value>);

impl EditGuild {
/// Set the "AFK voice channel" that users are to move to if they have been
Expand Down
4 changes: 2 additions & 2 deletions src/builder/edit_member.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use internal::prelude::*;
use model::id::{ChannelId, RoleId};
use std::collections::HashMap;
use super::VecMap;

/// A builder which edits the properties of a [`Member`], to be used in
/// conjunction with [`Member::edit`].
///
/// [`Member`]: ../model/struct.Member.html
/// [`Member::edit`]: ../model/struct.Member.html#method.edit
#[derive(Clone, Debug, Default)]
pub struct EditMember(pub HashMap<&'static str, Value>);
pub struct EditMember(pub VecMap<&'static str, Value>);

impl EditMember {
/// Whether to deafen the member.
Expand Down
4 changes: 2 additions & 2 deletions src/builder/edit_profile.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use internal::prelude::*;
use std::collections::HashMap;
use super::VecMap;

/// A builder to edit the current user's settings, to be used in conjunction
/// with [`CurrentUser::edit`].
///
/// [`CurrentUser::edit`]: ../model/struct.CurrentUser.html#method.edit
#[derive(Clone, Debug, Default)]
pub struct EditProfile(pub HashMap<&'static str, Value>);
pub struct EditProfile(pub VecMap<&'static str, Value>);

impl EditProfile {
/// Sets the avatar of the current user. `None` can be passed to remove an
Expand Down
6 changes: 3 additions & 3 deletions src/builder/edit_role.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use internal::prelude::*;
use std::collections::HashMap;
use model::guild::Role;
use model::Permissions;
use super::VecMap;

/// A builer to create or edit a [`Role`] for use via a number of model methods.
///
Expand Down Expand Up @@ -40,14 +40,14 @@ use model::Permissions;
/// [`Role`]: ../model/struct.Role.html
/// [`Role::edit`]: ../model/struct.Role.html#method.edit
#[derive(Clone, Debug, Default)]
pub struct EditRole(pub HashMap<&'static str, Value>);
pub struct EditRole(pub VecMap<&'static str, Value>);

impl EditRole {
/// Creates a new builder with the values of the given [`Role`].
///
/// [`Role`]: ../model/struct.Role.html
pub fn new(role: &Role) -> Self {
let mut map = HashMap::new();
let mut map = VecMap::with_capacity(8);

#[cfg(feature = "utils")]
{
Expand Down
6 changes: 3 additions & 3 deletions src/builder/execute_webhook.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde_json::Value;
use std::collections::HashMap;
use std::default::Default;
use super::VecMap;

/// A builder to create the inner content of a [`Webhook`]'s execution.
///
Expand Down Expand Up @@ -47,7 +47,7 @@ use std::default::Default;
/// [`Webhook::execute`]: ../model/struct.Webhook.html#method.execute
/// [`execute_webhook`]: ../http/fn.execute_webhook.html
#[derive(Clone, Debug)]
pub struct ExecuteWebhook(pub HashMap<&'static str, Value>);
pub struct ExecuteWebhook(pub VecMap<&'static str, Value>);

impl ExecuteWebhook {
/// Override the default avatar of the webhook with an image URL.
Expand Down Expand Up @@ -179,7 +179,7 @@ impl Default for ExecuteWebhook {
/// [`Webhook`]: ../model/struct.Webhook.html
/// [`tts`]: #method.tts
fn default() -> ExecuteWebhook {
let mut map = HashMap::new();
let mut map = VecMap::new();
map.insert("tts", Value::Bool(false));

ExecuteWebhook(map)
Expand Down
4 changes: 2 additions & 2 deletions src/builder/get_messages.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use model::id::MessageId;
use std::collections::HashMap;
use super::VecMap;

/// Builds a request for a request to the API to retrieve messages.
///
Expand Down Expand Up @@ -50,7 +50,7 @@ use std::collections::HashMap;
///
/// [`GuildChannel::messages`]: ../model/struct.GuildChannel.html#method.messages
#[derive(Clone, Debug, Default)]
pub struct GetMessages(pub HashMap<&'static str, u64>);
pub struct GetMessages(pub VecMap<&'static str, u64>);

impl GetMessages {
/// Indicates to retrieve the messages after a specific message, given by
Expand Down
2 changes: 2 additions & 0 deletions src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ mod edit_profile;
mod edit_role;
mod execute_webhook;
mod get_messages;
mod vec_map;

pub use self::vec_map::VecMap;
pub use self::create_embed::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter};
pub use self::create_invite::CreateInvite;
pub use self::create_message::CreateMessage;
Expand Down
120 changes: 120 additions & 0 deletions src/builder/vec_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Most of this is
// shamelessly copied from https://github.com/hyperium/hyper/blob/master/src/header/internals/vec_map.rs

/// Like `HashMap` but solely uses a vector instead.
///
/// note: This is for internal use.
#[derive(Clone, Debug, Default)]
pub struct VecMap<K, V>(Vec<(K, V)>);

impl<K: PartialEq, V> VecMap<K, V> {
pub fn new() -> Self {
VecMap(Vec::new())
}

pub fn with_capacity(cap: usize) -> Self {
VecMap(Vec::with_capacity(cap))
}

#[inline]
pub fn insert(&mut self, key: K, value: V) {
self.0.push((key, value));
}

pub fn remove<Q: ?Sized + PartialEq<K>>(&mut self, key: &Q) -> Option<V> {
self.pos(key).map(|pos| self.0.remove(pos)).map(|entry| entry.1)
}

pub fn entry(&mut self, key: K) -> Entry<K, V> {
match self.pos(&key) {
Some(pos) => Entry::Occupied(OccupiedEntry {
vec: &mut self.0,
pos: pos,
}),
None => Entry::Vacant(VacantEntry {
vec: &mut self.0,
key: key,
})
}
}

pub fn get<Q: PartialEq<K> + ?Sized>(&self, key: &Q) -> Option<&V> {
self.iter().find(|entry| key == &entry.0).map(|entry| &entry.1)
}

#[inline]
pub fn iter(&self) -> ::std::slice::Iter<(K, V)> {
self.into_iter()
}

fn pos<Q: PartialEq<K> + ?Sized>(&self, key: &Q) -> Option<usize> {
self.iter().position(|entry| key == &entry.0)
}
}

impl<K, V> IntoIterator for VecMap<K, V> {
type Item = (K, V);
type IntoIter = ::std::vec::IntoIter<(K, V)>;

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

impl<'a, K, V> IntoIterator for &'a VecMap<K, V> {
type Item = &'a (K, V);
type IntoIter = ::std::slice::Iter<'a, (K, V)>;

fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}

pub enum Entry<'a, K: 'a, V: 'a> {
Vacant(VacantEntry<'a, K, V>),
Occupied(OccupiedEntry<'a, K, V>)
}

impl<'a, K, V> Entry<'a, K, V> {
pub fn or_insert(self, val: V) -> &'a mut V {
use self::Entry::*;

match self {
Vacant(entry) => entry.insert(val),
Occupied(entry) => entry.into_mut(),
}
}

pub fn or_insert_with<F: FnOnce() -> V>(self, val: F) -> &'a mut V {
use self::Entry::*;

match self {
Vacant(entry) => entry.insert(val()),
Occupied(entry) => entry.into_mut(),
}
}
}

pub struct VacantEntry<'a, K: 'a, V: 'a> {
vec: &'a mut Vec<(K, V)>,
key: K,
}

impl<'a, K, V> VacantEntry<'a, K, V> {
pub fn insert(self, val: V) -> &'a mut V {
self.vec.push((self.key, val));
let pos = self.vec.len() - 1;
&mut self.vec[pos].1
}
}

pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
vec: &'a mut Vec<(K, V)>,
pos: usize,
}

impl<'a, K, V> OccupiedEntry<'a, K, V> {
pub fn into_mut(self) -> &'a mut V {
&mut self.vec[self.pos].1
}
}
Loading

0 comments on commit 3a0c890

Please sign in to comment.