<commit_before>package webhook

import (
	"encoding/json"
	"time"
)

type oldW struct {
	// Configuration for message delivery
	Config struct {
		// The URL to deliver messages to.
		URL string `json:"url"`

		// The content-type to set the messages to (unless specified by WRP).
		ContentType string `json:"content_type"`

		// The secret to use for the SHA1 HMAC.
		// Optional, set to "" to disable behavior.
		Secret string `json:"secret,omitempty"`
	} `json:"config"`

	// The list of regular expressions to match event type against.
	Events []string `json:"events"`

	// Matcher type contains values to match against the metadata.
	Matcher struct {
		// The list of regular expressions to match device id type against.
		DeviceId []string `json:"device_id"`
	} `json:"matcher,omitempty"`

	// The specified duration for this hook to live
	Duration int64 `json:"duration"`

	// The absolute time when this hook is to be disabled
	Until int64 `json:"until"`

	// The address that performed the registration
	Address string `json:"registered_from_address"`
}

func doOldHookConvert(oldHook oldW) (newHook W) {
	newHook.Config.URL = oldHook.Config.URL
	newHook.Config.ContentType = oldHook.Config.ContentType
	newHook.Config.Secret = oldHook.Config.Secret
	newHook.Events = oldHook.Events
	newHook.Matcher = oldHook.Matcher
	newHook.Duration = time.Duration(oldHook.Duration) * time.Second
	newHook.Until = time.Unix(oldHook.Until, 0)
	newHook.Address = oldHook.Address

	return
}

func convertOldHooksToNewHooks(body []byte) (hooks []W, err error) {
	var oldHooks []oldW
	err = json.Unmarshal(body, &oldHooks)
	if err != nil {
		return
	}

	for _, oldHook := range oldHooks {
		hooks = append(hooks, doOldHookConvert(oldHook))
	}

	return
}
<commit_msg>fixes old hook until time conversion of 0 to be equal to time.Time{}<commit_after>package webhook

import (
	"encoding/json"
	"time"
)

type oldW struct {
	// Configuration for message delivery
	Config struct {
		// The URL to deliver messages to.
		URL string `json:"url"`

		// The content-type to set the messages to (unless specified by WRP).
		ContentType string `json:"content_type"`

		// The secret to use for the SHA1 HMAC.
		// Optional, set to "" to disable behavior.
		Secret string `json:"secret,omitempty"`
	} `json:"config"`

	// The list of regular expressions to match event type against.
	Events []string `json:"events"`

	// Matcher type contains values to match against the metadata.
	Matcher struct {
		// The list of regular expressions to match device id type against.
		DeviceId []string `json:"device_id"`
	} `json:"matcher,omitempty"`

	// The specified duration for this hook to live
	Duration int64 `json:"duration"`

	// The absolute time when this hook is to be disabled
	Until int64 `json:"until"`

	// The address that performed the registration
	Address string `json:"registered_from_address"`
}

func doOldHookConvert(oldHook oldW) (newHook W) {
	newHook.Config.URL = oldHook.Config.URL
	newHook.Config.ContentType = oldHook.Config.ContentType
	newHook.Config.Secret = oldHook.Config.Secret
	newHook.Events = oldHook.Events
	newHook.Matcher = oldHook.Matcher
	newHook.Duration = time.Duration(oldHook.Duration) * time.Second

	newHook.Until = time.Time{}
	if oldHook.Until > 0 {
		newHook.Until = time.Unix(oldHook.Until, 0)
	}

	newHook.Address = oldHook.Address

	return
}

func convertOldHooksToNewHooks(body []byte) (hooks []W, err error) {
	var oldHooks []oldW
	err = json.Unmarshal(body, &oldHooks)
	if err != nil {
		return
	}

	for _, oldHook := range oldHooks {
		hooks = append(hooks, doOldHookConvert(oldHook))
	}

	return
}
<|endoftext|>


<commit_before>package gron

import (
	"time"

	"github.com/roylee0704/gron/xtime"
)

// Schedule is the interface that wraps the basic Next method.
//
// Next deduces next occuring time based on t and underlying states.
type Schedule interface {
	Next(t time.Time) time.Time
}

// Every returns a Schedule that adds period p to time, p must be at least
// time.Second.
func Every(p time.Duration) Schedule {

	if p < time.Second {
		p = xtime.Second
	}

	p = p - time.Duration(p.Nanoseconds())%time.Second // round-off time.seconds

	return &periodicSchedule{
		period: p,
	}
}

type periodicSchedule struct {
	period time.Duration
}

func (ps periodicSchedule) Next(t time.Time) time.Time {
	return t.Truncate(time.Second).Add(ps.period)
}
<commit_msg>updated comment<commit_after>package gron

import (
	"time"

	"github.com/roylee0704/gron/xtime"
)

// Schedule is the interface that wraps the basic Next method.
//
// Next deduces next occuring time based on t and underlying states.
type Schedule interface {
	Next(t time.Time) time.Time
}

// Every returns a Schedule reoccurs every period p, p must be at least
// time.Second.
func Every(p time.Duration) Schedule {

	if p < time.Second {
		p = xtime.Second
	}

	p = p - time.Duration(p.Nanoseconds())%time.Second // round-off time.seconds

	return &periodicSchedule{
		period: p,
	}
}

type periodicSchedule struct {
	period time.Duration
}

// Next adds time t to underlying period, truncates up to unit of seconds.
func (ps periodicSchedule) Next(t time.Time) time.Time {
	return t.Truncate(time.Second).Add(ps.period)
}
<|endoftext|>


