Skip to content

Commit 07d8afa

Browse files
committed
perf(utils): remove get_filters and reduce allocations
1 parent 83b7453 commit 07d8afa

4 files changed

Lines changed: 51 additions & 52 deletions

File tree

src/post.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
8282
let prefs = Preferences::new(&req);
8383
let filters: HashSet<String> = prefs.filters.iter().cloned().collect();
8484
let comments = match query.as_str() {
85-
"" => parse_comments(&response[1], &post.permalink, &post.author.name, highlighted_comment, &filters, &req),
86-
_ => query_comments(&response[1], &post.permalink, &post.author.name, highlighted_comment, &filters, &query, &req),
85+
"" => parse_comments(&response[1], &post.permalink, &post.author.name, highlighted_comment, &filters, &prefs),
86+
_ => query_comments(&response[1], &post.permalink, &post.author.name, highlighted_comment, &filters, &query, &prefs),
8787
};
8888

8989
// Use the Post and Comment structs to generate a website to show users
@@ -112,7 +112,7 @@ pub async fn item(req: Request<Body>) -> Result<Response<Body>, String> {
112112

113113
// COMMENTS
114114

115-
fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str, highlighted_comment: &str, filters: &HashSet<String>, req: &Request<Body>) -> Vec<Comment> {
115+
fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str, highlighted_comment: &str, filters: &HashSet<String>, prefs: &Preferences) -> Vec<Comment> {
116116
// Parse the comment JSON into a Vector of Comments
117117
let comments = json["data"]["children"].as_array().map_or(Vec::new(), std::borrow::ToOwned::to_owned);
118118

@@ -122,11 +122,11 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str,
122122
.map(|comment| {
123123
let data = &comment["data"];
124124
let replies: Vec<Comment> = if data["replies"].is_object() {
125-
parse_comments(&data["replies"], post_link, post_author, highlighted_comment, filters, req)
125+
parse_comments(&data["replies"], post_link, post_author, highlighted_comment, filters, prefs)
126126
} else {
127127
Vec::new()
128128
};
129-
build_comment(&comment, data, replies, post_link, post_author, highlighted_comment, filters, req)
129+
build_comment(&comment, data, replies, post_link, post_author, highlighted_comment, filters, prefs)
130130
})
131131
.collect()
132132
}
@@ -138,7 +138,7 @@ fn query_comments(
138138
highlighted_comment: &str,
139139
filters: &HashSet<String>,
140140
query: &str,
141-
req: &Request<Body>,
141+
prefs: &Preferences,
142142
) -> Vec<Comment> {
143143
let comments = json["data"]["children"].as_array().map_or(Vec::new(), std::borrow::ToOwned::to_owned);
144144
let mut results = Vec::new();
@@ -148,10 +148,10 @@ fn query_comments(
148148

149149
// If this comment contains replies, handle those too
150150
if data["replies"].is_object() {
151-
results.append(&mut query_comments(&data["replies"], post_link, post_author, highlighted_comment, filters, query, req));
151+
results.append(&mut query_comments(&data["replies"], post_link, post_author, highlighted_comment, filters, query, prefs));
152152
}
153153

154-
let c = build_comment(&comment, data, Vec::new(), post_link, post_author, highlighted_comment, filters, req);
154+
let c = build_comment(&comment, data, Vec::new(), post_link, post_author, highlighted_comment, filters, prefs);
155155
if c.body.to_lowercase().contains(&query.to_lowercase()) {
156156
results.push(c);
157157
}
@@ -168,7 +168,7 @@ fn build_comment(
168168
post_author: &str,
169169
highlighted_comment: &str,
170170
filters: &HashSet<String>,
171-
req: &Request<Body>,
171+
prefs: &Preferences,
172172
) -> Comment {
173173
let id = val(comment, "id");
174174
let comment_author = val(comment, "author");
@@ -252,6 +252,6 @@ fn build_comment(
252252
collapsed,
253253
is_filtered,
254254
more_count,
255-
prefs: Preferences::new(req),
255+
prefs: prefs.clone(),
256256
}
257257
}

src/search.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![allow(clippy::cmp_owned)]
2-
use crate::utils::{self, catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, setting, template, val, Post, Preferences};
2+
use crate::utils::{self, catch_random, error, filter_posts, format_num, format_url, param, redirect, setting, template, val, Post, Preferences};
33
use crate::{
44
client::json,
55
server::RequestExt,
@@ -92,7 +92,8 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
9292
let typed = param(&path, "type").unwrap_or_default();
9393

9494
let sort = param(&path, "sort").unwrap_or_else(|| "relevance".to_string());
95-
let filters = get_filters(&req);
95+
let prefs = Preferences::new(&req);
96+
let filters: std::collections::HashSet<String> = prefs.filters.iter().cloned().collect();
9697

9798
// If search is not restricted to this subreddit, show other subreddits in search results
9899
let subreddits = if param(&path, "restrict_sr").is_none() {
@@ -120,7 +121,7 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
120121
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
121122
typed,
122123
},
123-
prefs: Preferences::new(&req),
124+
prefs: prefs.clone(),
124125
url,
125126
is_filtered: true,
126127
all_posts_filtered: false,
@@ -146,7 +147,7 @@ pub async fn find(req: Request<Body>) -> Result<Response<Body>, String> {
146147
restrict_sr: param(&path, "restrict_sr").unwrap_or_default(),
147148
typed,
148149
},
149-
prefs: Preferences::new(&req),
150+
prefs,
150151
url,
151152
is_filtered: false,
152153
all_posts_filtered,

src/subreddit.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![allow(clippy::cmp_owned)]
22

33
use crate::utils::{
4-
Post, Preferences, Subreddit, catch_random, error, filter_posts, format_num, format_url, get_filters, info, nsfw_landing, param, redirect, rewrite_urls, setting, template, to_absolute_url, val
4+
Post, Preferences, Subreddit, catch_random, error, filter_posts, format_num, format_url, info, nsfw_landing, param, redirect, rewrite_urls, setting, setting_or_default, template, to_absolute_url, val
55
};
66
use crate::{client::json, server::RequestExt, server::ResponseExt};
77
use crate::{config, utils};
@@ -144,7 +144,8 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
144144
let path = format!("/r/{}/{sort}.json?{}{params}", sub_name.replace('+', "%2B"), req.uri().query().unwrap_or_default());
145145
let url = String::from(req.uri().path_and_query().map_or("", |val| val.as_str()));
146146
let redirect_url = url[1..].replace('?', "%3F").replace('&', "%26").replace('+', "%2B");
147-
let filters = get_filters(&req);
147+
let prefs = Preferences::new(&req);
148+
let filters: std::collections::HashSet<String> = prefs.filters.iter().cloned().collect();
148149

149150
// If all requested subs are filtered, we don't need to fetch posts.
150151
if sub_name.split('+').all(|s| filters.contains(s)) {
@@ -153,7 +154,7 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
153154
posts: Vec::new(),
154155
sort: (sort, param(&path, "t").unwrap_or_default()),
155156
ends: (param(&path, "after").unwrap_or_default(), String::new()),
156-
prefs: Preferences::new(&req),
157+
prefs: prefs.clone(),
157158
url,
158159
redirect_url,
159160
is_filtered: true,
@@ -176,7 +177,7 @@ pub async fn community(req: Request<Body>) -> Result<Response<Body>, String> {
176177
posts,
177178
sort: (sort, param(&path, "t").unwrap_or_default()),
178179
ends: (param(&path, "after").unwrap_or_default(), after),
179-
prefs: Preferences::new(&req),
180+
prefs,
180181
url,
181182
redirect_url,
182183
is_filtered: false,

src/utils.rs

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -450,13 +450,13 @@ impl Post {
450450
rel_time,
451451
created,
452452
created_ts,
453-
num_duplicates: post["data"]["num_duplicates"].as_u64().unwrap_or(0),
453+
num_duplicates: data["num_duplicates"].as_u64().unwrap_or(0),
454454
comments: format_num(data["num_comments"].as_i64().unwrap_or_default()),
455455
gallery,
456456
awards,
457-
nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
457+
nsfw: data["over_18"].as_bool().unwrap_or_default(),
458458
ws_url: val(post, "websocket_url"),
459-
out_url: post["data"]["url_overridden_by_dest"].as_str().map(|a| a.to_string()),
459+
out_url: data["url_overridden_by_dest"].as_str().map(|a| a.to_string()),
460460
});
461461
}
462462
Ok((posts, res["data"]["after"].as_str().unwrap_or_default().to_string()))
@@ -611,7 +611,7 @@ pub struct Params {
611611
pub before: Option<String>,
612612
}
613613

614-
#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq)]
614+
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
615615
#[revisioned(revision = 1)]
616616
pub struct Preferences {
617617
#[revision(start = 1)]
@@ -752,11 +752,6 @@ pub fn deflate_decompress(i: Vec<u8>) -> Result<Vec<u8>, String> {
752752
Ok(out)
753753
}
754754

755-
/// Gets a `HashSet` of filters from the cookie in the given `Request`.
756-
pub fn get_filters(req: &Request<Body>) -> HashSet<String> {
757-
setting(req, "filters").split('+').map(String::from).filter(|s| !s.is_empty()).collect::<HashSet<String>>()
758-
}
759-
760755
/// Filters a `Vec<Post>` by the given `HashSet` of filters (each filter being
761756
/// a subreddit name or a user name). If a `Post`'s subreddit or author is
762757
/// found in the filters, it is removed.
@@ -765,7 +760,7 @@ pub fn get_filters(req: &Request<Body>) -> HashSet<String> {
765760
/// second return value is `true` if all posts were filtered.
766761
pub fn filter_posts(posts: &mut Vec<Post>, filters: &HashSet<String>) -> (u64, bool) {
767762
// This is the length of the Vec<Post> prior to applying the filter.
768-
let lb: u64 = posts.len().try_into().unwrap_or(0);
763+
let lb = posts.len() as u64;
769764

770765
if posts.is_empty() {
771766
(0, false)
@@ -774,30 +769,32 @@ pub fn filter_posts(posts: &mut Vec<Post>, filters: &HashSet<String>) -> (u64, b
774769

775770
// Get the length of the Vec<Post> after applying the filter.
776771
// If lb > la, then at least one post was removed.
777-
let la: u64 = posts.len().try_into().unwrap_or(0);
772+
let la = posts.len() as u64;
778773

779774
(lb - la, posts.is_empty())
780775
}
781776
}
782777

783778
/// Creates a [`Post`] from a provided JSON.
784779
pub async fn parse_post(post: &Value) -> Post {
780+
let data = &post["data"];
781+
785782
// Grab UTC time as unix timestamp
786-
let (rel_time, created) = time(post["data"]["created_utc"].as_f64().unwrap_or_default());
783+
let created_utc = data["created_utc"].as_f64().unwrap_or_default();
784+
let (rel_time, created) = time(created_utc);
785+
let created_ts = created_utc.round() as u64;
787786
// Parse post score and upvote ratio
788-
let score = post["data"]["score"].as_i64().unwrap_or_default();
789-
let ratio: f64 = post["data"]["upvote_ratio"].as_f64().unwrap_or(1.0) * 100.0;
787+
let score = data["score"].as_i64().unwrap_or_default();
788+
let ratio: f64 = data["upvote_ratio"].as_f64().unwrap_or(1.0) * 100.0;
790789

791790
// Determine the type of media along with the media URL
792-
let (post_type, media, gallery) = Media::parse(&post["data"]).await;
793-
794-
let created_ts = post["data"]["created_utc"].as_f64().unwrap_or_default().round() as u64;
791+
let (post_type, media, gallery) = Media::parse(data).await;
795792

796-
let awards: Awards = Awards::parse(&post["data"]["all_awardings"]);
793+
let awards: Awards = Awards::parse(&data["all_awardings"]);
797794

798795
let permalink = val(post, "permalink");
799796

800-
let poll = Poll::parse(&post["data"]["poll_data"]);
797+
let poll = Poll::parse(&data["poll_data"]);
801798

802799
let body = if val(post, "removed_by_category") == "moderator" {
803800
format!(
@@ -826,9 +823,9 @@ pub async fn parse_post(post: &Value) -> Post {
826823
name: val(post, "author"),
827824
flair: Flair {
828825
flair_parts: FlairPart::parse(
829-
post["data"]["author_flair_type"].as_str().unwrap_or_default(),
830-
post["data"]["author_flair_richtext"].as_array(),
831-
post["data"]["author_flair_text"].as_str(),
826+
data["author_flair_type"].as_str().unwrap_or_default(),
827+
data["author_flair_richtext"].as_array(),
828+
data["author_flair_text"].as_str(),
832829
),
833830
text: val(post, "link_flair_text"),
834831
background_color: val(post, "author_flair_background_color"),
@@ -846,16 +843,16 @@ pub async fn parse_post(post: &Value) -> Post {
846843
thumbnail: Media {
847844
url: format_url(val(post, "thumbnail").as_str()),
848845
alt_url: String::new(),
849-
width: post["data"]["thumbnail_width"].as_i64().unwrap_or_default(),
850-
height: post["data"]["thumbnail_height"].as_i64().unwrap_or_default(),
846+
width: data["thumbnail_width"].as_i64().unwrap_or_default(),
847+
height: data["thumbnail_height"].as_i64().unwrap_or_default(),
851848
poster: String::new(),
852849
download_name: String::new(),
853850
},
854851
flair: Flair {
855852
flair_parts: FlairPart::parse(
856-
post["data"]["link_flair_type"].as_str().unwrap_or_default(),
857-
post["data"]["link_flair_richtext"].as_array(),
858-
post["data"]["link_flair_text"].as_str(),
853+
data["link_flair_type"].as_str().unwrap_or_default(),
854+
data["link_flair_richtext"].as_array(),
855+
data["link_flair_text"].as_str(),
859856
),
860857
text: val(post, "link_flair_text"),
861858
background_color: val(post, "link_flair_background_color"),
@@ -866,21 +863,21 @@ pub async fn parse_post(post: &Value) -> Post {
866863
},
867864
},
868865
flags: Flags {
869-
spoiler: post["data"]["spoiler"].as_bool().unwrap_or_default(),
870-
nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
871-
stickied: post["data"]["stickied"].as_bool().unwrap_or_default() || post["data"]["pinned"].as_bool().unwrap_or(false),
866+
spoiler: data["spoiler"].as_bool().unwrap_or_default(),
867+
nsfw: data["over_18"].as_bool().unwrap_or_default(),
868+
stickied: data["stickied"].as_bool().unwrap_or_default() || data["pinned"].as_bool().unwrap_or(false),
872869
},
873870
domain: val(post, "domain"),
874871
rel_time,
875872
created,
876873
created_ts,
877-
num_duplicates: post["data"]["num_duplicates"].as_u64().unwrap_or(0),
878-
comments: format_num(post["data"]["num_comments"].as_i64().unwrap_or_default()),
874+
num_duplicates: data["num_duplicates"].as_u64().unwrap_or(0),
875+
comments: format_num(data["num_comments"].as_i64().unwrap_or_default()),
879876
gallery,
880877
awards,
881-
nsfw: post["data"]["over_18"].as_bool().unwrap_or_default(),
878+
nsfw: data["over_18"].as_bool().unwrap_or_default(),
882879
ws_url: val(post, "websocket_url"),
883-
out_url: post["data"]["url_overridden_by_dest"].as_str().map(|a| a.to_string()),
880+
out_url: data["url_overridden_by_dest"].as_str().map(|a| a.to_string()),
884881
}
885882
}
886883

0 commit comments

Comments
 (0)