implement WHOWAS with a shared ringbuffer

This commit is contained in:
Jeremy Latt 2014-03-06 13:55:25 -08:00
parent 41a6027d4e
commit 76852b0370
4 changed files with 88 additions and 5 deletions

@ -225,6 +225,7 @@ func (client *Client) ChangeNickname(nickname string) {
// Make reply before changing nick to capture original source id.
reply := RplNick(client, nickname)
client.server.clients.Remove(client)
client.server.whoWas.Append(client)
client.nick = nickname
client.server.clients.Add(client)
for friend := range client.Friends() {
@ -242,8 +243,8 @@ func (client *Client) Quit(message string) {
}
client.Reply(RplError("connection closed"))
client.hasQuit = true
client.server.whoWas.Append(client)
friends := client.Friends()
friends.Remove(client)
client.destroy()

@ -394,9 +394,10 @@ func (target *Client) RplTime() {
"%s :%s", target.server.name, time.Now().Format(time.RFC1123))
}
func (target *Client) RplWhoWasUser(nickname, username, hostname, realname string) {
func (target *Client) RplWhoWasUser(whoWas *WhoWas) {
target.NumericReply(RPL_WHOWASUSER,
"%s %s %s * :%s", nickname, username, hostname, realname)
"%s %s %s * :%s",
whoWas.nickname, whoWas.username, whoWas.hostname, whoWas.realname)
}
func (target *Client) RplEndOfWhoWas(nickname string) {

@ -30,6 +30,7 @@ type Server struct {
password []byte
signals chan os.Signal
timeout chan *Client
whoWas *WhoWasList
}
func NewServer(config *Config) *Server {
@ -46,6 +47,7 @@ func NewServer(config *Config) *Server {
operators: config.Operators(),
signals: make(chan os.Signal, 1),
timeout: make(chan *Client, 16),
whoWas: NewWhoWasList(100),
}
if config.Server.Password != "" {
@ -846,8 +848,14 @@ func (msg *KillCommand) HandleServer(server *Server) {
func (msg *WhoWasCommand) HandleServer(server *Server) {
client := msg.Client()
for _, nickname := range msg.nicknames {
// TODO implement nick history
client.ErrWasNoSuchNick(nickname)
results := server.whoWas.Find(nickname, msg.count)
if len(results) == 0 {
client.ErrWasNoSuchNick(nickname)
} else {
for _, whoWas := range results {
client.RplWhoWasUser(whoWas)
}
}
client.RplEndOfWhoWas(nickname)
}
}

73
irc/whowas.go Normal file

@ -0,0 +1,73 @@
package irc
type WhoWasList struct {
buffer []*WhoWas
start uint
end uint
}
type WhoWas struct {
nickname string
username string
hostname string
realname string
}
func NewWhoWasList(size uint) *WhoWasList {
return &WhoWasList{
buffer: make([]*WhoWas, size),
}
}
func (list *WhoWasList) Append(client *Client) {
list.buffer[list.end] = &WhoWas{
nickname: client.Nick(),
username: client.username,
hostname: client.hostname,
realname: client.realname,
}
list.end = (list.end + 1) % uint(len(list.buffer))
if list.end == list.start {
list.start = (list.end + 1) % uint(len(list.buffer))
}
}
func (list *WhoWasList) Find(nickname string, limit int64) []*WhoWas {
results := make([]*WhoWas, 0)
for whoWas := range list.Each() {
if nickname != whoWas.nickname {
continue
}
results = append(results, whoWas)
if int64(len(results)) >= limit {
break
}
}
return results
}
func (list *WhoWasList) prev(index uint) uint {
index -= 1
if index < 0 {
index += uint(len(list.buffer))
}
return index
}
// Iterate the buffer in reverse.
func (list *WhoWasList) Each() <-chan *WhoWas {
ch := make(chan *WhoWas)
go func() {
defer close(ch)
if list.start == list.end {
return
}
start := list.prev(list.end)
end := list.prev(list.start)
for start != end {
ch <- list.buffer[start]
start = list.prev(start)
}
}()
return ch
}