<commit_before>// Copyright 2014 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package prometheus provides metrics primitives to instrument code for
// monitoring. It also offers a registry for metrics. Sub-packages allow to
// expose the registered metrics via HTTP (package promhttp) or push them to a
// Pushgateway (package push).
//
// All exported functions and methods are safe to be used concurrently unless
// specified otherwise.
//
// A Basic Example
//
// As a starting point, a very basic usage example:
//
//    package main
//
//    import (
//    	"log"
//    	"net/http"
//
//    	"github.com/prometheus/client_golang/prometheus"
//    	"github.com/prometheus/client_golang/prometheus/promhttp"
//    )
//
//    var (
//    	cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
//    		Name: "cpu_temperature_celsius",
//    		Help: "Current temperature of the CPU.",
//    	})
//    	hdFailures = prometheus.NewCounterVec(
//    		prometheus.CounterOpts{
//    			Name: "hd_errors_total",
//    			Help: "Number of hard-disk errors.",
//    		},
//    		[]string{"device"},
//    	)
//    )
//
//    func init() {
//    	// Metrics have to be registered to be exposed:
//    	prometheus.MustRegister(cpuTemp)
//    	prometheus.MustRegister(hdFailures)
//    }
//
//    func main() {
//    	cpuTemp.Set(65.3)
//    	hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
//
//    	// The Handler function provides a default handler to expose metrics
//    	// via an HTTP server. "/metrics" is the usual endpoint for that.
//    	http.Handle("/metrics", promhttp.Handler())
//    	log.Fatal(http.ListenAndServe(":8080", nil))
//    }
//
//
// This is a complete program that exports two metrics, a Gauge and a Counter,
// the latter with a label attached to turn it into a (one-dimensional) vector.
//
// Metrics
//
// The number of exported identifiers in this package might appear a bit
// overwhelming. However, in addition to the basic plumbing shown in the example
// above, you only need to understand the different metric types and their
// vector versions for basic usage.
//
// Above, you have already touched the Counter and the Gauge. There are two more
// advanced metric types: the Summary and Histogram. A more thorough description
// of those four metric types can be found in the Prometheus docs:
// https://prometheus.io/docs/concepts/metric_types/
//
// A fifth "type" of metric is Untyped. It behaves like a Gauge, but signals the
// Prometheus server not to assume anything about its type.
//
// In addition to the fundamental metric types Gauge, Counter, Summary,
// Histogram, and Untyped, a very important part of the Prometheus data model is
// the partitioning of samples along dimensions called labels, which results in
// metric vectors. The fundamental types are GaugeVec, CounterVec, SummaryVec,
// HistogramVec, and UntypedVec.
//
// While only the fundamental metric types implement the Metric interface, both
// the metrics and their vector versions implement the Collector interface. A
// Collector manages the collection of a number of Metrics, but for convenience,
// a Metric can also “collect itself”. Note that Gauge, Counter, Summary,
// Histogram, and Untyped are interfaces themselves while GaugeVec, CounterVec,
// SummaryVec, HistogramVec, and UntypedVec are not.
//
// To create instances of Metrics and their vector versions, you need a suitable
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, HistogramOpts, or
// UntypedOpts.
//
// Custom Collectors and constant Metrics
//
// While you could create your own implementations of Metric, most likely you
// will only ever implement the Collector interface on your own. At a first
// glance, a custom Collector seems handy to bundle Metrics for common
// registration (with the prime example of the different metric vectors above,
// which bundle all the metrics of the same name but with different labels).
//
// There is a more involved use case, too: If you already have metrics
// available, created outside of the Prometheus context, you don't need the
// interface of the various Metric types. You essentially want to mirror the
// existing numbers into Prometheus Metrics during collection. An own
// implementation of the Collector interface is perfect for that. You can create
// Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and
// NewConstSummary (and their respective Must… versions). That will happen in
// the Collect method. The Describe method has to return separate Desc
// instances, representative of the “throw-away” metrics to be created later.
// NewDesc comes in handy to create those Desc instances.
//
// The Collector example illustrates the use case. You can also look at the
// source code of the processCollector (mirroring process metrics), the
// goCollector (mirroring Go metrics), or the expvarCollector (mirroring expvar
// metrics) as examples that are used in this package itself.
//
// If you just need to call a function to get a single float value to collect as
// a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting
// shortcuts.
//
// Advanced Uses of the Registry
//
// While MustRegister is the by far most common way of registering a Collector,
// sometimes you might want to handle the errors the registration might cause.
// As suggested by the name, MustRegister panics if an error occurs. With the
// Register function, the error is returned and can be handled.
//
// An error is returned if the registered Collector is incompatible or
// inconsistent with already registered metrics. The registry aims for
// consistency of the collected metrics according to the Prometheus data model.
// Inconsistencies are ideally detected at registration time, not at collect
// time. The former will usually be detected at start-up time of a program,
// while the latter will only happen at scrape time, possibly not even on the
// first scrape if the inconsistency only becomes relevant later. That is the
// main reason why a Collector and a Metric have to describe themselves to the
// registry.
//
// So far, everything we did operated on the so-called default registry, as it
// can be found in the global DefaultRegistry variable. With NewRegistry, you
// can create a custom registry, or you can even implement the Registerer or
// Gatherer interfaces yourself. The methods Register and Unregister work in the
// same way on a custom registry as the global functions Register and Unregister
// on the default registry.
//
// There are a number of uses for custom registries: You can use registries with
// special properties, see NewPedanticRegistry. You can avoid global state, as
// it is imposed by the DefaultRegistry. You can use multiple registries at the
// same time to expose different metrics in different ways. You can use separate
// registries for testing purposes.
//
// Also note that the DefaultRegistry comes registered with a Collector for Go
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
// NewProcessCollector). With a custom registry, you are in control and decide
// yourself about the Collectors to register.
//
// HTTP Exposition
//
// The Registry implements the Gatherer interface. The caller of the Gather
// method can then expose the gathered metrics in some way. Usually, the metrics
// are served via HTTP on the /metrics endpoint. That's happening in the example
// above. The tools to expose metrics via HTTP are in the promhttp sub-package.
// (The top-level functions in the prometheus package are deprecated.)
//
// Pushing to the Pushgateway
//
// Function for pushing to the Pushgateway can be found in the push sub-package.
//
// Graphite Bridge
//
// Functions and examples to push metrics from a Gatherer to Graphite can be
// found in the graphite sub-package.
//
// Other Means of Exposition
//
// More ways of exposing metrics can easily be added by following the approaches
// of the existing implementations.
package prometheus
<commit_msg>Correct DefaultRegistry to DefaultRegisterer<commit_after>// Copyright 2014 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package prometheus provides metrics primitives to instrument code for
// monitoring. It also offers a registry for metrics. Sub-packages allow to
// expose the registered metrics via HTTP (package promhttp) or push them to a
// Pushgateway (package push).
//
// All exported functions and methods are safe to be used concurrently unless
// specified otherwise.
//
// A Basic Example
//
// As a starting point, a very basic usage example:
//
//    package main
//
//    import (
//    	"log"
//    	"net/http"
//
//    	"github.com/prometheus/client_golang/prometheus"
//    	"github.com/prometheus/client_golang/prometheus/promhttp"
//    )
//
//    var (
//    	cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
//    		Name: "cpu_temperature_celsius",
//    		Help: "Current temperature of the CPU.",
//    	})
//    	hdFailures = prometheus.NewCounterVec(
//    		prometheus.CounterOpts{
//    			Name: "hd_errors_total",
//    			Help: "Number of hard-disk errors.",
//    		},
//    		[]string{"device"},
//    	)
//    )
//
//    func init() {
//    	// Metrics have to be registered to be exposed:
//    	prometheus.MustRegister(cpuTemp)
//    	prometheus.MustRegister(hdFailures)
//    }
//
//    func main() {
//    	cpuTemp.Set(65.3)
//    	hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
//
//    	// The Handler function provides a default handler to expose metrics
//    	// via an HTTP server. "/metrics" is the usual endpoint for that.
//    	http.Handle("/metrics", promhttp.Handler())
//    	log.Fatal(http.ListenAndServe(":8080", nil))
//    }
//
//
// This is a complete program that exports two metrics, a Gauge and a Counter,
// the latter with a label attached to turn it into a (one-dimensional) vector.
//
// Metrics
//
// The number of exported identifiers in this package might appear a bit
// overwhelming. However, in addition to the basic plumbing shown in the example
// above, you only need to understand the different metric types and their
// vector versions for basic usage.
//
// Above, you have already touched the Counter and the Gauge. There are two more
// advanced metric types: the Summary and Histogram. A more thorough description
// of those four metric types can be found in the Prometheus docs:
// https://prometheus.io/docs/concepts/metric_types/
//
// A fifth "type" of metric is Untyped. It behaves like a Gauge, but signals the
// Prometheus server not to assume anything about its type.
//
// In addition to the fundamental metric types Gauge, Counter, Summary,
// Histogram, and Untyped, a very important part of the Prometheus data model is
// the partitioning of samples along dimensions called labels, which results in
// metric vectors. The fundamental types are GaugeVec, CounterVec, SummaryVec,
// HistogramVec, and UntypedVec.
//
// While only the fundamental metric types implement the Metric interface, both
// the metrics and their vector versions implement the Collector interface. A
// Collector manages the collection of a number of Metrics, but for convenience,
// a Metric can also “collect itself”. Note that Gauge, Counter, Summary,
// Histogram, and Untyped are interfaces themselves while GaugeVec, CounterVec,
// SummaryVec, HistogramVec, and UntypedVec are not.
//
// To create instances of Metrics and their vector versions, you need a suitable
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, HistogramOpts, or
// UntypedOpts.
//
// Custom Collectors and constant Metrics
//
// While you could create your own implementations of Metric, most likely you
// will only ever implement the Collector interface on your own. At a first
// glance, a custom Collector seems handy to bundle Metrics for common
// registration (with the prime example of the different metric vectors above,
// which bundle all the metrics of the same name but with different labels).
//
// There is a more involved use case, too: If you already have metrics
// available, created outside of the Prometheus context, you don't need the
// interface of the various Metric types. You essentially want to mirror the
// existing numbers into Prometheus Metrics during collection. An own
// implementation of the Collector interface is perfect for that. You can create
// Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and
// NewConstSummary (and their respective Must… versions). That will happen in
// the Collect method. The Describe method has to return separate Desc
// instances, representative of the “throw-away” metrics to be created later.
// NewDesc comes in handy to create those Desc instances.
//
// The Collector example illustrates the use case. You can also look at the
// source code of the processCollector (mirroring process metrics), the
// goCollector (mirroring Go metrics), or the expvarCollector (mirroring expvar
// metrics) as examples that are used in this package itself.
//
// If you just need to call a function to get a single float value to collect as
// a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting
// shortcuts.
//
// Advanced Uses of the Registry
//
// While MustRegister is the by far most common way of registering a Collector,
// sometimes you might want to handle the errors the registration might cause.
// As suggested by the name, MustRegister panics if an error occurs. With the
// Register function, the error is returned and can be handled.
//
// An error is returned if the registered Collector is incompatible or
// inconsistent with already registered metrics. The registry aims for
// consistency of the collected metrics according to the Prometheus data model.
// Inconsistencies are ideally detected at registration time, not at collect
// time. The former will usually be detected at start-up time of a program,
// while the latter will only happen at scrape time, possibly not even on the
// first scrape if the inconsistency only becomes relevant later. That is the
// main reason why a Collector and a Metric have to describe themselves to the
// registry.
//
// So far, everything we did operated on the so-called default registry, as it
// can be found in the global DefaultRegisterer variable. With NewRegistry, you
// can create a custom registry, or you can even implement the Registerer or
// Gatherer interfaces yourself. The methods Register and Unregister work in the
// same way on a custom registry as the global functions Register and Unregister
// on the default registry.
//
// There are a number of uses for custom registries: You can use registries with
// special properties, see NewPedanticRegistry. You can avoid global state, as
// it is imposed by the DefaultRegisterer. You can use multiple registries at
// the same time to expose different metrics in different ways.  You can use
// separate registries for testing purposes.
//
// Also note that the DefaultRegisterer comes registered with a Collector for Go
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
// NewProcessCollector). With a custom registry, you are in control and decide
// yourself about the Collectors to register.
//
// HTTP Exposition
//
// The Registry implements the Gatherer interface. The caller of the Gather
// method can then expose the gathered metrics in some way. Usually, the metrics
// are served via HTTP on the /metrics endpoint. That's happening in the example
// above. The tools to expose metrics via HTTP are in the promhttp sub-package.
// (The top-level functions in the prometheus package are deprecated.)
//
// Pushing to the Pushgateway
//
// Function for pushing to the Pushgateway can be found in the push sub-package.
//
// Graphite Bridge
//
// Functions and examples to push metrics from a Gatherer to Graphite can be
// found in the graphite sub-package.
//
// Other Means of Exposition
//
// More ways of exposing metrics can easily be added by following the approaches
// of the existing implementations.
package prometheus
<|endoftext|>


<commit_before>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package proto

import (
	"encoding/json"
	"net"
	"time"
)

type netConn struct {
	conn net.Conn
	enc  *json.Encoder
	dec  *json.Decoder
}

func newNetConn(conn net.Conn) *netConn {
	return &netConn{
		conn,
		json.NewEncoder(conn),
		json.NewDecoder(conn),
	}
}

func (c *netConn) Write(msg Message) error {
	if err := msg.IsValid(); err != nil {
		return err
	}
	c.conn.SetWriteDeadline(time.Now().Add(5 * time.Minute))
	return c.enc.Encode(msg)
}

func (c *netConn) KeepaliveLoop(ka time.Duration) error {
	for _ = range time.Tick(ka) {
		c.conn.SetWriteDeadline(time.Now().Add(5 * time.Minute))
		if _, err := c.conn.Write([]byte(" ")); err != nil {
			return err
		}
	}
	return nil
}

func (c *netConn) Read() (Message, error) {
	var msg Message
	c.conn.SetReadDeadline(time.Now().Add(time.Hour))
	if err := c.dec.Decode(&msg); err != nil {
		return msg, err
	}
	return msg, msg.IsValid()
}

func (c *netConn) Close() error {
	return c.conn.Close()
}
<commit_msg>Proto: Fix keepalive interval.<commit_after>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package proto

import (
	"encoding/json"
	"net"
	"time"
)

type netConn struct {
	conn net.Conn
	enc  *json.Encoder
	dec  *json.Decoder
}

func newNetConn(conn net.Conn) *netConn {
	return &netConn{
		conn,
		json.NewEncoder(conn),
		json.NewDecoder(conn),
	}
}

func (c *netConn) Write(msg Message) error {
	if err := msg.IsValid(); err != nil {
		return err
	}
	c.conn.SetWriteDeadline(time.Now().Add(5 * time.Minute))
	return c.enc.Encode(msg)
}

func (c *netConn) KeepaliveLoop(ka time.Duration) error {
	for {
		time.Sleep(ka)
		c.conn.SetWriteDeadline(time.Now().Add(3 * ka))
		if _, err := c.conn.Write([]byte(" ")); err != nil {
			return err
		}
	}
	return nil
}

func (c *netConn) Read() (Message, error) {
	var msg Message
	c.conn.SetReadDeadline(time.Now().Add(time.Hour))
	if err := c.dec.Decode(&msg); err != nil {
		return msg, err
	}
	return msg, msg.IsValid()
}

func (c *netConn) Close() error {
	return c.conn.Close()
}
<|endoftext|>


<commit_before>package parse

import (
	"encoding/binary"
	"fmt"
	//	"io"
	"os"
)

// STATUS wip

var (
	gctToLengthMap = map[byte]int64{
		0: 2 * 3,
		1: 4 * 3,
		2: 8 * 3,
		3: 16 * 3,
		4: 32 * 3,
		5: 64 * 3,
		6: 128 * 3,
		7: 256 * 3,
	}
)

// Section indicators.
const (
	sExtension       = 0x21
	sImageDescriptor = 0x2C
	sTrailer         = 0x3B
)

// Extensions.
const (
	eText           = 0x01 // Plain Text
	eGraphicControl = 0xF9 // Graphic Control
	eComment        = 0xFE // Comment
	eApplication    = 0xFF // Application
)

// misc
const (
	imgDescriptorLen = 10
)

func GIF(file *os.File) (*ParsedLayout, error) {

	if !isGIF(file) {
		return nil, nil
	}
	return parseGIF(file)
}

func isGIF(file *os.File) bool {

	file.Seek(0, os.SEEK_SET)

	var b [5]byte
	if err := binary.Read(file, binary.LittleEndian, &b); err != nil {
		return false
	}
	if b[0] != 'G' || b[1] != 'I' || b[2] != 'F' || b[3] != '8' {
		return false
	}
	if b[4] != '7' && b[4] != '9' {
		return false
	}
	return true
}

