Add generic set type (#21408)

This PR adds a generic set type to get rid of maps used as sets.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
KN4CK3R 2022-10-12 07:18:26 +02:00 committed by GitHub
parent e84558b093
commit 0e57ff7eee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 328 additions and 324 deletions

View file

@ -18,13 +18,11 @@ import (
type ActionList []*Action type ActionList []*Action
func (actions ActionList) getUserIDs() []int64 { func (actions ActionList) getUserIDs() []int64 {
userIDs := make(map[int64]struct{}, len(actions)) userIDs := make(container.Set[int64], len(actions))
for _, action := range actions { for _, action := range actions {
if _, ok := userIDs[action.ActUserID]; !ok { userIDs.Add(action.ActUserID)
userIDs[action.ActUserID] = struct{}{}
}
} }
return container.KeysInt64(userIDs) return userIDs.Values()
} }
func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model.User, error) { 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 { func (actions ActionList) getRepoIDs() []int64 {
repoIDs := make(map[int64]struct{}, len(actions)) repoIDs := make(container.Set[int64], len(actions))
for _, action := range actions { for _, action := range actions {
if _, ok := repoIDs[action.RepoID]; !ok { repoIDs.Add(action.RepoID)
repoIDs[action.RepoID] = struct{}{}
}
} }
return container.KeysInt64(repoIDs) return repoIDs.Values()
} }
func (actions ActionList) loadRepositories(ctx context.Context) error { func (actions ActionList) loadRepositories(ctx context.Context) error {

View file

@ -200,7 +200,7 @@ func CreateOrUpdateIssueNotifications(issueID, commentID, notificationAuthorID,
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error { func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
// init // init
var toNotify map[int64]struct{} var toNotify container.Set[int64]
notifications, err := getNotificationsByIssueID(ctx, issueID) notifications, err := getNotificationsByIssueID(ctx, issueID)
if err != nil { if err != nil {
return err return err
@ -212,33 +212,27 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
} }
if receiverID > 0 { if receiverID > 0 {
toNotify = make(map[int64]struct{}, 1) toNotify = make(container.Set[int64], 1)
toNotify[receiverID] = struct{}{} toNotify.Add(receiverID)
} else { } else {
toNotify = make(map[int64]struct{}, 32) toNotify = make(container.Set[int64], 32)
issueWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, true) issueWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, true)
if err != nil { if err != nil {
return err return err
} }
for _, id := range issueWatches { toNotify.AddMultiple(issueWatches...)
toNotify[id] = struct{}{}
}
if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) { if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) {
repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID) repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID)
if err != nil { if err != nil {
return err return err
} }
for _, id := range repoWatches { toNotify.AddMultiple(repoWatches...)
toNotify[id] = struct{}{}
}
} }
issueParticipants, err := issue.GetParticipantIDsByIssue(ctx) issueParticipants, err := issue.GetParticipantIDsByIssue(ctx)
if err != nil { if err != nil {
return err return err
} }
for _, id := range issueParticipants { toNotify.AddMultiple(issueParticipants...)
toNotify[id] = struct{}{}
}
// dont notify user who cause notification // dont notify user who cause notification
delete(toNotify, notificationAuthorID) delete(toNotify, notificationAuthorID)
@ -248,7 +242,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
return err return err
} }
for _, id := range issueUnWatches { for _, id := range issueUnWatches {
delete(toNotify, id) toNotify.Remove(id)
} }
} }
@ -499,16 +493,14 @@ func (nl NotificationList) LoadAttributes() error {
} }
func (nl NotificationList) getPendingRepoIDs() []int64 { func (nl NotificationList) getPendingRepoIDs() []int64 {
ids := make(map[int64]struct{}, len(nl)) ids := make(container.Set[int64], len(nl))
for _, notification := range nl { for _, notification := range nl {
if notification.Repository != nil { if notification.Repository != nil {
continue continue
} }
if _, ok := ids[notification.RepoID]; !ok { ids.Add(notification.RepoID)
ids[notification.RepoID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
// LoadRepos loads repositories from database // LoadRepos loads repositories from database
@ -575,16 +567,14 @@ func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error)
} }
func (nl NotificationList) getPendingIssueIDs() []int64 { func (nl NotificationList) getPendingIssueIDs() []int64 {
ids := make(map[int64]struct{}, len(nl)) ids := make(container.Set[int64], len(nl))
for _, notification := range nl { for _, notification := range nl {
if notification.Issue != nil { if notification.Issue != nil {
continue continue
} }
if _, ok := ids[notification.IssueID]; !ok { ids.Add(notification.IssueID)
ids[notification.IssueID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
// LoadIssues loads issues from database // LoadIssues loads issues from database
@ -661,16 +651,14 @@ func (nl NotificationList) Without(failures []int) NotificationList {
} }
func (nl NotificationList) getPendingCommentIDs() []int64 { func (nl NotificationList) getPendingCommentIDs() []int64 {
ids := make(map[int64]struct{}, len(nl)) ids := make(container.Set[int64], len(nl))
for _, notification := range nl { for _, notification := range nl {
if notification.CommentID == 0 || notification.Comment != nil { if notification.CommentID == 0 || notification.Comment != nil {
continue continue
} }
if _, ok := ids[notification.CommentID]; !ok { ids.Add(notification.CommentID)
ids[notification.CommentID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
// LoadComments loads comments from database // LoadComments loads comments from database

View file

@ -17,13 +17,11 @@ import (
type CommentList []*Comment type CommentList []*Comment
func (comments CommentList) getPosterIDs() []int64 { func (comments CommentList) getPosterIDs() []int64 {
posterIDs := make(map[int64]struct{}, len(comments)) posterIDs := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if _, ok := posterIDs[comment.PosterID]; !ok { posterIDs.Add(comment.PosterID)
posterIDs[comment.PosterID] = struct{}{}
}
} }
return container.KeysInt64(posterIDs) return posterIDs.Values()
} }
func (comments CommentList) loadPosters(ctx context.Context) error { func (comments CommentList) loadPosters(ctx context.Context) error {
@ -70,13 +68,11 @@ func (comments CommentList) getCommentIDs() []int64 {
} }
func (comments CommentList) getLabelIDs() []int64 { func (comments CommentList) getLabelIDs() []int64 {
ids := make(map[int64]struct{}, len(comments)) ids := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if _, ok := ids[comment.LabelID]; !ok { ids.Add(comment.LabelID)
ids[comment.LabelID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (comments CommentList) loadLabels(ctx context.Context) error { //nolint 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 { func (comments CommentList) getMilestoneIDs() []int64 {
ids := make(map[int64]struct{}, len(comments)) ids := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if _, ok := ids[comment.MilestoneID]; !ok { ids.Add(comment.MilestoneID)
ids[comment.MilestoneID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (comments CommentList) loadMilestones(ctx context.Context) error { 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 { func (comments CommentList) getOldMilestoneIDs() []int64 {
ids := make(map[int64]struct{}, len(comments)) ids := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if _, ok := ids[comment.OldMilestoneID]; !ok { ids.Add(comment.OldMilestoneID)
ids[comment.OldMilestoneID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (comments CommentList) loadOldMilestones(ctx context.Context) error { 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 { func (comments CommentList) getAssigneeIDs() []int64 {
ids := make(map[int64]struct{}, len(comments)) ids := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if _, ok := ids[comment.AssigneeID]; !ok { ids.Add(comment.AssigneeID)
ids[comment.AssigneeID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (comments CommentList) loadAssignees(ctx context.Context) error { 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 // getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded
func (comments CommentList) getIssueIDs() []int64 { func (comments CommentList) getIssueIDs() []int64 {
ids := make(map[int64]struct{}, len(comments)) ids := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if comment.Issue != nil { if comment.Issue != nil {
continue continue
} }
if _, ok := ids[comment.IssueID]; !ok { ids.Add(comment.IssueID)
ids[comment.IssueID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
// Issues returns all the issues of comments // Issues returns all the issues of comments
@ -334,16 +322,14 @@ func (comments CommentList) loadIssues(ctx context.Context) error {
} }
func (comments CommentList) getDependentIssueIDs() []int64 { func (comments CommentList) getDependentIssueIDs() []int64 {
ids := make(map[int64]struct{}, len(comments)) ids := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if comment.DependentIssue != nil { if comment.DependentIssue != nil {
continue continue
} }
if _, ok := ids[comment.DependentIssueID]; !ok { ids.Add(comment.DependentIssueID)
ids[comment.DependentIssueID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (comments CommentList) loadDependentIssues(ctx context.Context) error { 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 { func (comments CommentList) getReviewIDs() []int64 {
ids := make(map[int64]struct{}, len(comments)) ids := make(container.Set[int64], len(comments))
for _, comment := range comments { for _, comment := range comments {
if _, ok := ids[comment.ReviewID]; !ok { ids.Add(comment.ReviewID)
ids[comment.ReviewID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (comments CommentList) loadReviews(ctx context.Context) error { //nolint func (comments CommentList) loadReviews(ctx context.Context) error { //nolint

View file

@ -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 // get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo
func (issues IssueList) getRepoIDs() []int64 { func (issues IssueList) getRepoIDs() []int64 {
repoIDs := make(map[int64]struct{}, len(issues)) repoIDs := make(container.Set[int64], len(issues))
for _, issue := range issues { for _, issue := range issues {
if issue.Repo == nil { if issue.Repo == nil {
repoIDs[issue.RepoID] = struct{}{} repoIDs.Add(issue.RepoID)
} }
if issue.PullRequest != nil && issue.PullRequest.HeadRepo == nil { 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) { 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 { func (issues IssueList) getPosterIDs() []int64 {
posterIDs := make(map[int64]struct{}, len(issues)) posterIDs := make(container.Set[int64], len(issues))
for _, issue := range issues { for _, issue := range issues {
if _, ok := posterIDs[issue.PosterID]; !ok { posterIDs.Add(issue.PosterID)
posterIDs[issue.PosterID] = struct{}{}
}
} }
return container.KeysInt64(posterIDs) return posterIDs.Values()
} }
func (issues IssueList) loadPosters(ctx context.Context) error { 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 { func (issues IssueList) getMilestoneIDs() []int64 {
ids := make(map[int64]struct{}, len(issues)) ids := make(container.Set[int64], len(issues))
for _, issue := range issues { for _, issue := range issues {
if _, ok := ids[issue.MilestoneID]; !ok { ids.Add(issue.MilestoneID)
ids[issue.MilestoneID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (issues IssueList) loadMilestones(ctx context.Context) error { 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 { func (issues IssueList) getProjectIDs() []int64 {
ids := make(map[int64]struct{}, len(issues)) ids := make(container.Set[int64], len(issues))
for _, issue := range issues { for _, issue := range issues {
projectID := issue.ProjectID() ids.Add(issue.ProjectID())
if _, ok := ids[projectID]; !ok {
ids[projectID] = struct{}{}
}
} }
return container.KeysInt64(ids) return ids.Values()
} }
func (issues IssueList) loadProjects(ctx context.Context) error { func (issues IssueList) loadProjects(ctx context.Context) error {

View file

@ -211,7 +211,7 @@ type ReactionOptions struct {
// CreateReaction creates reaction for issue or comment. // CreateReaction creates reaction for issue or comment.
func CreateReaction(opts *ReactionOptions) (*Reaction, error) { func CreateReaction(opts *ReactionOptions) (*Reaction, error) {
if !setting.UI.ReactionsMap[opts.Type] { if !setting.UI.ReactionsLookup.Contains(opts.Type) {
return nil, ErrForbiddenIssueReaction{opts.Type} return nil, ErrForbiddenIssueReaction{opts.Type}
} }
@ -316,16 +316,14 @@ func (list ReactionList) GroupByType() map[string]ReactionList {
} }
func (list ReactionList) getUserIDs() []int64 { func (list ReactionList) getUserIDs() []int64 {
userIDs := make(map[int64]struct{}, len(list)) userIDs := make(container.Set[int64], len(list))
for _, reaction := range list { for _, reaction := range list {
if reaction.OriginalAuthor != "" { if reaction.OriginalAuthor != "" {
continue continue
} }
if _, ok := userIDs[reaction.UserID]; !ok { userIDs.Add(reaction.UserID)
userIDs[reaction.UserID] = struct{}{}
}
} }
return container.KeysInt64(userIDs) return userIDs.Values()
} }
func valuesUser(m map[int64]*user_model.User) []*user_model.User { func valuesUser(m map[int64]*user_model.User) []*user_model.User {

View file

@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
) )
@ -99,9 +100,9 @@ func InsertIssueComments(comments []*issues_model.Comment) error {
return nil return nil
} }
issueIDs := make(map[int64]bool) issueIDs := make(container.Set[int64])
for _, comment := range comments { for _, comment := range comments {
issueIDs[comment.IssueID] = true issueIDs.Add(comment.IssueID)
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext()

View file

@ -13,6 +13,7 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "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) log.Info("%d User Avatar(s) to migrate ...", count)
deleteList := make(map[string]struct{}) deleteList := make(container.Set[string])
start := 0 start := 0
migrated := 0 migrated := 0
for { for {
@ -86,7 +87,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
return fmt.Errorf("[user: %s] user table update: %v", user.LowerName, err) 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++ migrated++
select { select {
case <-ticker.C: case <-ticker.C:

View file

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/container"
conan_module "code.gitea.io/gitea/modules/packages/conan" conan_module "code.gitea.io/gitea/modules/packages/conan"
"xorm.io/builder" "xorm.io/builder"
@ -88,7 +89,7 @@ func SearchRecipes(ctx context.Context, opts *RecipeSearchOptions) ([]string, er
return nil, err return nil, err
} }
unique := make(map[string]bool) unique := make(container.Set[string])
for _, info := range results { for _, info := range results {
recipe := fmt.Sprintf("%s/%s", info.Name, info.Version) 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)) recipes := make([]string, 0, len(unique))

View file

@ -68,10 +68,10 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error {
return nil return nil
} }
set := make(map[int64]struct{}) set := make(container.Set[int64])
repoIDs := make([]int64, len(repos)) repoIDs := make([]int64, len(repos))
for i := range repos { for i := range repos {
set[repos[i].OwnerID] = struct{}{} set.Add(repos[i].OwnerID)
repoIDs[i] = repos[i].ID 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)) users := make(map[int64]*user_model.User, len(set))
if err := db.GetEngine(ctx). if err := db.GetEngine(ctx).
Where("id > 0"). Where("id > 0").
In("id", container.KeysInt64(set)). In("id", set.Values()).
Find(&users); err != nil { Find(&users); err != nil {
return fmt.Errorf("find users: %v", err) return fmt.Errorf("find users: %v", err)
} }

View file

@ -11,6 +11,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder" "xorm.io/builder"
@ -62,7 +63,7 @@ func ValidateTopic(topic string) bool {
// SanitizeAndValidateTopics sanitizes and checks an array or topics // SanitizeAndValidateTopics sanitizes and checks an array or topics
func SanitizeAndValidateTopics(topics []string) (validTopics, invalidTopics []string) { func SanitizeAndValidateTopics(topics []string) (validTopics, invalidTopics []string) {
validTopics = make([]string, 0) validTopics = make([]string, 0)
mValidTopics := make(map[string]struct{}) mValidTopics := make(container.Set[string])
invalidTopics = make([]string, 0) invalidTopics = make([]string, 0)
for _, topic := range topics { for _, topic := range topics {
@ -72,12 +73,12 @@ func SanitizeAndValidateTopics(topics []string) (validTopics, invalidTopics []st
continue continue
} }
// ignore same topic twice // ignore same topic twice
if _, ok := mValidTopics[topic]; ok { if mValidTopics.Contains(topic) {
continue continue
} }
if ValidateTopic(topic) { if ValidateTopic(topic) {
validTopics = append(validTopics, topic) validTopics = append(validTopics, topic)
mValidTopics[topic] = struct{}{} mValidTopics.Add(topic)
} else { } else {
invalidTopics = append(invalidTopics, topic) invalidTopics = append(invalidTopics, topic)
} }

View file

@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"xorm.io/builder" "xorm.io/builder"
@ -83,37 +84,19 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
return nil, err return nil, err
} }
uidMap := map[int64]bool{} uniqueUserIDs := make(container.Set[int64])
i := 0 uniqueUserIDs.AddMultiple(userIDs...)
for _, uid := range userIDs { uniqueUserIDs.AddMultiple(additionalUserIDs...)
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]
// Leave a seat for owner itself to append later, but if owner is an organization // 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. // 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 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 return nil, err
} }
} }
if !repo.Owner.IsOrganization() && !uidMap[repo.OwnerID] { if !repo.Owner.IsOrganization() && !uniqueUserIDs.Contains(repo.OwnerID) {
users = append(users, repo.Owner) users = append(users, repo.Owner)
} }

View file

@ -241,15 +241,6 @@ func Int64sToStrings(ints []int64) []string {
return strs 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 // Int64sContains returns if a int64 in a slice of int64
func Int64sContains(intsSlice []int64, a int64) bool { func Int64sContains(intsSlice []int64, a int64) bool {
for _, c := range intsSlice { for _, c := range intsSlice {

View file

@ -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) { 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{6, 44324, 4324, 32, 1, 2323}, 1))
assert.True(t, Int64sContains([]int64{2323}, 2323)) assert.True(t, Int64sContains([]int64{2323}, 2323))
assert.False(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 232)) assert.False(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 232))

View file

@ -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
}

57
modules/container/set.go Normal file
View file

@ -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
}

View file

@ -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"))
}

View file

@ -14,6 +14,7 @@ import (
"strings" "strings"
asymkey_model "code.gitea.io/gitea/models/asymkey" 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/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@ -40,7 +41,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
} }
defer f.Close() defer f.Close()
linesInAuthorizedKeys := map[string]bool{} linesInAuthorizedKeys := make(container.Set[string])
scanner := bufio.NewScanner(f) scanner := bufio.NewScanner(f)
for scanner.Scan() { for scanner.Scan() {
@ -48,7 +49,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
if strings.HasPrefix(line, tplCommentPrefix) { if strings.HasPrefix(line, tplCommentPrefix) {
continue continue
} }
linesInAuthorizedKeys[line] = true linesInAuthorizedKeys.Add(line)
} }
f.Close() f.Close()
@ -64,7 +65,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
if strings.HasPrefix(line, tplCommentPrefix) { if strings.HasPrefix(line, tplCommentPrefix) {
continue continue
} }
if ok := linesInAuthorizedKeys[line]; ok { if linesInAuthorizedKeys.Contains(line) {
continue continue
} }
if !autofix { if !autofix {

View file

@ -14,6 +14,8 @@ import (
"sort" "sort"
"strings" "strings"
"code.gitea.io/gitea/modules/container"
"github.com/djherbis/buffer" "github.com/djherbis/buffer"
"github.com/djherbis/nio/v3" "github.com/djherbis/nio/v3"
) )
@ -339,7 +341,7 @@ func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath st
lastEmptyParent := head.ID.String() lastEmptyParent := head.ID.String()
commitSinceLastEmptyParent := uint64(0) commitSinceLastEmptyParent := uint64(0)
commitSinceNextRestart := uint64(0) commitSinceNextRestart := uint64(0)
parentRemaining := map[string]bool{} parentRemaining := make(container.Set[string])
changed := make([]bool, len(paths)) changed := make([]bool, len(paths))
@ -365,7 +367,7 @@ heaploop:
if current == nil { if current == nil {
break heaploop break heaploop
} }
delete(parentRemaining, current.CommitID) parentRemaining.Remove(current.CommitID)
if current.Paths != nil { if current.Paths != nil {
for i, found := range current.Paths { for i, found := range current.Paths {
if !found { if !found {
@ -410,14 +412,12 @@ heaploop:
} }
} }
g = NewLogNameStatusRepoParser(ctx, repo.Path, lastEmptyParent, treepath, remainingPaths...) g = NewLogNameStatusRepoParser(ctx, repo.Path, lastEmptyParent, treepath, remainingPaths...)
parentRemaining = map[string]bool{} parentRemaining = make(container.Set[string])
nextRestart = (remaining * 3) / 4 nextRestart = (remaining * 3) / 4
continue heaploop continue heaploop
} }
} }
for _, parent := range current.ParentIDs { parentRemaining.AddMultiple(current.ParentIDs...)
parentRemaining[parent] = true
}
} }
g.Close() g.Close()

View file

@ -13,6 +13,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"code.gitea.io/gitea/modules/container"
) )
// CodeActivityStats represents git statistics data // CodeActivityStats represents git statistics data
@ -80,7 +82,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
stats.Additions = 0 stats.Additions = 0
stats.Deletions = 0 stats.Deletions = 0
authors := make(map[string]*CodeActivityAuthor) authors := make(map[string]*CodeActivityAuthor)
files := make(map[string]bool) files := make(container.Set[string])
var author string var author string
p := 0 p := 0
for scanner.Scan() { for scanner.Scan() {
@ -119,9 +121,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
stats.Deletions += c stats.Deletions += c
} }
} }
if _, ok := files[parts[2]]; !ok { files.Add(parts[2])
files[parts[2]] = true
}
} }
} }
} }

View file

@ -11,6 +11,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"code.gitea.io/gitea/modules/container"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"gitea.com/go-chi/binding" "gitea.com/go-chi/binding"
@ -43,7 +44,7 @@ func validateYaml(template *api.IssueTemplate) error {
if len(template.Fields) == 0 { if len(template.Fields) == 0 {
return fmt.Errorf("'body' is required") return fmt.Errorf("'body' is required")
} }
ids := map[string]struct{}{} ids := make(container.Set[string])
for idx, field := range template.Fields { for idx, field := range template.Fields {
if err := validateID(field, idx, ids); err != nil { if err := validateID(field, idx, ids); err != nil {
return err return err
@ -125,7 +126,7 @@ func validateRequired(field *api.IssueFormField, idx int) error {
return validateBoolItem(newErrorPosition(idx, field.Type), field.Validations, "required") 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 { if field.Type == api.IssueFormFieldTypeMarkdown {
// The ID is not required for a markdown field // The ID is not required for a markdown field
return nil return nil
@ -139,10 +140,9 @@ func validateID(field *api.IssueFormField, idx int, ids map[string]struct{}) err
if binding.AlphaDashPattern.MatchString(field.ID) { if binding.AlphaDashPattern.MatchString(field.ID) {
return position.Errorf("'id' should contain only alphanumeric, '-' and '_'") 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") return position.Errorf("'id' should be unique")
} }
ids[field.ID] = struct{}{}
return nil return nil
} }

View file

@ -10,6 +10,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/common" "code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/setting" "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 { type prefixedIDs struct {
values map[string]bool values container.Set[string]
} }
// Generate generates a new element id. // 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-")) { if !bytes.HasPrefix(result, []byte("user-content-")) {
result = append([]byte("user-content-"), result...) result = append([]byte("user-content-"), result...)
} }
if _, ok := p.values[util.BytesToReadOnlyString(result)]; !ok { if p.values.Add(util.BytesToReadOnlyString(result)) {
p.values[util.BytesToReadOnlyString(result)] = true
return result return result
} }
for i := 1; ; i++ { for i := 1; ; i++ {
newResult := fmt.Sprintf("%s-%d", result, i) newResult := fmt.Sprintf("%s-%d", result, i)
if _, ok := p.values[newResult]; !ok { if p.values.Add(newResult) {
p.values[newResult] = true
return []byte(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. // Put puts a given element id to the used ids table.
func (p *prefixedIDs) Put(value []byte) { func (p *prefixedIDs) Put(value []byte) {
p.values[util.BytesToReadOnlyString(value)] = true p.values.Add(util.BytesToReadOnlyString(value))
} }
func newPrefixedIDs() *prefixedIDs { func newPrefixedIDs() *prefixedIDs {
return &prefixedIDs{ return &prefixedIDs{
values: map[string]bool{}, values: make(container.Set[string]),
} }
} }

View file

@ -10,6 +10,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" 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/graceful"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification/base" "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) log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err)
return return
} }
toNotify := make(map[int64]struct{}, 32) toNotify := make(container.Set[int64], 32)
repoWatchers, err := repo_model.GetRepoWatchersIDs(db.DefaultContext, pr.Issue.RepoID) repoWatchers, err := repo_model.GetRepoWatchersIDs(db.DefaultContext, pr.Issue.RepoID)
if err != nil { if err != nil {
log.Error("GetRepoWatchersIDs: %v", err) log.Error("GetRepoWatchersIDs: %v", err)
return return
} }
for _, id := range repoWatchers { for _, id := range repoWatchers {
toNotify[id] = struct{}{} toNotify.Add(id)
} }
issueParticipants, err := issues_model.GetParticipantsIDsByIssueID(pr.IssueID) issueParticipants, err := issues_model.GetParticipantsIDsByIssueID(pr.IssueID)
if err != nil { if err != nil {
@ -138,11 +139,11 @@ func (ns *notificationService) NotifyNewPullRequest(pr *issues_model.PullRequest
return return
} }
for _, id := range issueParticipants { for _, id := range issueParticipants {
toNotify[id] = struct{}{} toNotify.Add(id)
} }
delete(toNotify, pr.Issue.PosterID) delete(toNotify, pr.Issue.PosterID)
for _, mention := range mentions { for _, mention := range mentions {
toNotify[mention.ID] = struct{}{} toNotify.Add(mention.ID)
} }
for receiverID := range toNotify { for receiverID := range toNotify {
_ = ns.issueQueue.Push(issueNotificationOpts{ _ = ns.issueQueue.Push(issueNotificationOpts{

View file

@ -11,6 +11,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "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 // 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, ";") parts := strings.Split(val, ";")
types := make(map[string]bool) types := make(container.Set[string])
for _, v := range strings.Split(parts[0], ",") { for _, v := range strings.Split(parts[0], ",") {
types[strings.TrimSpace(v)] = true types.Add(strings.TrimSpace(v))
} }
return types return types
} }

View file

@ -7,28 +7,23 @@ package public
import ( import (
"testing" "testing"
"code.gitea.io/gitea/modules/container"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestParseAcceptEncoding(t *testing.T) { func TestParseAcceptEncoding(t *testing.T) {
kases := []struct { kases := []struct {
Header string Header string
Expected map[string]bool Expected container.Set[string]
}{ }{
{ {
Header: "deflate, gzip;q=1.0, *;q=0.5", Header: "deflate, gzip;q=1.0, *;q=0.5",
Expected: map[string]bool{ Expected: container.SetOf("deflate", "gzip"),
"deflate": true,
"gzip": true,
},
}, },
{ {
Header: " gzip, deflate, br", Header: " gzip, deflate, br",
Expected: map[string]bool{ Expected: container.SetOf("deflate", "gzip", "br"),
"deflate": true,
"gzip": true,
"br": true,
},
}, },
} }

View file

@ -60,7 +60,7 @@ func AssetIsDir(name string) (bool, error) {
// serveContent serve http content // serveContent serve http content
func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) { func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) {
encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding")) encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding"))
if encodings["gzip"] { if encodings.Contains("gzip") {
if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok { if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok {
rdGzip := bytes.NewReader(cf.GzipBytes()) rdGzip := bytes.NewReader(cf.GzipBytes())
// all static files are managed by Gitea, so we can make sure every file has the correct ext name // all static files are managed by Gitea, so we can make sure every file has the correct ext name

View file

@ -12,6 +12,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
) )
@ -33,7 +34,7 @@ type ChannelUniqueQueueConfiguration ChannelQueueConfiguration
type ChannelUniqueQueue struct { type ChannelUniqueQueue struct {
*WorkerPool *WorkerPool
lock sync.Mutex lock sync.Mutex
table map[string]bool table container.Set[string]
shutdownCtx context.Context shutdownCtx context.Context
shutdownCtxCancel context.CancelFunc shutdownCtxCancel context.CancelFunc
terminateCtx context.Context terminateCtx context.Context
@ -58,7 +59,7 @@ func NewChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue
shutdownCtx, shutdownCtxCancel := context.WithCancel(terminateCtx) shutdownCtx, shutdownCtxCancel := context.WithCancel(terminateCtx)
queue := &ChannelUniqueQueue{ queue := &ChannelUniqueQueue{
table: map[string]bool{}, table: make(container.Set[string]),
shutdownCtx: shutdownCtx, shutdownCtx: shutdownCtx,
shutdownCtxCancel: shutdownCtxCancel, shutdownCtxCancel: shutdownCtxCancel,
terminateCtx: terminateCtx, terminateCtx: terminateCtx,
@ -73,7 +74,7 @@ func NewChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue
bs, _ := json.Marshal(datum) bs, _ := json.Marshal(datum)
queue.lock.Lock() queue.lock.Lock()
delete(queue.table, string(bs)) queue.table.Remove(string(bs))
queue.lock.Unlock() queue.lock.Unlock()
if u := handle(datum); u != nil { if u := handle(datum); u != nil {
@ -127,16 +128,15 @@ func (q *ChannelUniqueQueue) PushFunc(data Data, fn func() error) error {
q.lock.Unlock() q.lock.Unlock()
} }
}() }()
if _, ok := q.table[string(bs)]; ok { if !q.table.Add(string(bs)) {
return ErrAlreadyInQueue return ErrAlreadyInQueue
} }
// FIXME: We probably need to implement some sort of limit here // FIXME: We probably need to implement some sort of limit here
// If the downstream queue blocks this table will grow without limit // If the downstream queue blocks this table will grow without limit
q.table[string(bs)] = true
if fn != nil { if fn != nil {
err := fn() err := fn()
if err != nil { if err != nil {
delete(q.table, string(bs)) q.table.Remove(string(bs))
return err return err
} }
} }
@ -155,8 +155,7 @@ func (q *ChannelUniqueQueue) Has(data Data) (bool, error) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
_, has := q.table[string(bs)] return q.table.Contains(string(bs)), nil
return has, nil
} }
// Flush flushes the channel with a timeout - the Flush worker will be registered as a flush worker with the manager // Flush flushes the channel with a timeout - the Flush worker will be registered as a flush worker with the manager

View file

@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" 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/git"
"code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@ -275,7 +276,7 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
return pullMirrorReleaseSync(repo, gitRepo) return pullMirrorReleaseSync(repo, gitRepo)
} }
existingRelTags := make(map[string]struct{}) existingRelTags := make(container.Set[string])
opts := repo_model.FindReleasesOptions{ opts := repo_model.FindReleasesOptions{
IncludeDrafts: true, IncludeDrafts: true,
IncludeTags: 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) return fmt.Errorf("unable to PushUpdateDeleteTag: %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
} }
} else { } 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 { _, err := gitRepo.WalkReferences(git.ObjectTag, 0, 0, func(sha1, refname string) error {
tagName := strings.TrimPrefix(refname, git.TagPrefix) tagName := strings.TrimPrefix(refname, git.TagPrefix)
if _, ok := existingRelTags[strings.ToLower(tagName)]; ok { if existingRelTags.Contains(strings.ToLower(tagName)) {
return nil return nil
} }

View file

@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"time" "time"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1" ini "gopkg.in/ini.v1"
@ -109,8 +110,8 @@ func NewQueueService() {
// Now handle the old issue_indexer configuration // Now handle the old issue_indexer configuration
// FIXME: DEPRECATED to be removed in v1.18.0 // FIXME: DEPRECATED to be removed in v1.18.0
section := Cfg.Section("queue.issue_indexer") section := Cfg.Section("queue.issue_indexer")
directlySet := toDirectlySetKeysMap(section) directlySet := toDirectlySetKeysSet(section)
if !directlySet["TYPE"] && defaultType == "" { if !directlySet.Contains("TYPE") && defaultType == "" {
switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ { switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ {
case "levelqueue": case "levelqueue":
_, _ = section.NewKey("TYPE", "level") _, _ = section.NewKey("TYPE", "level")
@ -124,25 +125,25 @@ func NewQueueService() {
log.Fatal("Unsupported indexer queue type: %v", typ) 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) length := Cfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0)
if length != 0 { if length != 0 {
_, _ = section.NewKey("LENGTH", strconv.Itoa(length)) _, _ = 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) fallback := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0)
if fallback != 0 { if fallback != 0 {
_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback)) _, _ = 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("")) queueDir := filepath.ToSlash(Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString(""))
if queueDir != "" { if queueDir != "" {
_, _ = section.NewKey("DATADIR", 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("") connStr := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("")
if connStr != "" { if connStr != "" {
_, _ = section.NewKey("CONN_STR", connStr) _, _ = section.NewKey("CONN_STR", connStr)
@ -178,19 +179,19 @@ func handleOldLengthConfiguration(queueName, oldSection, oldKey string, defaultV
} }
section := Cfg.Section("queue." + queueName) section := Cfg.Section("queue." + queueName)
directlySet := toDirectlySetKeysMap(section) directlySet := toDirectlySetKeysSet(section)
if !directlySet["LENGTH"] { if !directlySet.Contains("LENGTH") {
_, _ = section.NewKey("LENGTH", strconv.Itoa(value)) _, _ = 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 // 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. // but this section does not.
func toDirectlySetKeysMap(section *ini.Section) map[string]bool { func toDirectlySetKeysSet(section *ini.Section) container.Set[string] {
sectionMap := map[string]bool{} sections := make(container.Set[string])
for _, key := range section.Keys() { for _, key := range section.Keys() {
sectionMap[key.Name()] = true sections.Add(key.Name())
} }
return sectionMap return sections
} }

View file

@ -21,6 +21,7 @@ import (
"text/template" "text/template"
"time" "time"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/user"
@ -234,7 +235,7 @@ var (
DefaultTheme string DefaultTheme string
Themes []string Themes []string
Reactions []string Reactions []string
ReactionsMap map[string]bool `ini:"-"` ReactionsLookup container.Set[string] `ini:"-"`
CustomEmojis []string CustomEmojis []string
CustomEmojisMap map[string]string `ini:"-"` CustomEmojisMap map[string]string `ini:"-"`
SearchRepoDescription bool SearchRepoDescription bool
@ -1100,9 +1101,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
newMarkup() newMarkup()
UI.ReactionsMap = make(map[string]bool) UI.ReactionsLookup = make(container.Set[string])
for _, reaction := range UI.Reactions { for _, reaction := range UI.Reactions {
UI.ReactionsMap[reaction] = true UI.ReactionsLookup.Add(reaction)
} }
UI.CustomEmojisMap = make(map[string]string) UI.CustomEmojisMap = make(map[string]string)
for _, emoji := range UI.CustomEmojis { for _, emoji := range UI.CustomEmojis {

View file

@ -6,6 +6,8 @@ package sync
import ( import (
"sync" "sync"
"code.gitea.io/gitea/modules/container"
) )
// StatusTable is a table maintains true/false values. // StatusTable is a table maintains true/false values.
@ -14,13 +16,13 @@ import (
// in different goroutines. // in different goroutines.
type StatusTable struct { type StatusTable struct {
lock sync.RWMutex lock sync.RWMutex
pool map[string]struct{} pool container.Set[string]
} }
// NewStatusTable initializes and returns a new StatusTable object. // NewStatusTable initializes and returns a new StatusTable object.
func NewStatusTable() *StatusTable { func NewStatusTable() *StatusTable {
return &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 // Returns whether set value was set to true
func (p *StatusTable) StartIfNotRunning(name string) bool { func (p *StatusTable) StartIfNotRunning(name string) bool {
p.lock.Lock() p.lock.Lock()
_, ok := p.pool[name] added := p.pool.Add(name)
if !ok {
p.pool[name] = struct{}{}
}
p.lock.Unlock() p.lock.Unlock()
return !ok return added
} }
// Start sets value of given name to true in the pool. // Start sets value of given name to true in the pool.
func (p *StatusTable) Start(name string) { func (p *StatusTable) Start(name string) {
p.lock.Lock() p.lock.Lock()
p.pool[name] = struct{}{} p.pool.Add(name)
p.lock.Unlock() p.lock.Unlock()
} }
// Stop sets value of given name to false in the pool. // Stop sets value of given name to false in the pool.
func (p *StatusTable) Stop(name string) { func (p *StatusTable) Stop(name string) {
p.lock.Lock() p.lock.Lock()
delete(p.pool, name) p.pool.Remove(name)
p.lock.Unlock() p.lock.Unlock()
} }
// IsRunning checks if value of given name is set to true in the pool. // IsRunning checks if value of given name is set to true in the pool.
func (p *StatusTable) IsRunning(name string) bool { func (p *StatusTable) IsRunning(name string) bool {
p.lock.RLock() p.lock.RLock()
_, ok := p.pool[name] exists := p.pool.Contains(name)
p.lock.RUnlock() p.lock.RUnlock()
return ok return exists
} }

View file

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
conan_model "code.gitea.io/gitea/models/packages/conan" 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/context"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@ -33,20 +34,18 @@ const (
packageReferenceKey = "PackageReference" packageReferenceKey = "PackageReference"
) )
type stringSet map[string]struct{}
var ( var (
recipeFileList = stringSet{ recipeFileList = container.SetOf(
conanfileFile: struct{}{}, conanfileFile,
"conanmanifest.txt": struct{}{}, "conanmanifest.txt",
"conan_sources.tgz": struct{}{}, "conan_sources.tgz",
"conan_export.tgz": struct{}{}, "conan_export.tgz",
} )
packageFileList = stringSet{ packageFileList = container.SetOf(
conaninfoFile: struct{}{}, conaninfoFile,
"conanmanifest.txt": struct{}{}, "conanmanifest.txt",
"conan_package.tgz": struct{}{}, "conan_package.tgz",
} )
) )
func jsonResponse(ctx *context.Context, status int, obj interface{}) { 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() defer ctx.Req.Body.Close()
var files map[string]int64 var files map[string]int64
@ -279,7 +278,7 @@ func serveUploadURLs(ctx *context.Context, fileFilter stringSet, uploadURL strin
urls := make(map[string]string) urls := make(map[string]string)
for file := range files { for file := range files {
if _, ok := fileFilter[file]; ok { if fileFilter.Contains(file) {
urls[file] = fmt.Sprintf("%s/%s", uploadURL, file) urls[file] = fmt.Sprintf("%s/%s", uploadURL, file)
} }
} }
@ -301,12 +300,12 @@ func UploadPackageFile(ctx *context.Context) {
uploadFile(ctx, packageFileList, pref.AsKey()) 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) rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
pref := ctx.Data[packageReferenceKey].(*conan_module.PackageReference) pref := ctx.Data[packageReferenceKey].(*conan_module.PackageReference)
filename := ctx.Params("filename") filename := ctx.Params("filename")
if _, ok := fileFilter[filename]; !ok { if !fileFilter.Contains(filename) {
apiError(ctx, http.StatusBadRequest, nil) apiError(ctx, http.StatusBadRequest, nil)
return return
} }
@ -442,11 +441,11 @@ func DownloadPackageFile(ctx *context.Context) {
downloadFile(ctx, packageFileList, pref.AsKey()) 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) rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
filename := ctx.Params("filename") filename := ctx.Params("filename")
if _, ok := fileFilter[filename]; !ok { if !fileFilter.Contains(filename) {
apiError(ctx, http.StatusBadRequest, nil) apiError(ctx, http.StatusBadRequest, nil)
return return
} }

View file

@ -30,6 +30,7 @@ import (
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
@ -947,10 +948,11 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull
if err != nil { if err != nil {
return nil, nil, 0, 0 return nil, nil, 0, 0
} }
labelIDMark := base.Int64sToMap(labelIDs) labelIDMark := make(container.Set[int64])
labelIDMark.AddMultiple(labelIDs...)
for i := range labels { for i := range labels {
if labelIDMark[labels[i].ID] { if labelIDMark.Contains(labels[i].ID) {
labels[i].IsChecked = true labels[i].IsChecked = true
hasSelected = true hasSelected = true
} }
@ -1293,9 +1295,9 @@ func ViewIssue(ctx *context.Context) {
// Metas. // Metas.
// Check labels. // Check labels.
labelIDMark := make(map[int64]bool) labelIDMark := make(container.Set[int64])
for i := range issue.Labels { for _, label := range issue.Labels {
labelIDMark[issue.Labels[i].ID] = true labelIDMark.Add(label.ID)
} }
labels, err := issues_model.GetLabelsByRepoID(ctx, repo.ID, "", db.ListOptions{}) labels, err := issues_model.GetLabelsByRepoID(ctx, repo.ID, "", db.ListOptions{})
if err != nil { if err != nil {
@ -1317,7 +1319,7 @@ func ViewIssue(ctx *context.Context) {
hasSelected := false hasSelected := false
for i := range labels { for i := range labels {
if labelIDMark[labels[i].ID] { if labelIDMark.Contains(labels[i].ID) {
labels[i].IsChecked = true labels[i].IsChecked = true
hasSelected = true hasSelected = true
} }

View file

@ -18,6 +18,7 @@ import (
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/pipeline" "code.gitea.io/gitea/modules/git/pipeline"
@ -176,14 +177,12 @@ func LFSLocks(ctx *context.Context) {
return return
} }
filemap := make(map[string]bool, len(filelist)) fileset := make(container.Set[string], len(filelist))
for _, name := range filelist { fileset.AddMultiple(filelist...)
filemap[name] = true
}
linkable := make([]bool, len(lfsLocks)) linkable := make([]bool, len(lfsLocks))
for i, lock := range lfsLocks { for i, lock := range lfsLocks {
linkable[i] = filemap[lock.Path] linkable[i] = fileset.Contains(lock.Path)
} }
ctx.Data["Linkable"] = linkable ctx.Data["Linkable"] = linkable

View file

@ -28,6 +28,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/highlight"
@ -811,16 +812,14 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
defer cancel() defer cancel()
} }
selected := map[string]bool{} selected := make(container.Set[string])
for _, pth := range ctx.FormStrings("f[]") { selected.AddMultiple(ctx.FormStrings("f[]")...)
selected[pth] = true
}
entries := allEntries entries := allEntries
if len(selected) > 0 { if len(selected) > 0 {
entries = make(git.Entries, 0, len(selected)) entries = make(git.Entries, 0, len(selected))
for _, entry := range allEntries { for _, entry := range allEntries {
if selected[entry.Name()] { if selected.Contains(entry.Name()) {
entries = append(entries, entry) entries = append(entries, entry)
} }
} }

View file

@ -18,6 +18,7 @@ import (
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" 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/log"
"code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/repository"
@ -111,7 +112,7 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm
Action references.XRefAction Action references.XRefAction
} }
refMarked := make(map[markKey]bool) refMarked := make(container.Set[markKey])
var refRepo *repo_model.Repository var refRepo *repo_model.Repository
var refIssue *issues_model.Issue var refIssue *issues_model.Issue
var err error 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} key := markKey{ID: refIssue.ID, Action: ref.Action}
if refMarked[key] { if !refMarked.Add(key) {
continue continue
} }
refMarked[key] = true
// FIXME: this kind of condition is all over the code, it should be consolidated in a single place // 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 canclose := perm.IsAdmin() || perm.IsOwner() || perm.CanWriteIssuesOrPulls(refIssue.IsPull) || refIssue.PosterID == doer.ID

View file

@ -10,6 +10,7 @@ import (
activities_model "code.gitea.io/gitea/models/activities" activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user" 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/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@ -46,8 +47,8 @@ func MailMentionsComment(ctx context.Context, pr *issues_model.PullRequest, c *i
return nil return nil
} }
visited := make(map[int64]bool, len(mentions)+1) visited := make(container.Set[int64], len(mentions)+1)
visited[c.Poster.ID] = true visited.Add(c.Poster.ID)
if err = mailIssueCommentBatch( if err = mailIssueCommentBatch(
&mailCommentContext{ &mailCommentContext{
Context: ctx, Context: ctx,

View file

@ -14,6 +14,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" 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/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@ -89,11 +90,11 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
unfiltered = append(ids, unfiltered...) 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 // Avoid mailing the doer
if ctx.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn { if ctx.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn {
visited[ctx.Doer.ID] = true visited.Add(ctx.Doer.ID)
} }
// =========== Mentions =========== // =========== Mentions ===========
@ -106,9 +107,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
if err != nil { if err != nil {
return fmt.Errorf("GetIssueWatchersIDs(%d): %v", ctx.Issue.ID, err) return fmt.Errorf("GetIssueWatchersIDs(%d): %v", ctx.Issue.ID, err)
} }
for _, i := range ids { visited.AddMultiple(ids...)
visited[i] = true
}
unfilteredUsers, err := user_model.GetMaileableUsersByIDs(unfiltered, false) unfilteredUsers, err := user_model.GetMaileableUsersByIDs(unfiltered, false)
if err != nil { if err != nil {
@ -121,7 +120,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
return nil 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 checkUnit := unit.TypeIssues
if ctx.Issue.IsPull { if ctx.Issue.IsPull {
checkUnit = unit.TypePullRequests 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 we have already visited this user we exclude them
if _, ok := visited[user.ID]; ok { if !visited.Add(user.ID) {
continue continue
} }
// now mark them as visited
visited[user.ID] = true
// test if this user is allowed to see the issue/pull // test if this user is allowed to see the issue/pull
if !access_model.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) { if !access_model.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
continue continue

View file

@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log" "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 appliedPatchPrefix = "Applied patch to '"
const withConflicts = "' with conflicts." const withConflicts = "' with conflicts."
conflictMap := map[string]bool{} conflicts := make(container.Set[string])
// Now scan the output from the command // Now scan the output from the command
scanner := bufio.NewScanner(stderrReader) scanner := bufio.NewScanner(stderrReader)
@ -418,7 +419,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
if strings.HasPrefix(line, prefix) { if strings.HasPrefix(line, prefix) {
conflict = true conflict = true
filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0]) filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0])
conflictMap[filepath] = true conflicts.Add(filepath)
} else if is3way && line == threewayFailed { } else if is3way && line == threewayFailed {
conflict = true conflict = true
} else if strings.HasPrefix(line, errorPrefix) { } 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) { if strings.HasSuffix(line, suffix) {
filepath := strings.TrimSpace(strings.TrimSuffix(line[len(errorPrefix):], suffix)) filepath := strings.TrimSpace(strings.TrimSuffix(line[len(errorPrefix):], suffix))
if filepath != "" { if filepath != "" {
conflictMap[filepath] = true conflicts.Add(filepath)
} }
break break
} }
@ -436,18 +437,18 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
conflict = true conflict = true
filepath := strings.TrimPrefix(strings.TrimSuffix(line, withConflicts), appliedPatchPrefix) filepath := strings.TrimPrefix(strings.TrimSuffix(line, withConflicts), appliedPatchPrefix)
if filepath != "" { if filepath != "" {
conflictMap[filepath] = true conflicts.Add(filepath)
} }
} }
// only list 10 conflicted files // only list 10 conflicted files
if len(conflictMap) >= 10 { if len(conflicts) >= 10 {
break break
} }
} }
if len(conflictMap) > 0 { if len(conflicts) > 0 {
pr.ConflictedFiles = make([]string, 0, len(conflictMap)) pr.ConflictedFiles = make([]string, 0, len(conflicts))
for key := range conflictMap { for key := range conflicts {
pr.ConflictedFiles = append(pr.ConflictedFiles, key) pr.ConflictedFiles = append(pr.ConflictedFiles, key)
} }
} }

View file

@ -20,6 +20,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" 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/git"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json" "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() posterSig := pr.Issue.Poster.NewGitSig().String()
authorsMap := map[string]bool{} uniqueAuthors := make(container.Set[string])
authors := make([]string, 0, len(commits)) authors := make([]string, 0, len(commits))
stringBuilder := strings.Builder{} stringBuilder := strings.Builder{}
@ -687,9 +688,8 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
} }
authorString := commit.Author.String() authorString := commit.Author.String()
if !authorsMap[authorString] && authorString != posterSig { if uniqueAuthors.Add(authorString) && authorString != posterSig {
authors = append(authors, authorString) authors = append(authors, authorString)
authorsMap[authorString] = true
} }
} }
@ -709,9 +709,8 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
} }
for _, commit := range commits { for _, commit := range commits {
authorString := commit.Author.String() authorString := commit.Author.String()
if !authorsMap[authorString] && authorString != posterSig { if uniqueAuthors.Add(authorString) && authorString != posterSig {
authors = append(authors, authorString) authors = append(authors, authorString)
authorsMap[authorString] = true
} }
} }
skip += limit skip += limit

View file

@ -15,6 +15,7 @@ import (
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" 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/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "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) return fmt.Errorf("AddReleaseAttachments: %v", err)
} }
deletedUUIDsMap := make(map[string]bool) deletedUUIDs := make(container.Set[string])
if len(delAttachmentUUIDs) > 0 { if len(delAttachmentUUIDs) > 0 {
// Check attachments // Check attachments
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs) 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 { if attach.ReleaseID != rel.ID {
return errors.New("delete attachement of release permission denied") 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 { 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 { for uuid, newName := range editAttachments {
if !deletedUUIDsMap[uuid] { if !deletedUUIDs.Contains(uuid) {
if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{ if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{
UUID: uuid, UUID: uuid,
Name: newName, Name: newName,

View file

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" 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/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
@ -257,12 +258,12 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad
if len(repos) == len(repoNamesToCheck) { if len(repos) == len(repoNamesToCheck) {
return nil return nil
} }
repoNames := make(map[string]bool, len(repos)) repoNames := make(container.Set[string], len(repos))
for _, repo := range repos { for _, repo := range repos {
repoNames[repo.LowerName] = true repoNames.Add(repo.LowerName)
} }
for _, repoName := range repoNamesToCheck { for _, repoName := range repoNamesToCheck {
if _, ok := repoNames[repoName]; !ok { if !repoNames.Contains(repoName) {
unadopted.add(filepath.Join(userName, repoName)) unadopted.add(filepath.Join(userName, repoName))
} }
} }