<commit_before>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/gophr-pm/gophr/lib/semver"
)

const (
	at                          = '@'
	dot                         = '.'
	slash                       = '/'
	hyphen                      = '-'
	shaLength                   = 40
	minShortSHALength           = 6
	semverSelectorRegexTemplate = `([\%c\%c]?)([0-9]+)(?:\.([0-9]+|%c))?(?:\.([0-9]+|%c))?(?:\-([a-zA-Z0-9\-_]+[a-zA-Z0-9])(?:\.([0-9]+|%c))?)?([\%c\%c]?)`
)

var (
	// semverSelectorRegex is the regular expression used to parse semver package
	// version selectors.
	semverSelectorRegex = regexp.MustCompile(fmt.Sprintf(
		semverSelectorRegexTemplate,
		semver.SelectorTildeChar,
		semver.SelectorCaratChar,
		semver.SelectorWildcardChar,
		semver.SelectorWildcardChar,
		semver.SelectorWildcardChar,
		semver.SelectorLessThanChar,
		semver.SelectorGreaterThanChar,
	))
)

// packageRequestParts represents the piecewise breakdown of a package request.
// TODO: Remove shaSelector and replace it with fullSHA and shortSHA
type packageRequestParts struct {
	url                   string
	repo                  string
	author                string
	subpath               string
	selector              string
	shaSelector           string
	semverSelector        semver.Selector
	hasFullSHASelector    bool
	hasShortSHASelector   bool
	semverSelectorDefined bool
}

// hasSHASelector returns true if this parts struct has a sha selector.
func (parts *packageRequestParts) hasSHASelector() bool {
	return len(parts.shaSelector) > 0
}

// hasSemverSelector returns true if this parts struct has a semver selector.
func (parts *packageRequestParts) hasSemverSelector() bool {
	return parts.semverSelectorDefined
}

// getBasePackagePath returns the base package path of the data in parts.
// Simply, it is everything minus the base path and the domain.
func (parts *packageRequestParts) getBasePackagePath() string {
	var buffer bytes.Buffer
	buffer.WriteByte(slash)
	buffer.WriteString(parts.author)
	buffer.WriteByte(slash)
	buffer.WriteString(parts.repo)

	if len(parts.selector) > 0 {
		buffer.WriteByte(at)
		buffer.WriteString(parts.selector)
	}

	return buffer.String()
}

// String returns a string representation of this struct. This function returns
// a JSON-like representation of this struct.
func (parts *packageRequestParts) String() string {
	var b bytes.Buffer
	b.WriteString("{ ")
	b.WriteString("url: \"")
	if parts != nil {
		b.WriteString(parts.url)
	}
	b.WriteString("\", repo: \"")
	if parts != nil {
		b.WriteString(parts.repo)
	}
	b.WriteString("\", author: \"")
	if parts != nil {
		b.WriteString(parts.author)
	}
	b.WriteString("\", subpath: \"")
	if parts != nil {
		b.WriteString(parts.subpath)
	}
	b.WriteString("\", selector: \"")
	if parts != nil {
		b.WriteString(parts.selector)
	}
	b.WriteString("\", shaSelector: \"")
	if parts != nil {
		b.WriteString(parts.shaSelector)
	}
	b.WriteString("\", semverSelector: ")
	if parts != nil {
		b.WriteString(parts.semverSelector.String())
	}
	b.WriteString("\", hasFullSHASelector: ")
	if parts != nil {
		if parts.hasFullSHASelector {
			b.WriteString("true")
		} else {
			b.WriteString("false")
		}
	}
	b.WriteString("\", hasShortSHASelector: ")
	if parts != nil {
		if parts.hasShortSHASelector {
			b.WriteString("true")
		} else {
			b.WriteString("false")
		}
	}
	b.WriteString(" }")

	return b.String()
}

// readPackageRequestParts reads an http request in the format of a package
// request, and breaks down the URL of the request into parts. Lastly, the parts
// are composed into a parts struct and returned.
func readPackageRequestParts(req *http.Request) (*packageRequestParts, error) {
	var (
		i      = 0
		url    = strings.TrimSpace(req.URL.Path)
		urlLen = len(url)

		repoEndIndex        = -1 // Exclusive
		repoStartIndex      = -1 // Inclusive
		authorEndIndex      = -1
		authorStartIndex    = -1
		subpathStartIndex   = -1
		selectorStartIndex  = -1
		hasFullSHASelector  = false
		hasShortSHASelector = false

		selector              string
		shaSelector           string
		semverSelector        semver.Selector
		semverSelectorDefined bool
	)

	// Exit if the the url is empty or just a slash or doesn't start with a slash.
	// If so, return an empty parts.
	if len(url) < 2 || url[0] != slash {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}

	// So, the first character should be a slash. That means the next character is
	// the beginning of the author.
	authorStartIndex = 1

	// Next step is to scan to the next slash to find the beginning of the repo.
	for i = authorStartIndex + 1; i < urlLen && url[i] != slash; i = i + 1 {
	}
	// Make sure the author exists (it is required).
	if (i - authorStartIndex) < 2 {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}
	// If we ran out bytes, then this is an invalid pac
	if i == urlLen {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}

	// So, we have arrived at the slash that prefixes the repo.
	authorEndIndex = i
	repoStartIndex = i + 1

	// Next step is to scan to the next slash OR at OR end of the string.
	for i = repoStartIndex; i < urlLen; i = i + 1 {
		char := url[i]
		// Time to read the subpath!
		if char == slash {
			repoEndIndex = i
			subpathStartIndex = i
			break
		}
		// Time to read the selector!
		if char == at {
			repoEndIndex = i
			selectorStartIndex = i + 1
			break
		}
	}
	// Make sure the repo exists (it is required).
	if (i - repoStartIndex) < 2 {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}
	// If we're out of url bytes, exit with just the author and the repo.
	if i == urlLen {
		return &packageRequestParts{
			url:    url,
			repo:   url[repoStartIndex:urlLen],
			author: url[authorStartIndex:authorEndIndex],
		}, nil
	}

	// Only read the selector if there is evidence that it exists.
	if selectorStartIndex != -1 {
		// If we got here, then we are reading until the end of the url or the
		// beginning of the subpath.
		for i = selectorStartIndex; i < urlLen && url[i] != slash; i = i + 1 {
		}
		// Make sure the selector exists (it is required if we see an @).
		if (i - selectorStartIndex) < 2 {
			return nil, NewInvalidPackageVersionRequestURLError(url)
		}
		// Whatever the case may be, this is where the selector ends.
		selector = url[selectorStartIndex:i]

		// Read the selector to figure out what it is.
		if isShortSHASelector(selector) {
			shaSelector = selector
			hasShortSHASelector = true
		} else if isFullSHASelector(selector) {
			shaSelector = selector
			hasFullSHASelector = true
		} else {
			var err error
			if semverSelector, err = readSemverSelector(selector); err != nil {
				return nil, NewInvalidPackageVersionRequestURLError(url, err)
			}

			// If we got here, the semver selector exists.
			semverSelectorDefined = true
		}

		// If we're out of url bytes then there is no subpath.
		if i == urlLen {
			return &packageRequestParts{
				url:                   url,
				repo:                  url[repoStartIndex:repoEndIndex],
				author:                url[authorStartIndex:authorEndIndex],
				selector:              selector,
				shaSelector:           shaSelector,
				semverSelector:        semverSelector,
				hasFullSHASelector:    hasFullSHASelector,
				hasShortSHASelector:   hasShortSHASelector,
				semverSelectorDefined: semverSelectorDefined,
			}, nil
		}

		// Otherwise, this is the subpath start index.
		subpathStartIndex = i
	}

	// Make sure the subpath is valid.
	if (urlLen - subpathStartIndex) < 2 {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}

	// If there are still unexplored bytes, there is a subpath as well.
	return &packageRequestParts{
		url:                   url,
		repo:                  url[repoStartIndex:repoEndIndex],
		author:                url[authorStartIndex:authorEndIndex],
		subpath:               url[subpathStartIndex:urlLen],
		selector:              selector,
		shaSelector:           shaSelector,
		semverSelector:        semverSelector,
		hasFullSHASelector:    hasFullSHASelector,
		hasShortSHASelector:   hasShortSHASelector,
		semverSelectorDefined: semverSelectorDefined,
	}, nil
}

// isFullSHASelector returns true if the selector is in a full sha
func isFullSHASelector(selector string) bool {
	// If it isn't a sha hash (which is 40 characters long), then its a semver
	// selector for out purposes.
	return len(selector) == shaLength && strings.IndexByte(selector, dot) == -1
}

func isShortSHASelector(selector string) bool {
	// If it isn't a 6 character short SHA return false, it might be semvar
	return len(selector) >= minShortSHALength &&
		len(selector) < shaLength &&
		strings.IndexByte(selector, dot) == -1 &&
		strings.IndexByte(selector, hyphen) == -1
}

// isSemverSelector converts a semver selector string into a semver selector.
func readSemverSelector(selector string) (semver.Selector, error) {
	match := semverSelectorRegex.FindStringSubmatch(selector)
	if match == nil {
		return semver.Selector{}, fmt.Errorf("Invalid version selector \"%s\"", selector)
	}

	semverSelector, err := semver.NewSemverSelector(
		match[1], // Prefix
		match[2], // Major Version
		match[3], // Minor Version
		match[4], // Patch Version
		match[5], // Pre-release Label
		match[6], // Pre-release Version
		match[7], // Suffix
	)
	if err != nil {
		return semver.Selector{}, err
	}

	return semverSelector, nil
}
<commit_msg>left out fixing the router parts<commit_after>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"regexp"
	"strings"

	"github.com/gophr-pm/gophr/lib/semver"
)

const (
	at                          = '@'
	dot                         = '.'
	slash                       = '/'
	hyphen                      = '-'
	shaLength                   = 40
	minShortSHALength           = 6
	semverSelectorRegexTemplate = `([\%c\%c]?)([0-9]+)(?:\.([0-9]+|%c))?(?:\.([0-9]+|%c))?(?:\-([a-zA-Z0-9\-_]+[a-zA-Z0-9])(?:\.([0-9]+|%c))?)?([\%c\%c]?)`
)

var (
	// semverSelectorRegex is the regular expression used to parse semver package
	// version selectors.
	semverSelectorRegex = regexp.MustCompile(fmt.Sprintf(
		semverSelectorRegexTemplate,
		semver.SemverSelectorTildeChar,
		semver.SemverSelectorCaratChar,
		semver.SemverSelectorWildcardChar,
		semver.SemverSelectorWildcardChar,
		semver.SemverSelectorWildcardChar,
		semver.SemverSelectorLessThanChar,
		semver.SemverSelectorGreaterThanChar,
	))
)

// packageRequestParts represents the piecewise breakdown of a package request.
// TODO: Remove shaSelector and replace it with fullSHA and shortSHA
type packageRequestParts struct {
	url                   string
	repo                  string
	author                string
	subpath               string
	selector              string
	shaSelector           string
	semverSelector        semver.SemverSelector
	hasFullSHASelector    bool
	hasShortSHASelector   bool
	semverSelectorDefined bool
}

// hasSHASelector returns true if this parts struct has a sha selector.
func (parts *packageRequestParts) hasSHASelector() bool {
	return len(parts.shaSelector) > 0
}

// hasSemverSelector returns true if this parts struct has a semver selector.
func (parts *packageRequestParts) hasSemverSelector() bool {
	return parts.semverSelectorDefined
}

// getBasePackagePath returns the base package path of the data in parts.
// Simply, it is everything minus the base path and the domain.
func (parts *packageRequestParts) getBasePackagePath() string {
	var buffer bytes.Buffer
	buffer.WriteByte(slash)
	buffer.WriteString(parts.author)
	buffer.WriteByte(slash)
	buffer.WriteString(parts.repo)

	if len(parts.selector) > 0 {
		buffer.WriteByte(at)
		buffer.WriteString(parts.selector)
	}

	return buffer.String()
}