func parseGIF(file *os.File) (*ParsedLayout, error) {

	res := ParsedLayout{}

	res.Layout = append(res.Layout, gifHeader(file))
	res.Layout = append(res.Layout, gifLogicalDescriptor(file))

	// XXX hack... decodeBitfieldFromInfo should return 1 but returns 2 now for soem reason?!
	if res.decodeBitfieldFromInfo(file, "global color table flag") != 0 {
		sizeOfGCT := res.decodeBitfieldFromInfo(file, "size of global color table")
		if gctByteLen, ok := gctToLengthMap[byte(sizeOfGCT)]; ok {
			res.Layout = append(res.Layout, gifGlobalColorTable(file, gctByteLen))
		}
	}

	for {

		offset, _ := file.Seek(0, os.SEEK_CUR)

		var b byte
		if err := binary.Read(file, binary.LittleEndian, &b); err != nil {
			return nil, err
		}

		switch b {
		case sExtension:
			gfxExt, err := gifExtension(file, offset)
			if err != nil {
				return nil, err
			}
			res.Layout = append(res.Layout, *gfxExt)

		case sImageDescriptor:
			imgDescriptor := gifImageDescriptor(file, offset)
			if imgDescriptor != nil {
				res.Layout = append(res.Layout, *imgDescriptor)
			}
			if res.decodeBitfieldFromInfo(file, "local color table flag") == 1 {
				// XXX this is untested due to lack of sample with a local color table
				sizeOfLCT := res.decodeBitfieldFromInfo(file, "size of local color table")
				if lctByteLen, ok := gctToLengthMap[byte(sizeOfLCT)]; ok {
					localTbl := gifLocalColorTable(file, offset+imgDescriptorLen, lctByteLen)
					res.Layout = append(res.Layout, localTbl)
				}
			}

			imgData, err := gifImageData(file, offset+imgDescriptorLen)
			if err != nil {
				return nil, err
			}
			res.Layout = append(res.Layout, *imgData)

		case sTrailer:
			res.Layout = append(res.Layout, gifTrailer(file, offset))
			return &res, nil
		}
	}
}

func gifHeader(file *os.File) Layout {

	return Layout{
		Offset: 0,
		Length: 6,
		Info:   "header",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: 0, Length: 3, Info: "signature", Type: ASCII},
			Layout{Offset: 3, Length: 3, Info: "version", Type: ASCII},
		},
	}
}

func gifImageDescriptor(file *os.File, baseOffset int64) *Layout {

	res := Layout{
		Offset: baseOffset,
		Length: imgDescriptorLen,
		Info:   "image descriptor",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: baseOffset, Length: 1, Info: "image separator", Type: Uint8},
			Layout{Offset: baseOffset + 1, Length: 2, Info: "image left", Type: Uint16le},
			Layout{Offset: baseOffset + 3, Length: 2, Info: "image top", Type: Uint16le},
			Layout{Offset: baseOffset + 5, Length: 2, Info: "image width", Type: Uint16le},
			Layout{Offset: baseOffset + 7, Length: 2, Info: "image height", Type: Uint16le},

			Layout{Offset: baseOffset + 9, Length: 1, Info: "packed #3", Type: Uint8, Masks: []Mask{
				Mask{Low: 0, Length: 2, Info: "size of local color table"},
				Mask{Low: 3, Length: 2, Info: "reserved"},
				Mask{Low: 5, Length: 1, Info: "sort flag"},
				Mask{Low: 6, Length: 1, Info: "interlace flag"},
				Mask{Low: 7, Length: 1, Info: "local color table flag"},
			}},
		},
	}
	return &res
}

func gifGlobalColorTable(file *os.File, byteLen int64) Layout {

	baseOffset := int64(0x0d)

	childs := []Layout{}

	cnt := 0
	for i := int64(0); i < byteLen; i += 3 {
		cnt++
		childs = append(childs, Layout{Offset: baseOffset + i, Length: 3, Info: fmt.Sprintf("color %d", cnt), Type: RGB})
	}

	return Layout{
		Offset: baseOffset,
		Length: byteLen,
		Info:   "global color table",
		Type:   Group,
		Childs: childs,
	}
}

func gifLocalColorTable(file *os.File, baseOffset int64, byteLen int64) Layout {

	childs := []Layout{}

	cnt := 0
	for i := int64(0); i < byteLen; i += 3 {
		cnt++
		childs = append(childs, Layout{Offset: baseOffset + i, Length: 3, Info: fmt.Sprintf("color %d", cnt), Type: RGB})
	}

	return Layout{
		Offset: baseOffset,
		Length: byteLen,
		Info:   "local color table",
		Type:   Group,
		Childs: childs,
	}
}

func gifExtension(file *os.File, baseOffset int64) (*Layout, error) {

	var extType byte
	if err := binary.Read(file, binary.LittleEndian, &extType); err != nil {
		return nil, err
	}

	typeSpecific := []Layout{}
	typeInfo := ""

	size := int64(0)
	switch extType {
	case eText:
		size = 13
		typeInfo = "text"

	case eGraphicControl:
		size = 7
		typeInfo = "graphic control"
		typeSpecific = []Layout{
			Layout{Offset: baseOffset + 2, Length: 1, Info: "byte size", Type: Uint8},
			Layout{Offset: baseOffset + 3, Length: 1, Info: "packed #2", Type: Uint8},
			Layout{Offset: baseOffset + 4, Length: 2, Info: "delay time", Type: Uint16le},
			Layout{Offset: baseOffset + 6, Length: 1, Info: "transparent color index", Type: Uint8},
			Layout{Offset: baseOffset + 7, Length: 1, Info: "block terminator", Type: Uint8},
		}

	case eComment:
		// nothing to do but read the data.
		typeInfo = "comment"
		//size = 7 // XXX len
		fmt.Println(baseOffset)
		panic("comment len")

	case eApplication:
		typeInfo = "application"
		var lenByte byte
		if err := binary.Read(file, binary.LittleEndian, &lenByte); err != nil {
			return nil, err
		}

		size = 2 + int64(lenByte)

		typeSpecific = []Layout{
			Layout{Offset: baseOffset + 2, Length: 1, Info: "byte size", Type: Uint8},
			Layout{Offset: baseOffset + 3, Length: size - 2, Info: "data", Type: Uint8},
		}

		extData := readBytesFrom(file, baseOffset+3, size-2)

		if string(extData) == "NETSCAPE2.0" {
			// animated gif extension
			subBlocks, err := gifSubBlocks(file, baseOffset+3+11)
			if err != nil {
				return nil, err
			}
			typeSpecific = append(typeSpecific, subBlocks...)
			for _, b := range subBlocks {
				size += b.Length
			}
		}

	default:
		fmt.Printf("gif: unknown extension 0x%.2x", extType)
	}

	// skip past all data
	file.Seek(baseOffset+size+1, os.SEEK_SET)

	res := Layout{
		Offset: baseOffset,
		Length: size + 1,
		Info:   "extension",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: baseOffset, Length: 1, Info: "block id (extension)", Type: Uint8},
			Layout{Offset: baseOffset + 1, Length: 1, Info: typeInfo, Type: Uint8},
		},
	}

	res.Childs = append(res.Childs, typeSpecific...)

	return &res, nil
}

func gifReadBlock(file *os.File) (int, error) {

	var b byte
	if err := binary.Read(file, binary.LittleEndian, &b); err != nil {
		return 0, err
	}

	// return io.ReadFull(file, d.tmp[:n])
	return 0, nil
}

func gifLogicalDescriptor(file *os.File) Layout {
	base := int64(0x06)
	return Layout{
		Offset: base,
		Length: 7,
		Info:   "logical screen descriptor",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: base, Length: 2, Info: "width", Type: Uint16le},
			Layout{Offset: base + 2, Length: 2, Info: "height", Type: Uint16le},
			Layout{Offset: base + 4, Length: 1, Info: "packed", Type: Uint8, Masks: []Mask{
				Mask{Low: 0, Length: 3, Info: "size of global color table"},
				Mask{Low: 3, Length: 1, Info: "color table sort flag"},
				Mask{Low: 4, Length: 3, Info: "color resolution"},
				Mask{Low: 7, Length: 1, Info: "global color table flag"},
			}},
			Layout{Offset: base + 5, Length: 1, Info: "background color", Type: Uint8},
			Layout{Offset: base + 6, Length: 1, Info: "aspect ratio", Type: Uint8},
		},
	}
}

func gifImageData(file *os.File, baseOffset int64) (*Layout, error) {

	// XXX need to decode first bytes of lzw stream to decode stream length

	file.Seek(baseOffset+1, os.SEEK_SET)

	length := int64(1)

	childs := []Layout{}
	childs = append(childs, Layout{Offset: baseOffset, Length: 1, Info: "lzw code size", Type: Uint8})

	lzwSubBlocks, err := gifSubBlocks(file, baseOffset+1)
	if err != nil {
		return nil, err
	}
	childs = append(childs, lzwSubBlocks...)
	for _, b := range lzwSubBlocks {
		length += b.Length
	}

	res := Layout{
		Offset: baseOffset,
		Length: length,
		Info:   "image data",
		Type:   Group,
		Childs: childs,
	}

	return &res, nil
}

func gifSubBlocks(file *os.File, baseOffset int64) ([]Layout, error) {

	length := int64(0)
	childs := []Layout{}
	file.Seek(baseOffset, os.SEEK_SET)

	for {
		var follows byte // number of bytes follows
		if err := binary.Read(file, binary.LittleEndian, &follows); err != nil {
			return nil, err
		}

		childs = append(childs, Layout{Offset: baseOffset + length, Length: 1, Info: "block length", Type: Uint8})
		length += 1

		if follows == 0 {
			break
		}

		childs = append(childs, Layout{Offset: baseOffset + length, Length: int64(follows), Info: "block", Type: Uint8})

		length += int64(follows)
		file.Seek(baseOffset+length, os.SEEK_SET)
	}
	return childs, nil
}

func gifTrailer(file *os.File, baseOffset int64) Layout {

	res := Layout{
		Offset: baseOffset,
		Length: 1,
		Info:   "trailer",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: baseOffset, Length: 1, Info: "trailer", Type: Uint8},
		},
	}
	return res
}
<commit_msg>gif: break loops, handle EOF<commit_after>package parse

import (
	"encoding/binary"
	"fmt"
	"io"
	"os"
)

// STATUS gif89 most files ok
// XXX samples/gif/gif_89a_002_anim.gif  lzw block decode seems broken, start offset wrong?

var (
	gctToLengthMap = map[byte]int64{
		0: 2 * 3,
		1: 4 * 3,
		2: 8 * 3,
		3: 16 * 3,
		4: 32 * 3,
		5: 64 * 3,
		6: 128 * 3,
		7: 256 * 3,
	}
)

// Section indicators.
const (
	sExtension       = 0x21
	sImageDescriptor = 0x2C
	sTrailer         = 0x3B
)

// Extensions.
const (
	eText           = 0x01 // Plain Text
	eGraphicControl = 0xF9 // Graphic Control
	eComment        = 0xFE // Comment
	eApplication    = 0xFF // Application
)

// misc
const (
	imgDescriptorLen = 10
)

func GIF(file *os.File) (*ParsedLayout, error) {

	if !isGIF(file) {
		return nil, nil
	}
	return parseGIF(file)
}

func isGIF(file *os.File) bool {

	file.Seek(0, os.SEEK_SET)

	var b [5]byte
	if err := binary.Read(file, binary.LittleEndian, &b); err != nil {
		return false
	}
	if b[0] != 'G' || b[1] != 'I' || b[2] != 'F' || b[3] != '8' {
		return false
	}
	if b[4] != '7' && b[4] != '9' {
		return false
	}
	return true
}

