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:
Přemysl Eric Janouch 2018-07-30 10:04:05 +02:00
parent 90129ee2bc
commit 404aa8c9cc
1 changed files with 42 additions and 45 deletions

View File

@ -46,11 +46,6 @@ const (
projectVersion = "0" 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 --------------------------------------------------------------- // --- Utilities ---------------------------------------------------------------
// Split a string by a set of UTF-8 delimiters, optionally ignoring empty items. // 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 ------------------------------------------------------------ // --- Rate limiter ------------------------------------------------------------
type floodDetector struct { 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 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 pos uint // index of the oldest event
} }
func newFloodDetector(interval, limit uint) *floodDetector { func newFloodDetector(interval time.Duration, limit uint) *floodDetector {
return &floodDetector{ return &floodDetector{
interval: interval, interval: interval,
limit: limit, limit: limit,
timestamps: make([]int64, limit+1), timestamps: make([]time.Time, limit+1),
pos: 0, pos: 0,
} }
} }
func (fd *floodDetector) check() bool { func (fd *floodDetector) check() bool {
now := time.Now().UnixNano() now := time.Now()
fd.timestamps[fd.pos] = now fd.timestamps[fd.pos] = now
fd.pos++ fd.pos++
@ -232,9 +227,9 @@ func (fd *floodDetector) check() bool {
} }
var count uint var count uint
begin := now - int64(time.Second)*int64(fd.interval) begin := now.Add(-fd.interval)
for _, ts := range fd.timestamps { for _, ts := range fd.timestamps {
if ts >= begin { if ts.After(begin) {
count++ count++
} }
} }
@ -402,7 +397,7 @@ type client struct {
closing bool // whether we're closing the connection closing bool // whether we're closing the connection
killTimer *time.Timer // hard kill timeout 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 nSentMessages uint // number of sent messages total
sentBytes int // number of sent bytes total sentBytes int // number of sent bytes total
nReceivedMessages uint // number of received messages total nReceivedMessages uint // number of received messages total
@ -427,9 +422,9 @@ type client struct {
mode uint // user's mode mode uint // user's mode
awayMessage string // away message 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 invites map[string]bool // channel invitations by operators
antiflood floodDetector // flood detector antiflood *floodDetector // flood detector
} }
// --- Channels ---------------------------------------------------------------- // --- Channels ----------------------------------------------------------------
@ -454,11 +449,11 @@ type channel struct {
modes uint // channel modes modes uint // channel modes
key string // channel key key string // channel key
userLimit int // user limit or -1 userLimit int // user limit or -1
created int64 // creation time created time.Time // creation time
topic string // channel topic topic string // channel topic
topicWho string // who set the 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 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. // XXX: Beware that maps with identifier keys need to be indexed correctly.
// We might want to enforce accessor functions for users and channels. // We might want to enforce accessor functions for users and channels.
var ( 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 users map[string]*client // maps nicknames to clients
channels map[string]*channel // maps channel names to data channels map[string]*channel // maps channel names to data
@ -616,7 +611,7 @@ func initiateQuit() {
func ircChannelCreate(name string) *channel { func ircChannelCreate(name string) *channel {
ch := &channel{ ch := &channel{
name: name, name: name,
created: time.Now().UnixNano(), created: time.Now(),
userLimit: -1, userLimit: -1,
} }
channels[ircToCanon(name)] = ch channels[ircToCanon(name)] = ch
@ -941,7 +936,7 @@ func (c *client) tryFinishRegistration() {
c.sendReply(RPL_YOURHOST, serverName, projectVersion) c.sendReply(RPL_YOURHOST, serverName, projectVersion)
// The purpose of this message eludes me. // 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, c.sendReply(RPL_MYINFO, serverName, projectVersion,
ircSupportedUserModes, ircSupportedChanModes) ircSupportedUserModes, ircSupportedChanModes)
@ -1691,8 +1686,7 @@ func ircHandleMODE(msg *message, c *client) {
if len(msg.params) < 2 { if len(msg.params) < 2 {
_, present := ch.userModes[c] _, present := ch.userModes[c]
c.sendReply(RPL_CHANNELMODEIS, target, ch.getMode(present)) c.sendReply(RPL_CHANNELMODEIS, target, ch.getMode(present))
c.sendReply(RPL_CREATIONTIME, c.sendReply(RPL_CREATIONTIME, target, ch.created.Unix())
target, ch.created/int64(time.Second))
} else { } else {
ircHandleChanModeChange(c, ch, msg.params[1:]) ircHandleChanModeChange(c, ch, msg.params[1:])
} }
@ -1752,8 +1746,7 @@ func ircHandleUserMessage(msg *message, c *client,
func ircHandlePRIVMSG(msg *message, c *client) { func ircHandlePRIVMSG(msg *message, c *client) {
ircHandleUserMessage(msg, c, "PRIVMSG", true /* allowAwayReply */) ircHandleUserMessage(msg, c, "PRIVMSG", true /* allowAwayReply */)
// Let's not care too much about success or failure. c.lastActive = time.Now()
c.lastActive = time.Now().UnixNano()
} }
func ircHandleNOTICE(msg *message, c *client) { func ircHandleNOTICE(msg *message, c *client) {
@ -1980,7 +1973,7 @@ func ircSendWHOISReply(c, target *client) {
c.sendReply(RPL_WHOISOPERATOR, nick) c.sendReply(RPL_WHOISOPERATOR, nick)
} }
c.sendReply(RPL_WHOISIDLE, nick, c.sendReply(RPL_WHOISIDLE, nick,
(time.Now().UnixNano()-target.lastActive)/int64(time.Second)) time.Now().Sub(target.lastActive)/time.Second)
if target.awayMessage != "" { if target.awayMessage != "" {
c.sendReply(RPL_AWAY, nick, target.awayMessage) c.sendReply(RPL_AWAY, nick, target.awayMessage)
} }
@ -2073,7 +2066,7 @@ func ircSendRPLTOPIC(c *client, ch *channel) {
} else { } else {
c.sendReply(RPL_TOPIC, ch.name, ch.topic) c.sendReply(RPL_TOPIC, ch.name, ch.topic)
c.sendReply(RPL_TOPICWHOTIME, 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.topic = msg.params[1]
ch.topicWho = fmt.Sprintf("%s@%s@%s", c.nickname, c.username, c.hostname) 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", message := fmt.Sprintf(":%s!%s@%s TOPIC %s :%s",
c.nickname, c.username, c.hostname, target, ch.topic) c.nickname, c.username, c.hostname, target, ch.topic)
@ -2418,7 +2411,7 @@ func ircHandleStatsLinks(c *client, msg *message) {
len(client.sendQ), // sendq len(client.sendQ), // sendq
client.nSentMessages, client.sentBytes/1024, client.nSentMessages, client.sentBytes/1024,
client.nReceivedMessages, client.receivedBytes/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) { func ircHandleStatsUptime(c *client) {
uptime := (time.Now().UnixNano() - started) / int64(time.Second) uptime := time.Now().Sub(started) / time.Second
days := uptime / 60 / 60 / 24 days := uptime / 60 / 60 / 24
hours := (uptime % (60 * 60 * 24)) / 60 / 60 hours := (uptime % (60 * 60 * 24)) / 60 / 60
@ -2815,6 +2808,9 @@ func processOneEvent() {
address: address, address: address,
hostname: host, hostname: host,
port: port, port: port,
capVersion: 301,
// TODO: Make this configurable and more fine-grained.
antiflood: newFloodDetector(10*time.Second, 20),
} }
clients[c] = true clients[c] = true
go prepare(c) go prepare(c)
@ -2875,6 +2871,7 @@ func main() {
log.Fatalln(err) log.Fatalln(err)
} }
started = time.Now()
go accept(listener) go accept(listener)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)