Переглянути джерело

implement WHOWAS with a shared ringbuffer

tags/v0.1.0
Jeremy Latt 10 роки тому
джерело
коміт
76852b0370
4 змінених файлів з 88 додано та 5 видалено
  1. 2
    1
      irc/client.go
  2. 3
    2
      irc/reply.go
  3. 10
    2
      irc/server.go
  4. 73
    0
      irc/whowas.go

+ 2
- 1
irc/client.go Переглянути файл

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

+ 3
- 2
irc/reply.go Переглянути файл

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

+ 10
- 2
irc/server.go Переглянути файл

@@ -30,6 +30,7 @@ type Server struct {
30 30
 	password  []byte
31 31
 	signals   chan os.Signal
32 32
 	timeout   chan *Client
33
+	whoWas    *WhoWasList
33 34
 }
34 35
 
35 36
 func NewServer(config *Config) *Server {
@@ -46,6 +47,7 @@ func NewServer(config *Config) *Server {
46 47
 		operators: config.Operators(),
47 48
 		signals:   make(chan os.Signal, 1),
48 49
 		timeout:   make(chan *Client, 16),
50
+		whoWas:    NewWhoWasList(100),
49 51
 	}
50 52
 
51 53
 	if config.Server.Password != "" {
@@ -846,8 +848,14 @@ func (msg *KillCommand) HandleServer(server *Server) {
846 848
 func (msg *WhoWasCommand) HandleServer(server *Server) {
847 849
 	client := msg.Client()
848 850
 	for _, nickname := range msg.nicknames {
849
-		// TODO implement nick history
850
-		client.ErrWasNoSuchNick(nickname)
851
+		results := server.whoWas.Find(nickname, msg.count)
852
+		if len(results) == 0 {
853
+			client.ErrWasNoSuchNick(nickname)
854
+		} else {
855
+			for _, whoWas := range results {
856
+				client.RplWhoWasUser(whoWas)
857
+			}
858
+		}
851 859
 		client.RplEndOfWhoWas(nickname)
852 860
 	}
853 861
 }

+ 73
- 0
irc/whowas.go Переглянути файл

@@ -0,0 +1,73 @@
1
+package irc
2
+
3
+type WhoWasList struct {
4
+	buffer []*WhoWas
5
+	start  uint
6
+	end    uint
7
+}
8
+
9
+type WhoWas struct {
10
+	nickname string
11
+	username string
12
+	hostname string
13
+	realname string
14
+}
15
+
16
+func NewWhoWasList(size uint) *WhoWasList {
17
+	return &WhoWasList{
18
+		buffer: make([]*WhoWas, size),
19
+	}
20
+}
21
+
22
+func (list *WhoWasList) Append(client *Client) {
23
+	list.buffer[list.end] = &WhoWas{
24
+		nickname: client.Nick(),
25
+		username: client.username,
26
+		hostname: client.hostname,
27
+		realname: client.realname,
28
+	}
29
+	list.end = (list.end + 1) % uint(len(list.buffer))
30
+	if list.end == list.start {
31
+		list.start = (list.end + 1) % uint(len(list.buffer))
32
+	}
33
+}
34
+
35
+func (list *WhoWasList) Find(nickname string, limit int64) []*WhoWas {
36
+	results := make([]*WhoWas, 0)
37
+	for whoWas := range list.Each() {
38
+		if nickname != whoWas.nickname {
39
+			continue
40
+		}
41
+		results = append(results, whoWas)
42
+		if int64(len(results)) >= limit {
43
+			break
44
+		}
45
+	}
46
+	return results
47
+}
48
+
49
+func (list *WhoWasList) prev(index uint) uint {
50
+	index -= 1
51
+	if index < 0 {
52
+		index += uint(len(list.buffer))
53
+	}
54
+	return index
55
+}
56
+
57
+// Iterate the buffer in reverse.
58
+func (list *WhoWasList) Each() <-chan *WhoWas {
59
+	ch := make(chan *WhoWas)
60
+	go func() {
61
+		defer close(ch)
62
+		if list.start == list.end {
63
+			return
64
+		}
65
+		start := list.prev(list.end)
66
+		end := list.prev(list.start)
67
+		for start != end {
68
+			ch <- list.buffer[start]
69
+			start = list.prev(start)
70
+		}
71
+	}()
72
+	return ch
73
+}

Завантаження…
Відмінити
Зберегти