// String returns a string representation of this struct. This function returns
// a JSON-like representation of this struct.
func (parts *packageRequestParts) String() string {
	var b bytes.Buffer
	b.WriteString("{ ")
	b.WriteString("url: \"")
	if parts != nil {
		b.WriteString(parts.url)
	}
	b.WriteString("\", repo: \"")
	if parts != nil {
		b.WriteString(parts.repo)
	}
	b.WriteString("\", author: \"")
	if parts != nil {
		b.WriteString(parts.author)
	}
	b.WriteString("\", subpath: \"")
	if parts != nil {
		b.WriteString(parts.subpath)
	}
	b.WriteString("\", selector: \"")
	if parts != nil {
		b.WriteString(parts.selector)
	}
	b.WriteString("\", shaSelector: \"")
	if parts != nil {
		b.WriteString(parts.shaSelector)
	}
	b.WriteString("\", semverSelector: ")
	if parts != nil {
		b.WriteString(parts.semverSelector.String())
	}
	b.WriteString("\", hasFullSHASelector: ")
	if parts != nil {
		if parts.hasFullSHASelector {
			b.WriteString("true")
		} else {
			b.WriteString("false")
		}
	}
	b.WriteString("\", hasShortSHASelector: ")
	if parts != nil {
		if parts.hasShortSHASelector {
			b.WriteString("true")
		} else {
			b.WriteString("false")
		}
	}
	b.WriteString(" }")

	return b.String()
}

// readPackageRequestParts reads an http request in the format of a package
// request, and breaks down the URL of the request into parts. Lastly, the parts
// are composed into a parts struct and returned.
func readPackageRequestParts(req *http.Request) (*packageRequestParts, error) {
	var (
		i      = 0
		url    = strings.TrimSpace(req.URL.Path)
		urlLen = len(url)

		repoEndIndex        = -1 // Exclusive
		repoStartIndex      = -1 // Inclusive
		authorEndIndex      = -1
		authorStartIndex    = -1
		subpathStartIndex   = -1
		selectorStartIndex  = -1
		hasFullSHASelector  = false
		hasShortSHASelector = false

		selector              string
		shaSelector           string
		semverSelector        semver.SemverSelector
		semverSelectorDefined bool
	)

	// Exit if the the url is empty or just a slash or doesn't start with a slash.
	// If so, return an empty parts.
	if len(url) < 2 || url[0] != slash {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}

	// So, the first character should be a slash. That means the next character is
	// the beginning of the author.
	authorStartIndex = 1

	// Next step is to scan to the next slash to find the beginning of the repo.
	for i = authorStartIndex + 1; i < urlLen && url[i] != slash; i = i + 1 {
	}
	// Make sure the author exists (it is required).
	if (i - authorStartIndex) < 2 {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}
	// If we ran out bytes, then this is an invalid pac
	if i == urlLen {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}

	// So, we have arrived at the slash that prefixes the repo.
	authorEndIndex = i
	repoStartIndex = i + 1

	// Next step is to scan to the next slash OR at OR end of the string.
	for i = repoStartIndex; i < urlLen; i = i + 1 {
		char := url[i]
		// Time to read the subpath!
		if char == slash {
			repoEndIndex = i
			subpathStartIndex = i
			break
		}
		// Time to read the selector!
		if char == at {
			repoEndIndex = i
			selectorStartIndex = i + 1
			break
		}
	}
	// Make sure the repo exists (it is required).
	if (i - repoStartIndex) < 2 {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}
	// If we're out of url bytes, exit with just the author and the repo.
	if i == urlLen {
		return &packageRequestParts{
			url:    url,
			repo:   url[repoStartIndex:urlLen],
			author: url[authorStartIndex:authorEndIndex],
		}, nil
	}

	// Only read the selector if there is evidence that it exists.
	if selectorStartIndex != -1 {
		// If we got here, then we are reading until the end of the url or the
		// beginning of the subpath.
		for i = selectorStartIndex; i < urlLen && url[i] != slash; i = i + 1 {
		}
		// Make sure the selector exists (it is required if we see an @).
		if (i - selectorStartIndex) < 2 {
			return nil, NewInvalidPackageVersionRequestURLError(url)
		}
		// Whatever the case may be, this is where the selector ends.
		selector = url[selectorStartIndex:i]

		// Read the selector to figure out what it is.
		if isShortSHASelector(selector) {
			shaSelector = selector
			hasShortSHASelector = true
		} else if isFullSHASelector(selector) {
			shaSelector = selector
			hasFullSHASelector = true
		} else {
			var err error
			if semverSelector, err = readSemverSelector(selector); err != nil {
				return nil, NewInvalidPackageVersionRequestURLError(url, err)
			}

			// If we got here, the semver selector exists.
			semverSelectorDefined = true
		}

		// If we're out of url bytes then there is no subpath.
		if i == urlLen {
			return &packageRequestParts{
				url:                   url,
				repo:                  url[repoStartIndex:repoEndIndex],
				author:                url[authorStartIndex:authorEndIndex],
				selector:              selector,
				shaSelector:           shaSelector,
				semverSelector:        semverSelector,
				hasFullSHASelector:    hasFullSHASelector,
				hasShortSHASelector:   hasShortSHASelector,
				semverSelectorDefined: semverSelectorDefined,
			}, nil
		}

		// Otherwise, this is the subpath start index.
		subpathStartIndex = i
	}

	// Make sure the subpath is valid.
	if (urlLen - subpathStartIndex) < 2 {
		return nil, NewInvalidPackageVersionRequestURLError(url)
	}

	// If there are still unexplored bytes, there is a subpath as well.
	return &packageRequestParts{
		url:                   url,
		repo:                  url[repoStartIndex:repoEndIndex],
		author:                url[authorStartIndex:authorEndIndex],
		subpath:               url[subpathStartIndex:urlLen],
		selector:              selector,
		shaSelector:           shaSelector,
		semverSelector:        semverSelector,
		hasFullSHASelector:    hasFullSHASelector,
		hasShortSHASelector:   hasShortSHASelector,
		semverSelectorDefined: semverSelectorDefined,
	}, nil
}

// isFullSHASelector returns true if the selector is in a full sha
func isFullSHASelector(selector string) bool {
	// If it isn't a sha hash (which is 40 characters long), then its a semver
	// selector for out purposes.
	return len(selector) == shaLength && strings.IndexByte(selector, dot) == -1
}

func isShortSHASelector(selector string) bool {
	// If it isn't a 6 character short SHA return false, it might be semvar
	return len(selector) >= minShortSHALength &&
		len(selector) < shaLength &&
		strings.IndexByte(selector, dot) == -1 &&
		strings.IndexByte(selector, hyphen) == -1
}

// isSemverSelector converts a semver selector string into a semver selector.
func readSemverSelector(selector string) (semver.SemverSelector, error) {
	match := semverSelectorRegex.FindStringSubmatch(selector)
	if match == nil {
		return semver.SemverSelector{}, fmt.Errorf("Invalid version selector \"%s\"", selector)
	}

	semverSelector, err := semver.NewSemverSelector(
		match[1], // Prefix
		match[2], // Major Version
		match[3], // Minor Version
		match[4], // Patch Version
		match[5], // Pre-release Label
		match[6], // Pre-release Version
		match[7], // Suffix
	)
	if err != nil {
		return semver.SemverSelector{}, err
	}

	return semverSelector, nil
}
<|endoftext|>


<commit_before>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"sync/atomic"
	"time"

	"github.com/mercari/gcm"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge            int          `json:"badge,omitempty"`
	Sound            string       `json:"sound,omitempty"`
	ContentAvailable bool         `json:"content_available,omitempty"`
	Expiry           int          `json:"expiry,omitempty"`
	Retry            int          `json:"retry,omitempty"`
	Extend           []ExtendJSON `json:"extend,omitempty"`
	// meta
	ID uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitHttpClient() error {
	TransportGCM := &http.Transport{
		MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum,
		Dial: (&net.Dialer{
			Timeout:   time.Duration(ConfGaurun.Android.Timeout) * time.Second,
			KeepAlive: time.Duration(ConfGaurun.Android.KeepAliveTimeout) * time.Second,
		}).Dial,
	}
	GCMClient = &gcm.Sender{
		ApiKey: ConfGaurun.Android.ApiKey,
		Http: &http.Client{
			Transport: TransportGCM,
			Timeout:   time.Duration(ConfGaurun.Android.Timeout) * time.Second,
		},
	}

	var err error
	APNSClient, err = NewApnsClientHttp2(
		ConfGaurun.Ios.PemCertPath,
		ConfGaurun.Ios.PemKeyPath,
	)
	if err != nil {
		return err
	}
	return nil
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err)
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			// Enqueue notification per token
			for _, token := range notification.Tokens {
				notification2 := notification
				notification2.Tokens = []string{token}
				notification2.ID = numberingPush()
				LogPush(notification2.ID, StatusAcceptedPush, token, 0, notification2, nil)
				QueueNotification <- notification2
			}
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification) error {
	LogError.Debug("START push notification for iOS")

	service := NewApnsServiceHttp2(APNSClient)

	token := req.Tokens[0]

	headers := NewApnsHeadersHttp2(&req)
	payload := NewApnsPayloadHttp2(&req)

	stime := time.Now()
	err := ApnsPushHttp2(token, service, headers, payload)

	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()

	if err != nil {
		atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
		LogPush(req.ID, StatusFailedPush, token, ptime, req, err)
		return err
	}

	atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
	LogPush(req.ID, StatusSucceededPush, token, ptime, req, nil)

	LogError.Debug("END push notification for iOS")

	return nil
}

func pushNotificationAndroid(req RequestGaurunNotification) error {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	token := req.Tokens[0]

	msg := gcm.NewMessage(data, token)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		LogPush(req.ID, StatusFailedPush, token, ptime, req, err)
		return err
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		LogPush(req.ID, StatusFailedPush, token, ptime, req, errors.New(resp.Results[0].Error))
		return errors.New(resp.Results[0].Error)
	}

	LogPush(req.ID, StatusSucceededPush, token, ptime, req, nil)

	atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(len(req.Tokens)))
	LogError.Debug("END push notification for Android")

	return nil
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var respGaurun ResponseGaurun

	msgJson := "{\"message\":\"" + msg + "\"}"

	err := json.Unmarshal([]byte(msgJson), &respGaurun)
	if err != nil {
		msgJson = "{\"message\":\"unknown\"}"
	}

	w.WriteHeader(code)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", serverHeader())

	fmt.Fprint(w, msgJson)
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	LogError.Debug("content-length check")
	if r.ContentLength == 0 {
		sendResponse(w, "request body is empty", http.StatusBadRequest)
		return
	}

	var (
		reqGaurun RequestGaurun
		err       error
	)

	if ConfGaurun.Log.Level == "debug" {
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			sendResponse(w, "failed to read request-body", http.StatusInternalServerError)
			return
		}
		LogError.Debugf("parse request body: %s", reqBody)
		err = json.Unmarshal(reqBody, &reqGaurun)
	} else {
		LogError.Debug("parse request body")
		err = json.NewDecoder(r.Body).Decode(&reqGaurun)
	}

	if err != nil {
		LogError.Error(err)
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		LogError.Error("empty notification")
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		LogError.Error(msg)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<commit_msg>style: changed response message.<commit_after>package gaurun

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"sync/atomic"
	"time"

	"github.com/mercari/gcm"
)

type RequestGaurun struct {
	Notifications []RequestGaurunNotification `json:"notifications"`
}

type RequestGaurunNotification struct {
	// Common
	Tokens   []string `json:"token"`
	Platform int      `json:"platform"`
	Message  string   `json:"message"`
	// Android
	CollapseKey    string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool   `json:"delay_while_idle,omitempty"`
	TimeToLive     int    `json:"time_to_live,omitempty"`
	// iOS
	Badge            int          `json:"badge,omitempty"`
	Sound            string       `json:"sound,omitempty"`
	ContentAvailable bool         `json:"content_available,omitempty"`
	Expiry           int          `json:"expiry,omitempty"`
	Retry            int          `json:"retry,omitempty"`
	Extend           []ExtendJSON `json:"extend,omitempty"`
	// meta
	ID uint64 `json:"seq_id,omitempty"`
}

