hid: use time.Time and time.Duration
It improves the code significantly over explicit int64 conversions. Despite carrying unnecessary timezone information, time.Time also carries a monotonic reading of time, which allows for more precise measurement of time differences.
This commit is contained in:
parent
40370702d4
commit
24f1c4413a
55
hid/main.go
55
hid/main.go
@ -46,11 +46,6 @@ const (
|
||||
projectVersion = "0"
|
||||
)
|
||||
|
||||
// TODO: Consider using time.Time directly instead of storing Unix epoch
|
||||
// timestamps with nanosecond precision. Despite carrying unnecessary timezone
|
||||
// information, it also carries a monotonic reading of the time, which allows
|
||||
// for more precise measurement of time differences.
|
||||
|
||||
// --- Utilities ---------------------------------------------------------------
|
||||
|
||||
// Split a string by a set of UTF-8 delimiters, optionally ignoring empty items.
|
||||
@ -207,23 +202,23 @@ func readConfigFile(name string, output interface{}) error {
|
||||
// --- Rate limiter ------------------------------------------------------------
|
||||
|
||||
type floodDetector struct {
|
||||
interval uint // interval for the limit in seconds
|
||||
interval time.Duration // interval for the limit in seconds
|
||||
limit uint // maximum number of events allowed
|
||||
timestamps []int64 // timestamps of last events
|
||||
timestamps []time.Time // timestamps of last events
|
||||
pos uint // index of the oldest event
|
||||
}
|
||||
|
||||
func newFloodDetector(interval, limit uint) *floodDetector {
|
||||
func newFloodDetector(interval time.Duration, limit uint) *floodDetector {
|
||||
return &floodDetector{
|
||||
interval: interval,
|
||||
limit: limit,
|
||||
timestamps: make([]int64, limit+1),
|
||||
timestamps: make([]time.Time, limit+1),
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (fd *floodDetector) check() bool {
|
||||
now := time.Now().UnixNano()
|
||||
now := time.Now()
|
||||
fd.timestamps[fd.pos] = now
|
||||
|
||||
fd.pos++
|
||||
@ -232,9 +227,9 @@ func (fd *floodDetector) check() bool {
|
||||
}
|
||||
|
||||
var count uint
|
||||
begin := now - int64(time.Second)*int64(fd.interval)
|
||||
begin := now.Add(-fd.interval)
|
||||
for _, ts := range fd.timestamps {
|
||||
if ts >= begin {
|
||||
if ts.After(begin) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
@ -402,7 +397,7 @@ type client struct {
|
||||
closing bool // whether we're closing the connection
|
||||
killTimer *time.Timer // hard kill timeout
|
||||
|
||||
opened int64 // when the connection was opened
|
||||
opened time.Time // when the connection was opened
|
||||
nSentMessages uint // number of sent messages total
|
||||
sentBytes int // number of sent bytes total
|
||||
nReceivedMessages uint // number of received messages total
|
||||
@ -427,9 +422,9 @@ type client struct {
|
||||
|
||||
mode uint // user's mode
|
||||
awayMessage string // away message
|
||||
lastActive int64 // last PRIVMSG, to get idle time
|
||||
lastActive time.Time // last PRIVMSG, to get idle time
|
||||
invites map[string]bool // channel invitations by operators
|
||||
antiflood floodDetector // flood detector
|
||||
antiflood *floodDetector // flood detector
|
||||
}
|
||||
|
||||
// --- Channels ----------------------------------------------------------------
|
||||
@ -454,11 +449,11 @@ type channel struct {
|
||||
modes uint // channel modes
|
||||
key string // channel key
|
||||
userLimit int // user limit or -1
|
||||
created int64 // creation time
|
||||
created time.Time // creation time
|
||||
|
||||
topic string // channel topic
|
||||
topicWho string // who set the topic
|
||||
topicTime int64 // when the topic was set
|
||||
topicTime time.Time // when the topic was set
|
||||
|
||||
userModes map[*client]uint // modes for all channel users
|
||||
|
||||
@ -557,7 +552,7 @@ type writeEvent struct {
|
||||
// XXX: Beware that maps with identifier keys need to be indexed correctly.
|
||||
// We might want to enforce accessor functions for users and channels.
|
||||
var (
|
||||
started int64 // when has the server been started
|
||||
started time.Time // when has the server been started
|
||||
|
||||
users map[string]*client // maps nicknames to clients
|
||||
channels map[string]*channel // maps channel names to data
|
||||
@ -616,7 +611,7 @@ func initiateQuit() {
|
||||
func ircChannelCreate(name string) *channel {
|
||||
ch := &channel{
|
||||
name: name,
|
||||
created: time.Now().UnixNano(),
|
||||
created: time.Now(),
|
||||
userLimit: -1,
|
||||
}
|
||||
channels[ircToCanon(name)] = ch
|
||||
@ -941,7 +936,7 @@ func (c *client) tryFinishRegistration() {
|
||||
|
||||
c.sendReply(RPL_YOURHOST, serverName, projectVersion)
|
||||
// The purpose of this message eludes me.
|
||||
c.sendReply(RPL_CREATED, time.Unix(started, 0).Format("Mon, 02 Jan 2006"))
|
||||
c.sendReply(RPL_CREATED, started.Format("Mon, 02 Jan 2006"))
|
||||
c.sendReply(RPL_MYINFO, serverName, projectVersion,
|
||||
ircSupportedUserModes, ircSupportedChanModes)
|
||||
|
||||
@ -1691,8 +1686,7 @@ func ircHandleMODE(msg *message, c *client) {
|
||||
if len(msg.params) < 2 {
|
||||
_, present := ch.userModes[c]
|
||||
c.sendReply(RPL_CHANNELMODEIS, target, ch.getMode(present))
|
||||
c.sendReply(RPL_CREATIONTIME,
|
||||
target, ch.created/int64(time.Second))
|
||||
c.sendReply(RPL_CREATIONTIME, target, ch.created.Unix())
|
||||
} else {
|
||||
ircHandleChanModeChange(c, ch, msg.params[1:])
|
||||
}
|
||||
@ -1752,8 +1746,7 @@ func ircHandleUserMessage(msg *message, c *client,
|
||||
|
||||
func ircHandlePRIVMSG(msg *message, c *client) {
|
||||
ircHandleUserMessage(msg, c, "PRIVMSG", true /* allowAwayReply */)
|
||||
// Let's not care too much about success or failure.
|
||||
c.lastActive = time.Now().UnixNano()
|
||||
c.lastActive = time.Now()
|
||||
}
|
||||
|
||||
func ircHandleNOTICE(msg *message, c *client) {
|
||||
@ -1980,7 +1973,7 @@ func ircSendWHOISReply(c, target *client) {
|
||||
c.sendReply(RPL_WHOISOPERATOR, nick)
|
||||
}
|
||||
c.sendReply(RPL_WHOISIDLE, nick,
|
||||
(time.Now().UnixNano()-target.lastActive)/int64(time.Second))
|
||||
time.Now().Sub(target.lastActive)/time.Second)
|
||||
if target.awayMessage != "" {
|
||||
c.sendReply(RPL_AWAY, nick, target.awayMessage)
|
||||
}
|
||||
@ -2073,7 +2066,7 @@ func ircSendRPLTOPIC(c *client, ch *channel) {
|
||||
} else {
|
||||
c.sendReply(RPL_TOPIC, ch.name, ch.topic)
|
||||
c.sendReply(RPL_TOPICWHOTIME,
|
||||
ch.name, ch.topicWho, ch.topicTime/int64(time.Second))
|
||||
ch.name, ch.topicWho, ch.topicTime.Unix())
|
||||
}
|
||||
}
|
||||
|
||||
@ -2109,7 +2102,7 @@ func ircHandleTOPIC(msg *message, c *client) {
|
||||
|
||||
ch.topic = msg.params[1]
|
||||
ch.topicWho = fmt.Sprintf("%s@%s@%s", c.nickname, c.username, c.hostname)
|
||||
ch.topicTime = time.Now().UnixNano()
|
||||
ch.topicTime = time.Now()
|
||||
|
||||
message := fmt.Sprintf(":%s!%s@%s TOPIC %s :%s",
|
||||
c.nickname, c.username, c.hostname, target, ch.topic)
|
||||
@ -2418,7 +2411,7 @@ func ircHandleStatsLinks(c *client, msg *message) {
|
||||
len(client.sendQ), // sendq
|
||||
client.nSentMessages, client.sentBytes/1024,
|
||||
client.nReceivedMessages, client.receivedBytes/1024,
|
||||
(time.Now().UnixNano()-client.opened)/int64(time.Second))
|
||||
time.Now().Sub(client.opened)/time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2440,7 +2433,7 @@ func init() {
|
||||
}
|
||||
|
||||
func ircHandleStatsUptime(c *client) {
|
||||
uptime := (time.Now().UnixNano() - started) / int64(time.Second)
|
||||
uptime := time.Now().Sub(started) / time.Second
|
||||
|
||||
days := uptime / 60 / 60 / 24
|
||||
hours := (uptime % (60 * 60 * 24)) / 60 / 60
|
||||
@ -2815,6 +2808,9 @@ func processOneEvent() {
|
||||
address: address,
|
||||
hostname: host,
|
||||
port: port,
|
||||
capVersion: 301,
|
||||
// TODO: Make this configurable and more fine-grained.
|
||||
antiflood: newFloodDetector(10*time.Second, 20),
|
||||
}
|
||||
clients[c] = true
|
||||
go prepare(c)
|
||||
@ -2875,6 +2871,7 @@ func main() {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
started = time.Now()
|
||||
go accept(listener)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user