func parseGIF(file *os.File) (*ParsedLayout, error) {

	res := ParsedLayout{}

	res.Layout = append(res.Layout, gifHeader(file))
	res.Layout = append(res.Layout, gifLogicalDescriptor(file))

	// XXX hack... decodeBitfieldFromInfo should return 1 but returns 2 now for soem reason?!
	if res.decodeBitfieldFromInfo(file, "global color table flag") != 0 {
		sizeOfGCT := res.decodeBitfieldFromInfo(file, "size of global color table")
		if gctByteLen, ok := gctToLengthMap[byte(sizeOfGCT)]; ok {
			res.Layout = append(res.Layout, gifGlobalColorTable(file, gctByteLen))
		}
	}

	for {

		offset, _ := file.Seek(0, os.SEEK_CUR)

		var b byte
		if err := binary.Read(file, binary.LittleEndian, &b); err != nil {
			if err == io.EOF {
				fmt.Println("XXX did not find gif trailer!")
				return &res, nil
			}
			return nil, err
		}

		switch b {
		case sExtension:
			gfxExt, err := gifExtension(file, offset)
			if err != nil {
				return nil, err
			}
			res.Layout = append(res.Layout, *gfxExt)

		case sImageDescriptor:
			imgDescriptor := gifImageDescriptor(file, offset)
			if imgDescriptor != nil {
				res.Layout = append(res.Layout, *imgDescriptor)
			}
			if res.decodeBitfieldFromInfo(file, "local color table flag") == 1 {
				// XXX this is untested due to lack of sample with a local color table
				sizeOfLCT := res.decodeBitfieldFromInfo(file, "size of local color table")
				if lctByteLen, ok := gctToLengthMap[byte(sizeOfLCT)]; ok {
					localTbl := gifLocalColorTable(file, offset+imgDescriptorLen, lctByteLen)
					res.Layout = append(res.Layout, localTbl)
				}
			}

			imgData, err := gifImageData(file, offset+imgDescriptorLen)
			if err != nil {
				return nil, err
			}
			res.Layout = append(res.Layout, *imgData)

		case sTrailer:
			res.Layout = append(res.Layout, gifTrailer(file, offset))
			return &res, nil
		}
	}
}

func gifHeader(file *os.File) Layout {

	return Layout{
		Offset: 0,
		Length: 6,
		Info:   "header",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: 0, Length: 3, Info: "signature", Type: ASCII},
			Layout{Offset: 3, Length: 3, Info: "version", Type: ASCII},
		},
	}
}

func gifImageDescriptor(file *os.File, baseOffset int64) *Layout {

	res := Layout{
		Offset: baseOffset,
		Length: imgDescriptorLen,
		Info:   "image descriptor",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: baseOffset, Length: 1, Info: "image separator", Type: Uint8},
			Layout{Offset: baseOffset + 1, Length: 2, Info: "image left", Type: Uint16le},
			Layout{Offset: baseOffset + 3, Length: 2, Info: "image top", Type: Uint16le},
			Layout{Offset: baseOffset + 5, Length: 2, Info: "image width", Type: Uint16le},
			Layout{Offset: baseOffset + 7, Length: 2, Info: "image height", Type: Uint16le},

			Layout{Offset: baseOffset + 9, Length: 1, Info: "packed #3", Type: Uint8, Masks: []Mask{
				Mask{Low: 0, Length: 2, Info: "size of local color table"},
				Mask{Low: 3, Length: 2, Info: "reserved"},
				Mask{Low: 5, Length: 1, Info: "sort flag"},
				Mask{Low: 6, Length: 1, Info: "interlace flag"},
				Mask{Low: 7, Length: 1, Info: "local color table flag"},
			}},
		},
	}
	return &res
}

func gifGlobalColorTable(file *os.File, byteLen int64) Layout {

	baseOffset := int64(0x0d)

	childs := []Layout{}

	cnt := 0
	for i := int64(0); i < byteLen; i += 3 {
		cnt++
		childs = append(childs, Layout{Offset: baseOffset + i, Length: 3, Info: fmt.Sprintf("color %d", cnt), Type: RGB})
	}

	return Layout{
		Offset: baseOffset,
		Length: byteLen,
		Info:   "global color table",
		Type:   Group,
		Childs: childs,
	}
}

func gifLocalColorTable(file *os.File, baseOffset int64, byteLen int64) Layout {

	childs := []Layout{}

	cnt := 0
	for i := int64(0); i < byteLen; i += 3 {
		cnt++
		childs = append(childs, Layout{Offset: baseOffset + i, Length: 3, Info: fmt.Sprintf("color %d", cnt), Type: RGB})
	}

	return Layout{
		Offset: baseOffset,
		Length: byteLen,
		Info:   "local color table",
		Type:   Group,
		Childs: childs,
	}
}

func gifExtension(file *os.File, baseOffset int64) (*Layout, error) {

	var extType byte
	if err := binary.Read(file, binary.LittleEndian, &extType); err != nil {
		return nil, err
	}

	typeSpecific := []Layout{}
	typeInfo := ""

	size := int64(0)
	switch extType {
	case eText:
		size = 13
		typeInfo = "text"

	case eGraphicControl:
		size = 7
		typeInfo = "graphic control"
		typeSpecific = []Layout{
			Layout{Offset: baseOffset + 2, Length: 1, Info: "byte size", Type: Uint8},
			Layout{Offset: baseOffset + 3, Length: 1, Info: "packed #2", Type: Uint8},
			Layout{Offset: baseOffset + 4, Length: 2, Info: "delay time", Type: Uint16le},
			Layout{Offset: baseOffset + 6, Length: 1, Info: "transparent color index", Type: Uint8},
			Layout{Offset: baseOffset + 7, Length: 1, Info: "block terminator", Type: Uint8},
		}

	case eComment:
		// nothing to do but read the data.
		typeInfo = "comment"

		var lenByte byte
		if err := binary.Read(file, binary.LittleEndian, &lenByte); err != nil {
			return nil, err
		}

		size = 2 + int64(lenByte) + 1 // including terminating 0

		typeSpecific = []Layout{
			Layout{Offset: baseOffset + 2, Length: 1, Info: "byte size", Type: Uint8},
			Layout{Offset: baseOffset + 3, Length: size - 2, Info: "data", Type: ASCIIZ},
		}

	case eApplication:
		typeInfo = "application"
		var lenByte byte
		if err := binary.Read(file, binary.LittleEndian, &lenByte); err != nil {
			return nil, err
		}

		size = 2 + int64(lenByte)

		typeSpecific = []Layout{
			Layout{Offset: baseOffset + 2, Length: 1, Info: "byte size", Type: Uint8},
			Layout{Offset: baseOffset + 3, Length: size - 2, Info: "data", Type: Uint8},
		}

		extData := readBytesFrom(file, baseOffset+3, size-2)

		if string(extData) == "NETSCAPE2.0" {
			// animated gif extension
			subBlocks, err := gifSubBlocks(file, baseOffset+3+11)
			if err != nil {
				return nil, err
			}
			typeSpecific = append(typeSpecific, subBlocks...)
			for _, b := range subBlocks {
				size += b.Length
			}
		}

	default:
		fmt.Printf("gif: unknown extension 0x%.2x", extType)
	}

	// skip past all data
	file.Seek(baseOffset+size+1, os.SEEK_SET)

	res := Layout{
		Offset: baseOffset,
		Length: size + 1,
		Info:   "extension",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: baseOffset, Length: 1, Info: "block id (extension)", Type: Uint8},
			Layout{Offset: baseOffset + 1, Length: 1, Info: typeInfo, Type: Uint8},
		},
	}

	res.Childs = append(res.Childs, typeSpecific...)

	return &res, nil
}

func gifReadBlock(file *os.File) (int, error) {

	var b byte
	if err := binary.Read(file, binary.LittleEndian, &b); err != nil {
		return 0, err
	}

	// return io.ReadFull(file, d.tmp[:n])
	return 0, nil
}

func gifLogicalDescriptor(file *os.File) Layout {
	base := int64(0x06)
	return Layout{
		Offset: base,
		Length: 7,
		Info:   "logical screen descriptor",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: base, Length: 2, Info: "width", Type: Uint16le},
			Layout{Offset: base + 2, Length: 2, Info: "height", Type: Uint16le},
			Layout{Offset: base + 4, Length: 1, Info: "packed", Type: Uint8, Masks: []Mask{
				Mask{Low: 0, Length: 3, Info: "size of global color table"},
				Mask{Low: 3, Length: 1, Info: "color table sort flag"},
				Mask{Low: 4, Length: 3, Info: "color resolution"},
				Mask{Low: 7, Length: 1, Info: "global color table flag"},
			}},
			Layout{Offset: base + 5, Length: 1, Info: "background color", Type: Uint8},
			Layout{Offset: base + 6, Length: 1, Info: "aspect ratio", Type: Uint8},
		},
	}
}

func gifImageData(file *os.File, baseOffset int64) (*Layout, error) {

	// XXX need to decode first bytes of lzw stream to decode stream length

	file.Seek(baseOffset+1, os.SEEK_SET)

	length := int64(1)

	childs := []Layout{}
	childs = append(childs, Layout{Offset: baseOffset, Length: 1, Info: "lzw code size", Type: Uint8})

	lzwSubBlocks, err := gifSubBlocks(file, baseOffset+1)
	if err != nil {
		return nil, err
	}
	childs = append(childs, lzwSubBlocks...)
	for _, b := range lzwSubBlocks {
		length += b.Length
	}

	res := Layout{
		Offset: baseOffset,
		Length: length,
		Info:   "image data",
		Type:   Group,
		Childs: childs,
	}

	return &res, nil
}

func gifSubBlocks(file *os.File, baseOffset int64) ([]Layout, error) {

	length := int64(0)
	childs := []Layout{}
	file.Seek(baseOffset, os.SEEK_SET)

	for {
		var follows byte // number of bytes follows
		if err := binary.Read(file, binary.LittleEndian, &follows); err != nil {
			if err == io.EOF {
				fmt.Println("XXX sub blocks unexpected EOF")
				break
			}
			return nil, err
		}

		childs = append(childs, Layout{Offset: baseOffset + length, Length: 1, Info: "block length", Type: Uint8})
		length += 1

		if follows == 0 {
			break
		}

		childs = append(childs, Layout{Offset: baseOffset + length, Length: int64(follows), Info: "block", Type: Uint8})

		length += int64(follows)
		file.Seek(baseOffset+length, os.SEEK_SET)
	}
	return childs, nil
}