type ExtendJSON struct {
	Key   string `json:"key"`
	Value string `json:"val"`
}

type ResponseGaurun struct {
	Message string `json:"message"`
}

type CertificatePem struct {
	Cert []byte
	Key  []byte
}

func InitHttpClient() error {
	TransportGCM := &http.Transport{
		MaxIdleConnsPerHost: ConfGaurun.Core.WorkerNum,
		Dial: (&net.Dialer{
			Timeout:   time.Duration(ConfGaurun.Android.Timeout) * time.Second,
			KeepAlive: time.Duration(ConfGaurun.Android.KeepAliveTimeout) * time.Second,
		}).Dial,
	}
	GCMClient = &gcm.Sender{
		ApiKey: ConfGaurun.Android.ApiKey,
		Http: &http.Client{
			Transport: TransportGCM,
			Timeout:   time.Duration(ConfGaurun.Android.Timeout) * time.Second,
		},
	}

	var err error
	APNSClient, err = NewApnsClientHttp2(
		ConfGaurun.Ios.PemCertPath,
		ConfGaurun.Ios.PemKeyPath,
	)
	if err != nil {
		return err
	}
	return nil
}

func enqueueNotifications(notifications []RequestGaurunNotification) {
	for _, notification := range notifications {
		err := validateNotification(&notification)
		if err != nil {
			LogError.Error(err)
			continue
		}
		var enabledPush bool
		switch notification.Platform {
		case PlatFormIos:
			enabledPush = ConfGaurun.Ios.Enabled
		case PlatFormAndroid:
			enabledPush = ConfGaurun.Android.Enabled
		}
		if enabledPush {
			// Enqueue notification per token
			for _, token := range notification.Tokens {
				notification2 := notification
				notification2.Tokens = []string{token}
				notification2.ID = numberingPush()
				LogPush(notification2.ID, StatusAcceptedPush, token, 0, notification2, nil)
				QueueNotification <- notification2
			}
		}
	}
}

func classifyByDevice(reqGaurun *RequestGaurun) ([]RequestGaurunNotification, []RequestGaurunNotification) {
	var (
		reqGaurunNotificationIos     []RequestGaurunNotification
		reqGaurunNotificationAndroid []RequestGaurunNotification
	)
	for _, notification := range reqGaurun.Notifications {
		switch notification.Platform {
		case PlatFormIos:
			reqGaurunNotificationIos = append(reqGaurunNotificationIos, notification)
		case PlatFormAndroid:
			reqGaurunNotificationAndroid = append(reqGaurunNotificationAndroid, notification)
		}
	}
	return reqGaurunNotificationIos, reqGaurunNotificationAndroid
}

func pushNotificationIos(req RequestGaurunNotification) error {
	LogError.Debug("START push notification for iOS")

	service := NewApnsServiceHttp2(APNSClient)

	token := req.Tokens[0]

	headers := NewApnsHeadersHttp2(&req)
	payload := NewApnsPayloadHttp2(&req)

	stime := time.Now()
	err := ApnsPushHttp2(token, service, headers, payload)

	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()

	if err != nil {
		atomic.AddInt64(&StatGaurun.Ios.PushError, 1)
		LogPush(req.ID, StatusFailedPush, token, ptime, req, err)
		return err
	}

	atomic.AddInt64(&StatGaurun.Ios.PushSuccess, 1)
	LogPush(req.ID, StatusSucceededPush, token, ptime, req, nil)

	LogError.Debug("END push notification for iOS")

	return nil
}

func pushNotificationAndroid(req RequestGaurunNotification) error {
	LogError.Debug("START push notification for Android")

	data := map[string]interface{}{"message": req.Message}
	if len(req.Extend) > 0 {
		for _, extend := range req.Extend {
			data[extend.Key] = extend.Value
		}
	}

	token := req.Tokens[0]

	msg := gcm.NewMessage(data, token)
	msg.CollapseKey = req.CollapseKey
	msg.DelayWhileIdle = req.DelayWhileIdle
	msg.TimeToLive = req.TimeToLive

	stime := time.Now()
	resp, err := GCMClient.SendNoRetry(msg)
	etime := time.Now()
	ptime := etime.Sub(stime).Seconds()
	if err != nil {
		atomic.AddInt64(&StatGaurun.Android.PushError, 1)
		LogPush(req.ID, StatusFailedPush, token, ptime, req, err)
		return err
	}

	if resp.Failure > 0 {
		atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(resp.Success))
		atomic.AddInt64(&StatGaurun.Android.PushError, int64(resp.Failure))
		LogPush(req.ID, StatusFailedPush, token, ptime, req, errors.New(resp.Results[0].Error))
		return errors.New(resp.Results[0].Error)
	}

	LogPush(req.ID, StatusSucceededPush, token, ptime, req, nil)

	atomic.AddInt64(&StatGaurun.Android.PushSuccess, int64(len(req.Tokens)))
	LogError.Debug("END push notification for Android")

	return nil
}

func validateNotification(notification *RequestGaurunNotification) error {

	for _, token := range notification.Tokens {
		if len(token) == 0 {
			return errors.New("empty token")
		}
	}

	if notification.Platform < 1 || notification.Platform > 2 {
		return errors.New("invalid platform")
	}

	if len(notification.Message) == 0 {
		return errors.New("empty message")
	}

	return nil
}

func sendResponse(w http.ResponseWriter, msg string, code int) {
	var respGaurun ResponseGaurun

	msgJson := "{\"message\":\"" + msg + "\"}"

	err := json.Unmarshal([]byte(msgJson), &respGaurun)
	if err != nil {
		msgJson = "{\"message\":\"Response-body could not be created\"}"
	}

	w.WriteHeader(code)

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Server", serverHeader())

	fmt.Fprint(w, msgJson)
}

func PushNotificationHandler(w http.ResponseWriter, r *http.Request) {
	LogAcceptedRequest(ConfGaurun.Api.PushUri, r.Method, r.Proto, r.ContentLength)
	LogError.Debug("push-request is Accepted")

	LogError.Debug("method check")
	if r.Method != "POST" {
		sendResponse(w, "method must be POST", http.StatusBadRequest)
		return
	}

	LogError.Debug("content-length check")
	if r.ContentLength == 0 {
		sendResponse(w, "request body is empty", http.StatusBadRequest)
		return
	}

	var (
		reqGaurun RequestGaurun
		err       error
	)

	if ConfGaurun.Log.Level == "debug" {
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			sendResponse(w, "failed to read request-body", http.StatusInternalServerError)
			return
		}
		LogError.Debugf("parse request body: %s", reqBody)
		err = json.Unmarshal(reqBody, &reqGaurun)
	} else {
		LogError.Debug("parse request body")
		err = json.NewDecoder(r.Body).Decode(&reqGaurun)
	}

	if err != nil {
		LogError.Error(err)
		sendResponse(w, "Request-body is malformed", http.StatusBadRequest)
		return
	}

	if len(reqGaurun.Notifications) == 0 {
		LogError.Error("empty notification")
		sendResponse(w, "empty notification", http.StatusBadRequest)
		return
	} else if len(reqGaurun.Notifications) > ConfGaurun.Core.NotificationMax {
		msg := fmt.Sprintf("number of notifications(%d) over limit(%d)", len(reqGaurun.Notifications), ConfGaurun.Core.NotificationMax)
		LogError.Error(msg)
		sendResponse(w, msg, http.StatusBadRequest)
		return
	}

	LogError.Debug("enqueue notification")
	go enqueueNotifications(reqGaurun.Notifications)

	LogError.Debug("response to client")
	sendResponse(w, "ok", http.StatusOK)
}
<|endoftext|>


<commit_before>package gb

