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:
		
							
								
								
									
										87
									
								
								hid/main.go
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								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
 | 
			
		||||
	limit      uint    // maximum number of events allowed
 | 
			
		||||
	timestamps []int64 // timestamps of last events
 | 
			
		||||
	pos        uint    // index of the oldest event
 | 
			
		||||
	interval   time.Duration // interval for the limit in seconds
 | 
			
		||||
	limit      uint          // maximum number of events allowed
 | 
			
		||||
	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,11 +397,11 @@ type client struct {
 | 
			
		||||
	closing   bool           // whether we're closing the connection
 | 
			
		||||
	killTimer *time.Timer    // hard kill timeout
 | 
			
		||||
 | 
			
		||||
	opened            int64 // 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
 | 
			
		||||
	receivedBytes     int   // number of received bytes total
 | 
			
		||||
	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
 | 
			
		||||
	receivedBytes     int       // number of received bytes total
 | 
			
		||||
 | 
			
		||||
	hostname string // hostname or IP shown to the network
 | 
			
		||||
	port     string // port of the peer as a string
 | 
			
		||||
@@ -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 ----------------------------------------------------------------
 | 
			
		||||
@@ -450,15 +445,15 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type channel struct {
 | 
			
		||||
	name      string // channel name
 | 
			
		||||
	modes     uint   // channel modes
 | 
			
		||||
	key       string // channel key
 | 
			
		||||
	userLimit int    // user limit or -1
 | 
			
		||||
	created   int64  // creation time
 | 
			
		||||
	name      string    // channel name
 | 
			
		||||
	modes     uint      // channel modes
 | 
			
		||||
	key       string    // channel key
 | 
			
		||||
	userLimit int       // user limit or -1
 | 
			
		||||
	created   time.Time // creation time
 | 
			
		||||
 | 
			
		||||
	topic     string // channel topic
 | 
			
		||||
	topicWho  string // who set the topic
 | 
			
		||||
	topicTime int64  // when the topic was set
 | 
			
		||||
	topic     string    // channel topic
 | 
			
		||||
	topicWho  string    // who set the topic
 | 
			
		||||
	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
 | 
			
		||||
@@ -2811,10 +2804,13 @@ func processOneEvent() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c := &client{
 | 
			
		||||
			transport: conn,
 | 
			
		||||
			address:   address,
 | 
			
		||||
			hostname:  host,
 | 
			
		||||
			port:      port,
 | 
			
		||||
			transport:  conn,
 | 
			
		||||
			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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user