func gifTrailer(file *os.File, baseOffset int64) Layout {

	res := Layout{
		Offset: baseOffset,
		Length: 1,
		Info:   "trailer",
		Type:   Group,
		Childs: []Layout{
			Layout{Offset: baseOffset, Length: 1, Info: "trailer", Type: Uint8},
		},
	}
	return res
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package controllerhost

import (
	"errors"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"

	"github.com/uber-common/bark"
	"github.com/uber/cherami-server/common"
	"github.com/uber/cherami-server/common/metrics"
)

var errRetryable = errors.New("Retry later")

type (
	// Event represents the API interface that
	// all events must obey. The API is defined
	// to follow the try-catch-finally pattern.
	// When a new event arrives, the Handle()
	// method will be called to handle it. If
	// Handle returns a retryable error, it will
	// retried one or more times. After the event
	// is processed (success or failure), the Done
	// method will be called.
	Event interface {
		// Handle is the handler for the event.
		Handle(context *Context) error
		// Done is a callback to indicate that the
		// event processing is done. This is
		// an opportunity to cleanup any state that
		// the event had. The err arg is set to the
		// error value from the last attempt. Nil
		// indicates the handler finished successfully.
		Done(context *Context, err error)
	}

	// RetryableEventExecutor is an executor for
	// executing retryable event handlers. The
	// executor has in-bult retry/backoff logic
	// for retryable failures
	RetryableEventExecutor interface {
		// Submit takes a retryable event for
		// executing in the background. Returns
		// true on success, false otherwise
		Submit(event Event) bool
		// Stop stops the executor
		Stop()
	}

	// EventPipeline represents the
	// execution pipeline that
	// handles events in the background
	EventPipeline interface {
		common.Daemon
		// Add adds an event to the event pipeline
		// returns true on success
		// returns false when the event queue is full
		Add(event Event) bool
		// GetRetryableEventExecutor returns the executor for
		// running retryable events
		GetRetryableEventExecutor() RetryableEventExecutor
	}

	eventPipelineImpl struct {
		nWorkers      int // number of worker routines that handle events
		context       *Context
		eventQueue    chan Event // workers handle events from this queue
		retryExecutor RetryableEventExecutor
		started       int32
		log           bark.Logger
		shutdownC     chan struct{}
		startWG       sync.WaitGroup
		shutdownWG    sync.WaitGroup
	}
)

const (
	eventQueueSize = 2048
	// maxRetryWorkers determines the max number of event handlers
	// that can be retried in the background at given point of time
	maxRetryWorkers = 8192
)

// NewEventPipeline creates a new instance of EventPipeline
// The returned pipeline will have nWorkers number of concurrent
// worker routines that handle events in the background
func NewEventPipeline(context *Context, nWorkers int) EventPipeline {
	return &eventPipelineImpl{
		nWorkers:      nWorkers,
		context:       context,
		eventQueue:    make(chan Event, eventQueueSize),
		retryExecutor: NewRetryableEventExecutor(context, maxRetryWorkers),
		shutdownC:     make(chan struct{}),
		log:           context.log.WithField(common.TagModule, `EventPipeline`),
	}
}

func (ep *eventPipelineImpl) Start() {
	if !ep.setStarted() {
		return
	}
	// Spin up nWorkers routines in the
	// background and wait for them to
	// all start before returning from
	// this method
	for i := 0; i < ep.nWorkers; i++ {
		ep.shutdownWG.Add(1)
		ep.startWG.Add(1)
		go ep.eventLoop()
	}
	if !common.AwaitWaitGroup(&ep.startWG, time.Second) {
		ep.log.Fatal("Timed out waiting for workers to start")
	}

	ep.log.Info("Event pipeline started")
}

func (ep *eventPipelineImpl) Stop() {
	close(ep.shutdownC)
	ep.retryExecutor.Stop()
	if !common.AwaitWaitGroup(&ep.shutdownWG, time.Second) {
		ep.log.Error("Timed out waiting for event workers to shutdown")
		return
	}

	ep.log.Info("Event pipeline stopped")
}

// GetRetryableEventExecutor returns the retryable event executor
func (ep *eventPipelineImpl) GetRetryableEventExecutor() RetryableEventExecutor {
	return ep.retryExecutor
}

func (ep *eventPipelineImpl) Add(event Event) bool {
	if !ep.isStarted() {
		ep.log.Fatal("Attempt to add event before starting the event pipeline")
	}
	select {
	case ep.eventQueue <- event:
		return true
	default:
		ep.context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerEventsDropped)
		ep.log.WithField(common.TagEvent, event).Warn("EventQueue full, dropping event")
		return false
	}
}

func (ep *eventPipelineImpl) isStarted() bool {
	return atomic.LoadInt32(&ep.started) == 1
}

func (ep *eventPipelineImpl) setStarted() bool {
	return atomic.CompareAndSwapInt32(&ep.started, 0, 1)
}

// eventLoop is the main routine that dispatches
// events to handlers. Multiple event loops run
// simultaneously, picking up work from the event
// queue.
func (ep *eventPipelineImpl) eventLoop() {
	ep.startWG.Done()
	for {
		select {
		case event := <-ep.eventQueue:
			err := event.Handle(ep.context)
			if err == errRetryable {
				if ep.retryExecutor.Submit(event) {
					break
				}
			}
			event.Done(ep.context, err)
		case <-ep.shutdownC:
			ep.shutdownWG.Done()
			return
		}
	}
}

// retryableEventExecutor is an executor that runs
// retryable event handlers. The executor automatically
// retries processing of events on retryable
// failures.
type retryableEventExecutor struct {
	sync.RWMutex
	tokens     int // number of concurrent workers
	stopped    int32
	shutdownC  chan struct{}
	shutdownWG sync.WaitGroup
	context    *Context
}

const (
	eventHandlerRetryMaxAttempts = 20
	eventHandlerRetryInterval    = 4 * time.Minute
)

// NewRetryableEventExecutor returns a new instance of RetryableEventExecutor
// This executor spins up a separate go routine for processing every event
// that's submitted. maxWorkers is an upper bound on the number of events
// that can be processed simulatenously. There are a few reasons for having
// a separate executor for retries as opposed to using the event pipeline:
//
//   * Retries can span upto an hour
//   * Retry workers are mostly idle, sleeping, we can have lots of them
//     in the background without consuming any resources
//   * We want the ability to spin up go routines when there is lots
//     retries needed, but in steady state, we dont need these many
//     go routines
//   * Retries should not impact or delay the processing of other
//     events
func NewRetryableEventExecutor(context *Context, maxWorkers int) RetryableEventExecutor {
	return &retryableEventExecutor{
		tokens:    maxWorkers,
		context:   context,
		shutdownC: make(chan struct{}),
	}
}

// Submit accepts an retryable event and runs it in a go routine
// returns true if the task was accepted, false otherwise
func (executor *retryableEventExecutor) Submit(event Event) bool {
	if !executor.acquireToken() {
		executor.context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerErrNoRetryWorkers)
		return false
	}
	go executor.execute(event)
	return true
}

// Stop stops all the tasks started by this executor
func (executor *retryableEventExecutor) Stop() {
	executor.setStopped()
	close(executor.shutdownC)
	common.AwaitWaitGroup(&executor.shutdownWG, time.Second)
}

func (executor *retryableEventExecutor) execute(event Event) {

	defer executor.shutdownWG.Done()
	defer executor.releaseToken()

	context := executor.context

	if !executor.sleep(computeRetryInterval()) {
		// we are shutting down, exit asap
		event.Done(executor.context, errRetryable)
		return
	}

	var err error

	for i := 0; i < eventHandlerRetryMaxAttempts; i++ {

		context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerRetries)

		err = event.Handle(executor.context)
		if err == nil {
			break
		}
		if err == errRetryable {
			if !executor.sleep(computeRetryInterval()) {
				break
			}
		}
	}

	if err != nil {
		context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerRetriesExceeded)
	}

	event.Done(context, err)
}

func computeRetryInterval() time.Duration {
	jitter := int64(rand.Intn(60))
	return eventHandlerRetryInterval + time.Duration(int64(time.Second)*jitter)
}

func (executor *retryableEventExecutor) sleep(duration time.Duration) bool {
	select {
	case <-time.After(duration):
		return true
	case <-executor.shutdownC:
		return false
	}
}

func (executor *retryableEventExecutor) acquireToken() bool {
	executor.Lock()
	defer executor.Unlock()
	if executor.stopped == 1 || executor.tokens == 0 {
		return false
	}
	executor.tokens--
	executor.shutdownWG.Add(1)
	return true
}

func (executor *retryableEventExecutor) releaseToken() {
	executor.Lock()
	defer executor.Unlock()
	executor.tokens++
}

func (executor *retryableEventExecutor) setStopped() {
	executor.Lock()
	defer executor.Unlock()
	executor.stopped = 1
}
<commit_msg>Fix retryableEventHandler logic (#324)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package controllerhost

import (
	"errors"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"

	"github.com/uber-common/bark"
	"github.com/uber/cherami-server/common"
	"github.com/uber/cherami-server/common/metrics"
)

var errRetryable = errors.New("Retry later")

type (
	// Event represents the API interface that
	// all events must obey. The API is defined
	// to follow the try-catch-finally pattern.
	// When a new event arrives, the Handle()
	// method will be called to handle it. If
	// Handle returns a retryable error, it will
	// retried one or more times. After the event
	// is processed (success or failure), the Done
	// method will be called.
	Event interface {
		// Handle is the handler for the event.
		Handle(context *Context) error
		// Done is a callback to indicate that the
		// event processing is done. This is
		// an opportunity to cleanup any state that
		// the event had. The err arg is set to the
		// error value from the last attempt. Nil
		// indicates the handler finished successfully.
		Done(context *Context, err error)
	}

	// RetryableEventExecutor is an executor for
	// executing retryable event handlers. The
	// executor has in-bult retry/backoff logic
	// for retryable failures
	RetryableEventExecutor interface {
		// Submit takes a retryable event for
		// executing in the background. Returns
		// true on success, false otherwise
		Submit(event Event) bool
		// Stop stops the executor
		Stop()
	}

	// EventPipeline represents the
	// execution pipeline that
	// handles events in the background
	EventPipeline interface {
		common.Daemon
		// Add adds an event to the event pipeline
		// returns true on success
		// returns false when the event queue is full
		Add(event Event) bool
		// GetRetryableEventExecutor returns the executor for
		// running retryable events
		GetRetryableEventExecutor() RetryableEventExecutor
	}

	eventPipelineImpl struct {
		nWorkers      int // number of worker routines that handle events
		context       *Context
		eventQueue    chan Event // workers handle events from this queue
		retryExecutor RetryableEventExecutor
		started       int32
		log           bark.Logger
		shutdownC     chan struct{}
		startWG       sync.WaitGroup
		shutdownWG    sync.WaitGroup
	}
)

const (
	eventQueueSize = 2048
	// maxRetryWorkers determines the max number of event handlers
	// that can be retried in the background at given point of time
	maxRetryWorkers = 8192
)

// NewEventPipeline creates a new instance of EventPipeline
// The returned pipeline will have nWorkers number of concurrent
// worker routines that handle events in the background
func NewEventPipeline(context *Context, nWorkers int) EventPipeline {
	return &eventPipelineImpl{
		nWorkers:      nWorkers,
		context:       context,
		eventQueue:    make(chan Event, eventQueueSize),
		retryExecutor: NewRetryableEventExecutor(context, maxRetryWorkers),
		shutdownC:     make(chan struct{}),
		log:           context.log.WithField(common.TagModule, `EventPipeline`),
	}
}

func (ep *eventPipelineImpl) Start() {
	if !ep.setStarted() {
		return
	}
	// Spin up nWorkers routines in the
	// background and wait for them to
	// all start before returning from
	// this method
	for i := 0; i < ep.nWorkers; i++ {
		ep.shutdownWG.Add(1)
		ep.startWG.Add(1)
		go ep.eventLoop()
	}
	if !common.AwaitWaitGroup(&ep.startWG, time.Second) {
		ep.log.Fatal("Timed out waiting for workers to start")
	}

	ep.log.Info("Event pipeline started")
}

func (ep *eventPipelineImpl) Stop() {
	close(ep.shutdownC)
	ep.retryExecutor.Stop()
	if !common.AwaitWaitGroup(&ep.shutdownWG, time.Second) {
		ep.log.Error("Timed out waiting for event workers to shutdown")
		return
	}

	ep.log.Info("Event pipeline stopped")
}

// GetRetryableEventExecutor returns the retryable event executor
func (ep *eventPipelineImpl) GetRetryableEventExecutor() RetryableEventExecutor {
	return ep.retryExecutor
}

func (ep *eventPipelineImpl) Add(event Event) bool {
	if !ep.isStarted() {
		ep.log.Fatal("Attempt to add event before starting the event pipeline")
	}
	select {
	case ep.eventQueue <- event:
		return true
	default:
		ep.context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerEventsDropped)
		ep.log.WithField(common.TagEvent, event).Warn("EventQueue full, dropping event")
		return false
	}
}