import (
	"fmt"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Test that the CPU passes all of the test instructions
// in the cpu_instrs rom.
func TestInstructions(t *testing.T) {
	gb := Gameboy{}
	err := gb.init("./../roms/cpu_instrs.gb", false)
	require.NoError(t, err, "error in init gb %v", err)

	// Expect the output to be 68 characters long
	expected := 106

	output := ""
	gb.TransferFunction = func(val byte) {
		output += string(val)
	}

	// Run the CPU until the output has matched the expected
	// or until 4000 iterations have passed.
	for i := 0; i < 4000; i++ {
		gb.Update()
		if len(output) >= expected {
			break
		}
	}
	require.Equal(t, len(output), expected, "did not finish getting output in 4000 iterations")

	startLen := len("cpu_instr   ")
	testOutput := output[startLen:]
	for i := int64(0); i < 11; i++ {
		t.Run(fmt.Sprintf("Test %02v", i), func(t *testing.T) {
			testString := testOutput[0:7]
			testOutput = testOutput[7:]

			testNum, err := strconv.ParseInt(testString[:2], 10, 8)
			assert.NoError(t, err, "error in parsing number: %s", testString[:2])
			assert.Equal(t, i+1, testNum, "unexpected test number")

			status := testString[3:5]
			assert.Equal(t, "ok", status, "status was not ok")
		})
	}
}
<commit_msg>Fix failing test<commit_after>package gb

import (
	"fmt"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// Test that the CPU passes all of the test instructions
// in the cpu_instrs rom.
func TestInstructions(t *testing.T) {
	gb, err := NewGameboy("./../roms/cpu_instrs.gb")
	require.NoError(t, err, "error in init gb %v", err)

	// Expect the output to be 68 characters long
	expected := 106

	output := ""
	gb.TransferFunction = func(val byte) {
		output += string(val)
	}

	// Run the CPU until the output has matched the expected
	// or until 4000 iterations have passed.
	for i := 0; i < 4000; i++ {
		gb.Update()
		if len(output) >= expected {
			break
		}
	}
	require.Equal(t, len(output), expected, "did not finish getting output in 4000 iterations")

	startLen := len("cpu_instr   ")
	testOutput := output[startLen:]
	for i := int64(0); i < 11; i++ {
		t.Run(fmt.Sprintf("Test %02v", i), func(t *testing.T) {
			testString := testOutput[0:7]
			testOutput = testOutput[7:]

			testNum, err := strconv.ParseInt(testString[:2], 10, 8)
			assert.NoError(t, err, "error in parsing number: %s", testString[:2])
			assert.Equal(t, i+1, testNum, "unexpected test number")

			status := testString[3:5]
			assert.Equal(t, "ok", status, "status was not ok")
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcsproxy

import (
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/jacobsa/gcloud/gcs"
	"golang.org/x/net/context"
)

// A read-only view on a particular generation of an object in GCS. Reads may
// involve reading from a local cache.
//
// This type is not safe for concurrent access. The user must provide external
// synchronization around the methods where it is not otherwise noted.
type ReadProxy struct {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	wrapped lease.ReadProxy
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

// Create a view on the given GCS object generation. If rl is non-nil, it must
// contain a lease for the contents of the object and will be used when
// possible instead of re-reading the object.
//
// If the object is larger than the given chunk size, we will only read
// and cache portions of it at a time.
func NewReadProxy(
	chunkSize uint64,
	leaser lease.FileLeaser,
	bucket gcs.Bucket,
	o *gcs.Object,
	rl lease.ReadLease) (rp *ReadProxy) {
	// Set up a lease.ReadProxy.
	//
	// TODO(jacobsa): Branch on chunkSize and use lease.NewMultiReadProxy if
	// necessary.
	wrapped := lease.NewReadProxy(
		leaser,
		&objectRefresher{
			Bucket: bucket,
			O:      o,
		},
		rl)

	// Serve from that.
	rp = &ReadProxy{
		wrapped: wrapped,
	}

	return
}

// Panic if any internal invariants are violated.
func (rp *ReadProxy) CheckInvariants() {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	rp.wrapped.CheckInvariants()
}

// Destroy any local file caches, putting the proxy into an indeterminate
// state. Should be used before dropping the final reference to the proxy.
func (rp *ReadProxy) Destroy() (err error) {
	rp.wrapped.Destroy()
	return
}

// Return a read/write lease for the contents of the object. This implicitly
// destroys the proxy, which must not be used further.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	rwl, err = rp.wrapped.Upgrade(ctx)
	return
}

// Return the size of the object generation in bytes.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.wrapped.Size()
	return
}

// Make a random access read into our view of the content. May block for
// network access.
//
// Guarantees that err != nil if n < len(buf)
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	n, err = rp.wrapped.ReadAt(ctx, buf, offset)
	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A refresher that returns the contents of a particular generation of a GCS
// object. Optionally, only a particular range is returned.
type objectRefresher struct {
	Bucket gcs.Bucket
	O      *gcs.Object
	Range  *gcs.ByteRange
}

func (r *objectRefresher) Size() (size int64) {
	if r.Range != nil {
		size = int64(r.Range.Limit - r.Range.Start)
		return
	}

	size = int64(r.O.Size)
	return
}

func (r *objectRefresher) Refresh(
	ctx context.Context) (rc io.ReadCloser, err error) {
	req := &gcs.ReadObjectRequest{
		Name:       r.O.Name,
		Generation: r.O.Generation,
		Range:      r.Range,
	}

	rc, err = r.Bucket.NewReader(ctx, req)
	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	return
}
<commit_msg>Refactored NewReadProxy.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcsproxy

import (
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/jacobsa/gcloud/gcs"
	"golang.org/x/net/context"
)

// A read-only view on a particular generation of an object in GCS. Reads may
// involve reading from a local cache.
//
// This type is not safe for concurrent access. The user must provide external
// synchronization around the methods where it is not otherwise noted.
type ReadProxy struct {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	wrapped lease.ReadProxy
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func makeRefreshers(
	chunkSize uint64,
	o *gcs.Object,
	bucket gcs.Bucket) (refreshers []lease.Refresher)

// Create a view on the given GCS object generation. If rl is non-nil, it must
// contain a lease for the contents of the object and will be used when
// possible instead of re-reading the object.
//
// If the object is larger than the given chunk size, we will only read
// and cache portions of it at a time.
func NewReadProxy(
	chunkSize uint64,
	leaser lease.FileLeaser,
	bucket gcs.Bucket,
	o *gcs.Object,
	rl lease.ReadLease) (rp *ReadProxy) {
	// Set up a lease.ReadProxy.
	//
	// Special case: don't bring in the complication of a multi-read proxy if we
	// have only one refresher.
	var wrapped lease.ReadProxy
	refreshers := makeRefreshers(chunkSize, o, bucket)
	if len(refreshers) == 1 {
		wrapped = lease.NewReadProxy(leaser, refreshers[0], rl)
	} else {
		wrapped = lease.NewMultiReadProxy(leaser, refreshers, rl)
	}

	// Serve from that.
	rp = &ReadProxy{
		wrapped: wrapped,
	}

	return
}

// Panic if any internal invariants are violated.
func (rp *ReadProxy) CheckInvariants() {
	// INVARIANT: wrapped.CheckInvariants does not panic.
	rp.wrapped.CheckInvariants()
}

// Destroy any local file caches, putting the proxy into an indeterminate
// state. Should be used before dropping the final reference to the proxy.
func (rp *ReadProxy) Destroy() (err error) {
	rp.wrapped.Destroy()
	return
}

// Return a read/write lease for the contents of the object. This implicitly
// destroys the proxy, which must not be used further.
func (rp *ReadProxy) Upgrade(
	ctx context.Context) (rwl lease.ReadWriteLease, err error) {
	rwl, err = rp.wrapped.Upgrade(ctx)
	return
}

// Return the size of the object generation in bytes.
func (rp *ReadProxy) Size() (size int64) {
	size = rp.wrapped.Size()
	return
}

// Make a random access read into our view of the content. May block for
// network access.
//
// Guarantees that err != nil if n < len(buf)
func (rp *ReadProxy) ReadAt(
	ctx context.Context,
	buf []byte,
	offset int64) (n int, err error) {
	n, err = rp.wrapped.ReadAt(ctx, buf, offset)
	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// A refresher that returns the contents of a particular generation of a GCS
// object. Optionally, only a particular range is returned.
type objectRefresher struct {
	Bucket gcs.Bucket
	O      *gcs.Object
	Range  *gcs.ByteRange
}

func (r *objectRefresher) Size() (size int64) {
	if r.Range != nil {
		size = int64(r.Range.Limit - r.Range.Start)
		return
	}

	size = int64(r.O.Size)
	return
}

func (r *objectRefresher) Refresh(
	ctx context.Context) (rc io.ReadCloser, err error) {
	req := &gcs.ReadObjectRequest{
		Name:       r.O.Name,
		Generation: r.O.Generation,
		Range:      r.Range,
	}

	rc, err = r.Bucket.NewReader(ctx, req)
	if err != nil {
		err = fmt.Errorf("NewReader: %v", err)
		return
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"code.google.com/p/ebml-go/webm"
	"code.google.com/p/vp8-go/vp8"
	"flag"
	"fmt"
	"image"
	"image/png"
	"io"
	"log"
	"os"
	"code.google.com/p/ffvp8-go/ffvp8"
)

var in = flag.String("i", "", "Input file")
var out = flag.String("o", "", "Output prefix")

func main() {
	flag.Parse()
	r, err := os.Open(*in)
	if err != nil {
		log.Panic("unable to open file " + *in)
	}
	br := bufio.NewReader(r)
	var wm webm.WebM
	e, rest, err := webm.Parse(br, &wm)
	d := vp8.NewDecoder()
	i := 0
	for err == nil {
		t := make([]byte, 4, 4)
		io.ReadFull(e.R, t)
		d.Init(e.R, int(e.Size()))
		_, err = d.DecodeFrameHeader()
		var img image.Image
		img, err = d.DecodeFrame()
		if err == nil {
			path := fmt.Sprint(*out, i, ".png")
			i++
			var w io.Writer
			w, err = os.Create(path)
			if err != nil {
				log.Panic("unable to open file " + *in)
			}
			png.Encode(w, img)
		}
		_, err = e.ReadData()
		e, err = rest.Next()
	}
}
<commit_msg>Use ffvp8<commit_after>package main

import (
	"bufio"
	"code.google.com/p/ebml-go/webm"
	"code.google.com/p/ffvp8-go/ffvp8"
	"flag"
	"fmt"
	"image/png"
	"io"
	"log"
	"os"
)

var in = flag.String("i", "", "Input file")
var out = flag.String("o", "", "Output prefix")

func main() {
	flag.Parse()
	r, err := os.Open(*in)
	if err != nil {
		log.Panic("unable to open file " + *in)
	}
	br := bufio.NewReader(r)
	var wm webm.WebM
	e, rest, err := webm.Parse(br, &wm)
	dec := ffvp8.NewDecoder()
	i := 0
	var track webm.TrackEntry
	for _,track = range wm.Segment.Tracks.TrackEntry {
		if track.IsVideo() {
			break
		}
	}

	for err == nil {
		t := make([]byte, 4)
		io.ReadFull(e.R, t)
		if uint(t[0]) & 0x7f == track.TrackNumber {
			data := make([]byte, e.Size())
			io.ReadFull(e.R, data)
			img := dec.Decode(data)
			path := fmt.Sprint(*out, i, ".png")
			i++
			f, err := os.Create(path)
			if err != nil {
				log.Panic("unable to open file " + *in)
			}
			png.Encode(f, img)
			f.Close()
		}
		_, err = e.ReadData()
		e, err = rest.Next()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/ebml-go/common"
	"flag"
	"fmt"
	"image"
	"image/png"
	"log"
	"os"
	"runtime"
)

var out = flag.String("o", "", "Output prefix")

func write(ch <-chan *image.YCbCr) {
	for i, img := 0, <-ch; img != nil; i, img = i+1, <-ch {
		if *out != "" {
			path := fmt.Sprint(*out, i, ".png")
			f, err := os.Create(path)
			if err != nil {
				log.Panic("unable to open file " + *out)
			}
			png.Encode(f, img)
			f.Close()
			runtime.GC()
		}
	}
}

func main() {
	common.Main(write)
}
<commit_msg>More idiomatic<commit_after>package main

import (
	"code.google.com/p/ebml-go/common"
	"code.google.com/p/ffvp8-go/ffvp8"
	"flag"
	"fmt"
	"image/png"
	"log"
	"os"
	"runtime"
)

var out = flag.String("o", "", "Output prefix")

func write(ch <-chan *ffvp8.Frame) {
	i := 0
	for img := range ch {
		if (*out != "") {
			path := fmt.Sprint(*out, i, ".png")
			f, err := os.Create(path)
			if err != nil {
				log.Panic("unable to open file " + *out)
			}
			png.Encode(f, img)
			f.Close()
			runtime.GC()
		}
		i++
	}
}

func main() {
	common.Main(write)
}
<|endoftext|>


<commit_before>package oci

import (
	"context"
	"path"
	"sync"

	"github.com/containerd/containerd/containers"
	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/namespaces"
	"github.com/containerd/containerd/oci"
	"github.com/containerd/continuity/fs"
	"github.com/docker/docker/pkg/idtools"
	"github.com/mitchellh/hashstructure"
	"github.com/moby/buildkit/executor"
	"github.com/moby/buildkit/snapshot"
	"github.com/moby/buildkit/util/network"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
)

// ProcessMode configures PID namespaces
type ProcessMode int

const (
	// ProcessSandbox unshares pidns and mount procfs.
	ProcessSandbox ProcessMode = iota
	// NoProcessSandbox uses host pidns and bind-mount procfs.
	// Note that NoProcessSandbox allows build containers to kill (and potentially ptrace) an arbitrary process in the BuildKit host namespace.
	// NoProcessSandbox should be enabled only when the BuildKit is running in a container as an unprivileged user.
	NoProcessSandbox
)

// Ideally we don't have to import whole containerd just for the default spec

// GenerateSpec generates spec using containerd functionality.
// opts are ignored for s.Process, s.Hostname, and s.Mounts .
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, idmap *idtools.IdentityMapping, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
	c := &containers.Container{
		ID: id,
	}

	// containerd/oci.GenerateSpec requires a namespace, which
	// will be used to namespace specs.Linux.CgroupsPath if generated
	if _, ok := namespaces.Namespace(ctx); !ok {
		ctx = namespaces.WithNamespace(ctx, "buildkit")
	}

	if mountOpts, err := generateMountOpts(resolvConf, hostsFile); err == nil {
		opts = append(opts, mountOpts...)
	} else {
		return nil, nil, err
	}

	if securityOpts, err := generateSecurityOpts(meta.SecurityMode); err == nil {
		opts = append(opts, securityOpts...)
	} else {
		return nil, nil, err
	}

	if processModeOpts, err := generateProcessModeOpts(processMode); err == nil {
		opts = append(opts, processModeOpts...)
	} else {
		return nil, nil, err
	}

	if idmapOpts, err := generateIDmapOpts(idmap); err == nil {
		opts = append(opts, idmapOpts...)
	} else {
		return nil, nil, err
	}

	opts = append(opts,
		oci.WithProcessArgs(meta.Args...),
		oci.WithEnv(meta.Env),
		oci.WithProcessCwd(meta.Cwd),
		oci.WithNewPrivileges,
		oci.WithHostname("buildkitsandbox"),
	)

	s, err := oci.GenerateSpec(ctx, nil, c, opts...)
	if err != nil {
		return nil, nil, err
	}
	// set the networking information on the spec
	namespace.Set(s)

	s.Process.Rlimits = nil // reset open files limit

	sm := &submounts{}

	var releasers []func() error
	releaseAll := func() {
		sm.cleanup()
		for _, f := range releasers {
			f()
		}
	}

	for _, m := range mounts {
		if m.Src == nil {
			return nil, nil, errors.Errorf("mount %s has no source", m.Dest)
		}
		mountable, err := m.Src.Mount(ctx, m.Readonly)
		if err != nil {
			releaseAll()
			return nil, nil, errors.Wrapf(err, "failed to mount %s", m.Dest)
		}
		mounts, release, err := mountable.Mount()
		if err != nil {
			releaseAll()
			return nil, nil, errors.WithStack(err)
		}
		releasers = append(releasers, release)
		for _, mount := range mounts {
			mount, err = sm.subMount(mount, m.Selector)
			if err != nil {
				releaseAll()
				return nil, nil, err
			}
			s.Mounts = append(s.Mounts, specs.Mount{
				Destination: m.Dest,
				Type:        mount.Type,
				Source:      mount.Source,
				Options:     mount.Options,
			})
		}
	}

	return s, releaseAll, nil
}

type mountRef struct {
	mount   mount.Mount
	unmount func() error
}

type submounts struct {
	m map[uint64]mountRef
}

func (s *submounts) subMount(m mount.Mount, subPath string) (mount.Mount, error) {
	if path.Join("/", subPath) == "/" {
		return m, nil
	}
	if s.m == nil {
		s.m = map[uint64]mountRef{}
	}
	h, err := hashstructure.Hash(m, nil)
	if err != nil {
		return mount.Mount{}, nil
	}
	if mr, ok := s.m[h]; ok {
		sm, err := sub(mr.mount, subPath)
		if err != nil {
			return mount.Mount{}, nil
		}
		return sm, nil
	}

	lm := snapshot.LocalMounterWithMounts([]mount.Mount{m})

	mp, err := lm.Mount()
	if err != nil {
		return mount.Mount{}, err
	}

	opts := []string{"rbind"}
	for _, opt := range m.Options {
		if opt == "ro" {
			opts = append(opts, opt)
		}
	}

	s.m[h] = mountRef{
		mount: mount.Mount{
			Source:  mp,
			Type:    "bind",
			Options: opts,
		},
		unmount: lm.Unmount,
	}

	sm, err := sub(s.m[h].mount, subPath)
	if err != nil {
		return mount.Mount{}, err
	}
	return sm, nil
}

func (s *submounts) cleanup() {
	var wg sync.WaitGroup
	wg.Add(len(s.m))
	for _, m := range s.m {
		func(m mountRef) {
			go func() {
				m.unmount()
				wg.Done()
			}()
		}(m)
	}
	wg.Wait()
}

func sub(m mount.Mount, subPath string) (mount.Mount, error) {
	src, err := fs.RootPath(m.Source, subPath)
	if err != nil {
		return mount.Mount{}, err
	}
	m.Source = src
	return m, nil
}

func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping {
	var ids []specs.LinuxIDMapping
	for _, item := range s {
		ids = append(ids, specs.LinuxIDMapping{
			HostID:      uint32(item.HostID),
			ContainerID: uint32(item.ContainerID),
			Size:        uint32(item.Size),
		})
	}
	return ids
}
<commit_msg>Don't ignore failure to setup networking<commit_after>package oci

import (
	"context"
	"path"
	"sync"

	"github.com/containerd/containerd/containers"
	"github.com/containerd/containerd/mount"
	"github.com/containerd/containerd/namespaces"
	"github.com/containerd/containerd/oci"
	"github.com/containerd/continuity/fs"
	"github.com/docker/docker/pkg/idtools"
	"github.com/mitchellh/hashstructure"
	"github.com/moby/buildkit/executor"
	"github.com/moby/buildkit/snapshot"
	"github.com/moby/buildkit/util/network"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
)

// ProcessMode configures PID namespaces
type ProcessMode int

const (
	// ProcessSandbox unshares pidns and mount procfs.
	ProcessSandbox ProcessMode = iota
	// NoProcessSandbox uses host pidns and bind-mount procfs.
	// Note that NoProcessSandbox allows build containers to kill (and potentially ptrace) an arbitrary process in the BuildKit host namespace.
	// NoProcessSandbox should be enabled only when the BuildKit is running in a container as an unprivileged user.
	NoProcessSandbox
)

// Ideally we don't have to import whole containerd just for the default spec

// GenerateSpec generates spec using containerd functionality.
// opts are ignored for s.Process, s.Hostname, and s.Mounts .
func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, idmap *idtools.IdentityMapping, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
	c := &containers.Container{
		ID: id,
	}

	// containerd/oci.GenerateSpec requires a namespace, which
	// will be used to namespace specs.Linux.CgroupsPath if generated
	if _, ok := namespaces.Namespace(ctx); !ok {
		ctx = namespaces.WithNamespace(ctx, "buildkit")
	}

	if mountOpts, err := generateMountOpts(resolvConf, hostsFile); err == nil {
		opts = append(opts, mountOpts...)
	} else {
		return nil, nil, err
	}

	if securityOpts, err := generateSecurityOpts(meta.SecurityMode); err == nil {
		opts = append(opts, securityOpts...)
	} else {
		return nil, nil, err
	}

	if processModeOpts, err := generateProcessModeOpts(processMode); err == nil {
		opts = append(opts, processModeOpts...)
	} else {
		return nil, nil, err
	}

	if idmapOpts, err := generateIDmapOpts(idmap); err == nil {
		opts = append(opts, idmapOpts...)
	} else {
		return nil, nil, err
	}

	opts = append(opts,
		oci.WithProcessArgs(meta.Args...),
		oci.WithEnv(meta.Env),
		oci.WithProcessCwd(meta.Cwd),
		oci.WithNewPrivileges,
		oci.WithHostname("buildkitsandbox"),
	)

	s, err := oci.GenerateSpec(ctx, nil, c, opts...)
	if err != nil {
		return nil, nil, err
	}

	// set the networking information on the spec
	if err := namespace.Set(s); err != nil {
		return nil, nil, err
	}

	s.Process.Rlimits = nil // reset open files limit

	sm := &submounts{}

	var releasers []func() error
	releaseAll := func() {
		sm.cleanup()
		for _, f := range releasers {
			f()
		}
	}

	for _, m := range mounts {
		if m.Src == nil {
			return nil, nil, errors.Errorf("mount %s has no source", m.Dest)
		}
		mountable, err := m.Src.Mount(ctx, m.Readonly)
		if err != nil {
			releaseAll()
			return nil, nil, errors.Wrapf(err, "failed to mount %s", m.Dest)
		}
		mounts, release, err := mountable.Mount()
		if err != nil {
			releaseAll()
			return nil, nil, errors.WithStack(err)
		}
		releasers = append(releasers, release)
		for _, mount := range mounts {
			mount, err = sm.subMount(mount, m.Selector)
			if err != nil {
				releaseAll()
				return nil, nil, err
			}
			s.Mounts = append(s.Mounts, specs.Mount{
				Destination: m.Dest,
				Type:        mount.Type,
				Source:      mount.Source,
				Options:     mount.Options,
			})
		}
	}

	return s, releaseAll, nil
}

type mountRef struct {
	mount   mount.Mount
	unmount func() error
}

type submounts struct {
	m map[uint64]mountRef
}

func (s *submounts) subMount(m mount.Mount, subPath string) (mount.Mount, error) {
	if path.Join("/", subPath) == "/" {
		return m, nil
	}
	if s.m == nil {
		s.m = map[uint64]mountRef{}
	}
	h, err := hashstructure.Hash(m, nil)
	if err != nil {
		return mount.Mount{}, nil
	}
	if mr, ok := s.m[h]; ok {
		sm, err := sub(mr.mount, subPath)
		if err != nil {
			return mount.Mount{}, nil
		}
		return sm, nil
	}

	lm := snapshot.LocalMounterWithMounts([]mount.Mount{m})

	mp, err := lm.Mount()
	if err != nil {
		return mount.Mount{}, err
	}

	opts := []string{"rbind"}
	for _, opt := range m.Options {
		if opt == "ro" {
			opts = append(opts, opt)
		}
	}

	s.m[h] = mountRef{
		mount: mount.Mount{
			Source:  mp,
			Type:    "bind",
			Options: opts,
		},
		unmount: lm.Unmount,
	}

	sm, err := sub(s.m[h].mount, subPath)
	if err != nil {
		return mount.Mount{}, err
	}
	return sm, nil
}

func (s *submounts) cleanup() {
	var wg sync.WaitGroup
	wg.Add(len(s.m))
	for _, m := range s.m {
		func(m mountRef) {
			go func() {
				m.unmount()
				wg.Done()
			}()
		}(m)
	}
	wg.Wait()
}

func sub(m mount.Mount, subPath string) (mount.Mount, error) {
	src, err := fs.RootPath(m.Source, subPath)
	if err != nil {
		return mount.Mount{}, err
	}
	m.Source = src
	return m, nil
}

func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping {
	var ids []specs.LinuxIDMapping
	for _, item := range s {
		ids = append(ids, specs.LinuxIDMapping{
			HostID:      uint32(item.HostID),
			ContainerID: uint32(item.ContainerID),
			Size:        uint32(item.Size),
		})
	}
	return ids
}
<|endoftext|>


<commit_before>package signalfx

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"strings"
	"time"
	"unicode"

	"bytes"
	"encoding/json"
	"io/ioutil"

	"sync"

	"errors"

	log "github.com/Sirupsen/logrus"
	"github.com/cep21/gohelpers/structdefaults"
	"github.com/cep21/gohelpers/workarounds"
	"github.com/golang/protobuf/proto"
	"github.com/signalfx/com_signalfx_metrics_protobuf"
	"github.com/signalfx/golib/datapoint"
	"github.com/signalfx/metricproxy/config"
	"github.com/signalfx/metricproxy/dp/dpbuffered"
	"github.com/signalfx/metricproxy/dp/dpsink"
	"github.com/signalfx/metricproxy/protocol"
	"github.com/signalfx/metricproxy/stats"
	"golang.org/x/net/context"
)

// Forwarder controls forwarding datapoints to SignalFx
type Forwarder struct {
	propertyLock          sync.Mutex
	url                   string
	defaultAuthToken      string
	tr                    *http.Transport
	client                *http.Client
	userAgent             string
	defaultSource         string
	dimensionSources      []string
	emptyMetricNameFilter dpsink.EmptyMetricFilter

	protoMarshal func(pb proto.Message) ([]byte, error)
}

var defaultConfigV2 = &config.ForwardTo{
	URL:               workarounds.GolangDoesnotAllowPointerToStringLiteral("https://ingest.signalfx.com/v2/datapoint"),
	DefaultSource:     workarounds.GolangDoesnotAllowPointerToStringLiteral(""),
	MetricCreationURL: workarounds.GolangDoesnotAllowPointerToStringLiteral(""), // Not used
	TimeoutDuration:   workarounds.GolangDoesnotAllowPointerToTimeLiteral(time.Second * 60),
	BufferSize:        workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(1000000)),
	DrainingThreads:   workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(10)),
	Name:              workarounds.GolangDoesnotAllowPointerToStringLiteral("signalfx-forwarder"),
	MaxDrainSize:      workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(3000)),
	SourceDimensions:  workarounds.GolangDoesnotAllowPointerToStringLiteral(""),
	FormatVersion:     workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(3)),
}

// ForwarderLoader loads a json forwarder forwarding points from proxy to SignalFx
func ForwarderLoader(ctx context.Context, forwardTo *config.ForwardTo) (protocol.Forwarder, error) {
	f, _, err := ForwarderLoader1(ctx, forwardTo)
	return f, err
}

// ForwarderLoader1 is a more strictly typed version of ForwarderLoader
func ForwarderLoader1(ctx context.Context, forwardTo *config.ForwardTo) (protocol.Forwarder, *Forwarder, error) {
	if forwardTo.FormatVersion == nil {
		forwardTo.FormatVersion = workarounds.GolangDoesnotAllowPointerToUintLiteral(3)
	}
	if *forwardTo.FormatVersion == 1 {
		log.WithField("forwardTo", forwardTo).Warn("Old formats not supported in signalfxforwarder.  Using newer format.  Please update config to use format version 2 or 3")
	}
	structdefaults.FillDefaultFrom(forwardTo, defaultConfigV2)
	log.WithField("forwardTo", forwardTo).Info("Creating signalfx forwarder using final config")
	fwd := NewSignalfxJSONForwarer(*forwardTo.URL, *forwardTo.TimeoutDuration,
		*forwardTo.DefaultAuthToken, *forwardTo.DrainingThreads,
		*forwardTo.DefaultSource, *forwardTo.SourceDimensions)

	counter := &dpsink.Counter{}
	dims := map[string]string{
		"forwarder": *forwardTo.Name,
	}
	buffer := dpbuffered.NewBufferedForwarder(ctx, *(&dpbuffered.Config{}).FromConfig(forwardTo), fwd)
	return &protocol.CompositeForwarder{
		Sink:   dpsink.FromChain(buffer, dpsink.NextWrap(counter)),
		Keeper: stats.ToKeeperMany(dims, counter, buffer),
		Closer: protocol.CompositeCloser(protocol.OkCloser(buffer.Close)),
	}, fwd, nil
}

// NewSignalfxJSONForwarer creates a new JSON forwarder
func NewSignalfxJSONForwarer(url string, timeout time.Duration,
	defaultAuthToken string, drainingThreads uint32,
	defaultSource string, sourceDimensions string) *Forwarder {
	tr := &http.Transport{
		TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
		MaxIdleConnsPerHost:   int(drainingThreads) * 2,
		ResponseHeaderTimeout: timeout,
		Dial: func(network, addr string) (net.Conn, error) {
			return net.DialTimeout(network, addr, timeout)
		},
	}
	ret := &Forwarder{
		url:              url,
		defaultAuthToken: defaultAuthToken,
		userAgent:        fmt.Sprintf("SignalfxProxy/0.3 (gover %s)", runtime.Version()),
		tr:               tr,
		client: &http.Client{
			Transport: tr,
		},
		protoMarshal:  proto.Marshal,
		defaultSource: defaultSource,
		// sf_source is always a dimension that can be a source
		dimensionSources: append([]string{"sf_source"}, strings.Split(sourceDimensions, ",")...),
	}
	return ret
}

func (connector *Forwarder) encodePostBodyProtobufV2(datapoints []*datapoint.Datapoint) ([]byte, string, error) {
	dps := make([]*com_signalfx_metrics_protobuf.DataPoint, 0, len(datapoints))
	for _, dp := range datapoints {
		dps = append(dps, connector.coreDatapointToProtobuf(dp))
	}
	msg := &com_signalfx_metrics_protobuf.DataPointUploadMessage{
		Datapoints: dps,
	}
	protobytes, err := connector.protoMarshal(msg)

	// Now we can send datapoints
	return protobytes, "application/x-protobuf", err
}

func datumForPoint(pv datapoint.Value) *com_signalfx_metrics_protobuf.Datum {
	switch t := pv.(type) {
	case datapoint.IntValue:
		x := t.Int()
		return &com_signalfx_metrics_protobuf.Datum{IntValue: &x}
	case datapoint.FloatValue:
		x := t.Float()
		return &com_signalfx_metrics_protobuf.Datum{DoubleValue: &x}
	default:
		x := t.String()
		return &com_signalfx_metrics_protobuf.Datum{StrValue: &x}
	}
}

func (connector *Forwarder) figureOutReasonableSource(point *datapoint.Datapoint) string {
	for _, sourceName := range connector.dimensionSources {
		thisPointSource := point.Dimensions[sourceName]
		if thisPointSource != "" {
			return thisPointSource
		}
	}
	return connector.defaultSource
}

func (connector *Forwarder) coreDatapointToProtobuf(point *datapoint.Datapoint) *com_signalfx_metrics_protobuf.DataPoint {
	thisPointSource := connector.figureOutReasonableSource(point)
	m := point.Metric
	ts := point.Timestamp.UnixNano() / time.Millisecond.Nanoseconds()
	mt := toMT(point.MetricType)
	v := &com_signalfx_metrics_protobuf.DataPoint{
		Metric:     &m,
		Timestamp:  &ts,
		Value:      datumForPoint(point.Value),
		MetricType: &mt,
		Dimensions: mapToDimensions(point.Dimensions),
	}
	if thisPointSource != "" {
		v.Source = &thisPointSource
	}
	return v
}

func mapToDimensions(dimensions map[string]string) []*com_signalfx_metrics_protobuf.Dimension {
	ret := make([]*com_signalfx_metrics_protobuf.Dimension, 0, len(dimensions))
	for k, v := range dimensions {
		if k == "" || v == "" {
			continue
		}
		// If someone knows a better way to do this, let me know.  I can't just take the &
		// of k and v because their content changes as the range iterates
		copyOfK := filterSignalfxKey(string([]byte(k)))
		copyOfV := (string([]byte(v)))
		ret = append(ret, (&com_signalfx_metrics_protobuf.Dimension{
			Key:   &copyOfK,
			Value: &copyOfV,
		}))
	}
	return ret
}

func filterSignalfxKey(str string) string {
	return strings.Map(runeFilterMap, str)
}

func runeFilterMap(r rune) rune {
	if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '_' {
		return r
	}
	return '_'
}

// Endpoint sets where metrics are sent
func (connector *Forwarder) Endpoint(endpoint string) {
	connector.propertyLock.Lock()
	defer connector.propertyLock.Unlock()
	connector.url = endpoint
}

// UserAgent sets the User-Agent header on the request
func (connector *Forwarder) UserAgent(ua string) {
	connector.propertyLock.Lock()
	defer connector.propertyLock.Unlock()
	connector.userAgent = ua
}

// AuthToken identifies who is sending the request
func (connector *Forwarder) AuthToken(authToken string) {
	connector.propertyLock.Lock()
	defer connector.propertyLock.Unlock()
	connector.defaultAuthToken = authToken
}

// TokenHeaderName is the header key for the auth token in the HTTP request
const TokenHeaderName = "X-SF-TOKEN"

type forwardError struct {
	originalError error
	message       string
}

func (f *forwardError) Error() string {
	return fmt.Sprintf("%s: %s", f.message, f.originalError.Error())
}

var _ error = &forwardError{}

// AddDatapoints forwards datapoints to SignalFx
func (connector *Forwarder) AddDatapoints(ctx context.Context, datapoints []*datapoint.Datapoint) error {
	connector.propertyLock.Lock()
	defer connector.propertyLock.Unlock()
	datapoints = connector.emptyMetricNameFilter.FilterDatapoints(datapoints)
	if len(datapoints) == 0 {
		return nil
	}
	jsonBytes, bodyType, err := connector.encodePostBodyProtobufV2(datapoints)

	if err != nil {
		return &forwardError{
			originalError: err,
			message:       "Unable to marshal object",
		}
	}
	req, _ := http.NewRequest("POST", connector.url, bytes.NewBuffer(jsonBytes))
	req.Header.Set("Content-Type", bodyType)
	req.Header.Set(TokenHeaderName, connector.defaultAuthToken)
	req.Header.Set("User-Agent", connector.userAgent)

	req.Header.Set("Connection", "Keep-Alive")

	// TODO: Set timeout from ctx
	resp, err := connector.client.Do(req)

	if err != nil {
		return &forwardError{
			originalError: err,
			message:       "Unable to POST request",
		}
	}

	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return &forwardError{
			originalError: err,
			message:       "Unable to verify response body",
		}
	}
	if resp.StatusCode != 200 {
		return &forwardError{
			originalError: fmt.Errorf("invalid status code: %d", resp.StatusCode),
			message:       string(respBody),
		}
	}
	var body string
	err = json.Unmarshal(respBody, &body)
	if err != nil {
		return &forwardError{
			originalError: err,
			message:       string(respBody),
		}
	}
	if body != "OK" {
		return &forwardError{
			originalError: errors.New("body decode error"),
			message:       body,
		}
	}
	return nil
}
<commit_msg>Hold lock less<commit_after>package signalfx

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"strings"
	"time"
	"unicode"

	"bytes"
	"encoding/json"
	"io/ioutil"

	"sync"

	"errors"

	log "github.com/Sirupsen/logrus"
	"github.com/cep21/gohelpers/structdefaults"
	"github.com/cep21/gohelpers/workarounds"
	"github.com/golang/protobuf/proto"
	"github.com/signalfx/com_signalfx_metrics_protobuf"
	"github.com/signalfx/golib/datapoint"
	"github.com/signalfx/metricproxy/config"
	"github.com/signalfx/metricproxy/dp/dpbuffered"
	"github.com/signalfx/metricproxy/dp/dpsink"
	"github.com/signalfx/metricproxy/protocol"
	"github.com/signalfx/metricproxy/stats"
	"golang.org/x/net/context"
)

