-
Notifications
You must be signed in to change notification settings - Fork 0
/
chat_buffer.go
86 lines (67 loc) · 2.45 KB
/
chat_buffer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package games
import (
"encoding/binary"
"github.com/google/uuid"
)
const (
maxScrollback = 50 // must be 255 or less because we only use 1 byte for message count
maxMessageLen = 100 // must be 255 or less because we only use 1 byte for message length
lineLen = 16 + 1 + maxMessageLen // 16-byte client UUID, message length, message capacity
)
type chatBuffer struct {
buff [maxScrollback * lineLen]byte
hist uint16
}
func (cb *chatBuffer) numMessages() int {
if cb.hist > maxScrollback {
return maxScrollback
}
return int(cb.hist)
}
func (cb *chatBuffer) addMessage(clientID uuid.UUID, msg []byte) bool {
if len(msg) < 1 || len(msg) > maxMessageLen {
return false
}
pos := (cb.hist % maxScrollback) * lineLen
copy(cb.buff[pos:pos+16], clientID[:])
cb.buff[pos+16] = byte(len(msg))
copy(cb.buff[pos+17:pos+17+uint16(len(msg))], msg)
cb.hist++
return true
}
// Calculate size of encoded history (number of bytes required) up-front to
// avoid wasteful memory allocations due to automatic slice growth
func (cb *chatBuffer) encodedHistoryLen() int {
numMessages := cb.numMessages()
// 2 bytes for uint16 length of message HISTORY (not scrollback) +
// <number of retained messages> * (16 bytes for client UUID + 1 byte for message length)
encLen := 2 + numMessages*(16+1)
for i := 0; i < numMessages; i++ {
encLen += int(cb.buff[i*lineLen+16])
}
return encLen
}
func (cb *chatBuffer) appendHistory(dst []byte) []byte {
dst = binary.BigEndian.AppendUint16(dst, cb.hist)
currentLine := int(cb.hist % maxScrollback)
numMessages := cb.numMessages()
// Start from the current offset
for i := currentLine; i < numMessages; i++ {
pos := i * lineLen
msgLen := cb.buff[pos+16]
dst = append(dst, cb.buff[pos:pos+16]...) // client UUID
dst = append(dst, msgLen) // message length
dst = append(dst, cb.buff[pos+17:pos+17+int(msgLen)]...) // message contents
}
// In case we have more than <maxScrollback> messages, we need to start
// from beginning of buffer and work our way to the current line to get
// the newest messages
for i := 0; i < currentLine; i++ {
pos := i * lineLen
msgLen := cb.buff[pos+16]
dst = append(dst, cb.buff[pos:pos+16]...) // client UUID
dst = append(dst, msgLen) // message length
dst = append(dst, cb.buff[pos+17:pos+17+int(msgLen)]...) // message contents
}
return dst
}