func (ep *eventPipelineImpl) isStarted() bool {
	return atomic.LoadInt32(&ep.started) == 1
}

func (ep *eventPipelineImpl) setStarted() bool {
	return atomic.CompareAndSwapInt32(&ep.started, 0, 1)
}

// eventLoop is the main routine that dispatches
// events to handlers. Multiple event loops run
// simultaneously, picking up work from the event
// queue.
func (ep *eventPipelineImpl) eventLoop() {
	ep.startWG.Done()
	for {
		select {
		case event := <-ep.eventQueue:
			err := event.Handle(ep.context)
			if err == errRetryable {
				if ep.retryExecutor.Submit(event) {
					break
				}
			}
			event.Done(ep.context, err)
		case <-ep.shutdownC:
			ep.shutdownWG.Done()
			return
		}
	}
}

// retryableEventExecutor is an executor that runs
// retryable event handlers. The executor automatically
// retries processing of events on retryable
// failures.
type retryableEventExecutor struct {
	sync.RWMutex
	tokens     int // number of concurrent workers
	stopped    int32
	shutdownC  chan struct{}
	shutdownWG sync.WaitGroup
	context    *Context
}

const (
	eventHandlerRetryMaxAttempts = 20
	eventHandlerRetryInterval    = 4 * time.Minute
)

// NewRetryableEventExecutor returns a new instance of RetryableEventExecutor
// This executor spins up a separate go routine for processing every event
// that's submitted. maxWorkers is an upper bound on the number of events
// that can be processed simulatenously. There are a few reasons for having
// a separate executor for retries as opposed to using the event pipeline:
//
//   * Retries can span upto an hour
//   * Retry workers are mostly idle, sleeping, we can have lots of them
//     in the background without consuming any resources
//   * We want the ability to spin up go routines when there is lots
//     retries needed, but in steady state, we dont need these many
//     go routines
//   * Retries should not impact or delay the processing of other
//     events
func NewRetryableEventExecutor(context *Context, maxWorkers int) RetryableEventExecutor {
	return &retryableEventExecutor{
		tokens:    maxWorkers,
		context:   context,
		shutdownC: make(chan struct{}),
	}
}

// Submit accepts an retryable event and runs it in a go routine
// returns true if the task was accepted, false otherwise
func (executor *retryableEventExecutor) Submit(event Event) bool {
	if !executor.acquireToken() {
		executor.context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerErrNoRetryWorkers)
		return false
	}
	go executor.execute(event)
	return true
}

// Stop stops all the tasks started by this executor
func (executor *retryableEventExecutor) Stop() {
	executor.setStopped()
	close(executor.shutdownC)
	common.AwaitWaitGroup(&executor.shutdownWG, time.Second)
}

func (executor *retryableEventExecutor) execute(event Event) {

	defer executor.shutdownWG.Done()
	defer executor.releaseToken()

	context := executor.context

	if !executor.sleep(computeRetryInterval()) {
		// we are shutting down, exit asap
		event.Done(executor.context, errRetryable)
		return
	}

	var err error

	for i := 0; i < eventHandlerRetryMaxAttempts; i++ {

		context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerRetries)

		err = event.Handle(executor.context)
		if err != errRetryable || !executor.sleep(computeRetryInterval()) {
			// break if non retryable event, or retryable, but cannot sleep due to shut down
			break
		}
	}

	if err != nil {
		context.m3Client.IncCounter(metrics.EventPipelineScope, metrics.ControllerRetriesExceeded)
	}

	event.Done(context, err)
}

func computeRetryInterval() time.Duration {
	jitter := int64(rand.Intn(60))
	return eventHandlerRetryInterval + time.Duration(int64(time.Second)*jitter)
}

func (executor *retryableEventExecutor) sleep(duration time.Duration) bool {
	select {
	case <-time.After(duration):
		return true
	case <-executor.shutdownC:
		return false
	}
}

func (executor *retryableEventExecutor) acquireToken() bool {
	executor.Lock()
	defer executor.Unlock()
	if executor.stopped == 1 || executor.tokens == 0 {
		return false
	}
	executor.tokens--
	executor.shutdownWG.Add(1)
	return true
}

func (executor *retryableEventExecutor) releaseToken() {
	executor.Lock()
	defer executor.Unlock()
	executor.tokens++
}

func (executor *retryableEventExecutor) setStopped() {
	executor.Lock()
	defer executor.Unlock()
	executor.stopped = 1
}
<|endoftext|>


<commit_before>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package scopegraph

import (
	"fmt"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/compilergraph"
	"github.com/serulian/compiler/graphs/scopegraph/proto"
	"github.com/serulian/compiler/graphs/srg"
	"github.com/serulian/compiler/graphs/typegraph"
	"github.com/serulian/compiler/sourceshape"
)

var _ = fmt.Printf

type namedScopeInfo struct {
	srgInfo  srg.SRGNamedScope        // The named scope from the SRG.
	typeInfo typegraph.TGTypeOrMember // The type or member from the type graph.
	sb       *scopeBuilder            // The parent scope builder.
}

// lookupNamedScope looks up the given name at the given node's context, returning the referenced scope for
// the named item, if any. For example, giving the name of a parameter (name) under a function's body (node),
// will return information referencing that parameter, its type, etc.
func (sb *scopeBuilder) lookupNamedScope(name string, node compilergraph.GraphNode) (namedScopeInfo, error) {
	srgInfo, found := sb.sg.srg.FindNameInScope(name, node)
	if !found {
		// Try resolving the name as a global type alias.
		aliasedType, typeFound := sb.sg.tdg.LookupGlobalAliasedType(name)
		if typeFound {
			return namedScopeInfo{srg.SRGNamedScope{}, aliasedType, sb}, nil
		}

		return namedScopeInfo{}, fmt.Errorf("The name '%v' could not be found in this context", name)
	}

	return sb.processSRGNameOrInfo(srgInfo)
}

// processSRGNameOrInfo handles an SRGScopeOrImport, either translating an SRGNamedScope into a namedScopeInfo
// or performing the actual lookup under the typegraph for non-SRG names.
func (sb *scopeBuilder) processSRGNameOrInfo(srgInfo srg.SRGScopeOrImport) (namedScopeInfo, error) {
	// If the SRG info refers to an SRG named scope, build the info for it.
	if srgInfo.IsNamedScope() {
		return sb.buildNamedScopeForSRGInfo(srgInfo.AsNamedScope()), nil
	}

	// Otherwise, the SRG info refers to a member of an external package, which we need to resolve
	// under the type graph.
	packageImportInfo := srgInfo.AsPackageImport()
	typeOrMember, found := sb.sg.tdg.ResolveTypeOrMemberUnderPackage(packageImportInfo.ImportedName(), packageImportInfo.Package())
	if !found {
		return namedScopeInfo{}, fmt.Errorf("Could not find type or member '%v' under package %v", packageImportInfo.ImportedName(), packageImportInfo.Package())
	}

	return namedScopeInfo{srg.SRGNamedScope{}, typeOrMember, sb}, nil
}

// buildNamedScopeForSRGInfo builds a namedScopeInfo for the given SRG scope info.
func (sb *scopeBuilder) buildNamedScopeForSRGInfo(srgInfo srg.SRGNamedScope) namedScopeInfo {
	switch srgInfo.ScopeKind() {
	case srg.NamedScopeType:
		typeInfo, found := sb.sg.tdg.GetTypeForSourceNode(srgInfo.GraphNode)
		if !found {
			panic("Missing type info for SRG node")
		}

		return namedScopeInfo{srgInfo, typeInfo, sb}

	case srg.NamedScopeMember:
		memberInfo, found := sb.sg.tdg.GetMemberForSourceNode(srgInfo.GraphNode)
		if !found {
			panic("Missing member info for SRG node")
		}

		return namedScopeInfo{srgInfo, memberInfo, sb}

	case srg.NamedScopeImport:
		fallthrough

	case srg.NamedScopeParameter:
		fallthrough

	case srg.NamedScopeValue:
		fallthrough

	case srg.NamedScopeVariable:
		return namedScopeInfo{srgInfo, nil, sb}

	default:
		panic(fmt.Sprintf("Unknown named scope type: %v", srgInfo.ScopeKind()))
	}
}

// getNamedScopeForMember returns namedScopeInfo for the given type member.
func (sb *scopeBuilder) getNamedScopeForMember(member typegraph.TGMember) namedScopeInfo {
	return namedScopeInfo{srg.SRGNamedScope{}, member, sb}
}

// getNamedScopeForScope returns namedScopeInfo for the given name, if it references a node by name.
func (sb *scopeBuilder) getNamedScopeForScope(scope *proto.ScopeInfo) (namedScopeInfo, bool) {
	if scope.GetNamedReference() == nil {
		return namedScopeInfo{}, false
	}

	namedReference := scope.GetNamedReference()
	nodeId := compilergraph.GraphNodeId(namedReference.GetReferencedNode())

	if namedReference.GetIsSRGNode() {
		referencedNode := sb.sg.srg.GetNamedScope(nodeId)
		return namedScopeInfo{referencedNode, nil, sb}, true
	} else {
		referencedNode := sb.sg.tdg.GetTypeOrMember(nodeId)
		return namedScopeInfo{srg.SRGNamedScope{}, referencedNode, sb}, true
	}
}

// Title returns a human-readable title for the named scope.
func (nsi *namedScopeInfo) Title() string {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.Title()
	}

	return nsi.srgInfo.Title()
}

// Name returns a human-readable name for the named scope.
func (nsi *namedScopeInfo) Name() (string, bool) {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.Name(), true
	}

	return nsi.srgInfo.Name()
}

// NonEmptyName always returns a name for the named scope.
func (nsi *namedScopeInfo) NonEmptyName() string {
	name, hasName := nsi.Name()
	if !hasName {
		return "?"
	}
	return name
}

// ResolveStaticMember attempts to resolve a member (child) with the given name under this named scope, which
// must be Static.
func (nsi *namedScopeInfo) ResolveStaticMember(name string, module compilercommon.InputSource, staticType typegraph.TypeReference) (namedScopeInfo, error) {
	if !nsi.IsStatic() {
		return namedScopeInfo{}, fmt.Errorf("Could not find static name '%v' under non-static scope", name)
	}

	if nsi.typeInfo != nil {
		typeMember, rerr := staticType.ResolveAccessibleMember(name, module, typegraph.MemberResolutionStatic)
		if rerr != nil {
			return namedScopeInfo{}, rerr
		}

		return namedScopeInfo{srg.SRGNamedScope{}, typeMember, nsi.sb}, nil
	} else {
		namedScopeOrImport, found := nsi.srgInfo.ResolveNameUnderScope(name)
		if !found {
			srgName, _ := nsi.srgInfo.Name()
			return namedScopeInfo{}, fmt.Errorf("Could not find static name '%v' under %v %v", name, nsi.srgInfo.Title(), srgName)
		}

		return nsi.sb.processSRGNameOrInfo(namedScopeOrImport)
	}
}