// Forwarder controls forwarding datapoints to SignalFx
type Forwarder struct {
	propertyLock          sync.Mutex
	url                   string
	defaultAuthToken      string
	tr                    *http.Transport
	client                *http.Client
	userAgent             string
	defaultSource         string
	dimensionSources      []string
	emptyMetricNameFilter dpsink.EmptyMetricFilter

	protoMarshal func(pb proto.Message) ([]byte, error)
}

var defaultConfigV2 = &config.ForwardTo{
	URL:               workarounds.GolangDoesnotAllowPointerToStringLiteral("https://ingest.signalfx.com/v2/datapoint"),
	DefaultSource:     workarounds.GolangDoesnotAllowPointerToStringLiteral(""),
	MetricCreationURL: workarounds.GolangDoesnotAllowPointerToStringLiteral(""), // Not used
	TimeoutDuration:   workarounds.GolangDoesnotAllowPointerToTimeLiteral(time.Second * 60),
	BufferSize:        workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(1000000)),
	DrainingThreads:   workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(10)),
	Name:              workarounds.GolangDoesnotAllowPointerToStringLiteral("signalfx-forwarder"),
	MaxDrainSize:      workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(3000)),
	SourceDimensions:  workarounds.GolangDoesnotAllowPointerToStringLiteral(""),
	FormatVersion:     workarounds.GolangDoesnotAllowPointerToUintLiteral(uint32(3)),
}

