From 0e57ff7eee4ac71d923f970d15889ad4d50f97a9 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Wed, 12 Oct 2022 07:18:26 +0200 Subject: [PATCH] Add generic set type (#21408) This PR adds a generic set type to get rid of maps used as sets. Co-authored-by: wxiaoguang --- models/activities/action_list.go | 16 +++---- models/activities/notification.go | 46 +++++++------------ models/issues/comment_list.go | 64 ++++++++++----------------- models/issues/issue_list.go | 33 ++++++-------- models/issues/reaction.go | 10 ++--- models/migrate.go | 5 ++- models/migrations/v115.go | 5 ++- models/packages/conan/search.go | 5 ++- models/repo/repo_list.go | 6 +-- models/repo/topic.go | 7 +-- models/repo/user_repo.go | 31 +++---------- modules/base/tool.go | 9 ---- modules/base/tool_test.go | 9 ---- modules/container/map.go | 14 ------ modules/container/set.go | 57 ++++++++++++++++++++++++ modules/container/set_test.go | 37 ++++++++++++++++ modules/doctor/authorizedkeys.go | 7 +-- modules/git/log_name_status.go | 12 ++--- modules/git/repo_stats.go | 8 ++-- modules/issue/template/template.go | 8 ++-- modules/markup/markdown/goldmark.go | 13 +++--- modules/notification/ui/ui.go | 9 ++-- modules/public/public.go | 7 +-- modules/public/public_test.go | 19 +++----- modules/public/serve_static.go | 2 +- modules/queue/unique_queue_channel.go | 15 +++---- modules/repository/repo.go | 7 +-- modules/setting/queue.go | 27 +++++------ modules/setting/setting.go | 7 +-- modules/sync/status_pool.go | 21 +++++---- routers/api/packages/conan/conan.go | 37 ++++++++-------- routers/web/repo/issue.go | 14 +++--- routers/web/repo/lfs.go | 9 ++-- routers/web/repo/view.go | 9 ++-- services/issue/commit.go | 6 +-- services/mailer/mail_comment.go | 5 ++- services/mailer/mail_issue.go | 16 +++---- services/pull/patch.go | 17 +++---- services/pull/pull.go | 9 ++-- services/release/release.go | 7 +-- services/repository/adopt.go | 7 +-- 41 files changed, 328 insertions(+), 324 deletions(-) delete mode 100644 modules/container/map.go create mode 100644 modules/container/set.go create mode 100644 modules/container/set_test.go diff --git a/models/activities/action_list.go b/models/activities/action_list.go index 16fb4bac8c..f349b94ac8 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -18,13 +18,11 @@ import ( type ActionList []*Action func (actions ActionList) getUserIDs() []int64 { - userIDs := make(map[int64]struct{}, len(actions)) + userIDs := make(container.Set[int64], len(actions)) for _, action := range actions { - if _, ok := userIDs[action.ActUserID]; !ok { - userIDs[action.ActUserID] = struct{}{} - } + userIDs.Add(action.ActUserID) } - return container.KeysInt64(userIDs) + return userIDs.Values() } func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model.User, error) { @@ -48,13 +46,11 @@ func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model. } func (actions ActionList) getRepoIDs() []int64 { - repoIDs := make(map[int64]struct{}, len(actions)) + repoIDs := make(container.Set[int64], len(actions)) for _, action := range actions { - if _, ok := repoIDs[action.RepoID]; !ok { - repoIDs[action.RepoID] = struct{}{} - } + repoIDs.Add(action.RepoID) } - return container.KeysInt64(repoIDs) + return repoIDs.Values() } func (actions ActionList) loadRepositories(ctx context.Context) error { diff --git a/models/activities/notification.go b/models/activities/notification.go index 88776db42b..2f21dc74d1 100644 --- a/models/activities/notification.go +++ b/models/activities/notification.go @@ -200,7 +200,7 @@ func CreateOrUpdateIssueNotifications(issueID, commentID, notificationAuthorID, func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error { // init - var toNotify map[int64]struct{} + var toNotify container.Set[int64] notifications, err := getNotificationsByIssueID(ctx, issueID) if err != nil { return err @@ -212,33 +212,27 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n } if receiverID > 0 { - toNotify = make(map[int64]struct{}, 1) - toNotify[receiverID] = struct{}{} + toNotify = make(container.Set[int64], 1) + toNotify.Add(receiverID) } else { - toNotify = make(map[int64]struct{}, 32) + toNotify = make(container.Set[int64], 32) issueWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, true) if err != nil { return err } - for _, id := range issueWatches { - toNotify[id] = struct{}{} - } + toNotify.AddMultiple(issueWatches...) if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) { repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID) if err != nil { return err } - for _, id := range repoWatches { - toNotify[id] = struct{}{} - } + toNotify.AddMultiple(repoWatches...) } issueParticipants, err := issue.GetParticipantIDsByIssue(ctx) if err != nil { return err } - for _, id := range issueParticipants { - toNotify[id] = struct{}{} - } + toNotify.AddMultiple(issueParticipants...) // dont notify user who cause notification delete(toNotify, notificationAuthorID) @@ -248,7 +242,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n return err } for _, id := range issueUnWatches { - delete(toNotify, id) + toNotify.Remove(id) } } @@ -499,16 +493,14 @@ func (nl NotificationList) LoadAttributes() error { } func (nl NotificationList) getPendingRepoIDs() []int64 { - ids := make(map[int64]struct{}, len(nl)) + ids := make(container.Set[int64], len(nl)) for _, notification := range nl { if notification.Repository != nil { continue } - if _, ok := ids[notification.RepoID]; !ok { - ids[notification.RepoID] = struct{}{} - } + ids.Add(notification.RepoID) } - return container.KeysInt64(ids) + return ids.Values() } // LoadRepos loads repositories from database @@ -575,16 +567,14 @@ func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error) } func (nl NotificationList) getPendingIssueIDs() []int64 { - ids := make(map[int64]struct{}, len(nl)) + ids := make(container.Set[int64], len(nl)) for _, notification := range nl { if notification.Issue != nil { continue } - if _, ok := ids[notification.IssueID]; !ok { - ids[notification.IssueID] = struct{}{} - } + ids.Add(notification.IssueID) } - return container.KeysInt64(ids) + return ids.Values() } // LoadIssues loads issues from database @@ -661,16 +651,14 @@ func (nl NotificationList) Without(failures []int) NotificationList { } func (nl NotificationList) getPendingCommentIDs() []int64 { - ids := make(map[int64]struct{}, len(nl)) + ids := make(container.Set[int64], len(nl)) for _, notification := range nl { if notification.CommentID == 0 || notification.Comment != nil { continue } - if _, ok := ids[notification.CommentID]; !ok { - ids[notification.CommentID] = struct{}{} - } + ids.Add(notification.CommentID) } - return container.KeysInt64(ids) + return ids.Values() } // LoadComments loads comments from database diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index e3406a5cbe..70105d7ff0 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -17,13 +17,11 @@ import ( type CommentList []*Comment func (comments CommentList) getPosterIDs() []int64 { - posterIDs := make(map[int64]struct{}, len(comments)) + posterIDs := make(container.Set[int64], len(comments)) for _, comment := range comments { - if _, ok := posterIDs[comment.PosterID]; !ok { - posterIDs[comment.PosterID] = struct{}{} - } + posterIDs.Add(comment.PosterID) } - return container.KeysInt64(posterIDs) + return posterIDs.Values() } func (comments CommentList) loadPosters(ctx context.Context) error { @@ -70,13 +68,11 @@ func (comments CommentList) getCommentIDs() []int64 { } func (comments CommentList) getLabelIDs() []int64 { - ids := make(map[int64]struct{}, len(comments)) + ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - if _, ok := ids[comment.LabelID]; !ok { - ids[comment.LabelID] = struct{}{} - } + ids.Add(comment.LabelID) } - return container.KeysInt64(ids) + return ids.Values() } func (comments CommentList) loadLabels(ctx context.Context) error { //nolint @@ -120,13 +116,11 @@ func (comments CommentList) loadLabels(ctx context.Context) error { //nolint } func (comments CommentList) getMilestoneIDs() []int64 { - ids := make(map[int64]struct{}, len(comments)) + ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - if _, ok := ids[comment.MilestoneID]; !ok { - ids[comment.MilestoneID] = struct{}{} - } + ids.Add(comment.MilestoneID) } - return container.KeysInt64(ids) + return ids.Values() } func (comments CommentList) loadMilestones(ctx context.Context) error { @@ -163,13 +157,11 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { } func (comments CommentList) getOldMilestoneIDs() []int64 { - ids := make(map[int64]struct{}, len(comments)) + ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - if _, ok := ids[comment.OldMilestoneID]; !ok { - ids[comment.OldMilestoneID] = struct{}{} - } + ids.Add(comment.OldMilestoneID) } - return container.KeysInt64(ids) + return ids.Values() } func (comments CommentList) loadOldMilestones(ctx context.Context) error { @@ -206,13 +198,11 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { } func (comments CommentList) getAssigneeIDs() []int64 { - ids := make(map[int64]struct{}, len(comments)) + ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - if _, ok := ids[comment.AssigneeID]; !ok { - ids[comment.AssigneeID] = struct{}{} - } + ids.Add(comment.AssigneeID) } - return container.KeysInt64(ids) + return ids.Values() } func (comments CommentList) loadAssignees(ctx context.Context) error { @@ -259,16 +249,14 @@ func (comments CommentList) loadAssignees(ctx context.Context) error { // getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded func (comments CommentList) getIssueIDs() []int64 { - ids := make(map[int64]struct{}, len(comments)) + ids := make(container.Set[int64], len(comments)) for _, comment := range comments { if comment.Issue != nil { continue } - if _, ok := ids[comment.IssueID]; !ok { - ids[comment.IssueID] = struct{}{} - } + ids.Add(comment.IssueID) } - return container.KeysInt64(ids) + return ids.Values() } // Issues returns all the issues of comments @@ -334,16 +322,14 @@ func (comments CommentList) loadIssues(ctx context.Context) error { } func (comments CommentList) getDependentIssueIDs() []int64 { - ids := make(map[int64]struct{}, len(comments)) + ids := make(container.Set[int64], len(comments)) for _, comment := range comments { if comment.DependentIssue != nil { continue } - if _, ok := ids[comment.DependentIssueID]; !ok { - ids[comment.DependentIssueID] = struct{}{} - } + ids.Add(comment.DependentIssueID) } - return container.KeysInt64(ids) + return ids.Values() } func (comments CommentList) loadDependentIssues(ctx context.Context) error { @@ -439,13 +425,11 @@ func (comments CommentList) loadAttachments(ctx context.Context) (err error) { } func (comments CommentList) getReviewIDs() []int64 { - ids := make(map[int64]struct{}, len(comments)) + ids := make(container.Set[int64], len(comments)) for _, comment := range comments { - if _, ok := ids[comment.ReviewID]; !ok { - ids[comment.ReviewID] = struct{}{} - } + ids.Add(comment.ReviewID) } - return container.KeysInt64(ids) + return ids.Values() } func (comments CommentList) loadReviews(ctx context.Context) error { //nolint diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 874f2a6368..deadb6a564 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -22,16 +22,16 @@ type IssueList []*Issue // get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo func (issues IssueList) getRepoIDs() []int64 { - repoIDs := make(map[int64]struct{}, len(issues)) + repoIDs := make(container.Set[int64], len(issues)) for _, issue := range issues { if issue.Repo == nil { - repoIDs[issue.RepoID] = struct{}{} + repoIDs.Add(issue.RepoID) } if issue.PullRequest != nil && issue.PullRequest.HeadRepo == nil { - repoIDs[issue.PullRequest.HeadRepoID] = struct{}{} + repoIDs.Add(issue.PullRequest.HeadRepoID) } } - return container.KeysInt64(repoIDs) + return repoIDs.Values() } func (issues IssueList) loadRepositories(ctx context.Context) ([]*repo_model.Repository, error) { @@ -79,13 +79,11 @@ func (issues IssueList) LoadRepositories() ([]*repo_model.Repository, error) { } func (issues IssueList) getPosterIDs() []int64 { - posterIDs := make(map[int64]struct{}, len(issues)) + posterIDs := make(container.Set[int64], len(issues)) for _, issue := range issues { - if _, ok := posterIDs[issue.PosterID]; !ok { - posterIDs[issue.PosterID] = struct{}{} - } + posterIDs.Add(issue.PosterID) } - return container.KeysInt64(posterIDs) + return posterIDs.Values() } func (issues IssueList) loadPosters(ctx context.Context) error { @@ -185,13 +183,11 @@ func (issues IssueList) loadLabels(ctx context.Context) error { } func (issues IssueList) getMilestoneIDs() []int64 { - ids := make(map[int64]struct{}, len(issues)) + ids := make(container.Set[int64], len(issues)) for _, issue := range issues { - if _, ok := ids[issue.MilestoneID]; !ok { - ids[issue.MilestoneID] = struct{}{} - } + ids.Add(issue.MilestoneID) } - return container.KeysInt64(ids) + return ids.Values() } func (issues IssueList) loadMilestones(ctx context.Context) error { @@ -224,14 +220,11 @@ func (issues IssueList) loadMilestones(ctx context.Context) error { } func (issues IssueList) getProjectIDs() []int64 { - ids := make(map[int64]struct{}, len(issues)) + ids := make(container.Set[int64], len(issues)) for _, issue := range issues { - projectID := issue.ProjectID() - if _, ok := ids[projectID]; !ok { - ids[projectID] = struct{}{} - } + ids.Add(issue.ProjectID()) } - return container.KeysInt64(ids) + return ids.Values() } func (issues IssueList) loadProjects(ctx context.Context) error { diff --git a/models/issues/reaction.go b/models/issues/reaction.go index e7295c8af8..ccda10be2c 100644 --- a/models/issues/reaction.go +++ b/models/issues/reaction.go @@ -211,7 +211,7 @@ type ReactionOptions struct { // CreateReaction creates reaction for issue or comment. func CreateReaction(opts *ReactionOptions) (*Reaction, error) { - if !setting.UI.ReactionsMap[opts.Type] { + if !setting.UI.ReactionsLookup.Contains(opts.Type) { return nil, ErrForbiddenIssueReaction{opts.Type} } @@ -316,16 +316,14 @@ func (list ReactionList) GroupByType() map[string]ReactionList { } func (list ReactionList) getUserIDs() []int64 { - userIDs := make(map[int64]struct{}, len(list)) + userIDs := make(container.Set[int64], len(list)) for _, reaction := range list { if reaction.OriginalAuthor != "" { continue } - if _, ok := userIDs[reaction.UserID]; !ok { - userIDs[reaction.UserID] = struct{}{} - } + userIDs.Add(reaction.UserID) } - return container.KeysInt64(userIDs) + return userIDs.Values() } func valuesUser(m map[int64]*user_model.User) []*user_model.User { diff --git a/models/migrate.go b/models/migrate.go index f6bceaa019..d842fb967b 100644 --- a/models/migrate.go +++ b/models/migrate.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/structs" ) @@ -99,9 +100,9 @@ func InsertIssueComments(comments []*issues_model.Comment) error { return nil } - issueIDs := make(map[int64]bool) + issueIDs := make(container.Set[int64]) for _, comment := range comments { - issueIDs[comment.IssueID] = true + issueIDs.Add(comment.IssueID) } ctx, committer, err := db.TxContext() diff --git a/models/migrations/v115.go b/models/migrations/v115.go index 7708ed5e28..b242ccf238 100644 --- a/models/migrations/v115.go +++ b/models/migrations/v115.go @@ -13,6 +13,7 @@ import ( "path/filepath" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -39,7 +40,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error { } log.Info("%d User Avatar(s) to migrate ...", count) - deleteList := make(map[string]struct{}) + deleteList := make(container.Set[string]) start := 0 migrated := 0 for { @@ -86,7 +87,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error { return fmt.Errorf("[user: %s] user table update: %v", user.LowerName, err) } - deleteList[filepath.Join(setting.Avatar.Path, oldAvatar)] = struct{}{} + deleteList.Add(filepath.Join(setting.Avatar.Path, oldAvatar)) migrated++ select { case <-ticker.C: diff --git a/models/packages/conan/search.go b/models/packages/conan/search.go index 6a2cfa38f5..39a9000459 100644 --- a/models/packages/conan/search.go +++ b/models/packages/conan/search.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/container" conan_module "code.gitea.io/gitea/modules/packages/conan" "xorm.io/builder" @@ -88,7 +89,7 @@ func SearchRecipes(ctx context.Context, opts *RecipeSearchOptions) ([]string, er return nil, err } - unique := make(map[string]bool) + unique := make(container.Set[string]) for _, info := range results { recipe := fmt.Sprintf("%s/%s", info.Name, info.Version) @@ -111,7 +112,7 @@ func SearchRecipes(ctx context.Context, opts *RecipeSearchOptions) ([]string, er } } - unique[recipe] = true + unique.Add(recipe) } recipes := make([]string, 0, len(unique)) diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index cc524a417e..0cd0a3c8e3 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -68,10 +68,10 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error { return nil } - set := make(map[int64]struct{}) + set := make(container.Set[int64]) repoIDs := make([]int64, len(repos)) for i := range repos { - set[repos[i].OwnerID] = struct{}{} + set.Add(repos[i].OwnerID) repoIDs[i] = repos[i].ID } @@ -79,7 +79,7 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error { users := make(map[int64]*user_model.User, len(set)) if err := db.GetEngine(ctx). Where("id > 0"). - In("id", container.KeysInt64(set)). + In("id", set.Values()). Find(&users); err != nil { return fmt.Errorf("find users: %v", err) } diff --git a/models/repo/topic.go b/models/repo/topic.go index 2a16467215..7ba9a49e89 100644 --- a/models/repo/topic.go +++ b/models/repo/topic.go @@ -11,6 +11,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" @@ -62,7 +63,7 @@ func ValidateTopic(topic string) bool { // SanitizeAndValidateTopics sanitizes and checks an array or topics func SanitizeAndValidateTopics(topics []string) (validTopics, invalidTopics []string) { validTopics = make([]string, 0) - mValidTopics := make(map[string]struct{}) + mValidTopics := make(container.Set[string]) invalidTopics = make([]string, 0) for _, topic := range topics { @@ -72,12 +73,12 @@ func SanitizeAndValidateTopics(topics []string) (validTopics, invalidTopics []st continue } // ignore same topic twice - if _, ok := mValidTopics[topic]; ok { + if mValidTopics.Contains(topic) { continue } if ValidateTopic(topic) { validTopics = append(validTopics, topic) - mValidTopics[topic] = struct{}{} + mValidTopics.Add(topic) } else { invalidTopics = append(invalidTopics, topic) } diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index 6c0a241dc5..e7125f70f8 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" api "code.gitea.io/gitea/modules/structs" "xorm.io/builder" @@ -83,37 +84,19 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us return nil, err } - uidMap := map[int64]bool{} - i := 0 - for _, uid := range userIDs { - if uidMap[uid] { - continue - } - uidMap[uid] = true - userIDs[i] = uid - i++ - } - userIDs = userIDs[:i] - userIDs = append(userIDs, additionalUserIDs...) - - for _, uid := range additionalUserIDs { - if uidMap[uid] { - continue - } - userIDs[i] = uid - i++ - } - userIDs = userIDs[:i] + uniqueUserIDs := make(container.Set[int64]) + uniqueUserIDs.AddMultiple(userIDs...) + uniqueUserIDs.AddMultiple(additionalUserIDs...) // Leave a seat for owner itself to append later, but if owner is an organization // and just waste 1 unit is cheaper than re-allocate memory once. - users := make([]*user_model.User, 0, len(userIDs)+1) + users := make([]*user_model.User, 0, len(uniqueUserIDs)+1) if len(userIDs) > 0 { - if err = e.In("id", userIDs).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil { + if err = e.In("id", uniqueUserIDs.Values()).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil { return nil, err } } - if !repo.Owner.IsOrganization() && !uidMap[repo.OwnerID] { + if !repo.Owner.IsOrganization() && !uniqueUserIDs.Contains(repo.OwnerID) { users = append(users, repo.Owner) } diff --git a/modules/base/tool.go b/modules/base/tool.go index a981fd6c57..f1e4a3bf97 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -241,15 +241,6 @@ func Int64sToStrings(ints []int64) []string { return strs } -// Int64sToMap converts a slice of int64 to a int64 map. -func Int64sToMap(ints []int64) map[int64]bool { - m := make(map[int64]bool) - for _, i := range ints { - m[i] = true - } - return m -} - // Int64sContains returns if a int64 in a slice of int64 func Int64sContains(intsSlice []int64, a int64) bool { for _, c := range intsSlice { diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index 6685168bac..87de898e0b 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -214,16 +214,7 @@ func TestInt64sToStrings(t *testing.T) { ) } -func TestInt64sToMap(t *testing.T) { - assert.Equal(t, map[int64]bool{}, Int64sToMap([]int64{})) - assert.Equal(t, - map[int64]bool{1: true, 4: true, 16: true}, - Int64sToMap([]int64{1, 4, 16}), - ) -} - func TestInt64sContains(t *testing.T) { - assert.Equal(t, map[int64]bool{}, Int64sToMap([]int64{})) assert.True(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 1)) assert.True(t, Int64sContains([]int64{2323}, 2323)) assert.False(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 232)) diff --git a/modules/container/map.go b/modules/container/map.go deleted file mode 100644 index 3519de0951..0000000000 --- a/modules/container/map.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package container - -// KeysInt64 returns keys slice for a map with int64 key -func KeysInt64(m map[int64]struct{}) []int64 { - keys := make([]int64, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - return keys -} diff --git a/modules/container/set.go b/modules/container/set.go new file mode 100644 index 0000000000..4b4c74525d --- /dev/null +++ b/modules/container/set.go @@ -0,0 +1,57 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package container + +type Set[T comparable] map[T]struct{} + +// SetOf creates a set and adds the specified elements to it. +func SetOf[T comparable](values ...T) Set[T] { + s := make(Set[T], len(values)) + s.AddMultiple(values...) + return s +} + +// Add adds the specified element to a set. +// Returns true if the element is added; false if the element is already present. +func (s Set[T]) Add(value T) bool { + if _, has := s[value]; !has { + s[value] = struct{}{} + return true + } + return false +} + +// AddMultiple adds the specified elements to a set. +func (s Set[T]) AddMultiple(values ...T) { + for _, value := range values { + s.Add(value) + } +} + +// Contains determines whether a set contains the specified element. +// Returns true if the set contains the specified element; otherwise, false. +func (s Set[T]) Contains(value T) bool { + _, has := s[value] + return has +} + +// Remove removes the specified element. +// Returns true if the element is successfully found and removed; otherwise, false. +func (s Set[T]) Remove(value T) bool { + if _, has := s[value]; has { + delete(s, value) + return true + } + return false +} + +// Values gets a list of all elements in the set. +func (s Set[T]) Values() []T { + keys := make([]T, 0, len(s)) + for k := range s { + keys = append(keys, k) + } + return keys +} diff --git a/modules/container/set_test.go b/modules/container/set_test.go new file mode 100644 index 0000000000..6654763e56 --- /dev/null +++ b/modules/container/set_test.go @@ -0,0 +1,37 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package container + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSet(t *testing.T) { + s := make(Set[string]) + + assert.True(t, s.Add("key1")) + assert.False(t, s.Add("key1")) + assert.True(t, s.Add("key2")) + + assert.True(t, s.Contains("key1")) + assert.True(t, s.Contains("key2")) + assert.False(t, s.Contains("key3")) + + assert.True(t, s.Remove("key2")) + assert.False(t, s.Contains("key2")) + + assert.False(t, s.Remove("key3")) + + s.AddMultiple("key4", "key5") + assert.True(t, s.Contains("key4")) + assert.True(t, s.Contains("key5")) + + s = SetOf("key6", "key7") + assert.False(t, s.Contains("key1")) + assert.True(t, s.Contains("key6")) + assert.True(t, s.Contains("key7")) +} diff --git a/modules/doctor/authorizedkeys.go b/modules/doctor/authorizedkeys.go index 34dfe939d3..d4ceef87c0 100644 --- a/modules/doctor/authorizedkeys.go +++ b/modules/doctor/authorizedkeys.go @@ -14,6 +14,7 @@ import ( "strings" asymkey_model "code.gitea.io/gitea/models/asymkey" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) @@ -40,7 +41,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e } defer f.Close() - linesInAuthorizedKeys := map[string]bool{} + linesInAuthorizedKeys := make(container.Set[string]) scanner := bufio.NewScanner(f) for scanner.Scan() { @@ -48,7 +49,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e if strings.HasPrefix(line, tplCommentPrefix) { continue } - linesInAuthorizedKeys[line] = true + linesInAuthorizedKeys.Add(line) } f.Close() @@ -64,7 +65,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e if strings.HasPrefix(line, tplCommentPrefix) { continue } - if ok := linesInAuthorizedKeys[line]; ok { + if linesInAuthorizedKeys.Contains(line) { continue } if !autofix { diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index 80f1602708..469932525f 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -14,6 +14,8 @@ import ( "sort" "strings" + "code.gitea.io/gitea/modules/container" + "github.com/djherbis/buffer" "github.com/djherbis/nio/v3" ) @@ -339,7 +341,7 @@ func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath st lastEmptyParent := head.ID.String() commitSinceLastEmptyParent := uint64(0) commitSinceNextRestart := uint64(0) - parentRemaining := map[string]bool{} + parentRemaining := make(container.Set[string]) changed := make([]bool, len(paths)) @@ -365,7 +367,7 @@ heaploop: if current == nil { break heaploop } - delete(parentRemaining, current.CommitID) + parentRemaining.Remove(current.CommitID) if current.Paths != nil { for i, found := range current.Paths { if !found { @@ -410,14 +412,12 @@ heaploop: } } g = NewLogNameStatusRepoParser(ctx, repo.Path, lastEmptyParent, treepath, remainingPaths...) - parentRemaining = map[string]bool{} + parentRemaining = make(container.Set[string]) nextRestart = (remaining * 3) / 4 continue heaploop } } - for _, parent := range current.ParentIDs { - parentRemaining[parent] = true - } + parentRemaining.AddMultiple(current.ParentIDs...) } g.Close() diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go index c0c91c6fc6..cb44c58cec 100644 --- a/modules/git/repo_stats.go +++ b/modules/git/repo_stats.go @@ -13,6 +13,8 @@ import ( "strconv" "strings" "time" + + "code.gitea.io/gitea/modules/container" ) // CodeActivityStats represents git statistics data @@ -80,7 +82,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) stats.Additions = 0 stats.Deletions = 0 authors := make(map[string]*CodeActivityAuthor) - files := make(map[string]bool) + files := make(container.Set[string]) var author string p := 0 for scanner.Scan() { @@ -119,9 +121,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) stats.Deletions += c } } - if _, ok := files[parts[2]]; !ok { - files[parts[2]] = true - } + files.Add(parts[2]) } } } diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go index a4c0fb5aa6..3b33852cb5 100644 --- a/modules/issue/template/template.go +++ b/modules/issue/template/template.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/container" api "code.gitea.io/gitea/modules/structs" "gitea.com/go-chi/binding" @@ -43,7 +44,7 @@ func validateYaml(template *api.IssueTemplate) error { if len(template.Fields) == 0 { return fmt.Errorf("'body' is required") } - ids := map[string]struct{}{} + ids := make(container.Set[string]) for idx, field := range template.Fields { if err := validateID(field, idx, ids); err != nil { return err @@ -125,7 +126,7 @@ func validateRequired(field *api.IssueFormField, idx int) error { return validateBoolItem(newErrorPosition(idx, field.Type), field.Validations, "required") } -func validateID(field *api.IssueFormField, idx int, ids map[string]struct{}) error { +func validateID(field *api.IssueFormField, idx int, ids container.Set[string]) error { if field.Type == api.IssueFormFieldTypeMarkdown { // The ID is not required for a markdown field return nil @@ -139,10 +140,9 @@ func validateID(field *api.IssueFormField, idx int, ids map[string]struct{}) err if binding.AlphaDashPattern.MatchString(field.ID) { return position.Errorf("'id' should contain only alphanumeric, '-' and '_'") } - if _, ok := ids[field.ID]; ok { + if !ids.Add(field.ID) { return position.Errorf("'id' should be unique") } - ids[field.ID] = struct{}{} return nil } diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 24f1ab7a01..8417019ddb 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -10,6 +10,7 @@ import ( "regexp" "strings" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/common" "code.gitea.io/gitea/modules/setting" @@ -198,7 +199,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa } type prefixedIDs struct { - values map[string]bool + values container.Set[string] } // Generate generates a new element id. @@ -219,14 +220,12 @@ func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte { if !bytes.HasPrefix(result, []byte("user-content-")) { result = append([]byte("user-content-"), result...) } - if _, ok := p.values[util.BytesToReadOnlyString(result)]; !ok { - p.values[util.BytesToReadOnlyString(result)] = true + if p.values.Add(util.BytesToReadOnlyString(result)) { return result } for i := 1; ; i++ { newResult := fmt.Sprintf("%s-%d", result, i) - if _, ok := p.values[newResult]; !ok { - p.values[newResult] = true + if p.values.Add(newResult) { return []byte(newResult) } } @@ -234,12 +233,12 @@ func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte { // Put puts a given element id to the used ids table. func (p *prefixedIDs) Put(value []byte) { - p.values[util.BytesToReadOnlyString(value)] = true + p.values.Add(util.BytesToReadOnlyString(value)) } func newPrefixedIDs() *prefixedIDs { return &prefixedIDs{ - values: map[string]bool{}, + values: make(container.Set[string]), } } diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index 5e5196a70a..4d96a6b0ed 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -10,6 +10,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification/base" @@ -123,14 +124,14 @@ func (ns *notificationService) NotifyNewPullRequest(pr *issues_model.PullRequest log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err) return } - toNotify := make(map[int64]struct{}, 32) + toNotify := make(container.Set[int64], 32) repoWatchers, err := repo_model.GetRepoWatchersIDs(db.DefaultContext, pr.Issue.RepoID) if err != nil { log.Error("GetRepoWatchersIDs: %v", err) return } for _, id := range repoWatchers { - toNotify[id] = struct{}{} + toNotify.Add(id) } issueParticipants, err := issues_model.GetParticipantsIDsByIssueID(pr.IssueID) if err != nil { @@ -138,11 +139,11 @@ func (ns *notificationService) NotifyNewPullRequest(pr *issues_model.PullRequest return } for _, id := range issueParticipants { - toNotify[id] = struct{}{} + toNotify.Add(id) } delete(toNotify, pr.Issue.PosterID) for _, mention := range mentions { - toNotify[mention.ID] = struct{}{} + toNotify.Add(mention.ID) } for receiverID := range toNotify { _ = ns.issueQueue.Push(issueNotificationOpts{ diff --git a/modules/public/public.go b/modules/public/public.go index 7804e945e7..ac1d80c860 100644 --- a/modules/public/public.go +++ b/modules/public/public.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strings" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -83,11 +84,11 @@ func AssetsHandlerFunc(opts *Options) http.HandlerFunc { } // parseAcceptEncoding parse Accept-Encoding: deflate, gzip;q=1.0, *;q=0.5 as compress methods -func parseAcceptEncoding(val string) map[string]bool { +func parseAcceptEncoding(val string) container.Set[string] { parts := strings.Split(val, ";") - types := make(map[string]bool) + types := make(container.Set[string]) for _, v := range strings.Split(parts[0], ",") { - types[strings.TrimSpace(v)] = true + types.Add(strings.TrimSpace(v)) } return types } diff --git a/modules/public/public_test.go b/modules/public/public_test.go index 430e734564..8b58d6af33 100644 --- a/modules/public/public_test.go +++ b/modules/public/public_test.go @@ -7,28 +7,23 @@ package public import ( "testing" + "code.gitea.io/gitea/modules/container" + "github.com/stretchr/testify/assert" ) func TestParseAcceptEncoding(t *testing.T) { kases := []struct { Header string - Expected map[string]bool + Expected container.Set[string] }{ { - Header: "deflate, gzip;q=1.0, *;q=0.5", - Expected: map[string]bool{ - "deflate": true, - "gzip": true, - }, + Header: "deflate, gzip;q=1.0, *;q=0.5", + Expected: container.SetOf("deflate", "gzip"), }, { - Header: " gzip, deflate, br", - Expected: map[string]bool{ - "deflate": true, - "gzip": true, - "br": true, - }, + Header: " gzip, deflate, br", + Expected: container.SetOf("deflate", "gzip", "br"), }, } diff --git a/modules/public/serve_static.go b/modules/public/serve_static.go index 9666880adf..10120bf85d 100644 --- a/modules/public/serve_static.go +++ b/modules/public/serve_static.go @@ -60,7 +60,7 @@ func AssetIsDir(name string) (bool, error) { // serveContent serve http content func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) { encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding")) - if encodings["gzip"] { + if encodings.Contains("gzip") { if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok { rdGzip := bytes.NewReader(cf.GzipBytes()) // all static files are managed by Gitea, so we can make sure every file has the correct ext name diff --git a/modules/queue/unique_queue_channel.go b/modules/queue/unique_queue_channel.go index 6e8d37a20c..d1bf7239eb 100644 --- a/modules/queue/unique_queue_channel.go +++ b/modules/queue/unique_queue_channel.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" ) @@ -33,7 +34,7 @@ type ChannelUniqueQueueConfiguration ChannelQueueConfiguration type ChannelUniqueQueue struct { *WorkerPool lock sync.Mutex - table map[string]bool + table container.Set[string] shutdownCtx context.Context shutdownCtxCancel context.CancelFunc terminateCtx context.Context @@ -58,7 +59,7 @@ func NewChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue shutdownCtx, shutdownCtxCancel := context.WithCancel(terminateCtx) queue := &ChannelUniqueQueue{ - table: map[string]bool{}, + table: make(container.Set[string]), shutdownCtx: shutdownCtx, shutdownCtxCancel: shutdownCtxCancel, terminateCtx: terminateCtx, @@ -73,7 +74,7 @@ func NewChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue bs, _ := json.Marshal(datum) queue.lock.Lock() - delete(queue.table, string(bs)) + queue.table.Remove(string(bs)) queue.lock.Unlock() if u := handle(datum); u != nil { @@ -127,16 +128,15 @@ func (q *ChannelUniqueQueue) PushFunc(data Data, fn func() error) error { q.lock.Unlock() } }() - if _, ok := q.table[string(bs)]; ok { + if !q.table.Add(string(bs)) { return ErrAlreadyInQueue } // FIXME: We probably need to implement some sort of limit here // If the downstream queue blocks this table will grow without limit - q.table[string(bs)] = true if fn != nil { err := fn() if err != nil { - delete(q.table, string(bs)) + q.table.Remove(string(bs)) return err } } @@ -155,8 +155,7 @@ func (q *ChannelUniqueQueue) Has(data Data) (bool, error) { q.lock.Lock() defer q.lock.Unlock() - _, has := q.table[string(bs)] - return has, nil + return q.table.Contains(string(bs)), nil } // Flush flushes the channel with a timeout - the Flush worker will be registered as a flush worker with the manager diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 48c3edf60f..b01be322d2 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -275,7 +276,7 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository) return pullMirrorReleaseSync(repo, gitRepo) } - existingRelTags := make(map[string]struct{}) + existingRelTags := make(container.Set[string]) opts := repo_model.FindReleasesOptions{ IncludeDrafts: true, IncludeTags: true, @@ -303,14 +304,14 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository) return fmt.Errorf("unable to PushUpdateDeleteTag: %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err) } } else { - existingRelTags[strings.ToLower(rel.TagName)] = struct{}{} + existingRelTags.Add(strings.ToLower(rel.TagName)) } } } _, err := gitRepo.WalkReferences(git.ObjectTag, 0, 0, func(sha1, refname string) error { tagName := strings.TrimPrefix(refname, git.TagPrefix) - if _, ok := existingRelTags[strings.ToLower(tagName)]; ok { + if existingRelTags.Contains(strings.ToLower(tagName)) { return nil } diff --git a/modules/setting/queue.go b/modules/setting/queue.go index cb86cbdfe0..d3bb33b248 100644 --- a/modules/setting/queue.go +++ b/modules/setting/queue.go @@ -9,6 +9,7 @@ import ( "strconv" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" ini "gopkg.in/ini.v1" @@ -109,8 +110,8 @@ func NewQueueService() { // Now handle the old issue_indexer configuration // FIXME: DEPRECATED to be removed in v1.18.0 section := Cfg.Section("queue.issue_indexer") - directlySet := toDirectlySetKeysMap(section) - if !directlySet["TYPE"] && defaultType == "" { + directlySet := toDirectlySetKeysSet(section) + if !directlySet.Contains("TYPE") && defaultType == "" { switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ { case "levelqueue": _, _ = section.NewKey("TYPE", "level") @@ -124,25 +125,25 @@ func NewQueueService() { log.Fatal("Unsupported indexer queue type: %v", typ) } } - if !directlySet["LENGTH"] { + if !directlySet.Contains("LENGTH") { length := Cfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0) if length != 0 { _, _ = section.NewKey("LENGTH", strconv.Itoa(length)) } } - if !directlySet["BATCH_LENGTH"] { + if !directlySet.Contains("BATCH_LENGTH") { fallback := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0) if fallback != 0 { _, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback)) } } - if !directlySet["DATADIR"] { + if !directlySet.Contains("DATADIR") { queueDir := filepath.ToSlash(Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString("")) if queueDir != "" { _, _ = section.NewKey("DATADIR", queueDir) } } - if !directlySet["CONN_STR"] { + if !directlySet.Contains("CONN_STR") { connStr := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("") if connStr != "" { _, _ = section.NewKey("CONN_STR", connStr) @@ -178,19 +179,19 @@ func handleOldLengthConfiguration(queueName, oldSection, oldKey string, defaultV } section := Cfg.Section("queue." + queueName) - directlySet := toDirectlySetKeysMap(section) - if !directlySet["LENGTH"] { + directlySet := toDirectlySetKeysSet(section) + if !directlySet.Contains("LENGTH") { _, _ = section.NewKey("LENGTH", strconv.Itoa(value)) } } -// toDirectlySetKeysMap returns a bool map of keys directly set by this section +// toDirectlySetKeysSet returns a set of keys directly set by this section // Note: we cannot use section.HasKey(...) as that will immediately set the Key if a parent section has the Key // but this section does not. -func toDirectlySetKeysMap(section *ini.Section) map[string]bool { - sectionMap := map[string]bool{} +func toDirectlySetKeysSet(section *ini.Section) container.Set[string] { + sections := make(container.Set[string]) for _, key := range section.Keys() { - sectionMap[key.Name()] = true + sections.Add(key.Name()) } - return sectionMap + return sections } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 6233437bf5..007e3ef61f 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -21,6 +21,7 @@ import ( "text/template" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/user" @@ -234,7 +235,7 @@ var ( DefaultTheme string Themes []string Reactions []string - ReactionsMap map[string]bool `ini:"-"` + ReactionsLookup container.Set[string] `ini:"-"` CustomEmojis []string CustomEmojisMap map[string]string `ini:"-"` SearchRepoDescription bool @@ -1100,9 +1101,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) { newMarkup() - UI.ReactionsMap = make(map[string]bool) + UI.ReactionsLookup = make(container.Set[string]) for _, reaction := range UI.Reactions { - UI.ReactionsMap[reaction] = true + UI.ReactionsLookup.Add(reaction) } UI.CustomEmojisMap = make(map[string]string) for _, emoji := range UI.CustomEmojis { diff --git a/modules/sync/status_pool.go b/modules/sync/status_pool.go index acbd93ab17..99e5ce9cb3 100644 --- a/modules/sync/status_pool.go +++ b/modules/sync/status_pool.go @@ -6,6 +6,8 @@ package sync import ( "sync" + + "code.gitea.io/gitea/modules/container" ) // StatusTable is a table maintains true/false values. @@ -14,13 +16,13 @@ import ( // in different goroutines. type StatusTable struct { lock sync.RWMutex - pool map[string]struct{} + pool container.Set[string] } // NewStatusTable initializes and returns a new StatusTable object. func NewStatusTable() *StatusTable { return &StatusTable{ - pool: make(map[string]struct{}), + pool: make(container.Set[string]), } } @@ -28,32 +30,29 @@ func NewStatusTable() *StatusTable { // Returns whether set value was set to true func (p *StatusTable) StartIfNotRunning(name string) bool { p.lock.Lock() - _, ok := p.pool[name] - if !ok { - p.pool[name] = struct{}{} - } + added := p.pool.Add(name) p.lock.Unlock() - return !ok + return added } // Start sets value of given name to true in the pool. func (p *StatusTable) Start(name string) { p.lock.Lock() - p.pool[name] = struct{}{} + p.pool.Add(name) p.lock.Unlock() } // Stop sets value of given name to false in the pool. func (p *StatusTable) Stop(name string) { p.lock.Lock() - delete(p.pool, name) + p.pool.Remove(name) p.lock.Unlock() } // IsRunning checks if value of given name is set to true in the pool. func (p *StatusTable) IsRunning(name string) bool { p.lock.RLock() - _, ok := p.pool[name] + exists := p.pool.Contains(name) p.lock.RUnlock() - return ok + return exists } diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go index c57f80ac2f..dd078d6ad3 100644 --- a/routers/api/packages/conan/conan.go +++ b/routers/api/packages/conan/conan.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" conan_model "code.gitea.io/gitea/models/packages/conan" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -33,20 +34,18 @@ const ( packageReferenceKey = "PackageReference" ) -type stringSet map[string]struct{} - var ( - recipeFileList = stringSet{ - conanfileFile: struct{}{}, - "conanmanifest.txt": struct{}{}, - "conan_sources.tgz": struct{}{}, - "conan_export.tgz": struct{}{}, - } - packageFileList = stringSet{ - conaninfoFile: struct{}{}, - "conanmanifest.txt": struct{}{}, - "conan_package.tgz": struct{}{}, - } + recipeFileList = container.SetOf( + conanfileFile, + "conanmanifest.txt", + "conan_sources.tgz", + "conan_export.tgz", + ) + packageFileList = container.SetOf( + conaninfoFile, + "conanmanifest.txt", + "conan_package.tgz", + ) ) func jsonResponse(ctx *context.Context, status int, obj interface{}) { @@ -268,7 +267,7 @@ func PackageUploadURLs(ctx *context.Context) { ) } -func serveUploadURLs(ctx *context.Context, fileFilter stringSet, uploadURL string) { +func serveUploadURLs(ctx *context.Context, fileFilter container.Set[string], uploadURL string) { defer ctx.Req.Body.Close() var files map[string]int64 @@ -279,7 +278,7 @@ func serveUploadURLs(ctx *context.Context, fileFilter stringSet, uploadURL strin urls := make(map[string]string) for file := range files { - if _, ok := fileFilter[file]; ok { + if fileFilter.Contains(file) { urls[file] = fmt.Sprintf("%s/%s", uploadURL, file) } } @@ -301,12 +300,12 @@ func UploadPackageFile(ctx *context.Context) { uploadFile(ctx, packageFileList, pref.AsKey()) } -func uploadFile(ctx *context.Context, fileFilter stringSet, fileKey string) { +func uploadFile(ctx *context.Context, fileFilter container.Set[string], fileKey string) { rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference) pref := ctx.Data[packageReferenceKey].(*conan_module.PackageReference) filename := ctx.Params("filename") - if _, ok := fileFilter[filename]; !ok { + if !fileFilter.Contains(filename) { apiError(ctx, http.StatusBadRequest, nil) return } @@ -442,11 +441,11 @@ func DownloadPackageFile(ctx *context.Context) { downloadFile(ctx, packageFileList, pref.AsKey()) } -func downloadFile(ctx *context.Context, fileFilter stringSet, fileKey string) { +func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKey string) { rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference) filename := ctx.Params("filename") - if _, ok := fileFilter[filename]; !ok { + if !fileFilter.Contains(filename) { apiError(ctx, http.StatusBadRequest, nil) return } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 5dab770d55..38ad593c17 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -30,6 +30,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" @@ -947,10 +948,11 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull if err != nil { return nil, nil, 0, 0 } - labelIDMark := base.Int64sToMap(labelIDs) + labelIDMark := make(container.Set[int64]) + labelIDMark.AddMultiple(labelIDs...) for i := range labels { - if labelIDMark[labels[i].ID] { + if labelIDMark.Contains(labels[i].ID) { labels[i].IsChecked = true hasSelected = true } @@ -1293,9 +1295,9 @@ func ViewIssue(ctx *context.Context) { // Metas. // Check labels. - labelIDMark := make(map[int64]bool) - for i := range issue.Labels { - labelIDMark[issue.Labels[i].ID] = true + labelIDMark := make(container.Set[int64]) + for _, label := range issue.Labels { + labelIDMark.Add(label.ID) } labels, err := issues_model.GetLabelsByRepoID(ctx, repo.ID, "", db.ListOptions{}) if err != nil { @@ -1317,7 +1319,7 @@ func ViewIssue(ctx *context.Context) { hasSelected := false for i := range labels { - if labelIDMark[labels[i].ID] { + if labelIDMark.Contains(labels[i].ID) { labels[i].IsChecked = true hasSelected = true } diff --git a/routers/web/repo/lfs.go b/routers/web/repo/lfs.go index baec48bfea..633b8ab1a5 100644 --- a/routers/web/repo/lfs.go +++ b/routers/web/repo/lfs.go @@ -18,6 +18,7 @@ import ( git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/pipeline" @@ -176,14 +177,12 @@ func LFSLocks(ctx *context.Context) { return } - filemap := make(map[string]bool, len(filelist)) - for _, name := range filelist { - filemap[name] = true - } + fileset := make(container.Set[string], len(filelist)) + fileset.AddMultiple(filelist...) linkable := make([]bool, len(lfsLocks)) for i, lock := range lfsLocks { - linkable[i] = filemap[lock.Path] + linkable[i] = fileset.Contains(lock.Path) } ctx.Data["Linkable"] = linkable diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 34305b7621..a43840467d 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -28,6 +28,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/highlight" @@ -811,16 +812,14 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri defer cancel() } - selected := map[string]bool{} - for _, pth := range ctx.FormStrings("f[]") { - selected[pth] = true - } + selected := make(container.Set[string]) + selected.AddMultiple(ctx.FormStrings("f[]")...) entries := allEntries if len(selected) > 0 { entries = make(git.Entries, 0, len(selected)) for _, entry := range allEntries { - if selected[entry.Name()] { + if selected.Contains(entry.Name()) { entries = append(entries, entry) } } diff --git a/services/issue/commit.go b/services/issue/commit.go index 0d04de81bc..c8cfa6cc8a 100644 --- a/services/issue/commit.go +++ b/services/issue/commit.go @@ -18,6 +18,7 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/repository" @@ -111,7 +112,7 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm Action references.XRefAction } - refMarked := make(map[markKey]bool) + refMarked := make(container.Set[markKey]) var refRepo *repo_model.Repository var refIssue *issues_model.Issue var err error @@ -144,10 +145,9 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm } key := markKey{ID: refIssue.ID, Action: ref.Action} - if refMarked[key] { + if !refMarked.Add(key) { continue } - refMarked[key] = true // FIXME: this kind of condition is all over the code, it should be consolidated in a single place canclose := perm.IsAdmin() || perm.IsOwner() || perm.CanWriteIssuesOrPulls(refIssue.IsPull) || refIssue.PosterID == doer.ID diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go index 2dab673b4e..af07821c29 100644 --- a/services/mailer/mail_comment.go +++ b/services/mailer/mail_comment.go @@ -10,6 +10,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) @@ -46,8 +47,8 @@ func MailMentionsComment(ctx context.Context, pr *issues_model.PullRequest, c *i return nil } - visited := make(map[int64]bool, len(mentions)+1) - visited[c.Poster.ID] = true + visited := make(container.Set[int64], len(mentions)+1) + visited.Add(c.Poster.ID) if err = mailIssueCommentBatch( &mailCommentContext{ Context: ctx, diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index ec6ddcf14e..15bfa4af41 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -14,6 +14,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) @@ -89,11 +90,11 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo unfiltered = append(ids, unfiltered...) } - visited := make(map[int64]bool, len(unfiltered)+len(mentions)+1) + visited := make(container.Set[int64], len(unfiltered)+len(mentions)+1) // Avoid mailing the doer if ctx.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn { - visited[ctx.Doer.ID] = true + visited.Add(ctx.Doer.ID) } // =========== Mentions =========== @@ -106,9 +107,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo if err != nil { return fmt.Errorf("GetIssueWatchersIDs(%d): %v", ctx.Issue.ID, err) } - for _, i := range ids { - visited[i] = true - } + visited.AddMultiple(ids...) unfilteredUsers, err := user_model.GetMaileableUsersByIDs(unfiltered, false) if err != nil { @@ -121,7 +120,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo return nil } -func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited map[int64]bool, fromMention bool) error { +func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited container.Set[int64], fromMention bool) error { checkUnit := unit.TypeIssues if ctx.Issue.IsPull { checkUnit = unit.TypePullRequests @@ -142,13 +141,10 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi } // if we have already visited this user we exclude them - if _, ok := visited[user.ID]; ok { + if !visited.Add(user.ID) { continue } - // now mark them as visited - visited[user.ID] = true - // test if this user is allowed to see the issue/pull if !access_model.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) { continue diff --git a/services/pull/patch.go b/services/pull/patch.go index 32895b2e78..dafd577069 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -409,7 +410,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * const appliedPatchPrefix = "Applied patch to '" const withConflicts = "' with conflicts." - conflictMap := map[string]bool{} + conflicts := make(container.Set[string]) // Now scan the output from the command scanner := bufio.NewScanner(stderrReader) @@ -418,7 +419,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * if strings.HasPrefix(line, prefix) { conflict = true filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0]) - conflictMap[filepath] = true + conflicts.Add(filepath) } else if is3way && line == threewayFailed { conflict = true } else if strings.HasPrefix(line, errorPrefix) { @@ -427,7 +428,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * if strings.HasSuffix(line, suffix) { filepath := strings.TrimSpace(strings.TrimSuffix(line[len(errorPrefix):], suffix)) if filepath != "" { - conflictMap[filepath] = true + conflicts.Add(filepath) } break } @@ -436,18 +437,18 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * conflict = true filepath := strings.TrimPrefix(strings.TrimSuffix(line, withConflicts), appliedPatchPrefix) if filepath != "" { - conflictMap[filepath] = true + conflicts.Add(filepath) } } // only list 10 conflicted files - if len(conflictMap) >= 10 { + if len(conflicts) >= 10 { break } } - if len(conflictMap) > 0 { - pr.ConflictedFiles = make([]string, 0, len(conflictMap)) - for key := range conflictMap { + if len(conflicts) > 0 { + pr.ConflictedFiles = make([]string, 0, len(conflicts)) + for key := range conflicts { pr.ConflictedFiles = append(pr.ConflictedFiles, key) } } diff --git a/services/pull/pull.go b/services/pull/pull.go index 103fdc340d..9de7cb5d4f 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -20,6 +20,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" @@ -640,7 +641,7 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ posterSig := pr.Issue.Poster.NewGitSig().String() - authorsMap := map[string]bool{} + uniqueAuthors := make(container.Set[string]) authors := make([]string, 0, len(commits)) stringBuilder := strings.Builder{} @@ -687,9 +688,8 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ } authorString := commit.Author.String() - if !authorsMap[authorString] && authorString != posterSig { + if uniqueAuthors.Add(authorString) && authorString != posterSig { authors = append(authors, authorString) - authorsMap[authorString] = true } } @@ -709,9 +709,8 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ } for _, commit := range commits { authorString := commit.Author.String() - if !authorsMap[authorString] && authorString != posterSig { + if uniqueAuthors.Add(authorString) && authorString != posterSig { authors = append(authors, authorString) - authorsMap[authorString] = true } } skip += limit diff --git a/services/release/release.go b/services/release/release.go index ae610b0e3c..187ebeb486 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -15,6 +15,7 @@ import ( git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" @@ -209,7 +210,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_mod return fmt.Errorf("AddReleaseAttachments: %v", err) } - deletedUUIDsMap := make(map[string]bool) + deletedUUIDs := make(container.Set[string]) if len(delAttachmentUUIDs) > 0 { // Check attachments attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs) @@ -220,7 +221,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_mod if attach.ReleaseID != rel.ID { return errors.New("delete attachement of release permission denied") } - deletedUUIDsMap[attach.UUID] = true + deletedUUIDs.Add(attach.UUID) } if _, err := repo_model.DeleteAttachments(ctx, attachments, false); err != nil { @@ -245,7 +246,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_mod } for uuid, newName := range editAttachments { - if !deletedUUIDsMap[uuid] { + if !deletedUUIDs.Contains(uuid) { if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{ UUID: uuid, Name: newName, diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 74876d8e76..9e04c15977 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" @@ -257,12 +258,12 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad if len(repos) == len(repoNamesToCheck) { return nil } - repoNames := make(map[string]bool, len(repos)) + repoNames := make(container.Set[string], len(repos)) for _, repo := range repos { - repoNames[repo.LowerName] = true + repoNames.Add(repo.LowerName) } for _, repoName := range repoNamesToCheck { - if _, ok := repoNames[repoName]; !ok { + if !repoNames.Contains(repoName) { unadopted.add(filepath.Join(userName, repoName)) } }