// StaticType returns the static type of the named scope. For scopes that are not static,
// will panic.
func (nsi *namedScopeInfo) StaticType(context scopeContext) typegraph.TypeReference {
	if !nsi.IsStatic() {
		panic("Cannot call StaticType on non-static scope")
	}

	// Static type of a type is a reference to that type, with full generics.
	if nsi.typeInfo != nil {
		return nsi.typeInfo.(typegraph.TGTypeDecl).GetTypeReference()
	}

	// Otherwise, the static type is void. This represents imports.
	return nsi.sb.sg.tdg.VoidTypeReference()
}

// ValueType returns the value type of the named scope. For scopes without types,
// this method will return void.
func (nsi *namedScopeInfo) ValueType(context scopeContext) (typegraph.TypeReference, bool) {
	if nsi.IsStatic() || nsi.IsGeneric() {
		return nsi.sb.sg.tdg.VoidTypeReference(), true
	}

	return nsi.ValueOrGenericType(context)
}

// AssignableType returns the type of values that can be assigned to this named scope. For
// non-assignable scopes, returns void.
func (nsi *namedScopeInfo) AssignableType(context scopeContext) (typegraph.TypeReference, bool) {
	if !nsi.IsAssignable() {
		return nsi.sb.sg.tdg.VoidTypeReference(), true
	}

	if nsi.typeInfo != nil {
		return nsi.typeInfo.(typegraph.TGMember).AssignableType(), true
	}

	return nsi.definedValueOrGenericType(context)
}

// ValueOrGenericType returns the value type of the named scope and whether that named scope was valid.
// For scopes without types, this method will return void. If the named scope is not valid,
// returns (any, false).
func (nsi *namedScopeInfo) ValueOrGenericType(context scopeContext) (typegraph.TypeReference, bool) {
	// Check for an explicit override.
	if nsi.typeInfo == nil {
		if typeOverride, hasTypeOverride := context.getTypeOverride(nsi.srgInfo.GraphNode); hasTypeOverride {
			return typeOverride, true
		}
	}

	return nsi.definedValueOrGenericType(context)
}

// definedValueOrGenericType returns the value type of the named scope and whether that named scope was valid.
// For scopes without types, this method will return void. If the named scope is not valid,
// returns (any, false).
func (nsi *namedScopeInfo) definedValueOrGenericType(context scopeContext) (typegraph.TypeReference, bool) {
	if nsi.IsStatic() {
		return nsi.sb.sg.tdg.VoidTypeReference(), true
	}

	// The value type of a member is its member type.
	if nsi.typeInfo != nil {
		return nsi.typeInfo.(typegraph.TGMember).MemberType(), true
	}

	// Otherwise, we need custom logic to retrieve the type.
	switch nsi.srgInfo.ScopeKind() {
	case srg.NamedScopeParameter:
		// Check for an inferred type.
		inferredType, hasInferredType := nsi.sb.inferredParameterTypes.Get(string(nsi.srgInfo.GraphNode.NodeId))
		if hasInferredType {
			return inferredType.(typegraph.TypeReference), true
		}

		// TODO: We should probably cache this in the type graph instead of resolving here.
		srg := nsi.sb.sg.srg
		parameterTypeNode := nsi.srgInfo.GraphNode.GetNode(sourceshape.NodeParameterType)
		typeref, _ := nsi.sb.sg.ResolveSRGTypeRef(srg.GetTypeRef(parameterTypeNode))
		return typeref, true

	case srg.NamedScopeValue:
		// The value type of a named value is found by scoping the node creating the named value
		// and then checking its scope info.
		creatingScope := nsi.sb.getScope(nsi.srgInfo.GraphNode, context)
		if !creatingScope.GetIsValid() {
			return nsi.sb.sg.tdg.AnyTypeReference(), false
		}

		return creatingScope.AssignableTypeRef(nsi.sb.sg.tdg), true

	case srg.NamedScopeVariable:
		// The value type of a variable is found by scoping the variable
		// and then checking its scope info.
		variableScope := nsi.sb.getScope(nsi.srgInfo.GraphNode, context)
		if !variableScope.GetIsValid() {
			return nsi.sb.sg.tdg.AnyTypeReference(), false
		}

		return variableScope.AssignableTypeRef(nsi.sb.sg.tdg), true

	default:
		panic(fmt.Sprintf("Unknown named scope type: %v", nsi.srgInfo.ScopeKind()))
	}
}

// IsValid returns whether the named scope is valid.
func (nsi *namedScopeInfo) IsValid(context scopeContext) bool {
	if nsi.typeInfo != nil || nsi.IsStatic() {
		return true
	}

	switch nsi.srgInfo.ScopeKind() {
	case srg.NamedScopeParameter:
		return true

	case srg.NamedScopeValue:
		fallthrough

	case srg.NamedScopeVariable:
		scope := nsi.sb.getScope(nsi.srgInfo.GraphNode, context)
		return scope.GetIsValid()

	default:
		panic(fmt.Sprintf("Unknown named scope type: %v", nsi.srgInfo.ScopeKind()))
	}
}

// IsAssignable returns whether the named scope can have a value assigned to it.
func (nsi *namedScopeInfo) IsAssignable() bool {
	if nsi.typeInfo != nil {
		return !nsi.typeInfo.IsReadOnly()
	} else {
		return nsi.srgInfo.IsAssignable()
	}
}

// IsType returns whether the named scope refers to a type.
func (nsi *namedScopeInfo) IsType() bool {
	return nsi.typeInfo != nil && nsi.typeInfo.IsType()
}

// IsStatic returns whether the named scope is static, referring to a non-instance value.
func (nsi *namedScopeInfo) IsStatic() bool {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.IsType()
	} else {
		return nsi.srgInfo.IsStatic()
	}
}

// UnderModule returns whether the named scope is defined under a module.
func (nsi *namedScopeInfo) UnderModule() bool {
	if nsi.typeInfo != nil {
		return !nsi.typeInfo.Parent().IsType()
	} else {
		return false
	}
}

// AccessIsUsage returns true if the named scope refers to a member or variable that
// is used immediately via the access. For example, a variable or property access will
// "use" that member, while a function or constructor is not used until invoked.
func (nsi *namedScopeInfo) AccessIsUsage() bool {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.IsImplicitlyCalled() || nsi.typeInfo.IsField()
	} else {
		return nsi.srgInfo.AccessIsUsage()
	}
}

// IsLocalName returns whether the named scope points to a name only available
// in the local context (parameters, values, and variables).
func (nsi *namedScopeInfo) IsLocalName() bool {
	if nsi.typeInfo != nil {
		return false
	}

	switch nsi.srgInfo.ScopeKind() {
	case srg.NamedScopeParameter:
		fallthrough

	case srg.NamedScopeValue:
		fallthrough

	case srg.NamedScopeVariable:
		return true

	default:
		return false
	}
}

// Generics returns the generics defined on the named scope, if any.
func (nsi *namedScopeInfo) Generics() []typegraph.TGGeneric {
	if !nsi.IsGeneric() {
		return make([]typegraph.TGGeneric, 0)
	}

	return nsi.typeInfo.Generics()
}

// IsGeneric returns whether the named scope is generic, requiring type specification before
// being usable.
func (nsi *namedScopeInfo) IsGeneric() bool {
	if nsi.typeInfo == nil {
		return false
	}

	return nsi.typeInfo.HasGenerics()
}

// Member returns the type/module member pointed to by this named scope, if any.
func (nsi *namedScopeInfo) Member() (typegraph.TGMember, bool) {
	if nsi.typeInfo == nil || nsi.typeInfo.IsType() {
		return typegraph.TGMember{}, false
	}

	return nsi.typeInfo.(typegraph.TGMember), true
}
<commit_msg>Make package lookup errors a bit nicer<commit_after>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package scopegraph

import (
	"fmt"

	"github.com/serulian/compiler/compilercommon"
	"github.com/serulian/compiler/compilergraph"
	"github.com/serulian/compiler/graphs/scopegraph/proto"
	"github.com/serulian/compiler/graphs/srg"
	"github.com/serulian/compiler/graphs/typegraph"
	"github.com/serulian/compiler/sourceshape"
)

var _ = fmt.Printf

type namedScopeInfo struct {
	srgInfo  srg.SRGNamedScope        // The named scope from the SRG.
	typeInfo typegraph.TGTypeOrMember // The type or member from the type graph.
	sb       *scopeBuilder            // The parent scope builder.
}

// lookupNamedScope looks up the given name at the given node's context, returning the referenced scope for
// the named item, if any. For example, giving the name of a parameter (name) under a function's body (node),
// will return information referencing that parameter, its type, etc.
func (sb *scopeBuilder) lookupNamedScope(name string, node compilergraph.GraphNode) (namedScopeInfo, error) {
	srgInfo, found := sb.sg.srg.FindNameInScope(name, node)
	if !found {
		// Try resolving the name as a global type alias.
		aliasedType, typeFound := sb.sg.tdg.LookupGlobalAliasedType(name)
		if typeFound {
			return namedScopeInfo{srg.SRGNamedScope{}, aliasedType, sb}, nil
		}

		return namedScopeInfo{}, fmt.Errorf("The name '%v' could not be found in this context", name)
	}

	return sb.processSRGNameOrInfo(srgInfo)
}

// processSRGNameOrInfo handles an SRGScopeOrImport, either translating an SRGNamedScope into a namedScopeInfo
// or performing the actual lookup under the typegraph for non-SRG names.
func (sb *scopeBuilder) processSRGNameOrInfo(srgInfo srg.SRGScopeOrImport) (namedScopeInfo, error) {
	// If the SRG info refers to an SRG named scope, build the info for it.
	if srgInfo.IsNamedScope() {
		return sb.buildNamedScopeForSRGInfo(srgInfo.AsNamedScope()), nil
	}

	// Otherwise, the SRG info refers to a member of an external package, which we need to resolve
	// under the type graph.
	packageImportInfo := srgInfo.AsPackageImport()
	typeOrMember, found := sb.sg.tdg.ResolveTypeOrMemberUnderPackage(packageImportInfo.ImportedName(), packageImportInfo.Package())
	if !found {
		return namedScopeInfo{}, fmt.Errorf("Could not find type or member '%v' under package %v", packageImportInfo.ImportedName(), packageImportInfo.Package().ReferenceID())
	}

	return namedScopeInfo{srg.SRGNamedScope{}, typeOrMember, sb}, nil
}