// ForwarderLoader loads a json forwarder forwarding points from proxy to SignalFx
func ForwarderLoader(ctx context.Context, forwardTo *config.ForwardTo) (protocol.Forwarder, error) {
	f, _, err := ForwarderLoader1(ctx, forwardTo)
	return f, err
}

// ForwarderLoader1 is a more strictly typed version of ForwarderLoader
func ForwarderLoader1(ctx context.Context, forwardTo *config.ForwardTo) (protocol.Forwarder, *Forwarder, error) {
	if forwardTo.FormatVersion == nil {
		forwardTo.FormatVersion = workarounds.GolangDoesnotAllowPointerToUintLiteral(3)
	}
	if *forwardTo.FormatVersion == 1 {
		log.WithField("forwardTo", forwardTo).Warn("Old formats not supported in signalfxforwarder.  Using newer format.  Please update config to use format version 2 or 3")
	}
	structdefaults.FillDefaultFrom(forwardTo, defaultConfigV2)
	log.WithField("forwardTo", forwardTo).Info("Creating signalfx forwarder using final config")
	fwd := NewSignalfxJSONForwarer(*forwardTo.URL, *forwardTo.TimeoutDuration,
		*forwardTo.DefaultAuthToken, *forwardTo.DrainingThreads,
		*forwardTo.DefaultSource, *forwardTo.SourceDimensions)

	counter := &dpsink.Counter{}
	dims := map[string]string{
		"forwarder": *forwardTo.Name,
	}
	buffer := dpbuffered.NewBufferedForwarder(ctx, *(&dpbuffered.Config{}).FromConfig(forwardTo), fwd)
	return &protocol.CompositeForwarder{
		Sink:   dpsink.FromChain(buffer, dpsink.NextWrap(counter)),
		Keeper: stats.ToKeeperMany(dims, counter, buffer),
		Closer: protocol.CompositeCloser(protocol.OkCloser(buffer.Close)),
	}, fwd, nil
}

