diff --git a/downstream.go b/downstream.go index 814a1c5..515a901 100644 --- a/downstream.go +++ b/downstream.go @@ -348,7 +348,7 @@ type downstreamConn struct { lastBatchRef uint64 - monitored casemapMap[struct{}] + monitored xirc.CaseMappingMap[struct{}] } func newDownstreamConn(srv *Server, ic ircConn, id uint64) *downstreamConn { @@ -362,7 +362,7 @@ func newDownstreamConn(srv *Server, ic ircConn, id uint64) *downstreamConn { nickCM: "*", username: "~u", caps: xirc.NewCapRegistry(), - monitored: newCasemapMap[struct{}](xirc.CaseMappingASCII), + monitored: xirc.NewCaseMappingMap[struct{}](xirc.CaseMappingASCII), registration: new(downstreamRegistration), } if host, _, err := net.SplitHostPort(remoteAddr); err == nil { @@ -2650,17 +2650,17 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc. } uc.updateMonitor() case "C": // clear - dc.monitored = newCasemapMap[struct{}](uc.network.casemap) + dc.monitored = xirc.NewCaseMappingMap[struct{}](uc.network.casemap) uc.updateMonitor() case "L": // list // TODO: be less lazy and pack the list - for _, entry := range dc.monitored.m { + dc.monitored.ForEach(func(name string, _ struct{}) { dc.SendMessage(&irc.Message{ Prefix: dc.srv.prefix(), Command: irc.RPL_MONLIST, - Params: []string{dc.nick, entry.originalKey}, + Params: []string{dc.nick, name}, }) - } + }) dc.SendMessage(&irc.Message{ Prefix: dc.srv.prefix(), Command: irc.RPL_ENDOFMONLIST, @@ -2668,9 +2668,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc. }) case "S": // status // TODO: be less lazy and pack the lists - for _, entry := range dc.monitored.m { - target := entry.originalKey - + dc.monitored.ForEach(func(target string, _ struct{}) { cmd := irc.RPL_MONOFFLINE if online := uc.monitored.Get(target); online { cmd = irc.RPL_MONONLINE @@ -2685,7 +2683,7 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc. Command: cmd, Params: []string{dc.nick, target}, }) - } + }) } case "CHATHISTORY": var subcommand string diff --git a/irc.go b/irc.go index ec7367b..0e2c069 100644 --- a/irc.go +++ b/irc.go @@ -218,74 +218,6 @@ func copyClientTags(tags irc.Tags) irc.Tags { var stdCaseMapping = xirc.CaseMappingRFC1459 -type casemapMap[V interface{}] struct { - m map[string]casemapEntry[V] - casemap xirc.CaseMapping -} - -type casemapEntry[V interface{}] struct { - originalKey string - value V -} - -func newCasemapMap[V interface{}](cm xirc.CaseMapping) casemapMap[V] { - return casemapMap[V]{ - m: make(map[string]casemapEntry[V]), - casemap: cm, - } -} - -func (cm *casemapMap[V]) Has(name string) bool { - _, ok := cm.m[cm.casemap(name)] - return ok -} - -func (cm *casemapMap[V]) Len() int { - return len(cm.m) -} - -func (cm *casemapMap[V]) Get(name string) V { - entry, ok := cm.m[cm.casemap(name)] - if !ok { - var v V - return v - } - return entry.value -} - -func (cm *casemapMap[V]) Set(name string, value V) { - nameCM := cm.casemap(name) - entry, ok := cm.m[nameCM] - if !ok { - cm.m[nameCM] = casemapEntry[V]{ - originalKey: name, - value: value, - } - return - } - entry.value = value - cm.m[nameCM] = entry -} - -func (cm *casemapMap[V]) Del(name string) { - delete(cm.m, cm.casemap(name)) -} - -func (cm *casemapMap[V]) ForEach(f func(string, V)) { - for _, entry := range cm.m { - f(entry.originalKey, entry.value) - } -} - -func (cm *casemapMap[V]) SetCaseMapping(newCasemap xirc.CaseMapping) { - cm.casemap = newCasemap - m := make(map[string]casemapEntry[V], len(cm.m)) - for _, entry := range cm.m { - m[cm.casemap(entry.originalKey)] = entry - } - cm.m = m -} - func isWordBoundary(r rune) bool { switch r { case '-', '_', '|': // inspired from weechat.look.highlight_regex diff --git a/upstream.go b/upstream.go index e6744cc..eba7dc8 100644 --- a/upstream.go +++ b/upstream.go @@ -89,7 +89,7 @@ type upstreamChannel struct { Status xirc.ChannelStatus modes channelModes creationTime string - Members casemapMap[*xirc.MembershipSet] + Members xirc.CaseMappingMap[*xirc.MembershipSet] complete bool detachTimer *time.Timer } @@ -208,14 +208,14 @@ type upstreamConn struct { realname string hostname string modes userModes - channels casemapMap[*upstreamChannel] - users casemapMap[*upstreamUser] + channels xirc.CaseMappingMap[*upstreamChannel] + users xirc.CaseMappingMap[*upstreamUser] caps xirc.CapRegistry batches map[string]upstreamBatch away bool account string nextLabelID uint64 - monitored casemapMap[bool] + monitored xirc.CaseMappingMap[bool] saslClient sasl.Client saslStarted bool @@ -367,8 +367,8 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options), network: network, user: network.user, - channels: newCasemapMap[*upstreamChannel](cm), - users: newCasemapMap[*upstreamUser](cm), + channels: xirc.NewCaseMappingMap[*upstreamChannel](cm), + users: xirc.NewCaseMappingMap[*upstreamUser](cm), caps: xirc.NewCapRegistry(), batches: make(map[string]upstreamBatch), serverPrefix: &irc.Prefix{Name: "*"}, @@ -377,7 +377,7 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er availableMemberships: stdMemberships, isupport: make(map[string]*string), pendingCmds: make(map[string][]pendingUpstreamCommand), - monitored: newCasemapMap[bool](cm), + monitored: xirc.NewCaseMappingMap[bool](cm), hasDesiredNick: true, } return uc, nil @@ -1174,7 +1174,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err for _, ch := range strings.Split(channels, ",") { if uc.isOurNick(msg.Prefix.Name) { uc.logger.Printf("joined channel %q", ch) - members := newCasemapMap[*xirc.MembershipSet](uc.network.casemap) + members := xirc.NewCaseMappingMap[*xirc.MembershipSet](uc.network.casemap) uc.channels.Set(ch, &upstreamChannel{ Name: ch, conn: uc, @@ -2259,10 +2259,10 @@ func (uc *upstreamConn) updateMonitor() { var addList []string seen := make(map[string]struct{}) uc.forEachDownstream(func(dc *downstreamConn) { - for _, entry := range dc.monitored.m { - targetCM := uc.network.casemap(entry.originalKey) + dc.monitored.ForEach(func(target string, _ struct{}) { + targetCM := uc.network.casemap(target) if targetCM == serviceNickCM { - continue + return } if !uc.monitored.Has(targetCM) { if _, ok := add[targetCM]; !ok { @@ -2272,7 +2272,7 @@ func (uc *upstreamConn) updateMonitor() { } else { seen[targetCM] = struct{}{} } - } + }) }) wantNick := database.GetNick(&uc.user.User, &uc.network.Network) diff --git a/user.go b/user.go index 26c48dd..392f788 100644 --- a/user.go +++ b/user.go @@ -93,11 +93,11 @@ type eventUserRun struct { type deliveredClientMap map[string]string // client name -> msg ID type deliveredStore struct { - m casemapMap[deliveredClientMap] + m xirc.CaseMappingMap[deliveredClientMap] } func newDeliveredStore(cm xirc.CaseMapping) deliveredStore { - return deliveredStore{newCasemapMap[deliveredClientMap](cm)} + return deliveredStore{xirc.NewCaseMappingMap[deliveredClientMap](cm)} } func (ds deliveredStore) HasTarget(target string) bool { @@ -147,9 +147,9 @@ type network struct { stopped chan struct{} conn *upstreamConn - channels casemapMap[*database.Channel] + channels xirc.CaseMappingMap[*database.Channel] delivered deliveredStore - pushTargets casemapMap[time.Time] + pushTargets xirc.CaseMappingMap[time.Time] lastError error casemap xirc.CaseMapping } @@ -161,7 +161,7 @@ func newNetwork(user *user, record *database.Network, channels []database.Channe // don't know which case-mapping will be used by the upstream server yet cm := xirc.CaseMappingNone - m := newCasemapMap[*database.Channel](cm) + m := xirc.NewCaseMappingMap[*database.Channel](cm) for _, ch := range channels { ch := ch m.Set(ch.Name, &ch) @@ -174,7 +174,7 @@ func newNetwork(user *user, record *database.Network, channels []database.Channe stopped: make(chan struct{}), channels: m, delivered: newDeliveredStore(cm), - pushTargets: newCasemapMap[time.Time](cm), + pushTargets: xirc.NewCaseMappingMap[time.Time](cm), casemap: stdCaseMapping, } } diff --git a/xirc/casemapping.go b/xirc/casemapping.go index c2f5c4f..4f8c7b0 100644 --- a/xirc/casemapping.go +++ b/xirc/casemapping.go @@ -75,3 +75,71 @@ func ParseCaseMapping(s string) CaseMapping { } return cm } + +type CaseMappingMap[V interface{}] struct { + m map[string]caseMappingEntry[V] + casemap CaseMapping +} + +type caseMappingEntry[V interface{}] struct { + originalKey string + value V +} + +func NewCaseMappingMap[V interface{}](cm CaseMapping) CaseMappingMap[V] { + return CaseMappingMap[V]{ + m: make(map[string]caseMappingEntry[V]), + casemap: cm, + } +} + +func (cmm *CaseMappingMap[V]) Has(name string) bool { + _, ok := cmm.m[cmm.casemap(name)] + return ok +} + +func (cmm *CaseMappingMap[V]) Len() int { + return len(cmm.m) +} + +func (cmm *CaseMappingMap[V]) Get(name string) V { + entry, ok := cmm.m[cmm.casemap(name)] + if !ok { + var v V + return v + } + return entry.value +} + +func (cmm *CaseMappingMap[V]) Set(name string, value V) { + nameCM := cmm.casemap(name) + entry, ok := cmm.m[nameCM] + if !ok { + cmm.m[nameCM] = caseMappingEntry[V]{ + originalKey: name, + value: value, + } + return + } + entry.value = value + cmm.m[nameCM] = entry +} + +func (cmm *CaseMappingMap[V]) Del(name string) { + delete(cmm.m, cmm.casemap(name)) +} + +func (cmm *CaseMappingMap[V]) ForEach(f func(string, V)) { + for _, entry := range cmm.m { + f(entry.originalKey, entry.value) + } +} + +func (cmm *CaseMappingMap[V]) SetCaseMapping(newCasemap CaseMapping) { + cmm.casemap = newCasemap + m := make(map[string]caseMappingEntry[V], len(cmm.m)) + for _, entry := range cmm.m { + m[cmm.casemap(entry.originalKey)] = entry + } + cmm.m = m +}