optimized implementation of Channel.Names()

This commit is contained in:
Shivaram Lingamneni 2018-04-24 05:46:01 -04:00
parent ef35c587fc
commit 6533893863
1 changed files with 38 additions and 48 deletions

View File

@ -6,6 +6,7 @@
package irc package irc
import ( import (
"bytes"
"crypto/subtle" "crypto/subtle"
"fmt" "fmt"
"strconv" "strconv"
@ -24,7 +25,7 @@ type Channel struct {
lists map[modes.Mode]*UserMaskSet lists map[modes.Mode]*UserMaskSet
key string key string
members MemberSet members MemberSet
membersCache []*Client // allow iteration over channel members without holding the lock membersCache []*Client // allow iteration over channel members without holding the lock
name string name string
nameCasefolded string nameCasefolded string
server *Server server *Server
@ -180,27 +181,47 @@ func (channel *Channel) regenerateMembersCache() {
// Names sends the list of users joined to the channel to the given client. // Names sends the list of users joined to the channel to the given client.
func (channel *Channel) Names(client *Client, rb *ResponseBuffer) { func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
currentNicks := channel.nicks(client) isMultiPrefix := client.capabilities.Has(caps.MultiPrefix)
// assemble and send replies isUserhostInNames := client.capabilities.Has(caps.UserhostInNames)
maxNamLen := 480 - len(client.server.name) - len(client.nick)
var buffer string maxNamLen := 480 - len(client.server.name) - len(client.Nick())
for _, nick := range currentNicks { var namesLines []string
if buffer == "" { var buffer bytes.Buffer
buffer += nick for _, target := range channel.Members() {
var nick string
if isUserhostInNames {
nick = target.NickMaskString()
} else {
nick = target.Nick()
}
channel.stateMutex.RLock()
modes := channel.members[target]
channel.stateMutex.RUnlock()
if modes == nil {
continue continue
} }
prefix := modes.Prefixes(isMultiPrefix)
if len(buffer)+1+len(nick) > maxNamLen { if buffer.Len()+len(nick)+len(prefix)+1 > maxNamLen {
rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, buffer) namesLines = append(namesLines, buffer.String())
buffer = nick // memset(&buffer, 0, sizeof(bytes.Buffer));
continue var newBuffer bytes.Buffer
buffer = newBuffer
} }
if buffer.Len() > 0 {
buffer += " " buffer.WriteString(" ")
buffer += nick }
buffer.WriteString(prefix)
buffer.WriteString(nick)
}
if buffer.Len() > 0 {
namesLines = append(namesLines, buffer.String())
} }
rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, buffer) for _, line := range namesLines {
if buffer.Len() > 0 {
rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, line)
}
}
rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list")) rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list"))
} }
@ -263,37 +284,6 @@ func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool
return result return result
} }
func (channel *Channel) nicks(target *Client) []string {
isMultiPrefix := (target != nil) && target.capabilities.Has(caps.MultiPrefix)
isUserhostInNames := (target != nil) && target.capabilities.Has(caps.UserhostInNames)
// slightly cumbersome: get the mutex and copy both the client pointers and
// the mode prefixes
channel.stateMutex.RLock()
length := len(channel.members)
clients := make([]*Client, length)
result := make([]string, length)
i := 0
for client, modes := range channel.members {
clients[i] = client
result[i] = modes.Prefixes(isMultiPrefix)
i++
}
channel.stateMutex.RUnlock()
i = 0
for i < length {
if isUserhostInNames {
result[i] += clients[i].NickMaskString()
} else {
result[i] += clients[i].Nick()
}
i++
}
return result
}
func (channel *Channel) hasClient(client *Client) bool { func (channel *Channel) hasClient(client *Client) bool {
channel.stateMutex.RLock() channel.stateMutex.RLock()
defer channel.stateMutex.RUnlock() defer channel.stateMutex.RUnlock()