// NewSignalfxJSONForwarer creates a new JSON forwarder
func NewSignalfxJSONForwarer(url string, timeout time.Duration,
	defaultAuthToken string, drainingThreads uint32,
	defaultSource string, sourceDimensions string) *Forwarder {
	tr := &http.Transport{
		TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
		MaxIdleConnsPerHost:   int(drainingThreads) * 2,
		ResponseHeaderTimeout: timeout,
		Dial: func(network, addr string) (net.Conn, error) {
			return net.DialTimeout(network, addr, timeout)
		},
	}
	ret := &Forwarder{
		url:              url,
		defaultAuthToken: defaultAuthToken,
		userAgent:        fmt.Sprintf("SignalfxProxy/0.3 (gover %s)", runtime.Version()),
		tr:               tr,
		client: &http.Client{
			Transport: tr,
		},
		protoMarshal:  proto.Marshal,
		defaultSource: defaultSource,
		// sf_source is always a dimension that can be a source
		dimensionSources: append([]string{"sf_source"}, strings.Split(sourceDimensions, ",")...),
	}
	return ret
}

func (connector *Forwarder) encodePostBodyProtobufV2(datapoints []*datapoint.Datapoint) ([]byte, string, error) {
	dps := make([]*com_signalfx_metrics_protobuf.DataPoint, 0, len(datapoints))
	for _, dp := range datapoints {
		dps = append(dps, connector.coreDatapointToProtobuf(dp))
	}
	msg := &com_signalfx_metrics_protobuf.DataPointUploadMessage{
		Datapoints: dps,
	}
	protobytes, err := connector.protoMarshal(msg)

	// Now we can send datapoints
	return protobytes, "application/x-protobuf", err
}

func datumForPoint(pv datapoint.Value) *com_signalfx_metrics_protobuf.Datum {
	switch t := pv.(type) {
	case datapoint.IntValue:
		x := t.Int()
		return &com_signalfx_metrics_protobuf.Datum{IntValue: &x}
	case datapoint.FloatValue:
		x := t.Float()
		return &com_signalfx_metrics_protobuf.Datum{DoubleValue: &x}
	default:
		x := t.String()
		return &com_signalfx_metrics_protobuf.Datum{StrValue: &x}
	}
}

func (connector *Forwarder) figureOutReasonableSource(point *datapoint.Datapoint) string {
	for _, sourceName := range connector.dimensionSources {
		thisPointSource := point.Dimensions[sourceName]
		if thisPointSource != "" {
			return thisPointSource
		}
	}
	return connector.defaultSource
}

func (connector *Forwarder) coreDatapointToProtobuf(point *datapoint.Datapoint) *com_signalfx_metrics_protobuf.DataPoint {
	thisPointSource := connector.figureOutReasonableSource(point)
	m := point.Metric
	ts := point.Timestamp.UnixNano() / time.Millisecond.Nanoseconds()
	mt := toMT(point.MetricType)
	v := &com_signalfx_metrics_protobuf.DataPoint{
		Metric:     &m,
		Timestamp:  &ts,
		Value:      datumForPoint(point.Value),
		MetricType: &mt,
		Dimensions: mapToDimensions(point.Dimensions),
	}
	if thisPointSource != "" {
		v.Source = &thisPointSource
	}
	return v
}

func mapToDimensions(dimensions map[string]string) []*com_signalfx_metrics_protobuf.Dimension {
	ret := make([]*com_signalfx_metrics_protobuf.Dimension, 0, len(dimensions))
	for k, v := range dimensions {
		if k == "" || v == "" {
			continue
		}
		// If someone knows a better way to do this, let me know.  I can't just take the &
		// of k and v because their content changes as the range iterates
		copyOfK := filterSignalfxKey(string([]byte(k)))
		copyOfV := (string([]byte(v)))
		ret = append(ret, (&com_signalfx_metrics_protobuf.Dimension{
			Key:   &copyOfK,
			Value: &copyOfV,
		}))
	}
	return ret
}

func filterSignalfxKey(str string) string {
	return strings.Map(runeFilterMap, str)
}

func runeFilterMap(r rune) rune {
	if unicode.IsDigit(r) || unicode.IsLetter(r) || r == '_' {
		return r
	}
	return '_'
}

// Endpoint sets where metrics are sent
func (connector *Forwarder) Endpoint(endpoint string) {
	connector.propertyLock.Lock()
	defer connector.propertyLock.Unlock()
	connector.url = endpoint
}

// UserAgent sets the User-Agent header on the request
func (connector *Forwarder) UserAgent(ua string) {
	connector.propertyLock.Lock()
	defer connector.propertyLock.Unlock()
	connector.userAgent = ua
}

// AuthToken identifies who is sending the request
func (connector *Forwarder) AuthToken(authToken string) {
	connector.propertyLock.Lock()
	defer connector.propertyLock.Unlock()
	connector.defaultAuthToken = authToken
}

// TokenHeaderName is the header key for the auth token in the HTTP request
const TokenHeaderName = "X-SF-TOKEN"

type forwardError struct {
	originalError error
	message       string
}

func (f *forwardError) Error() string {
	return fmt.Sprintf("%s: %s", f.message, f.originalError.Error())
}

var _ error = &forwardError{}

// AddDatapoints forwards datapoints to SignalFx
func (connector *Forwarder) AddDatapoints(ctx context.Context, datapoints []*datapoint.Datapoint) error {
	connector.propertyLock.Lock()
	endpoint := connector.url
	userAgent := connector.userAgent
	defautlAuthToken := connector.defaultAuthToken
	connector.propertyLock.Unlock()
	datapoints = connector.emptyMetricNameFilter.FilterDatapoints(datapoints)
	if len(datapoints) == 0 {
		return nil
	}
	jsonBytes, bodyType, err := connector.encodePostBodyProtobufV2(datapoints)

	if err != nil {
		return &forwardError{
			originalError: err,
			message:       "Unable to marshal object",
		}
	}
	req, _ := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonBytes))
	req.Header.Set("Content-Type", bodyType)
	req.Header.Set(TokenHeaderName, defautlAuthToken)
	req.Header.Set("User-Agent", userAgent)

	req.Header.Set("Connection", "Keep-Alive")

	// TODO: Set timeout from ctx
	resp, err := connector.client.Do(req)

	if err != nil {
		return &forwardError{
			originalError: err,
			message:       "Unable to POST request",
		}
	}

	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return &forwardError{
			originalError: err,
			message:       "Unable to verify response body",
		}
	}
	if resp.StatusCode != 200 {
		return &forwardError{
			originalError: fmt.Errorf("invalid status code: %d", resp.StatusCode),
			message:       string(respBody),
		}
	}
	var body string
	err = json.Unmarshal(respBody, &body)
	if err != nil {
		return &forwardError{
			originalError: err,
			message:       string(respBody),
		}
	}
	if body != "OK" {
		return &forwardError{
			originalError: errors.New("body decode error"),
			message:       body,
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go 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 types2

import (
	"cmd/compile/internal/syntax"
	"sync"
)

// TODO(gri) Clean up Named struct below; specifically the fromRHS field (can we use underlying?).

// A Named represents a named (defined) type.
type Named struct {
	check      *Checker
	info       typeInfo    // for cycle detection
	obj        *TypeName   // corresponding declared object
	orig       *Named      // original, uninstantiated type
	fromRHS    Type        // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
	underlying Type        // possibly a *Named during setup; never a *Named once set up completely
	instance   *instance   // position information for lazy instantiation, or nil
	tparams    *TypeParams // type parameters, or nil
	targs      []Type      // type arguments (after instantiation), or nil
	methods    []*Func     // methods declared for this type (not the method set of this type); signatures are type-checked lazily

	resolve func(*Named) ([]*TypeName, Type, []*Func)
	once    sync.Once
}

// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
// If the given type name obj doesn't have a type yet, its type is set to the returned named type.
// The underlying type must not be a *Named.
func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
	if _, ok := underlying.(*Named); ok {
		panic("underlying type must not be *Named")
	}
	return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
}

func (t *Named) load() *Named {
	// If t is an instantiated type, it derives its methods and tparams from its
	// base type. Since we expect type parameters and methods to be set after a
	// call to load, we must load the base and copy here.
	//
	// underlying is set when t is expanded.
	//
	// By convention, a type instance is loaded iff its tparams are set.
	if len(t.targs) > 0 && t.tparams == nil {
		t.orig.load()
		t.tparams = t.orig.tparams
		t.methods = t.orig.methods
	}
	if t.resolve == nil {
		return t
	}

	t.once.Do(func() {
		// TODO(mdempsky): Since we're passing t to resolve anyway
		// (necessary because types2 expects the receiver type for methods
		// on defined interface types to be the Named rather than the
		// underlying Interface), maybe it should just handle calling
		// SetTParams, SetUnderlying, and AddMethod instead?  Those
		// methods would need to support reentrant calls though.  It would
		// also make the API more future-proof towards further extensions
		// (like SetTParams).

		tparams, underlying, methods := t.resolve(t)

		switch underlying.(type) {
		case nil, *Named:
			panic("invalid underlying type")
		}

		t.tparams = bindTParams(tparams)
		t.underlying = underlying
		t.methods = methods
	})
	return t
}

// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParams, methods []*Func) *Named {
	typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
	if typ.orig == nil {
		typ.orig = typ
	}
	if obj.typ == nil {
		obj.typ = typ
	}
	// Ensure that typ is always expanded, at which point the check field can be
	// nilled out.
	//
	// Note that currently we cannot nil out check inside typ.under(), because
	// it's possible that typ is expanded multiple times.
	//
	// TODO(gri): clean this up so that under is the only function mutating
	//            named types.
	if check != nil {
		check.later(func() {
			switch typ.under().(type) {
			case *Named:
				panic("unexpanded underlying type")
			}
			typ.check = nil
		})
	}
	return typ
}

// Obj returns the type name for the named type t.
func (t *Named) Obj() *TypeName { return t.obj }

// Orig returns the original generic type an instantiated type is derived from.
// If t is not an instantiated type, the result is t.
func (t *Named) Orig() *Named { return t.orig }

// TODO(gri) Come up with a better representation and API to distinguish
//           between parameterized instantiated and non-instantiated types.

// TParams returns the type parameters of the named type t, or nil.
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
func (t *Named) TParams() *TypeParams { return t.load().tparams }

// SetTParams sets the type parameters of the named type t.
func (t *Named) SetTParams(tparams []*TypeName) { t.load().tparams = bindTParams(tparams) }

// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
func (t *Named) TArgs() []Type { return t.targs }

// NumMethods returns the number of explicit methods whose receiver is named type t.
func (t *Named) NumMethods() int { return len(t.load().methods) }

// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
func (t *Named) Method(i int) *Func { return t.load().methods[i] }

// SetUnderlying sets the underlying type and marks t as complete.
func (t *Named) SetUnderlying(underlying Type) {
	if underlying == nil {
		panic("underlying type must not be nil")
	}
	if _, ok := underlying.(*Named); ok {
		panic("underlying type must not be *Named")
	}
	t.load().underlying = underlying
}

// AddMethod adds method m unless it is already in the method list.
func (t *Named) AddMethod(m *Func) {
	t.load()
	if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
		t.methods = append(t.methods, m)
	}
}

func (t *Named) Underlying() Type { return t.load().expand(nil).underlying }
func (t *Named) String() string   { return TypeString(t, nil) }

// ----------------------------------------------------------------------------
// Implementation