// buildNamedScopeForSRGInfo builds a namedScopeInfo for the given SRG scope info.
func (sb *scopeBuilder) buildNamedScopeForSRGInfo(srgInfo srg.SRGNamedScope) namedScopeInfo {
	switch srgInfo.ScopeKind() {
	case srg.NamedScopeType:
		typeInfo, found := sb.sg.tdg.GetTypeForSourceNode(srgInfo.GraphNode)
		if !found {
			panic("Missing type info for SRG node")
		}

		return namedScopeInfo{srgInfo, typeInfo, sb}

	case srg.NamedScopeMember:
		memberInfo, found := sb.sg.tdg.GetMemberForSourceNode(srgInfo.GraphNode)
		if !found {
			panic("Missing member info for SRG node")
		}

		return namedScopeInfo{srgInfo, memberInfo, sb}

	case srg.NamedScopeImport:
		fallthrough

	case srg.NamedScopeParameter:
		fallthrough

	case srg.NamedScopeValue:
		fallthrough

	case srg.NamedScopeVariable:
		return namedScopeInfo{srgInfo, nil, sb}

	default:
		panic(fmt.Sprintf("Unknown named scope type: %v", srgInfo.ScopeKind()))
	}
}

// getNamedScopeForMember returns namedScopeInfo for the given type member.
func (sb *scopeBuilder) getNamedScopeForMember(member typegraph.TGMember) namedScopeInfo {
	return namedScopeInfo{srg.SRGNamedScope{}, member, sb}
}

// getNamedScopeForScope returns namedScopeInfo for the given name, if it references a node by name.
func (sb *scopeBuilder) getNamedScopeForScope(scope *proto.ScopeInfo) (namedScopeInfo, bool) {
	if scope.GetNamedReference() == nil {
		return namedScopeInfo{}, false
	}

	namedReference := scope.GetNamedReference()
	nodeId := compilergraph.GraphNodeId(namedReference.GetReferencedNode())

	if namedReference.GetIsSRGNode() {
		referencedNode := sb.sg.srg.GetNamedScope(nodeId)
		return namedScopeInfo{referencedNode, nil, sb}, true
	} else {
		referencedNode := sb.sg.tdg.GetTypeOrMember(nodeId)
		return namedScopeInfo{srg.SRGNamedScope{}, referencedNode, sb}, true
	}
}

// Title returns a human-readable title for the named scope.
func (nsi *namedScopeInfo) Title() string {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.Title()
	}

	return nsi.srgInfo.Title()
}

// Name returns a human-readable name for the named scope.
func (nsi *namedScopeInfo) Name() (string, bool) {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.Name(), true
	}

	return nsi.srgInfo.Name()
}

// NonEmptyName always returns a name for the named scope.
func (nsi *namedScopeInfo) NonEmptyName() string {
	name, hasName := nsi.Name()
	if !hasName {
		return "?"
	}
	return name
}

// ResolveStaticMember attempts to resolve a member (child) with the given name under this named scope, which
// must be Static.
func (nsi *namedScopeInfo) ResolveStaticMember(name string, module compilercommon.InputSource, staticType typegraph.TypeReference) (namedScopeInfo, error) {
	if !nsi.IsStatic() {
		return namedScopeInfo{}, fmt.Errorf("Could not find static name '%v' under non-static scope", name)
	}

	if nsi.typeInfo != nil {
		typeMember, rerr := staticType.ResolveAccessibleMember(name, module, typegraph.MemberResolutionStatic)
		if rerr != nil {
			return namedScopeInfo{}, rerr
		}

		return namedScopeInfo{srg.SRGNamedScope{}, typeMember, nsi.sb}, nil
	} else {
		namedScopeOrImport, found := nsi.srgInfo.ResolveNameUnderScope(name)
		if !found {
			srgName, _ := nsi.srgInfo.Name()
			return namedScopeInfo{}, fmt.Errorf("Could not find static name '%v' under %v %v", name, nsi.srgInfo.Title(), srgName)
		}

		return nsi.sb.processSRGNameOrInfo(namedScopeOrImport)
	}
}

// StaticType returns the static type of the named scope. For scopes that are not static,
// will panic.
func (nsi *namedScopeInfo) StaticType(context scopeContext) typegraph.TypeReference {
	if !nsi.IsStatic() {
		panic("Cannot call StaticType on non-static scope")
	}

	// Static type of a type is a reference to that type, with full generics.
	if nsi.typeInfo != nil {
		return nsi.typeInfo.(typegraph.TGTypeDecl).GetTypeReference()
	}

	// Otherwise, the static type is void. This represents imports.
	return nsi.sb.sg.tdg.VoidTypeReference()
}

// ValueType returns the value type of the named scope. For scopes without types,
// this method will return void.
func (nsi *namedScopeInfo) ValueType(context scopeContext) (typegraph.TypeReference, bool) {
	if nsi.IsStatic() || nsi.IsGeneric() {
		return nsi.sb.sg.tdg.VoidTypeReference(), true
	}

	return nsi.ValueOrGenericType(context)
}

// AssignableType returns the type of values that can be assigned to this named scope. For
// non-assignable scopes, returns void.
func (nsi *namedScopeInfo) AssignableType(context scopeContext) (typegraph.TypeReference, bool) {
	if !nsi.IsAssignable() {
		return nsi.sb.sg.tdg.VoidTypeReference(), true
	}

	if nsi.typeInfo != nil {
		return nsi.typeInfo.(typegraph.TGMember).AssignableType(), true
	}

	return nsi.definedValueOrGenericType(context)
}

// ValueOrGenericType returns the value type of the named scope and whether that named scope was valid.
// For scopes without types, this method will return void. If the named scope is not valid,
// returns (any, false).
func (nsi *namedScopeInfo) ValueOrGenericType(context scopeContext) (typegraph.TypeReference, bool) {
	// Check for an explicit override.
	if nsi.typeInfo == nil {
		if typeOverride, hasTypeOverride := context.getTypeOverride(nsi.srgInfo.GraphNode); hasTypeOverride {
			return typeOverride, true
		}
	}

	return nsi.definedValueOrGenericType(context)
}

// definedValueOrGenericType returns the value type of the named scope and whether that named scope was valid.
// For scopes without types, this method will return void. If the named scope is not valid,
// returns (any, false).
func (nsi *namedScopeInfo) definedValueOrGenericType(context scopeContext) (typegraph.TypeReference, bool) {
	if nsi.IsStatic() {
		return nsi.sb.sg.tdg.VoidTypeReference(), true
	}

	// The value type of a member is its member type.
	if nsi.typeInfo != nil {
		return nsi.typeInfo.(typegraph.TGMember).MemberType(), true
	}

	// Otherwise, we need custom logic to retrieve the type.
	switch nsi.srgInfo.ScopeKind() {
	case srg.NamedScopeParameter:
		// Check for an inferred type.
		inferredType, hasInferredType := nsi.sb.inferredParameterTypes.Get(string(nsi.srgInfo.GraphNode.NodeId))
		if hasInferredType {
			return inferredType.(typegraph.TypeReference), true
		}

		// TODO: We should probably cache this in the type graph instead of resolving here.
		srg := nsi.sb.sg.srg
		parameterTypeNode := nsi.srgInfo.GraphNode.GetNode(sourceshape.NodeParameterType)
		typeref, _ := nsi.sb.sg.ResolveSRGTypeRef(srg.GetTypeRef(parameterTypeNode))
		return typeref, true

	case srg.NamedScopeValue:
		// The value type of a named value is found by scoping the node creating the named value
		// and then checking its scope info.
		creatingScope := nsi.sb.getScope(nsi.srgInfo.GraphNode, context)
		if !creatingScope.GetIsValid() {
			return nsi.sb.sg.tdg.AnyTypeReference(), false
		}

		return creatingScope.AssignableTypeRef(nsi.sb.sg.tdg), true

	case srg.NamedScopeVariable:
		// The value type of a variable is found by scoping the variable
		// and then checking its scope info.
		variableScope := nsi.sb.getScope(nsi.srgInfo.GraphNode, context)
		if !variableScope.GetIsValid() {
			return nsi.sb.sg.tdg.AnyTypeReference(), false
		}

		return variableScope.AssignableTypeRef(nsi.sb.sg.tdg), true

	default:
		panic(fmt.Sprintf("Unknown named scope type: %v", nsi.srgInfo.ScopeKind()))
	}
}

// IsValid returns whether the named scope is valid.
func (nsi *namedScopeInfo) IsValid(context scopeContext) bool {
	if nsi.typeInfo != nil || nsi.IsStatic() {
		return true
	}

	switch nsi.srgInfo.ScopeKind() {
	case srg.NamedScopeParameter:
		return true

	case srg.NamedScopeValue:
		fallthrough

	case srg.NamedScopeVariable:
		scope := nsi.sb.getScope(nsi.srgInfo.GraphNode, context)
		return scope.GetIsValid()

	default:
		panic(fmt.Sprintf("Unknown named scope type: %v", nsi.srgInfo.ScopeKind()))
	}
}

// IsAssignable returns whether the named scope can have a value assigned to it.
func (nsi *namedScopeInfo) IsAssignable() bool {
	if nsi.typeInfo != nil {
		return !nsi.typeInfo.IsReadOnly()
	} else {
		return nsi.srgInfo.IsAssignable()
	}
}

// IsType returns whether the named scope refers to a type.
func (nsi *namedScopeInfo) IsType() bool {
	return nsi.typeInfo != nil && nsi.typeInfo.IsType()
}

// IsStatic returns whether the named scope is static, referring to a non-instance value.
func (nsi *namedScopeInfo) IsStatic() bool {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.IsType()
	} else {
		return nsi.srgInfo.IsStatic()
	}
}

// UnderModule returns whether the named scope is defined under a module.
func (nsi *namedScopeInfo) UnderModule() bool {
	if nsi.typeInfo != nil {
		return !nsi.typeInfo.Parent().IsType()
	} else {
		return false
	}
}

// AccessIsUsage returns true if the named scope refers to a member or variable that
// is used immediately via the access. For example, a variable or property access will
// "use" that member, while a function or constructor is not used until invoked.
func (nsi *namedScopeInfo) AccessIsUsage() bool {
	if nsi.typeInfo != nil {
		return nsi.typeInfo.IsImplicitlyCalled() || nsi.typeInfo.IsField()
	} else {
		return nsi.srgInfo.AccessIsUsage()
	}
}

// IsLocalName returns whether the named scope points to a name only available
// in the local context (parameters, values, and variables).
func (nsi *namedScopeInfo) IsLocalName() bool {
	if nsi.typeInfo != nil {
		return false
	}

	switch nsi.srgInfo.ScopeKind() {
	case srg.NamedScopeParameter:
		fallthrough

	case srg.NamedScopeValue:
		fallthrough

	case srg.NamedScopeVariable:
		return true

	default:
		return false
	}
}

// Generics returns the generics defined on the named scope, if any.
func (nsi *namedScopeInfo) Generics() []typegraph.TGGeneric {
	if !nsi.IsGeneric() {
		return make([]typegraph.TGGeneric, 0)
	}

	return nsi.typeInfo.Generics()
}

// IsGeneric returns whether the named scope is generic, requiring type specification before
// being usable.
func (nsi *namedScopeInfo) IsGeneric() bool {
	if nsi.typeInfo == nil {
		return false
	}

	return nsi.typeInfo.HasGenerics()
}

// Member returns the type/module member pointed to by this named scope, if any.
func (nsi *namedScopeInfo) Member() (typegraph.TGMember, bool) {
	if nsi.typeInfo == nil || nsi.typeInfo.IsType() {
		return typegraph.TGMember{}, false
	}

	return nsi.typeInfo.(typegraph.TGMember), true
}
<|endoftext|>