// under returns the expanded underlying type of n0; possibly by following
// forward chains of named types. If an underlying type is found, resolve
// the chain by setting the underlying type for each defined type in the
// chain before returning it. If no underlying type is found or a cycle
// is detected, the result is Typ[Invalid]. If a cycle is detected and
// n0.check != nil, the cycle is reported.
func (n0 *Named) under() Type {
	u := n0.Underlying()

	// If the underlying type of a defined type is not a defined
	// (incl. instance) type, then that is the desired underlying
	// type.
	var n1 *Named
	switch u1 := u.(type) {
	case nil:
		return Typ[Invalid]
	default:
		// common case
		return u
	case *Named:
		// handled below
		n1 = u1
	}

	if n0.check == nil {
		panic("Named.check == nil but type is incomplete")
	}

	// Invariant: after this point n0 as well as any named types in its
	// underlying chain should be set up when this function exits.
	check := n0.check
	n := n0

	seen := make(map[*Named]int) // types that need their underlying resolved
	var path []Object            // objects encountered, for cycle reporting

loop:
	for {
		seen[n] = len(seen)
		path = append(path, n.obj)
		n = n1
		if i, ok := seen[n]; ok {
			// cycle
			check.cycleError(path[i:])
			u = Typ[Invalid]
			break
		}
		u = n.Underlying()
		switch u1 := u.(type) {
		case nil:
			u = Typ[Invalid]
			break loop
		default:
			break loop
		case *Named:
			// Continue collecting *Named types in the chain.
			n1 = u1
		}
	}

	for n := range seen {
		// We should never have to update the underlying type of an imported type;
		// those underlying types should have been resolved during the import.
		// Also, doing so would lead to a race condition (was issue #31749).
		// Do this check always, not just in debug mode (it's cheap).
		if n.obj.pkg != check.pkg {
			panic("imported type with unresolved underlying type")
		}
		n.underlying = u
	}

	return u
}

func (n *Named) setUnderlying(typ Type) {
	if n != nil {
		n.underlying = typ
	}
}

// instance holds position information for use in lazy instantiation.
//
// TODO(rfindley): come up with a better name for this type, now that its usage
// has changed.
type instance struct {
	pos     syntax.Pos   // position of type instantiation; for error reporting only
	posList []syntax.Pos // position of each targ; for error reporting only
}

// expand ensures that the underlying type of n is instantiated.
// The underlying type will be Typ[Invalid] if there was an error.
func (n *Named) expand(typMap map[string]*Named) *Named {
	if n.instance != nil {
		// n must be loaded before instantiation, in order to have accurate
		// tparams. This is done implicitly by the call to n.TParams, but making it
		// explicit is harmless: load is idempotent.
		n.load()
		if typMap == nil {
			if n.check != nil {
				typMap = n.check.typMap
			} else {
				// If we're instantiating lazily, we might be outside the scope of a
				// type-checking pass. In that case we won't have a pre-existing
				// typMap, but don't want to create a duplicate of the current instance
				// in the process of expansion.
				h := instantiatedHash(n.orig, n.targs)
				typMap = map[string]*Named{h: n}
			}
		}

		inst := n.check.instantiate(n.instance.pos, n.orig.underlying, n.TParams().list(), n.targs, n.instance.posList, typMap)
		n.underlying = inst
		n.fromRHS = inst
		n.instance = nil
	}
	return n
}

// safeUnderlying returns the underlying of typ without expanding instances, to
// avoid infinite recursion.
//
// TODO(rfindley): eliminate this function or give it a better name.
func safeUnderlying(typ Type) Type {
	if t, _ := typ.(*Named); t != nil {
		return t.load().underlying
	}
	return typ.Underlying()
}
<commit_msg>cmd/compile/internal/types2: use the orig object for Named.Obj<commit_after>// Copyright 2011 The Go 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 types2

import (
	"cmd/compile/internal/syntax"
	"sync"
)

// TODO(gri) Clean up Named struct below; specifically the fromRHS field (can we use underlying?).

// A Named represents a named (defined) type.
type Named struct {
	check      *Checker
	info       typeInfo    // for cycle detection
	obj        *TypeName   // corresponding declared object for declared types; placeholder for instantiated types
	orig       *Named      // original, uninstantiated type
	fromRHS    Type        // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
	underlying Type        // possibly a *Named during setup; never a *Named once set up completely
	instance   *instance   // position information for lazy instantiation, or nil
	tparams    *TypeParams // type parameters, or nil
	targs      []Type      // type arguments (after instantiation), or nil
	methods    []*Func     // methods declared for this type (not the method set of this type); signatures are type-checked lazily

	resolve func(*Named) ([]*TypeName, Type, []*Func)
	once    sync.Once
}

// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
// If the given type name obj doesn't have a type yet, its type is set to the returned named type.
// The underlying type must not be a *Named.
func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
	if _, ok := underlying.(*Named); ok {
		panic("underlying type must not be *Named")
	}
	return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
}

func (t *Named) load() *Named {
	// If t is an instantiated type, it derives its methods and tparams from its
	// base type. Since we expect type parameters and methods to be set after a
	// call to load, we must load the base and copy here.
	//
	// underlying is set when t is expanded.
	//
	// By convention, a type instance is loaded iff its tparams are set.
	if len(t.targs) > 0 && t.tparams == nil {
		t.orig.load()
		t.tparams = t.orig.tparams
		t.methods = t.orig.methods
	}
	if t.resolve == nil {
		return t
	}

	t.once.Do(func() {
		// TODO(mdempsky): Since we're passing t to resolve anyway
		// (necessary because types2 expects the receiver type for methods
		// on defined interface types to be the Named rather than the
		// underlying Interface), maybe it should just handle calling
		// SetTParams, SetUnderlying, and AddMethod instead?  Those
		// methods would need to support reentrant calls though.  It would
		// also make the API more future-proof towards further extensions
		// (like SetTParams).

		tparams, underlying, methods := t.resolve(t)

		switch underlying.(type) {
		case nil, *Named:
			panic("invalid underlying type")
		}

		t.tparams = bindTParams(tparams)
		t.underlying = underlying
		t.methods = methods
	})
	return t
}

// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParams, methods []*Func) *Named {
	typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
	if typ.orig == nil {
		typ.orig = typ
	}
	if obj.typ == nil {
		obj.typ = typ
	}
	// Ensure that typ is always expanded, at which point the check field can be
	// nilled out.
	//
	// Note that currently we cannot nil out check inside typ.under(), because
	// it's possible that typ is expanded multiple times.
	//
	// TODO(gri): clean this up so that under is the only function mutating
	//            named types.
	if check != nil {
		check.later(func() {
			switch typ.under().(type) {
			case *Named:
				panic("unexpanded underlying type")
			}
			typ.check = nil
		})
	}
	return typ
}

// Obj returns the type name for the declaration defining the named type t. For
// instantiated types, this is the type name of the base type.
func (t *Named) Obj() *TypeName {
	return t.orig.obj // for non-instances this is the same as t.obj
}

// Orig returns the original generic type an instantiated type is derived from.
// If t is not an instantiated type, the result is t.
func (t *Named) Orig() *Named { return t.orig }

// TODO(gri) Come up with a better representation and API to distinguish
//           between parameterized instantiated and non-instantiated types.

// TParams returns the type parameters of the named type t, or nil.
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
func (t *Named) TParams() *TypeParams { return t.load().tparams }

// SetTParams sets the type parameters of the named type t.
func (t *Named) SetTParams(tparams []*TypeName) { t.load().tparams = bindTParams(tparams) }

// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
func (t *Named) TArgs() []Type { return t.targs }

// NumMethods returns the number of explicit methods whose receiver is named type t.
func (t *Named) NumMethods() int { return len(t.load().methods) }

// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
func (t *Named) Method(i int) *Func { return t.load().methods[i] }

// SetUnderlying sets the underlying type and marks t as complete.
func (t *Named) SetUnderlying(underlying Type) {
	if underlying == nil {
		panic("underlying type must not be nil")
	}
	if _, ok := underlying.(*Named); ok {
		panic("underlying type must not be *Named")
	}
	t.load().underlying = underlying
}

// AddMethod adds method m unless it is already in the method list.
func (t *Named) AddMethod(m *Func) {
	t.load()
	if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
		t.methods = append(t.methods, m)
	}
}

func (t *Named) Underlying() Type { return t.load().expand(nil).underlying }
func (t *Named) String() string   { return TypeString(t, nil) }

// ----------------------------------------------------------------------------
// Implementation

// under returns the expanded underlying type of n0; possibly by following
// forward chains of named types. If an underlying type is found, resolve
// the chain by setting the underlying type for each defined type in the
// chain before returning it. If no underlying type is found or a cycle
// is detected, the result is Typ[Invalid]. If a cycle is detected and
// n0.check != nil, the cycle is reported.
func (n0 *Named) under() Type {
	u := n0.Underlying()

	// If the underlying type of a defined type is not a defined
	// (incl. instance) type, then that is the desired underlying
	// type.
	var n1 *Named
	switch u1 := u.(type) {
	case nil:
		return Typ[Invalid]
	default:
		// common case
		return u
	case *Named:
		// handled below
		n1 = u1
	}

	if n0.check == nil {
		panic("Named.check == nil but type is incomplete")
	}

	// Invariant: after this point n0 as well as any named types in its
	// underlying chain should be set up when this function exits.
	check := n0.check
	n := n0

	seen := make(map[*Named]int) // types that need their underlying resolved
	var path []Object            // objects encountered, for cycle reporting

loop:
	for {
		seen[n] = len(seen)
		path = append(path, n.obj)
		n = n1
		if i, ok := seen[n]; ok {
			// cycle
			check.cycleError(path[i:])
			u = Typ[Invalid]
			break
		}
		u = n.Underlying()
		switch u1 := u.(type) {
		case nil:
			u = Typ[Invalid]
			break loop
		default:
			break loop
		case *Named:
			// Continue collecting *Named types in the chain.
			n1 = u1
		}
	}

	for n := range seen {
		// We should never have to update the underlying type of an imported type;
		// those underlying types should have been resolved during the import.
		// Also, doing so would lead to a race condition (was issue #31749).
		// Do this check always, not just in debug mode (it's cheap).
		if n.obj.pkg != check.pkg {
			panic("imported type with unresolved underlying type")
		}
		n.underlying = u
	}

	return u
}

func (n *Named) setUnderlying(typ Type) {
	if n != nil {
		n.underlying = typ
	}
}

// instance holds position information for use in lazy instantiation.
//
// TODO(rfindley): come up with a better name for this type, now that its usage
// has changed.
type instance struct {
	pos     syntax.Pos   // position of type instantiation; for error reporting only
	posList []syntax.Pos // position of each targ; for error reporting only
}

// expand ensures that the underlying type of n is instantiated.
// The underlying type will be Typ[Invalid] if there was an error.
func (n *Named) expand(typMap map[string]*Named) *Named {
	if n.instance != nil {
		// n must be loaded before instantiation, in order to have accurate
		// tparams. This is done implicitly by the call to n.TParams, but making it
		// explicit is harmless: load is idempotent.
		n.load()
		if typMap == nil {
			if n.check != nil {
				typMap = n.check.typMap
			} else {
				// If we're instantiating lazily, we might be outside the scope of a
				// type-checking pass. In that case we won't have a pre-existing
				// typMap, but don't want to create a duplicate of the current instance
				// in the process of expansion.
				h := instantiatedHash(n.orig, n.targs)
				typMap = map[string]*Named{h: n}
			}
		}

		inst := n.check.instantiate(n.instance.pos, n.orig.underlying, n.TParams().list(), n.targs, n.instance.posList, typMap)
		n.underlying = inst
		n.fromRHS = inst
		n.instance = nil
	}
	return n
}

// safeUnderlying returns the underlying of typ without expanding instances, to
// avoid infinite recursion.
//
// TODO(rfindley): eliminate this function or give it a better name.
func safeUnderlying(typ Type) Type {
	if t, _ := typ.(*Named); t != nil {
		return t.load().underlying
	}
	return typ.Underlying()
}
<|endoftext|>