<commit_before>package ruffnote

import (
	"fmt"
	"net/http"
	"runtime"
	"strings"

	"appengine"
	"appengine/datastore"
	"appengine/user"
	"github.com/GoogleCloudPlatform/go-endpoints/endpoints"
	"github.com/mjibson/goon"
)

const (
	clientId = "" //TODO
	debug    = true
)

var (
	scopes    = []string{endpoints.EmailScope}
	clientIds = []string{clientId, endpoints.APIExplorerClientID}
	audiences = []string{clientId}
)

func RegisterService() (rpcService *endpoints.RPCService, err error) {
	api := &RuffnoteApi{}
	rpcService, err = endpoints.RegisterService(api,
		"ruffnote_plus", "v1", "ruffnote+", true)
	if err != nil {
		return
	}

	info := rpcService.MethodByName("Items").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.list", "GET", "items", "List items."
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("Availabile").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.availabile", "GET", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("Create").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.create", "POST", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("ReName").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.rename", "PUT", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("Delete").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.delete", "DELETE", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	return
}

func makeEndpointsError(c endpoints.Context, err error) error {
	switch err.(type) {
	case *endpoints.APIError:
	default:
		if debug {
			c.Debugf("%s: %s", fmt.Sprintln(runtime.Caller(1)), err.Error())
		}
		message := err.Error()
		if appengine.IsCapabilityDisabled(err) {
			err = endpoints.NewInternalServerError(message)
			c.Errorf(message)
		} else if appengine.IsOverQuota(err) {
			err = endpoints.NewInternalServerError(message)
			c.Errorf(message)
		} else if appengine.IsTimeoutError(err) {
			err = endpoints.NewInternalServerError(message)
			c.Errorf(message)
		} else if strings.Contains(message, "500") {
			err = endpoints.InternalServerError
			c.Errorf(message)
		} else if strings.Contains(message, "400") {
			err = endpoints.NewBadRequestError(message)
		} else if strings.Contains(message, "401") {
			err = endpoints.NewUnauthorizedError(message)
		} else if strings.Contains(message, "403") {
			err = endpoints.NewForbiddenError(message)
		} else if strings.Contains(message, "404") {
			err = endpoints.NewNotFoundError(message)
		} else if strings.Contains(message, "409") {
			err = endpoints.NewConflictError(message)
		} else {
			err = endpoints.BadRequestError
			c.Errorf(message)
		}
	}
	return err
}

type RuffnoteApi struct {
}

func (ruffnoteApi *RuffnoteApi) Items(r *http.Request,
	req *ItemsRequestMessage, resp *ItemsResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	items, err := getItemsByUser(c, u, req.Limit)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}
	resp.Items = make([]*ItemResponseMessage, len(items))
	for i, item := range items {
		resp.Items[i] = item.toMessage(nil)
	}
	return
}

func (ruffnoteApi *RuffnoteApi) Availabile(r *http.Request,
	req *AvailabileRequestMessage, resp *AvailabileResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	_, err = getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	resp.Name = req.Name
	_, err = GetItemByName(c, req.Name)
	if err == datastore.ErrNoSuchEntity {
		err = nil
		resp.Status = true
		return
	}
	return
}

func (ruffnoteApi *RuffnoteApi) Create(r *http.Request,
	req *CreateRequestMessage, resp *ItemResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	err = req.Validate()
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	item, err := createItem(c, u, "id", req.Name)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}
	item.toMessage(resp)
	return
}

func (ruffnoteApi *RuffnoteApi) ReName(r *http.Request,
	req *ReNameRequestMessage, resp *ItemResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return err
	}

	//TODO check id
	id := req.Id
	item, err := createItem(c, u, id, req.Name)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}
	item.toMessage(resp)

	items, err := getItemsByUser(c, u, 100)
	if err != nil {
		c.Errorf("%v", err)
		err = makeEndpointsError(c, err)
		return
	}
	deleteItemsKey := make([]*datastore.Key, 0, len(items))
	for i, item := range items {
		if item.Id() != id {
			continue
		}
		if item.Name() == req.Name {
			continue
		}
		deleteItemsKey[i] = item.key
	}
	g := goon.FromContext(c)
	err = g.DeleteMulti(deleteItemsKey)
	if err != nil {
		c.Warningf("%v", err)
		err = makeEndpointsError(c, err)
	}
	return
}

func (ruffnoteApi *RuffnoteApi) Delete(r *http.Request,
	req *DeleteRequestMessage, resp *DeleteResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	items, err := getItemsByUser(c, u, 100)
	if err != nil {
		c.Errorf("%v", err)
		err = makeEndpointsError(c, err)
		return
	}
	deleteItemsKey := make([]*datastore.Key, 0, len(items))
	for i, item := range items {
		if item.Id() != req.Id {
			continue
		}
		deleteItemsKey[i] = item.key
	}
	g := goon.FromContext(c)
	err = g.DeleteMulti(deleteItemsKey)
	if err != nil {
		c.Warningf("%v", err)
		err = makeEndpointsError(c, err)
	}
	return
}

func getCurrentUser(c endpoints.Context) (u *user.User, err error) {
	u, err = endpoints.CurrentUser(c, scopes, audiences, clientIds)
	if err != nil {
		c.Infof("%v", err)
		err = endpoints.UnauthorizedError
		return
	}
	if u == nil {
		err = endpoints.UnauthorizedError
		return
	}
	return
}

func createItem(c endpoints.Context, u *user.User, id string, name string) (item *Item, err error) {
	item = newItem(id, name, u)

	g := goon.FromContext(c)
	err = g.RunInTransaction(func(tg *goon.Goon) (err error) {
		_, err = GetItemByName(c, name)
		if err != datastore.ErrNoSuchEntity {
			err = endpoints.ConflictError
			return
		}
		err = item.put(c)
		if err != nil {
			c.Errorf("%v", err)
			err = makeEndpointsError(c, err)
		}
		return
	}, nil)

	if err != datastore.ErrConcurrentTransaction {
		c.Errorf("Transaction failed: %v", err)
	}
	return
}
<commit_msg>notes in development<commit_after>package ruffnote

import (
	"fmt"
	"net/http"
	"runtime"
	"strings"

	"appengine"
	"appengine/datastore"
	"appengine/user"
	"github.com/GoogleCloudPlatform/go-endpoints/endpoints"
	"github.com/mjibson/goon"
)

const (
	clientId = "" //TODO
	debug    = true
)

var (
	scopes    = []string{endpoints.EmailScope}
	clientIds = []string{clientId, endpoints.APIExplorerClientID}
	audiences = []string{clientId}
)

func RegisterService() (rpcService *endpoints.RPCService, err error) {
	api := &RuffnoteApi{}
	rpcService, err = endpoints.RegisterService(api,
		"ruffnote_plus", "v1", "ruffnote+ in developmet. Data will be volatile.", true)
	if err != nil {
		return
	}

	info := rpcService.MethodByName("Items").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.list", "GET", "items", "List items."
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("Availabile").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.availabile", "GET", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("Create").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.create", "POST", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("ReName").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.rename", "PUT", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	info = rpcService.MethodByName("Delete").Info()
	info.Name, info.HTTPMethod, info.Path, info.Desc =
		"item.delete", "DELETE", "item", ""
	info.Scopes, info.ClientIds, info.Audiences = scopes, clientIds, audiences

	return
}

func makeEndpointsError(c endpoints.Context, err error) error {
	switch err.(type) {
	case *endpoints.APIError:
	default:
		if debug {
			c.Debugf("%s: %s", fmt.Sprintln(runtime.Caller(1)), err.Error())
		}
		message := err.Error()
		if appengine.IsCapabilityDisabled(err) {
			err = endpoints.NewInternalServerError(message)
			c.Errorf(message)
		} else if appengine.IsOverQuota(err) {
			err = endpoints.NewInternalServerError(message)
			c.Errorf(message)
		} else if appengine.IsTimeoutError(err) {
			err = endpoints.NewInternalServerError(message)
			c.Errorf(message)
		} else if strings.Contains(message, "500") {
			err = endpoints.InternalServerError
			c.Errorf(message)
		} else if strings.Contains(message, "400") {
			err = endpoints.NewBadRequestError(message)
		} else if strings.Contains(message, "401") {
			err = endpoints.NewUnauthorizedError(message)
		} else if strings.Contains(message, "403") {
			err = endpoints.NewForbiddenError(message)
		} else if strings.Contains(message, "404") {
			err = endpoints.NewNotFoundError(message)
		} else if strings.Contains(message, "409") {
			err = endpoints.NewConflictError(message)
		} else {
			err = endpoints.BadRequestError
			c.Errorf(message)
		}
	}
	return err
}

type RuffnoteApi struct {
}

func (ruffnoteApi *RuffnoteApi) Items(r *http.Request,
	req *ItemsRequestMessage, resp *ItemsResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	items, err := getItemsByUser(c, u, req.Limit)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}
	resp.Items = make([]*ItemResponseMessage, len(items))
	for i, item := range items {
		resp.Items[i] = item.toMessage(nil)
	}
	return
}

func (ruffnoteApi *RuffnoteApi) Availabile(r *http.Request,
	req *AvailabileRequestMessage, resp *AvailabileResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	_, err = getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	resp.Name = req.Name
	_, err = GetItemByName(c, req.Name)
	if err == datastore.ErrNoSuchEntity {
		err = nil
		resp.Status = true
		return
	}
	return
}

func (ruffnoteApi *RuffnoteApi) Create(r *http.Request,
	req *CreateRequestMessage, resp *ItemResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	err = req.Validate()
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	item, err := createItem(c, u, "id", req.Name)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}
	item.toMessage(resp)
	return
}

func (ruffnoteApi *RuffnoteApi) ReName(r *http.Request,
	req *ReNameRequestMessage, resp *ItemResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return err
	}

	//TODO check id
	id := req.Id
	item, err := createItem(c, u, id, req.Name)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}
	item.toMessage(resp)

	items, err := getItemsByUser(c, u, 100)
	if err != nil {
		c.Errorf("%v", err)
		err = makeEndpointsError(c, err)
		return
	}
	deleteItemsKey := make([]*datastore.Key, 0, len(items))
	for i, item := range items {
		if item.Id() != id {
			continue
		}
		if item.Name() == req.Name {
			continue
		}
		deleteItemsKey[i] = item.key
	}
	g := goon.FromContext(c)
	err = g.DeleteMulti(deleteItemsKey)
	if err != nil {
		c.Warningf("%v", err)
		err = makeEndpointsError(c, err)
	}
	return
}

func (ruffnoteApi *RuffnoteApi) Delete(r *http.Request,
	req *DeleteRequestMessage, resp *DeleteResponseMessage) (err error) {

	c := endpoints.NewContext(r)
	u, err := getCurrentUser(c)
	if err != nil {
		err = makeEndpointsError(c, err)
		return
	}

	items, err := getItemsByUser(c, u, 100)
	if err != nil {
		c.Errorf("%v", err)
		err = makeEndpointsError(c, err)
		return
	}
	deleteItemsKey := make([]*datastore.Key, 0, len(items))
	for i, item := range items {
		if item.Id() != req.Id {
			continue
		}
		deleteItemsKey[i] = item.key
	}
	g := goon.FromContext(c)
	err = g.DeleteMulti(deleteItemsKey)
	if err != nil {
		c.Warningf("%v", err)
		err = makeEndpointsError(c, err)
	}
	return
}

func getCurrentUser(c endpoints.Context) (u *user.User, err error) {
	u, err = endpoints.CurrentUser(c, scopes, audiences, clientIds)
	if err != nil {
		c.Infof("%v", err)
		err = endpoints.UnauthorizedError
		return
	}
	if u == nil {
		err = endpoints.UnauthorizedError
		return
	}
	return
}

func createItem(c endpoints.Context, u *user.User, id string, name string) (item *Item, err error) {
	item = newItem(id, name, u)

	g := goon.FromContext(c)
	err = g.RunInTransaction(func(tg *goon.Goon) (err error) {
		_, err = GetItemByName(c, name)
		if err != datastore.ErrNoSuchEntity {
			err = endpoints.ConflictError
			return
		}
		err = item.put(c)
		if err != nil {
			c.Errorf("%v", err)
			err = makeEndpointsError(c, err)
		}
		return
	}, nil)

	if err != datastore.ErrConcurrentTransaction {
		c.Errorf("Transaction failed: %v", err)
	}
	return
}
<|endoftext|>


<commit_before>package services

import (
	"context"

	"github.com/Golang-Coach/Lessons/GoBDD/models"
	"github.com/google/go-github/github"
)

// IRepositoryServices : This interface will be used to provide light coupling between github.RepositoryServices and its consumer
type IRepositoryServices interface {
	Get(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error)
}

// IGithub : This interface will be used to provide light coupling between GithubAPI and its consumer
type IGithub interface {
	GetPackageRepoInfo(owner string, repositoryName string) (*models.Package, error)
}

// Github : This struct will be used to get Github related information
type Github struct {
	repositoryServices IRepositoryServices
	context            context.Context
}

// NewGithub : It will intialized Github class
func NewGithub(context context.Context, repositoryServices IRepositoryServices) Github {
	return Github{
		repositoryServices: repositoryServices,
		context:            context,
	}
}

// GetPackageRepoInfo : This reciver provide Github related repository inforamtion
func (service *Github) GetPackageRepoInfo(owner string, repositoryName string) (*models.Package, error) {
	repo, _, err := service.repositoryServices.Get(service.context, owner, repositoryName)
	if err != nil {
		return nil, err
	}
	pack := &models.Package{
		FullName:    *repo.FullName,
		Description: *repo.Description,
		ForksCount:  *repo.ForksCount,
		StarsCount:  *repo.StargazersCount,
	}
	return pack, nil
}
<commit_msg>Few spelling mistake<commit_after>package services

import (
	"context"

	"github.com/Golang-Coach/Lessons/GoBDD/models"
	"github.com/google/go-github/github"
)

// IRepositoryServices : This interface will be used to provide light coupling between github.RepositoryServices and its consumer
type IRepositoryServices interface {
	Get(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error)
}

// IGithub : This interface will be used to provide light coupling between GithubAPI and its consumer
type IGithub interface {
	GetPackageRepoInfo(owner string, repositoryName string) (*models.Package, error)
}

// Github : This struct will be used to get Github related information
type Github struct {
	repositoryServices IRepositoryServices
	context            context.Context
}

// NewGithub : It will intialized Github class
func NewGithub(context context.Context, repositoryServices IRepositoryServices) Github {
	return Github{
		repositoryServices: repositoryServices,
		context:            context,
	}
}

// GetPackageRepoInfo : This receiver provide Github related repository information
func (service *Github) GetPackageRepoInfo(owner string, repositoryName string) (*models.Package, error) {
	repo, _, err := service.repositoryServices.Get(service.context, owner, repositoryName)
	if err != nil {
		return nil, err
	}
	pack := &models.Package{
		FullName:    *repo.FullName,
		Description: *repo.Description,
		ForksCount:  *repo.ForksCount,
		StarsCount:  *repo.StargazersCount,
	}
	return pack, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors 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 mungers

import (
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

// NeedsRebaseMunger will add the "needs-rebase" label to any issue which is
// unable to be automatically merged
type NeedsRebaseMunger struct{}

const needsRebase = "needs-rebase"

func init() {
	RegisterMungerOrDie(NeedsRebaseMunger{})
}

// Name is the name usable in --pr-mungers
func (NeedsRebaseMunger) Name() string { return "needs-rebase" }

// Initialize will initialize the munger
func (NeedsRebaseMunger) Initialize(config *github.Config) error { return nil }

// EachLoop is called at the start of every munge loop
func (NeedsRebaseMunger) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (NeedsRebaseMunger) AddFlags(cmd *cobra.Command, config *github.Config) {}

// Munge is the workhorse the will actually make updates to the PR
func (NeedsRebaseMunger) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	mergeable, err := obj.IsMergeable()
	if err != nil {
		glog.V(2).Infof("Skipping %d - problem determining mergeable", *obj.Issue.Number)
		return
	}
	if mergeable && obj.HasLabel(needsRebase) {
		obj.RemoveLabel(needsRebase)
	}
	if !mergeable && !obj.HasLabel(needsRebase) {
		obj.AddLabels([]string{needsRebase})
	}
}
<commit_msg>Add comment when PR requires rebase<commit_after>/*
Copyright 2015 The Kubernetes Authors 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 mungers

import (
	"k8s.io/contrib/mungegithub/github"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
)

// NeedsRebaseMunger will add the "needs-rebase" label to any issue which is
// unable to be automatically merged
type NeedsRebaseMunger struct{}

const needsRebase = "needs-rebase"

func init() {
	RegisterMungerOrDie(NeedsRebaseMunger{})
}

// Name is the name usable in --pr-mungers
func (NeedsRebaseMunger) Name() string { return "needs-rebase" }

// Initialize will initialize the munger
func (NeedsRebaseMunger) Initialize(config *github.Config) error { return nil }

// EachLoop is called at the start of every munge loop
func (NeedsRebaseMunger) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (NeedsRebaseMunger) AddFlags(cmd *cobra.Command, config *github.Config) {}

// Munge is the workhorse the will actually make updates to the PR
func (NeedsRebaseMunger) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	mergeable, err := obj.IsMergeable()
	if err != nil {
		glog.V(2).Infof("Skipping %d - problem determining mergeable", *obj.Issue.Number)
		return
	}
	if mergeable && obj.HasLabel(needsRebase) {
		obj.RemoveLabel(needsRebase)
	}
	if !mergeable && !obj.HasLabel(needsRebase) {
		obj.AddLabels([]string{needsRebase})

		body := "PR needs rebase"
		if err := obj.WriteComment(body); err != nil {
			return
		}
	}
}
<|endoftext|>


<commit_before>package library

import (
	"container/heap"
	"errors"
	"github.com/nytlabs/gojee"                 // jee
	"github.com/nytlabs/streamtools/st/blocks" // blocks
	"github.com/nytlabs/streamtools/st/util"   // util
	"time"
)

// specify those channels we're going to use to communicate with streamtools
type Cache struct {
	blocks.Block
	queryrule chan blocks.MsgChan
	inrule    blocks.MsgChan
	in        blocks.MsgChan
	lookup    blocks.MsgChan
	keys      chan blocks.MsgChan
	values    chan blocks.MsgChan
	dump      chan blocks.MsgChan
	out       blocks.MsgChan
	quit      blocks.MsgChan
}

// we need to build a simple factory so that streamtools can make new blocks of this kind
func NewCache() blocks.BlockInterface {
	return &Cache{}
}

type item struct {
	value    interface{}
	lastSeen time.Time
}

// Cacheup is called once before running the block. We build up the channels and specify what kind of block this is.
func (b *Cache) Setup() {
	b.Kind = "Cache"
	b.Desc = "stores a set of dictionary values queryable on key"
	b.inrule = b.InRoute("rule")
	b.queryrule = b.QueryRoute("rule")
	b.quit = b.Quit()
	b.out = b.Broadcast()

	b.in = b.InRoute("in")
	b.lookup = b.InRoute("lookup")
	b.keys = b.QueryRoute("keys")
	b.values = b.QueryRoute("values")
	b.dump = b.QueryRoute("dump")
}

// Run is the block's main loop. Here we listen on the different channels we set up.
func (b *Cache) Run() {
	var keyPath, valuePath, ttlString string
	var ttl time.Duration
	cache := make(map[string]item)
	ttlQueue := &PriorityQueue{}
	var keys []string
	var values []interface{}
	cacheDump := make(map[string]interface{})

	var keyTree, valueTree *jee.TokenTree
	var err error
	emitTick := time.NewTimer(500 * time.Millisecond)
	for {
		select {
		case <-emitTick.C:

		case ruleI := <-b.inrule:
			keyPath, err = util.ParseString(ruleI, "KeyPath")
			keyTree, err = util.BuildTokenTree(keyPath)
			if err != nil {
				b.Error(err)
				break
			}
			valuePath, err = util.ParseString(ruleI, "ValuePath")
			valueTree, err = util.BuildTokenTree(valuePath)
			if err != nil {
				b.Error(err)
				break
			}
			ttlString, err = util.ParseString(ruleI, "TimeToLive")
			if err != nil {
				b.Error(err)
				break
			}
			ttl, err = time.ParseDuration(ttlString)
			if err != nil {
				b.Error(err)
				break
			}
		case <-b.quit:
			return
		case msg := <-b.lookup:
			if keyTree == nil {
				continue
			}
			kI, err := jee.Eval(keyTree, msg)
			if err != nil {
				b.Error(err)
				break
			}
			k, ok := kI.(string)
			if !ok {
				b.Error(errors.New("key must be a string"))
				continue
			}
			i, ok := cache[k]
			var v interface{}
			if ok {
				v = i.value
				now := time.Now()
				i.lastSeen = now
				queueMessage := &PQMessage{
					val: k,
					t:   now,
				}
				heap.Push(ttlQueue, queueMessage)
			}
			b.out <- map[string]interface{}{
				"key":   k,
				"value": v,
			}

		case responseChan := <-b.keys:
			responseChan <- map[string]interface{}{
				"keys": keys,
			}

		case responseChan := <-b.values:
			responseChan <- map[string]interface{}{
				"values": values,
			}

		case responseChan := <-b.dump:
			responseChan <- map[string]interface{}{
				"dump": cacheDump,
			}

		case msg := <-b.in:
			if keyTree == nil {
				continue
			}
			if valueTree == nil {
				continue
			}
			kI, err := jee.Eval(keyTree, msg)
			if err != nil {
				b.Error(err)
				break
			}
			k, ok := kI.(string)
			if !ok {
				b.Error(errors.New("key must be a string"))
				continue
			}
			v, err := jee.Eval(valueTree, msg)
			if err != nil {
				b.Error(err)
				break
			}
			now := time.Now()
			cache[k] = item{
				value:    v,
				lastSeen: now,
			}

			keys = append(keys, k)
			values = append(values, v)
			cacheDump[k] = v

			queueMessage := &PQMessage{
				val: k,
				t:   now,
			}
			heap.Push(ttlQueue, queueMessage)
		case responseChan := <-b.queryrule:
			// deal with a query request
			responseChan <- map[string]interface{}{
				"KeyPath":    keyPath,
				"ValuePath":  valuePath,
				"TimeToLive": ttlString,
			}
		}
		now := time.Now()
		for {
			itemI, diff := ttlQueue.PeekAndShift(now, ttl)
			if itemI == nil {
				// then the queue is empty. don't check again for 5s
				if diff == 0 {
					diff = time.Duration(500) * time.Millisecond
				}
				emitTick.Reset(diff)
				break
			}
			i := itemI.(*PQMessage)
			k := i.val.(string)
			if i.t.Equal(cache[k].lastSeen) {
				delete(cache, k)
			}
		}
	}
}
<commit_msg>first pass at making better keys/values/dump endpoints<commit_after>package library

import (
	"container/heap"
	"encoding/json"
	"errors"
	"github.com/nytlabs/gojee"                 // jee
	"github.com/nytlabs/streamtools/st/blocks" // blocks
	"github.com/nytlabs/streamtools/st/util"   // util
	"time"
)

// specify those channels we're going to use to communicate with streamtools
type Cache struct {
	blocks.Block
	queryrule chan blocks.MsgChan
	inrule    blocks.MsgChan
	in        blocks.MsgChan
	lookup    blocks.MsgChan
	keys      chan blocks.MsgChan
	values    chan blocks.MsgChan
	dump      chan blocks.MsgChan
	out       blocks.MsgChan
	quit      blocks.MsgChan
}

// we need to build a simple factory so that streamtools can make new blocks of this kind
func NewCache() blocks.BlockInterface {
	return &Cache{}
}

type item struct {
	value    interface{}
	lastSeen time.Time
}

func (i *item) MarshalJSON() ([]byte, error) {
	return json.Marshal(i.value)
}

// Cacheup is called once before running the block. We build up the channels and specify what kind of block this is.
func (b *Cache) Setup() {
	b.Kind = "Cache"
	b.Desc = "stores a set of dictionary values queryable on key"
	b.inrule = b.InRoute("rule")
	b.queryrule = b.QueryRoute("rule")
	b.quit = b.Quit()
	b.out = b.Broadcast()

	b.in = b.InRoute("in")
	b.lookup = b.InRoute("lookup")
	b.keys = b.QueryRoute("keys")
	b.values = b.QueryRoute("values")
	b.dump = b.QueryRoute("dump")
}

// Run is the block's main loop. Here we listen on the different channels we set up.
func (b *Cache) Run() {
	var keyPath, valuePath, ttlString string
	var ttl time.Duration
	cache := make(map[string]item)
	ttlQueue := &PriorityQueue{}

	var keyTree, valueTree *jee.TokenTree
	var err error
	emitTick := time.NewTimer(500 * time.Millisecond)
	for {
		select {
		case <-emitTick.C:

		case ruleI := <-b.inrule:
			keyPath, err = util.ParseString(ruleI, "KeyPath")
			keyTree, err = util.BuildTokenTree(keyPath)
			if err != nil {
				b.Error(err)
				break
			}
			valuePath, err = util.ParseString(ruleI, "ValuePath")
			valueTree, err = util.BuildTokenTree(valuePath)
			if err != nil {
				b.Error(err)
				break
			}
			ttlString, err = util.ParseString(ruleI, "TimeToLive")
			if err != nil {
				b.Error(err)
				break
			}
			ttl, err = time.ParseDuration(ttlString)
			if err != nil {
				b.Error(err)
				break
			}
		case <-b.quit:
			return
		case msg := <-b.lookup:
			if keyTree == nil {
				continue
			}
			kI, err := jee.Eval(keyTree, msg)
			if err != nil {
				b.Error(err)
				break
			}
			k, ok := kI.(string)
			if !ok {
				b.Error(errors.New("key must be a string"))
				continue
			}
			i, ok := cache[k]
			var v interface{}
			if ok {
				v = i.value
				now := time.Now()
				i.lastSeen = now
				queueMessage := &PQMessage{
					val: k,
					t:   now,
				}
				heap.Push(ttlQueue, queueMessage)
			}
			b.out <- map[string]interface{}{
				"key":   k,
				"value": v,
			}

		case responseChan := <-b.keys:
			keys := make([]string, len(cache))
			i := 0
			for key := range cache {
				keys[i] = key
				i++
			}

			responseChan <- map[string]interface{}{
				"keys": keys,
			}

		case responseChan := <-b.values:
			values := make([]interface{}, len(cache))
			i := 0
			for _, item := range cache {
				values[i] = item.value
				i++
			}
			responseChan <- map[string]interface{}{
				"values": values,
			}

		case responseChan := <-b.dump:
			responseChan <- map[string]interface{}{
				"dump": cache,
			}

		case msg := <-b.in:
			if keyTree == nil {
				continue
			}
			if valueTree == nil {
				continue
			}
			kI, err := jee.Eval(keyTree, msg)
			if err != nil {
				b.Error(err)
				break
			}
			k, ok := kI.(string)
			if !ok {
				b.Error(errors.New("key must be a string"))
				continue
			}
			v, err := jee.Eval(valueTree, msg)
			if err != nil {
				b.Error(err)
				break
			}
			now := time.Now()
			cache[k] = item{
				value:    v,
				lastSeen: now,
			}

			queueMessage := &PQMessage{
				val: k,
				t:   now,
			}
			heap.Push(ttlQueue, queueMessage)
		case responseChan := <-b.queryrule:
			// deal with a query request
			responseChan <- map[string]interface{}{
				"KeyPath":    keyPath,
				"ValuePath":  valuePath,
				"TimeToLive": ttlString,
			}
		}
		now := time.Now()
		for {
			itemI, diff := ttlQueue.PeekAndShift(now, ttl)
			if itemI == nil {
				// then the queue is empty. don't check again for 5s
				if diff == 0 {
					diff = time.Duration(500) * time.Millisecond
				}
				emitTick.Reset(diff)
				break
			}
			i := itemI.(*PQMessage)
			k := i.val.(string)
			if i.t.Equal(cache[k].lastSeen) {
				delete(cache, k)
			}
		}
	}
}
<|endoftext|>


<commit_before>package sync

import (
	"bufio"
	"bytes"
	"io"

	"github.com/itchio/wharf/splitfunc"
)

// CreateSignature calculate the signature of target.
func (ctx *Context) CreateSignature(fileIndex int64, fileReader io.Reader, writeHash SignatureWriter) error {
	s := bufio.NewScanner(fileReader)
	s.Split(splitfunc.New(ctx.blockSize))

	blockIndex := int64(0)

	hashBlock := func(block []byte) error {
		weakHash, _, _ := βhash(block)
		strongHash := ctx.uniqueHash(block)

		blockHash := BlockHash{
			FileIndex:  fileIndex,
			BlockIndex: blockIndex,
			WeakHash:   weakHash,
			StrongHash: strongHash,
		}

		if len(block) < ctx.blockSize {
			blockHash.ShortSize = int32(len(block))
		}

		err := writeHash(blockHash)
		if err != nil {
			return err
		}
		blockIndex++
		return nil
	}

	for s.Scan() {
		err := hashBlock(s.Bytes())
		if err != nil {
			return err
		}
	}

	// let empty files have a 0-length shortblock
	if blockIndex == 0 {
		err := hashBlock([]byte{})
		if err != nil {
			return err
		}
	}

	return nil
}

// Use a more unique way to identify a set of bytes.
func (ctx *Context) uniqueHash(v []byte) []byte {
	ctx.uniqueHasher.Reset()
	_, err := ctx.uniqueHasher.Write(v)
	if err != nil {
		panic(err)
	}
	return ctx.uniqueHasher.Sum(nil)
}

// Searches for a given strong hash among all strong hashes in this bucket.
func findUniqueHash(hh []BlockHash, hashValue []byte, shortSize int32, preferredFileIndex int64) *BlockHash {
	if len(hashValue) == 0 {
		return nil
	}

	// try to find block in preferred file first
	// this helps detect files that aren't touched by patches
	if preferredFileIndex != -1 {
		for _, block := range hh {
			if block.FileIndex == preferredFileIndex {
				if block.ShortSize == shortSize && bytes.Equal(block.StrongHash, hashValue) {
					return &block
				}
			}
		}
	}

	for _, block := range hh {
		// full blocks have 0 shortSize
		if block.ShortSize == shortSize && bytes.Equal(block.StrongHash, hashValue) {
			return &block
		}
	}
	return nil
}

// βhash implements the rolling hash when signing an entire block at a time
func βhash(block []byte) (β uint32, β1 uint32, β2 uint32) {
	var a, b uint32
	for i, val := range block {
		a += uint32(val)
		b += (uint32(len(block)-1) - uint32(i) + 1) * uint32(val)
	}
	β = (a % _M) + (_M * (b % _M))
	β1 = a % _M
	β2 = b % _M
	return
}
<commit_msg>Don't ignore scanner error, set scanner buffer size explicitly<commit_after>package sync

import (
	"bufio"
	"bytes"
	"io"

	"github.com/itchio/wharf/splitfunc"
)

// CreateSignature calculate the signature of target.
func (ctx *Context) CreateSignature(fileIndex int64, fileReader io.Reader, writeHash SignatureWriter) error {
	s := bufio.NewScanner(fileReader)
	s.Buffer(make([]byte, ctx.blockSize), 0)
	s.Split(splitfunc.New(ctx.blockSize))

	blockIndex := int64(0)

	hashBlock := func(block []byte) error {
		weakHash, _, _ := βhash(block)
		strongHash := ctx.uniqueHash(block)

		blockHash := BlockHash{
			FileIndex:  fileIndex,
			BlockIndex: blockIndex,
			WeakHash:   weakHash,
			StrongHash: strongHash,
		}

		if len(block) < ctx.blockSize {
			blockHash.ShortSize = int32(len(block))
		}

		err := writeHash(blockHash)
		if err != nil {
			return err
		}
		blockIndex++
		return nil
	}

	for s.Scan() {
		err := hashBlock(s.Bytes())
		if err != nil {
			return err
		}
	}

	err := s.Err()
	if err != nil {
		return err
	}

	// let empty files have a 0-length shortblock
	if blockIndex == 0 {
		err := hashBlock([]byte{})
		if err != nil {
			return err
		}
	}

	return nil
}

// Use a more unique way to identify a set of bytes.
func (ctx *Context) uniqueHash(v []byte) []byte {
	ctx.uniqueHasher.Reset()
	_, err := ctx.uniqueHasher.Write(v)
	if err != nil {
		panic(err)
	}
	return ctx.uniqueHasher.Sum(nil)
}

// Searches for a given strong hash among all strong hashes in this bucket.
func findUniqueHash(hh []BlockHash, hashValue []byte, shortSize int32, preferredFileIndex int64) *BlockHash {
	if len(hashValue) == 0 {
		return nil
	}

	// try to find block in preferred file first
	// this helps detect files that aren't touched by patches
	if preferredFileIndex != -1 {
		for _, block := range hh {
			if block.FileIndex == preferredFileIndex {
				if block.ShortSize == shortSize && bytes.Equal(block.StrongHash, hashValue) {
					return &block
				}
			}
		}
	}

	for _, block := range hh {
		// full blocks have 0 shortSize
		if block.ShortSize == shortSize && bytes.Equal(block.StrongHash, hashValue) {
			return &block
		}
	}
	return nil
}

// βhash implements the rolling hash when signing an entire block at a time
func βhash(block []byte) (β uint32, β1 uint32, β2 uint32) {
	var a, b uint32
	for i, val := range block {
		a += uint32(val)
		b += (uint32(len(block)-1) - uint32(i) + 1) * uint32(val)
	}
	β = (a % _M) + (_M * (b % _M))
	β1 = a % _M
	β2 = b % _M
	return
}
<|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 syntax

import (
	"bytes"
	"strconv"
	"unicode"
)

// Compiled program.
// May not belong in this package, but convenient for now.

// A Prog is a compiled regular expression program.
type Prog struct {
	Inst   []Inst
	Start  int // index of start instruction
	NumCap int // number of InstCapture insts in re
}

// An InstOp is an instruction opcode.
type InstOp uint8

const (
	InstAlt InstOp = iota
	InstAltMatch
	InstCapture
	InstEmptyWidth
	InstMatch
	InstFail
	InstNop
	InstRune
	InstRune1
	InstRuneAny
	InstRuneAnyNotNL
)

var instOpNames = []string{
	"InstAlt",
	"InstAltMatch",
	"InstCapture",
	"InstEmptyWidth",
	"InstMatch",
	"InstFail",
	"InstNop",
	"InstRune",
	"InstRune1",
	"InstRuneAny",
	"InstRuneAnyNotNL",
}

func (i InstOp) String() string {
	if uint(i) >= uint(len(instOpNames)) {
		return ""
	}
	return instOpNames[i]
}

// An EmptyOp specifies a kind or mixture of zero-width assertions.
type EmptyOp uint8

const (
	EmptyBeginLine EmptyOp = 1 << iota
	EmptyEndLine
	EmptyBeginText
	EmptyEndText
	EmptyWordBoundary
	EmptyNoWordBoundary
)

// EmptyOpContext returns the zero-width assertions
// satisfied at the position between the runes r1 and r2.
// Passing r1 == -1 indicates that the position is
// at the beginning of the text.
// Passing r2 == -1 indicates that the position is
// at the end of the text.
func EmptyOpContext(r1, r2 rune) EmptyOp {
	var op EmptyOp = EmptyNoWordBoundary
	var boundary byte
	switch {
	case IsWordChar(r1):
		boundary = 1
	case r1 == '\n':
		op |= EmptyBeginLine
	case r1 < 0:
		op |= EmptyBeginText | EmptyBeginLine
	}
	switch {
	case IsWordChar(r2):
		boundary ^= 1
	case r2 == '\n':
		op |= EmptyEndLine
	case r2 < 0:
		op |= EmptyEndText | EmptyEndLine
	}
	if boundary != 0 { // IsWordChar(r1) != IsWordChar(r2)
		op ^= (EmptyWordBoundary | EmptyNoWordBoundary)
	}
	return op
}

// IsWordChar reports whether r is consider a ``word character''
// during the evaluation of the \b and \B zero-width assertions.
// These assertions are ASCII-only: the word characters are [A-Za-z0-9_].
func IsWordChar(r rune) bool {
	return 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '_'
}

// An Inst is a single instruction in a regular expression program.
type Inst struct {
	Op   InstOp
	Out  uint32 // all but InstMatch, InstFail
	Arg  uint32 // InstAlt, InstAltMatch, InstCapture, InstEmptyWidth
	Rune []rune
}

func (p *Prog) String() string {
	var b bytes.Buffer
	dumpProg(&b, p)
	return b.String()
}

// skipNop follows any no-op or capturing instructions
// and returns the resulting pc.
func (p *Prog) skipNop(pc uint32) (*Inst, uint32) {
	i := &p.Inst[pc]
	for i.Op == InstNop || i.Op == InstCapture {
		pc = i.Out
		i = &p.Inst[pc]
	}
	return i, pc
}

// op returns i.Op but merges all the Rune special cases into InstRune
func (i *Inst) op() InstOp {
	op := i.Op
	switch op {
	case InstRune1, InstRuneAny, InstRuneAnyNotNL:
		op = InstRune
	}
	return op
}

// Prefix returns a literal string that all matches for the
// regexp must start with.  Complete is true if the prefix
// is the entire match.
func (p *Prog) Prefix() (prefix string, complete bool) {
	i, _ := p.skipNop(uint32(p.Start))

	// Avoid allocation of buffer if prefix is empty.
	if i.op() != InstRune || len(i.Rune) != 1 {
		return "", i.Op == InstMatch
	}

	// Have prefix; gather characters.
	var buf bytes.Buffer
	for i.op() == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 {
		buf.WriteRune(i.Rune[0])
		i, _ = p.skipNop(i.Out)
	}
	return buf.String(), i.Op == InstMatch
}

// StartCond returns the leading empty-width conditions that must
// be true in any match.  It returns ^EmptyOp(0) if no matches are possible.
func (p *Prog) StartCond() EmptyOp {
	var flag EmptyOp
	pc := uint32(p.Start)
	i := &p.Inst[pc]
Loop:
	for {
		switch i.Op {
		case InstEmptyWidth:
			flag |= EmptyOp(i.Arg)
		case InstFail:
			return ^EmptyOp(0)
		case InstCapture, InstNop:
			// skip
		default:
			break Loop
		}
		pc = i.Out
		i = &p.Inst[pc]
	}
	return flag
}

const noMatch = -1

// MatchRune returns true if the instruction matches (and consumes) r.
// It should only be called when i.Op == InstRune.
func (i *Inst) MatchRune(r rune) bool {
	return i.MatchRunePos(r) != noMatch
}

// MatchRunePos checks whether the instruction matches (and consumes) r.
// If so, MatchRunePos returns the index of the matching rune pair
// (or, when len(i.Rune) == 1, rune singleton).
// If not, MatchRunePos returns -1.
// MatchRunePos should only be called when i.Op == InstRune.
func (i *Inst) MatchRunePos(r rune) int {
	rune := i.Rune

	// Special case: single-rune slice is from literal string, not char class.
	if len(rune) == 1 {
		r0 := rune[0]
		if r == r0 {
			return 0
		}
		if Flags(i.Arg)&FoldCase != 0 {
			for r1 := unicode.SimpleFold(r0); r1 != r0; r1 = unicode.SimpleFold(r1) {
				if r == r1 {
					return 0
				}
			}
		}
		return noMatch
	}

	// Peek at the first few pairs.
	// Should handle ASCII well.
	for j := 0; j < len(rune) && j <= 8; j += 2 {
		if r < rune[j] {
			return noMatch
		}
		if r <= rune[j+1] {
			return j / 2
		}
	}

	// Otherwise binary search.
	lo := 0
	hi := len(rune) / 2
	for lo < hi {
		m := lo + (hi-lo)/2
		if c := rune[2*m]; c <= r {
			if r <= rune[2*m+1] {
				return m
			}
			lo = m + 1
		} else {
			hi = m
		}
	}
	return noMatch
}

// As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char.
// Since we act on runes, it would be easy to support Unicode here.
func wordRune(r rune) bool {
	return r == '_' ||
		('A' <= r && r <= 'Z') ||
		('a' <= r && r <= 'z') ||
		('0' <= r && r <= '9')
}

// MatchEmptyWidth returns true if the instruction matches
// an empty string between the runes before and after.
// It should only be called when i.Op == InstEmptyWidth.
func (i *Inst) MatchEmptyWidth(before rune, after rune) bool {
	switch EmptyOp(i.Arg) {
	case EmptyBeginLine:
		return before == '\n' || before == -1
	case EmptyEndLine:
		return after == '\n' || after == -1
	case EmptyBeginText:
		return before == -1
	case EmptyEndText:
		return after == -1
	case EmptyWordBoundary:
		return wordRune(before) != wordRune(after)
	case EmptyNoWordBoundary:
		return wordRune(before) == wordRune(after)
	}
	panic("unknown empty width arg")
}

func (i *Inst) String() string {
	var b bytes.Buffer
	dumpInst(&b, i)
	return b.String()
}

func bw(b *bytes.Buffer, args ...string) {
	for _, s := range args {
		b.WriteString(s)
	}
}

func dumpProg(b *bytes.Buffer, p *Prog) {
	for j := range p.Inst {
		i := &p.Inst[j]
		pc := strconv.Itoa(j)
		if len(pc) < 3 {
			b.WriteString("   "[len(pc):])
		}
		if j == p.Start {
			pc += "*"
		}
		bw(b, pc, "\t")
		dumpInst(b, i)
		bw(b, "\n")
	}
}

func u32(i uint32) string {
	return strconv.FormatUint(uint64(i), 10)
}

func dumpInst(b *bytes.Buffer, i *Inst) {
	switch i.Op {
	case InstAlt:
		bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg))
	case InstAltMatch:
		bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg))
	case InstCapture:
		bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out))
	case InstEmptyWidth:
		bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out))
	case InstMatch:
		bw(b, "match")
	case InstFail:
		bw(b, "fail")
	case InstNop:
		bw(b, "nop -> ", u32(i.Out))
	case InstRune:
		if i.Rune == nil {
			// shouldn't happen
			bw(b, "rune <nil>")
		}
		bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)))
		if Flags(i.Arg)&FoldCase != 0 {
			bw(b, "/i")
		}
		bw(b, " -> ", u32(i.Out))
	case InstRune1:
		bw(b, "rune1 ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out))
	case InstRuneAny:
		bw(b, "any -> ", u32(i.Out))
	case InstRuneAnyNotNL:
		bw(b, "anynotnl -> ", u32(i.Out))
	}
}
<commit_msg>all: use "reports whether" in place of "returns true if(f)"<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 syntax

import (
	"bytes"
	"strconv"
	"unicode"
)

// Compiled program.
// May not belong in this package, but convenient for now.

// A Prog is a compiled regular expression program.
type Prog struct {
	Inst   []Inst
	Start  int // index of start instruction
	NumCap int // number of InstCapture insts in re
}

// An InstOp is an instruction opcode.
type InstOp uint8

const (
	InstAlt InstOp = iota
	InstAltMatch
	InstCapture
	InstEmptyWidth
	InstMatch
	InstFail
	InstNop
	InstRune
	InstRune1
	InstRuneAny
	InstRuneAnyNotNL
)

var instOpNames = []string{
	"InstAlt",
	"InstAltMatch",
	"InstCapture",
	"InstEmptyWidth",
	"InstMatch",
	"InstFail",
	"InstNop",
	"InstRune",
	"InstRune1",
	"InstRuneAny",
	"InstRuneAnyNotNL",
}

func (i InstOp) String() string {
	if uint(i) >= uint(len(instOpNames)) {
		return ""
	}
	return instOpNames[i]
}

// An EmptyOp specifies a kind or mixture of zero-width assertions.
type EmptyOp uint8

const (
	EmptyBeginLine EmptyOp = 1 << iota
	EmptyEndLine
	EmptyBeginText
	EmptyEndText
	EmptyWordBoundary
	EmptyNoWordBoundary
)

// EmptyOpContext returns the zero-width assertions
// satisfied at the position between the runes r1 and r2.
// Passing r1 == -1 indicates that the position is
// at the beginning of the text.
// Passing r2 == -1 indicates that the position is
// at the end of the text.
func EmptyOpContext(r1, r2 rune) EmptyOp {
	var op EmptyOp = EmptyNoWordBoundary
	var boundary byte
	switch {
	case IsWordChar(r1):
		boundary = 1
	case r1 == '\n':
		op |= EmptyBeginLine
	case r1 < 0:
		op |= EmptyBeginText | EmptyBeginLine
	}
	switch {
	case IsWordChar(r2):
		boundary ^= 1
	case r2 == '\n':
		op |= EmptyEndLine
	case r2 < 0:
		op |= EmptyEndText | EmptyEndLine
	}
	if boundary != 0 { // IsWordChar(r1) != IsWordChar(r2)
		op ^= (EmptyWordBoundary | EmptyNoWordBoundary)
	}
	return op
}

// IsWordChar reports whether r is consider a ``word character''
// during the evaluation of the \b and \B zero-width assertions.
// These assertions are ASCII-only: the word characters are [A-Za-z0-9_].
func IsWordChar(r rune) bool {
	return 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '_'
}

// An Inst is a single instruction in a regular expression program.
type Inst struct {
	Op   InstOp
	Out  uint32 // all but InstMatch, InstFail
	Arg  uint32 // InstAlt, InstAltMatch, InstCapture, InstEmptyWidth
	Rune []rune
}

func (p *Prog) String() string {
	var b bytes.Buffer
	dumpProg(&b, p)
	return b.String()
}

// skipNop follows any no-op or capturing instructions
// and returns the resulting pc.
func (p *Prog) skipNop(pc uint32) (*Inst, uint32) {
	i := &p.Inst[pc]
	for i.Op == InstNop || i.Op == InstCapture {
		pc = i.Out
		i = &p.Inst[pc]
	}
	return i, pc
}

// op returns i.Op but merges all the Rune special cases into InstRune
func (i *Inst) op() InstOp {
	op := i.Op
	switch op {
	case InstRune1, InstRuneAny, InstRuneAnyNotNL:
		op = InstRune
	}
	return op
}

// Prefix returns a literal string that all matches for the
// regexp must start with.  Complete is true if the prefix
// is the entire match.
func (p *Prog) Prefix() (prefix string, complete bool) {
	i, _ := p.skipNop(uint32(p.Start))

	// Avoid allocation of buffer if prefix is empty.
	if i.op() != InstRune || len(i.Rune) != 1 {
		return "", i.Op == InstMatch
	}

	// Have prefix; gather characters.
	var buf bytes.Buffer
	for i.op() == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 {
		buf.WriteRune(i.Rune[0])
		i, _ = p.skipNop(i.Out)
	}
	return buf.String(), i.Op == InstMatch
}

// StartCond returns the leading empty-width conditions that must
// be true in any match.  It returns ^EmptyOp(0) if no matches are possible.
func (p *Prog) StartCond() EmptyOp {
	var flag EmptyOp
	pc := uint32(p.Start)
	i := &p.Inst[pc]
Loop:
	for {
		switch i.Op {
		case InstEmptyWidth:
			flag |= EmptyOp(i.Arg)
		case InstFail:
			return ^EmptyOp(0)
		case InstCapture, InstNop:
			// skip
		default:
			break Loop
		}
		pc = i.Out
		i = &p.Inst[pc]
	}
	return flag
}

const noMatch = -1

// MatchRune reports whether the instruction matches (and consumes) r.
// It should only be called when i.Op == InstRune.
func (i *Inst) MatchRune(r rune) bool {
	return i.MatchRunePos(r) != noMatch
}

// MatchRunePos checks whether the instruction matches (and consumes) r.
// If so, MatchRunePos returns the index of the matching rune pair
// (or, when len(i.Rune) == 1, rune singleton).
// If not, MatchRunePos returns -1.
// MatchRunePos should only be called when i.Op == InstRune.
func (i *Inst) MatchRunePos(r rune) int {
	rune := i.Rune

	// Special case: single-rune slice is from literal string, not char class.
	if len(rune) == 1 {
		r0 := rune[0]
		if r == r0 {
			return 0
		}
		if Flags(i.Arg)&FoldCase != 0 {
			for r1 := unicode.SimpleFold(r0); r1 != r0; r1 = unicode.SimpleFold(r1) {
				if r == r1 {
					return 0
				}
			}
		}
		return noMatch
	}

	// Peek at the first few pairs.
	// Should handle ASCII well.
	for j := 0; j < len(rune) && j <= 8; j += 2 {
		if r < rune[j] {
			return noMatch
		}
		if r <= rune[j+1] {
			return j / 2
		}
	}

	// Otherwise binary search.
	lo := 0
	hi := len(rune) / 2
	for lo < hi {
		m := lo + (hi-lo)/2
		if c := rune[2*m]; c <= r {
			if r <= rune[2*m+1] {
				return m
			}
			lo = m + 1
		} else {
			hi = m
		}
	}
	return noMatch
}

// As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char.
// Since we act on runes, it would be easy to support Unicode here.
func wordRune(r rune) bool {
	return r == '_' ||
		('A' <= r && r <= 'Z') ||
		('a' <= r && r <= 'z') ||
		('0' <= r && r <= '9')
}

// MatchEmptyWidth reports whether the instruction matches
// an empty string between the runes before and after.
// It should only be called when i.Op == InstEmptyWidth.
func (i *Inst) MatchEmptyWidth(before rune, after rune) bool {
	switch EmptyOp(i.Arg) {
	case EmptyBeginLine:
		return before == '\n' || before == -1
	case EmptyEndLine:
		return after == '\n' || after == -1
	case EmptyBeginText:
		return before == -1
	case EmptyEndText:
		return after == -1
	case EmptyWordBoundary:
		return wordRune(before) != wordRune(after)
	case EmptyNoWordBoundary:
		return wordRune(before) == wordRune(after)
	}
	panic("unknown empty width arg")
}

func (i *Inst) String() string {
	var b bytes.Buffer
	dumpInst(&b, i)
	return b.String()
}

func bw(b *bytes.Buffer, args ...string) {
	for _, s := range args {
		b.WriteString(s)
	}
}

func dumpProg(b *bytes.Buffer, p *Prog) {
	for j := range p.Inst {
		i := &p.Inst[j]
		pc := strconv.Itoa(j)
		if len(pc) < 3 {
			b.WriteString("   "[len(pc):])
		}
		if j == p.Start {
			pc += "*"
		}
		bw(b, pc, "\t")
		dumpInst(b, i)
		bw(b, "\n")
	}
}

func u32(i uint32) string {
	return strconv.FormatUint(uint64(i), 10)
}

func dumpInst(b *bytes.Buffer, i *Inst) {
	switch i.Op {
	case InstAlt:
		bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg))
	case InstAltMatch:
		bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg))
	case InstCapture:
		bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out))
	case InstEmptyWidth:
		bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out))
	case InstMatch:
		bw(b, "match")
	case InstFail:
		bw(b, "fail")
	case InstNop:
		bw(b, "nop -> ", u32(i.Out))
	case InstRune:
		if i.Rune == nil {
			// shouldn't happen
			bw(b, "rune <nil>")
		}
		bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)))
		if Flags(i.Arg)&FoldCase != 0 {
			bw(b, "/i")
		}
		bw(b, " -> ", u32(i.Out))
	case InstRune1:
		bw(b, "rune1 ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out))
	case InstRuneAny:
		bw(b, "any -> ", u32(i.Out))
	case InstRuneAnyNotNL:
		bw(b, "anynotnl -> ", u32(i.Out))
	}
}
<|endoftext|>


<commit_before>package gopisysfs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	// pollInterval is how long we should wait between naieve polling of files
	pollInterval = 20 * time.Millisecond
	// forever is about 100 years.
	forever = 100 * 365 * 24 * time.Hour
)

var rootpath = "/"

// setRoot is designed to be called by the test cases to exercise some hard-to-change things on an actual pi.
func setRoot(rt string) {
	rootpath = rt
}

// file gets a file path inside the /sys file system,
// but it can be hooked by the test cases to use a test filesystem instead of the real /sys
func file(paths ...string) string {
	path := filepath.Join(paths...)
	if !filepath.IsAbs(path) {
		path = filepath.Join(rootpath, path)
	}
	return path
}

// readFilePanic reads a file returning the contents as a string, and panics if it cannot be read
func readFilePanic(name string) string {
	data, err := readFile(name)
	if err != nil {
		log.Panicf("Unable to read file %v: %v", name, err)
	}
	return data
}

// awaitFileCreate establishes an asynchronous poll on a file location until it exists
// at which point the returned channel will return a nil on the channel. A non-nil indicates
// an error in the polling.
func awaitFileCreate(name string, timeout time.Duration) (<-chan error, error) {

	ret := make(chan error, 1)

	if checkFile(name) {
		ret <- nil
		return ret, nil
	}

	dir := filepath.Dir(name)
	if stat, err := os.Stat(dir); err != nil || !stat.IsDir() {
		if err != nil {
			return nil, fmt.Errorf("Unable to poll for a file in a nonexistent folder %v: %v", dir, err)
		}
		return nil, fmt.Errorf("Unable to poll for a file in a non-folder %v: %v", dir, stat)
	}

	// set up notification and timeout
	tout := time.After(timeout)
	// intervals at every poll cycle
	interval := time.NewTicker(pollInterval).C
	// naieve polling system
	go func() {
		for {

			if checkFile(name) {
				// Found it!
				ret <- nil
				return
			}

			select {
			case <-tout:
				ret <- fmt.Errorf("Timed out waiting for %v after %v", name, timeout)
				return
			case <-interval:
				// ignore specific event, check actual file later
			}
		}
	}()

	return ret, nil

}

// awaitFileRemove establishes an asynchronous poll on a file location until it is removed
// at which point the returned channel will return a nil on the channel. A non-nil indicates
// an error in the polling.
func awaitFileRemove(name string, timeout time.Duration) (<-chan error, error) {

	ret := make(chan error, 1)

	// file is not there. Easy.
	if !checkFile(name) {
		ret <- nil
		return ret, nil
	}

	// set up notification and timeout
	tout := time.After(timeout)
	// intervals at every 20 milliseconds
	interval := time.NewTicker(pollInterval).C

	// naieve polling system
	go func() {
		for {

			if !checkFile(name) {
				// gone!
				ret <- nil
				return
			}

			select {
			case <-tout:
				ret <- fmt.Errorf("Timed out waiting for %v after %v", name, timeout)
				return
			case <-interval:
				// ignore specific event, check actual file later
			}
		}
	}()

	return ret, nil

}

func readStringFileAsInt(name string) (int, error) {
	data, err := readFile(name)
	if err != nil {
		return 0, err
	}
	val, err := strconv.Atoi(data)
	if err != nil {
		return 0, fmt.Errorf("Unable to convert value %v from %v to an int: %v", data, name, err)
	}
	return val, nil
}

//readFile reads the file and returns the contents as a string (trimmed)
func readFile(name string) (string, error) {
	data, err := ioutil.ReadFile(name)
	if err != nil {
		return "", err
	}
	str := string(data)
	str = strings.TrimSpace(str)
	return str, nil
}

// readBuffer reads a file in to a byte buffer
func readBytes(name string) ([]byte, error) {
	return ioutil.ReadFile(name)
}

// writeBuffer writes a buffer in to a file
func writeBuffer(name string, data []byte) error {
	return ioutil.WriteFile(name, data, 0444)
}

// writeFile will overwrite the specified file with the given string content
func writeFile(name, text string) error {
	data := []byte(text)
	return ioutil.WriteFile(name, data, 0444)
}

// checkFile retuns true if the specified file exists and is readable
func checkFile(name string) bool {
	if _, err := os.Stat(name); err == nil {
		// exists, but is it readable?
		// Note, you can open directories as well
		file, err := os.Open(name)
		if err != nil {
			fmt.Printf("Existing file %v but it is not readable")
			return false
		}
		file.Close()
		// already exists
		return true
	}
	return false
}
<commit_msg>require write access too.<commit_after>package gopisysfs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	// pollInterval is how long we should wait between naieve polling of files
	pollInterval = 20 * time.Millisecond
	// forever is about 100 years.
	forever = 100 * 365 * 24 * time.Hour
)

var rootpath = "/"

// setRoot is designed to be called by the test cases to exercise some hard-to-change things on an actual pi.
func setRoot(rt string) {
	rootpath = rt
}

// file gets a file path inside the /sys file system,
// but it can be hooked by the test cases to use a test filesystem instead of the real /sys
func file(paths ...string) string {
	path := filepath.Join(paths...)
	if !filepath.IsAbs(path) {
		path = filepath.Join(rootpath, path)
	}
	return path
}

// readFilePanic reads a file returning the contents as a string, and panics if it cannot be read
func readFilePanic(name string) string {
	data, err := readFile(name)
	if err != nil {
		log.Panicf("Unable to read file %v: %v", name, err)
	}
	return data
}

// awaitFileCreate establishes an asynchronous poll on a file location until it exists
// at which point the returned channel will return a nil on the channel. A non-nil indicates
// an error in the polling.
func awaitFileCreate(name string, timeout time.Duration) (<-chan error, error) {

	ret := make(chan error, 1)

	if checkFile(name) {
		ret <- nil
		return ret, nil
	}

	dir := filepath.Dir(name)
	if stat, err := os.Stat(dir); err != nil || !stat.IsDir() {
		if err != nil {
			return nil, fmt.Errorf("Unable to poll for a file in a nonexistent folder %v: %v", dir, err)
		}
		return nil, fmt.Errorf("Unable to poll for a file in a non-folder %v: %v", dir, stat)
	}

	// set up notification and timeout
	tout := time.After(timeout)
	// intervals at every poll cycle
	interval := time.NewTicker(pollInterval).C
	// naieve polling system
	go func() {
		for {

			if checkFile(name) {
				// Found it!
				ret <- nil
				return
			}

			select {
			case <-tout:
				ret <- fmt.Errorf("Timed out waiting for %v after %v", name, timeout)
				return
			case <-interval:
				// ignore specific event, check actual file later
			}
		}
	}()

	return ret, nil

}

// awaitFileRemove establishes an asynchronous poll on a file location until it is removed
// at which point the returned channel will return a nil on the channel. A non-nil indicates
// an error in the polling.
func awaitFileRemove(name string, timeout time.Duration) (<-chan error, error) {

	ret := make(chan error, 1)

	// file is not there. Easy.
	if !checkFile(name) {
		ret <- nil
		return ret, nil
	}

	// set up notification and timeout
	tout := time.After(timeout)
	// intervals at every 20 milliseconds
	interval := time.NewTicker(pollInterval).C

	// naieve polling system
	go func() {
		for {

			if !checkFile(name) {
				// gone!
				ret <- nil
				return
			}

			select {
			case <-tout:
				ret <- fmt.Errorf("Timed out waiting for %v after %v", name, timeout)
				return
			case <-interval:
				// ignore specific event, check actual file later
			}
		}
	}()

	return ret, nil

}

func readStringFileAsInt(name string) (int, error) {
	data, err := readFile(name)
	if err != nil {
		return 0, err
	}
	val, err := strconv.Atoi(data)
	if err != nil {
		return 0, fmt.Errorf("Unable to convert value %v from %v to an int: %v", data, name, err)
	}
	return val, nil
}

//readFile reads the file and returns the contents as a string (trimmed)
func readFile(name string) (string, error) {
	data, err := ioutil.ReadFile(name)
	if err != nil {
		return "", err
	}
	str := string(data)
	str = strings.TrimSpace(str)
	return str, nil
}

// readBuffer reads a file in to a byte buffer
func readBytes(name string) ([]byte, error) {
	return ioutil.ReadFile(name)
}

// writeBuffer writes a buffer in to a file
func writeBuffer(name string, data []byte) error {
	return ioutil.WriteFile(name, data, 0444)
}

// writeFile will overwrite the specified file with the given string content
func writeFile(name, text string) error {
	data := []byte(text)
	return ioutil.WriteFile(name, data, 0444)
}

// checkFile retuns true if the specified file exists and is writable
func checkFile(name string) bool {
	if _, err := os.Stat(name); err == nil {
		// exists, but is it writable?
		// Note, you can open directories as well
		file, err := os.OpenFile(name, os.O_RDWR, 0)
		if err != nil {
			fmt.Printf("Existing file %v but it is not writable: %v", name, err)
			return false
		}
		file.Close()
		// already exists
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package system

import (
	"html/template"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"

	"crypto/sha256"

	"github.com/golang/glog"
	"github.com/gorilla/sessions"
	"github.com/haruyama/golang-goji-sample/models"
	"github.com/pelletier/go-toml"
	"github.com/zenazn/goji/web"
	"gopkg.in/gorp.v1"
)

type CsrfProtection struct {
	Key    string
	Cookie string
	Header string
	Secure bool
}

type Application struct {
	Config         *toml.TomlTree
	Template       *template.Template
	Store          *sessions.CookieStore
	DbMap          *gorp.DbMap
	CsrfProtection *CsrfProtection
}

func (application *Application) Init(filename *string) {

	config, err := toml.LoadFile(*filename)
	if err != nil {
		glog.Fatalf("TOML load failed: %s\n", err)
	}

	hash := sha256.New()
	io.WriteString(hash, config.Get("cookie.mac_secret").(string))
	application.Store = sessions.NewCookieStore(hash.Sum(nil))
	application.Store.Options = &sessions.Options{
		Path:     "/",
		HttpOnly: true,
		Secure:   config.Get("cookie.secure").(bool),
	}
	dbConfig := config.Get("database").(*toml.TomlTree)
	application.DbMap = models.GetDbMap(
		dbConfig.Get("user").(string),
		dbConfig.Get("password").(string),
		dbConfig.Get("hostname").(string),
		dbConfig.Get("port").(string),
		dbConfig.Get("database").(string))

	application.CsrfProtection = &CsrfProtection{
		Key:    config.Get("csrf.key").(string),
		Cookie: config.Get("csrf.cookie").(string),
		Header: config.Get("csrf.header").(string),
		Secure: config.Get("cookie.secure").(bool),
	}

	application.Config = config
}

func (application *Application) LoadTemplates() error {
	var templates []string

	fn := func(path string, f os.FileInfo, err error) error {
		if f.IsDir() != true && strings.HasSuffix(f.Name(), ".html") {
			templates = append(templates, path)
		}
		return nil
	}

	err := filepath.Walk(application.Config.Get("general.template_path").(string), fn)

	if err != nil {
		return err
	}

	application.Template = template.Must(template.ParseFiles(templates...))
	return nil
}

func (application *Application) Close() {
	glog.Info("Bye!")
}

func (application *Application) Route(controller interface{}, route string) interface{} {
	fn := func(c web.C, w http.ResponseWriter, r *http.Request) {
		c.Env["Content-Type"] = "text/html"

		methodValue := reflect.ValueOf(controller).MethodByName(route)
		methodInterface := methodValue.Interface()
		method := methodInterface.(func(c web.C, r *http.Request) (string, int))

		body, code := method(c, r)

		if session, exists := c.Env["Session"]; exists {
			err := session.(*sessions.Session).Save(r, w)
			if err != nil {
				glog.Errorf("Can't save session: %v", err)
			}
		}

		switch code {
		case http.StatusOK:
			if _, exists := c.Env["Content-Type"]; exists {
				w.Header().Set("Content-Type", c.Env["Content-Type"].(string))
			}
			io.WriteString(w, body)
		case http.StatusSeeOther, http.StatusFound:
			http.Redirect(w, r, body, code)
		}
	}
	return fn
}
<commit_msg>toml.TomlTree -> toml.Tree<commit_after>package system

import (
	"html/template"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"

	"crypto/sha256"

	"github.com/golang/glog"
	"github.com/gorilla/sessions"
	"github.com/haruyama/golang-goji-sample/models"
	"github.com/pelletier/go-toml"
	"github.com/zenazn/goji/web"
	"gopkg.in/gorp.v1"
)

type CsrfProtection struct {
	Key    string
	Cookie string
	Header string
	Secure bool
}

type Application struct {
	Config         *toml.Tree
	Template       *template.Template
	Store          *sessions.CookieStore
	DbMap          *gorp.DbMap
	CsrfProtection *CsrfProtection
}

func (application *Application) Init(filename *string) {

	config, err := toml.LoadFile(*filename)
	if err != nil {
		glog.Fatalf("TOML load failed: %s\n", err)
	}

	hash := sha256.New()
	io.WriteString(hash, config.Get("cookie.mac_secret").(string))
	application.Store = sessions.NewCookieStore(hash.Sum(nil))
	application.Store.Options = &sessions.Options{
		Path:     "/",
		HttpOnly: true,
		Secure:   config.Get("cookie.secure").(bool),
	}
	dbConfig := config.Get("database").(*toml.Tree)
	application.DbMap = models.GetDbMap(
		dbConfig.Get("user").(string),
		dbConfig.Get("password").(string),
		dbConfig.Get("hostname").(string),
		dbConfig.Get("port").(string),
		dbConfig.Get("database").(string))

	application.CsrfProtection = &CsrfProtection{
		Key:    config.Get("csrf.key").(string),
		Cookie: config.Get("csrf.cookie").(string),
		Header: config.Get("csrf.header").(string),
		Secure: config.Get("cookie.secure").(bool),
	}

	application.Config = config
}

func (application *Application) LoadTemplates() error {
	var templates []string

	fn := func(path string, f os.FileInfo, err error) error {
		if f.IsDir() != true && strings.HasSuffix(f.Name(), ".html") {
			templates = append(templates, path)
		}
		return nil
	}

	err := filepath.Walk(application.Config.Get("general.template_path").(string), fn)

	if err != nil {
		return err
	}

	application.Template = template.Must(template.ParseFiles(templates...))
	return nil
}

func (application *Application) Close() {
	glog.Info("Bye!")
}

func (application *Application) Route(controller interface{}, route string) interface{} {
	fn := func(c web.C, w http.ResponseWriter, r *http.Request) {
		c.Env["Content-Type"] = "text/html"

		methodValue := reflect.ValueOf(controller).MethodByName(route)
		methodInterface := methodValue.Interface()
		method := methodInterface.(func(c web.C, r *http.Request) (string, int))

		body, code := method(c, r)

		if session, exists := c.Env["Session"]; exists {
			err := session.(*sessions.Session).Save(r, w)
			if err != nil {
				glog.Errorf("Can't save session: %v", err)
			}
		}

		switch code {
		case http.StatusOK:
			if _, exists := c.Env["Content-Type"]; exists {
				w.Header().Set("Content-Type", c.Env["Content-Type"].(string))
			}
			io.WriteString(w, body)
		case http.StatusSeeOther, http.StatusFound:
			http.Redirect(w, r, body, code)
		}
	}
	return fn
}
<|endoftext|>


<commit_before>// Copyright 2016 The Netstack 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 tcpip provides the interfaces and related types that users of the
// tcpip stack will use in order to create endpoints used to send and receive
// data over the network stack.
//
// The starting point is the creation and configuration of a stack. A stack can
// be created by calling the New() function of the tcpip/stack/stack package;
// configuring a stack involves creating NICs (via calls to Stack.CreateNIC()),
// adding network addresses (via calls to Stack.AddAddress()), and
// setting a route table (via a call to Stack.SetRouteTable()).
//
// Once a stack is configured, endpoints can be created by calling
// Stack.NewEndpoint(). Such endpoints can be used to send/receive data, connect
// to peers, listen for connections, accept connections, etc., depending on the
// transport protocol selected.
package tcpip

import (
	"errors"
	"fmt"
	"io"

	"github.com/google/netstack/tcpip/buffer"
	"github.com/google/netstack/waiter"
)

// Errors that can be returned by the network stack.
var (
	ErrUnknownProtocol       = errors.New("unknown protocol")
	ErrUnknownNICID          = errors.New("unknown nic id")
	ErrDuplicateNICID        = errors.New("duplicate nic id")
	ErrDuplicateAddress      = errors.New("duplicate address")
	ErrNoRoute               = errors.New("no route")
	ErrBadLinkEndpoint       = errors.New("bad link layer endpoint")
	ErrAlreadyBound          = errors.New("endpoint already bound")
	ErrInvalidEndpointState  = errors.New("endpoint is in invalid state")
	ErrAlreadyConnecting     = errors.New("endpoint is already connecting")
	ErrAlreadyConnected      = errors.New("endpoint is already connected")
	ErrNoPortAvailable       = errors.New("no ports are available")
	ErrPortInUse             = errors.New("port is in use")
	ErrBadLocalAddress       = errors.New("bad local address")
	ErrClosedForSend         = errors.New("endpoint is closed for send")
	ErrClosedForReceive      = errors.New("endpoint is closed for receive")
	ErrWouldBlock            = errors.New("operation would block")
	ErrConnectionRefused     = errors.New("connection was refused")
	ErrTimeout               = errors.New("operation timed out")
	ErrAborted               = errors.New("operation aborted")
	ErrConnectStarted        = errors.New("connection attempt started")
	ErrDestinationRequired   = errors.New("destination address is required")
	ErrNotSupported          = errors.New("operation not supported")
	ErrQueueSizeNotSupported = errors.New("queue size querying not supported")
	ErrNotConnected          = errors.New("endpoint not connected")
	ErrConnectionReset       = errors.New("connection reset by peer")
	ErrConnectionAborted     = errors.New("connection aborted")
)

// Errors related to Subnet
var (
	errSubnetLengthMismatch = errors.New("subnet length of address and mask differ")
	errSubnetAddressMasked  = errors.New("subnet address has bits set outside the mask")
)

// Address is a byte slice cast as a string that represents the address of a
// network node. Or, in the case of unix endpoints, it may represent a path.
type Address string

// AddressMask is a bitmask for an address.
type AddressMask string

// Subnet is a subnet defined by its address and mask.
type Subnet struct {
	address Address
	mask    AddressMask
}

// NewSubnet creates a new Subnet, checking that the address and mask are the same length.
func NewSubnet(a Address, m AddressMask) (Subnet, error) {
	if len(a) != len(m) {
		return Subnet{}, errSubnetLengthMismatch
	}
	for i := 0; i < len(a); i++ {
		if a[i]&^m[i] != 0 {
			return Subnet{}, errSubnetAddressMasked
		}
	}
	return Subnet{a, m}, nil
}

// Contains returns true iff the address is of the same length and matches the
// subnet address and mask.
func (s *Subnet) Contains(a Address) bool {
	if len(a) != len(s.address) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i]&s.mask[i] != s.address[i] {
			return false
		}
	}
	return true
}

// ID returns the subnet ID.
func (s *Subnet) ID() Address {
	return s.address
}

// Bits returns the number of ones (network bits) and zeros (host bits) in the
// subnet mask.
func (s *Subnet) Bits() (ones int, zeros int) {
	for _, b := range []byte(s.mask) {
		for i := uint(0); i < 8; i++ {
			if b&(1<<i) == 0 {
				zeros++
			} else {
				ones++
			}
		}
	}
	return
}

// Prefix returns the number of bits before the first host bit.
func (s *Subnet) Prefix() int {
	for i, b := range []byte(s.mask) {
		for j := 7; j >= 0; j-- {
			if b&(1<<uint(j)) == 0 {
				return i*8 + 7 - j
			}
		}
	}
	return len(s.mask) * 8
}

// NICID is a number that uniquely identifies a NIC.
type NICID int32

// ShutdownFlags represents flags that can be passed to the Shutdown() method
// of the Endpoint interface.
type ShutdownFlags int

// Values of the flags that can be passed to the Shutdown() method. They can
// be OR'ed together.
const (
	ShutdownRead ShutdownFlags = 1 << iota
	ShutdownWrite
)

// FullAddress represents a full transport node address, as required by the
// Connect() and Bind() methods.
type FullAddress struct {
	// NIC is the ID of the NIC this address refers to.
	//
	// This may not be used by all endpoint types.
	NIC NICID

	// Addr is the network address.
	Addr Address

	// Port is the transport port.
	//
	// This may not be used by all endpoint types.
	Port uint16
}

// Endpoint is the interface implemented by transport protocols (e.g., tcp, udp)
// that exposes functionality like read, write, connect, etc. to users of the
// networking stack.
type Endpoint interface {
	// Close puts the endpoint in a closed state and frees all resources
	// associated with it.
	Close()

	// Read reads data from the endpoint and optionally returns the sender.
	// This method does not block if there is no data pending.
	// It will also either return an error or data, never both.
	Read(*FullAddress) (buffer.View, error)

	// Write writes data to the endpoint's peer, or the provided address if
	// one is specified. This method does not block if the data cannot be
	// written.
	Write(buffer.View, *FullAddress) (uintptr, error)

	// Peek reads data without consuming it from the endpoint.
	//
	// This method does not block if there is no data pending.
	Peek(io.Writer) (uintptr, error)

	// Connect connects the endpoint to its peer. Specifying a NIC is
	// optional.
	//
	// There are three classes of return values:
	//	nil -- the attempt to connect succeeded.
	//	ErrConnectStarted -- the connect attempt started but hasn't
	//		completed yet. In this case, the actual result will
	//		become available via GetSockOpt(ErrorOption) when
	//		the endpoint becomes writable. (This mimics the
	//		connect(2) syscall behavior.)
	//	Anything else -- the attempt to connect failed.
	Connect(address FullAddress) error

	// Shutdown closes the read and/or write end of the endpoint connection
	// to its peer.
	Shutdown(flags ShutdownFlags) error

	// Listen puts the endpoint in "listen" mode, which allows it to accept
	// new connections.
	Listen(backlog int) error

	// Accept returns a new endpoint if a peer has established a connection
	// to an endpoint previously set to listen mode. This method does not
	// block if no new connections are available.
	//
	// The returned Queue is the wait queue for the newly created endpoint.
	Accept() (Endpoint, *waiter.Queue, error)

	// Bind binds the endpoint to a specific local address and port.
	// Specifying a NIC is optional.
	//
	// An optional commit function will be executed atomically with respect
	// to binding the endpoint. If this returns an error, the bind will not
	// occur and the error will be propagated back to the caller.
	Bind(address FullAddress, commit func() error) error

	// GetLocalAddress returns the address to which the endpoint is bound.
	GetLocalAddress() (FullAddress, error)

	// GetRemoteAddress returns the address to which the endpoint is
	// connected.
	GetRemoteAddress() (FullAddress, error)

	// Readiness returns the current readiness of the endpoint. For example,
	// if waiter.EventIn is set, the endpoint is immediately readable.
	Readiness(mask waiter.EventMask) waiter.EventMask

	// SetSockOpt sets a socket option. opt should be one of the *Option types.
	SetSockOpt(opt interface{}) error

	// GetSockOpt gets a socket option. opt should be a pointer to one of the
	// *Option types.
	GetSockOpt(opt interface{}) error
}

// ErrorOption is used in GetSockOpt to specify that the last error reported by
// the endpoint should be cleared and returned.
type ErrorOption struct{}

// SendBufferSizeOption is used by SetSockOpt/GetSockOpt to specify the send
// buffer size option.
type SendBufferSizeOption int

// ReceiveBufferSizeOption is used by SetSockOpt/GetSockOpt to specify the
// receive buffer size option.
type ReceiveBufferSizeOption int

// ReceiveQueueSizeOption is used in GetSockOpt to specify that the number of
// unread bytes in the input buffer should be returned.
type ReceiveQueueSizeOption int

// V6OnlyOption is used by SetSockOpt/GetSockOpt to specify whether an IPv6
// socket is to be restricted to sending and receiving IPv6 packets only.
type V6OnlyOption int

// NoDelayOption is used by SetSockOpt/GetSockOpt to specify if data should be
// sent out immediately by the transport protocol. For TCP, it determines if the
// Nagle algorithm is on or off.
type NoDelayOption int

// ReuseAddressOption is used by SetSockOpt/GetSockOpt to specify whether Bind()
// should allow reuse of local address.
type ReuseAddressOption int

// PasscredOption is used by SetSockOpt/GetSockOpt to specify whether
// SCM_CREDENTIALS socket control messages are enabled.
//
// Only supported on Unix sockets.
type PasscredOption int

// Route is a row in the routing table. It specifies through which NIC (and
// gateway) sets of packets should be routed. A row is considered viable if the
// masked target address matches the destination adddress in the row.
type Route struct {
	// Destination is the address that must be matched against the masked
	// target address to check if this row is viable.
	Destination Address

	// Mask specifies which bits of the Destination and the target address
	// must match for this row to be viable.
	Mask Address

	// Gateway is the gateway to be used if this row is viable.
	Gateway Address

	// NIC is the id of the nic to be used if this row is viable.
	NIC NICID
}

// Match determines if r is viable for the given destination address.
func (r *Route) Match(addr Address) bool {
	if len(addr) != len(r.Destination) {
		return false
	}

	for i := 0; i < len(r.Destination); i++ {
		if (addr[i] & r.Mask[i]) != r.Destination[i] {
			return false
		}
	}

	return true
}

// LinkEndpointID represents a data link layer endpoint.
type LinkEndpointID uint64

// TransportProtocolNumber is the number of a transport protocol.
type TransportProtocolNumber uint32

// NetworkProtocolNumber is the number of a network protocol.
type NetworkProtocolNumber uint32

// Stack represents a networking stack, with all supported protocols, NICs, and
// route table.
type Stack interface {
	// NewEndpoint creates a new transport layer endpoint of the given
	// protocol.
	NewEndpoint(transport TransportProtocolNumber, network NetworkProtocolNumber, waiterQueue *waiter.Queue) (Endpoint, error)

	// SetRouteTable assigns the route table to be used by this stack. It
	// specifies which NICs to use for given destination address ranges.
	SetRouteTable(table []Route)

	// CreateNIC creates a NIC with the provided id and link-layer sender.
	CreateNIC(id NICID, linkEndpoint LinkEndpointID) error

	// AddAddress adds a new network-layer address to the specified NIC.
	AddAddress(id NICID, protocol NetworkProtocolNumber, addr Address) error

	// Stats returns a snapshot of the current stats.
	// TODO: Make stats available in sentry for debugging/diag.
	Stats() Stats

	// NICSubnets returns a map of NICIDs to their associated subnets.
	NICSubnets() map[NICID][]Subnet

	// CheckNetworkProtocol checks if a given network protocol is enabled in the
	// stack.
	CheckNetworkProtocol(protocol NetworkProtocolNumber) bool
}

// Stats holds statistics about the networking stack.
type Stats struct {
	// UnkownProtocolRcvdPackets is the number of packets received by the
	// stack that were for an unknown or unsupported protocol.
	UnknownProtocolRcvdPackets uint64

	// UnknownNetworkEndpointRcvdPackets is the number of packets received
	// by the stack that were for a supported network protocol, but whose
	// destination address didn't having a matching endpoint.
	UnknownNetworkEndpointRcvdPackets uint64

	// MalformedRcvPackets is the number of packets received by the stack
	// that were deemed malformed.
	MalformedRcvdPackets uint64

	// DroppedPackets is the number of packets dropped due to full queues.
	DroppedPackets uint64
}

// String implements the fmt.Stringer interface.
func (a Address) String() string {
	switch len(a) {
	case 4:
		return fmt.Sprintf("%d.%d.%d.%d", int(a[0]), int(a[1]), int(a[2]), int(a[3]))
	default:
		return fmt.Sprintf("%x", []byte(a))
	}
}

// LinkAddress is a byte slice cast as a string that represents a link address.
// It is typically a 6-byte MAC address.
type LinkAddress string
<commit_msg>tcpip: add a link address printer and To4 address helper<commit_after>// Copyright 2016 The Netstack 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 tcpip provides the interfaces and related types that users of the
// tcpip stack will use in order to create endpoints used to send and receive
// data over the network stack.
//
// The starting point is the creation and configuration of a stack. A stack can
// be created by calling the New() function of the tcpip/stack/stack package;
// configuring a stack involves creating NICs (via calls to Stack.CreateNIC()),
// adding network addresses (via calls to Stack.AddAddress()), and
// setting a route table (via a call to Stack.SetRouteTable()).
//
// Once a stack is configured, endpoints can be created by calling
// Stack.NewEndpoint(). Such endpoints can be used to send/receive data, connect
// to peers, listen for connections, accept connections, etc., depending on the
// transport protocol selected.
package tcpip

import (
	"errors"
	"fmt"
	"io"

	"github.com/google/netstack/tcpip/buffer"
	"github.com/google/netstack/waiter"
)

// Errors that can be returned by the network stack.
var (
	ErrUnknownProtocol       = errors.New("unknown protocol")
	ErrUnknownNICID          = errors.New("unknown nic id")
	ErrDuplicateNICID        = errors.New("duplicate nic id")
	ErrDuplicateAddress      = errors.New("duplicate address")
	ErrNoRoute               = errors.New("no route")
	ErrBadLinkEndpoint       = errors.New("bad link layer endpoint")
	ErrAlreadyBound          = errors.New("endpoint already bound")
	ErrInvalidEndpointState  = errors.New("endpoint is in invalid state")
	ErrAlreadyConnecting     = errors.New("endpoint is already connecting")
	ErrAlreadyConnected      = errors.New("endpoint is already connected")
	ErrNoPortAvailable       = errors.New("no ports are available")
	ErrPortInUse             = errors.New("port is in use")
	ErrBadLocalAddress       = errors.New("bad local address")
	ErrClosedForSend         = errors.New("endpoint is closed for send")
	ErrClosedForReceive      = errors.New("endpoint is closed for receive")
	ErrWouldBlock            = errors.New("operation would block")
	ErrConnectionRefused     = errors.New("connection was refused")
	ErrTimeout               = errors.New("operation timed out")
	ErrAborted               = errors.New("operation aborted")
	ErrConnectStarted        = errors.New("connection attempt started")
	ErrDestinationRequired   = errors.New("destination address is required")
	ErrNotSupported          = errors.New("operation not supported")
	ErrQueueSizeNotSupported = errors.New("queue size querying not supported")
	ErrNotConnected          = errors.New("endpoint not connected")
	ErrConnectionReset       = errors.New("connection reset by peer")
	ErrConnectionAborted     = errors.New("connection aborted")
)

// Errors related to Subnet
var (
	errSubnetLengthMismatch = errors.New("subnet length of address and mask differ")
	errSubnetAddressMasked  = errors.New("subnet address has bits set outside the mask")
)

// Address is a byte slice cast as a string that represents the address of a
// network node. Or, in the case of unix endpoints, it may represent a path.
type Address string

// AddressMask is a bitmask for an address.
type AddressMask string

// Subnet is a subnet defined by its address and mask.
type Subnet struct {
	address Address
	mask    AddressMask
}

// NewSubnet creates a new Subnet, checking that the address and mask are the same length.
func NewSubnet(a Address, m AddressMask) (Subnet, error) {
	if len(a) != len(m) {
		return Subnet{}, errSubnetLengthMismatch
	}
	for i := 0; i < len(a); i++ {
		if a[i]&^m[i] != 0 {
			return Subnet{}, errSubnetAddressMasked
		}
	}
	return Subnet{a, m}, nil
}

// Contains returns true iff the address is of the same length and matches the
// subnet address and mask.
func (s *Subnet) Contains(a Address) bool {
	if len(a) != len(s.address) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i]&s.mask[i] != s.address[i] {
			return false
		}
	}
	return true
}

// ID returns the subnet ID.
func (s *Subnet) ID() Address {
	return s.address
}

// Bits returns the number of ones (network bits) and zeros (host bits) in the
// subnet mask.
func (s *Subnet) Bits() (ones int, zeros int) {
	for _, b := range []byte(s.mask) {
		for i := uint(0); i < 8; i++ {
			if b&(1<<i) == 0 {
				zeros++
			} else {
				ones++
			}
		}
	}
	return
}

// Prefix returns the number of bits before the first host bit.
func (s *Subnet) Prefix() int {
	for i, b := range []byte(s.mask) {
		for j := 7; j >= 0; j-- {
			if b&(1<<uint(j)) == 0 {
				return i*8 + 7 - j
			}
		}
	}
	return len(s.mask) * 8
}

// NICID is a number that uniquely identifies a NIC.
type NICID int32

// ShutdownFlags represents flags that can be passed to the Shutdown() method
// of the Endpoint interface.
type ShutdownFlags int

// Values of the flags that can be passed to the Shutdown() method. They can
// be OR'ed together.
const (
	ShutdownRead ShutdownFlags = 1 << iota
	ShutdownWrite
)

// FullAddress represents a full transport node address, as required by the
// Connect() and Bind() methods.
type FullAddress struct {
	// NIC is the ID of the NIC this address refers to.
	//
	// This may not be used by all endpoint types.
	NIC NICID

	// Addr is the network address.
	Addr Address

	// Port is the transport port.
	//
	// This may not be used by all endpoint types.
	Port uint16
}

// Endpoint is the interface implemented by transport protocols (e.g., tcp, udp)
// that exposes functionality like read, write, connect, etc. to users of the
// networking stack.
type Endpoint interface {
	// Close puts the endpoint in a closed state and frees all resources
	// associated with it.
	Close()

	// Read reads data from the endpoint and optionally returns the sender.
	// This method does not block if there is no data pending.
	// It will also either return an error or data, never both.
	Read(*FullAddress) (buffer.View, error)

	// Write writes data to the endpoint's peer, or the provided address if
	// one is specified. This method does not block if the data cannot be
	// written.
	Write(buffer.View, *FullAddress) (uintptr, error)

	// Peek reads data without consuming it from the endpoint.
	//
	// This method does not block if there is no data pending.
	Peek(io.Writer) (uintptr, error)

	// Connect connects the endpoint to its peer. Specifying a NIC is
	// optional.
	//
	// There are three classes of return values:
	//	nil -- the attempt to connect succeeded.
	//	ErrConnectStarted -- the connect attempt started but hasn't
	//		completed yet. In this case, the actual result will
	//		become available via GetSockOpt(ErrorOption) when
	//		the endpoint becomes writable. (This mimics the
	//		connect(2) syscall behavior.)
	//	Anything else -- the attempt to connect failed.
	Connect(address FullAddress) error

	// Shutdown closes the read and/or write end of the endpoint connection
	// to its peer.
	Shutdown(flags ShutdownFlags) error

	// Listen puts the endpoint in "listen" mode, which allows it to accept
	// new connections.
	Listen(backlog int) error

	// Accept returns a new endpoint if a peer has established a connection
	// to an endpoint previously set to listen mode. This method does not
	// block if no new connections are available.
	//
	// The returned Queue is the wait queue for the newly created endpoint.
	Accept() (Endpoint, *waiter.Queue, error)

	// Bind binds the endpoint to a specific local address and port.
	// Specifying a NIC is optional.
	//
	// An optional commit function will be executed atomically with respect
	// to binding the endpoint. If this returns an error, the bind will not
	// occur and the error will be propagated back to the caller.
	Bind(address FullAddress, commit func() error) error

	// GetLocalAddress returns the address to which the endpoint is bound.
	GetLocalAddress() (FullAddress, error)

	// GetRemoteAddress returns the address to which the endpoint is
	// connected.
	GetRemoteAddress() (FullAddress, error)

	// Readiness returns the current readiness of the endpoint. For example,
	// if waiter.EventIn is set, the endpoint is immediately readable.
	Readiness(mask waiter.EventMask) waiter.EventMask

	// SetSockOpt sets a socket option. opt should be one of the *Option types.
	SetSockOpt(opt interface{}) error

	// GetSockOpt gets a socket option. opt should be a pointer to one of the
	// *Option types.
	GetSockOpt(opt interface{}) error
}

// ErrorOption is used in GetSockOpt to specify that the last error reported by
// the endpoint should be cleared and returned.
type ErrorOption struct{}

// SendBufferSizeOption is used by SetSockOpt/GetSockOpt to specify the send
// buffer size option.
type SendBufferSizeOption int

// ReceiveBufferSizeOption is used by SetSockOpt/GetSockOpt to specify the
// receive buffer size option.
type ReceiveBufferSizeOption int

// ReceiveQueueSizeOption is used in GetSockOpt to specify that the number of
// unread bytes in the input buffer should be returned.
type ReceiveQueueSizeOption int

// V6OnlyOption is used by SetSockOpt/GetSockOpt to specify whether an IPv6
// socket is to be restricted to sending and receiving IPv6 packets only.
type V6OnlyOption int

// NoDelayOption is used by SetSockOpt/GetSockOpt to specify if data should be
// sent out immediately by the transport protocol. For TCP, it determines if the
// Nagle algorithm is on or off.
type NoDelayOption int

// ReuseAddressOption is used by SetSockOpt/GetSockOpt to specify whether Bind()
// should allow reuse of local address.
type ReuseAddressOption int

// PasscredOption is used by SetSockOpt/GetSockOpt to specify whether
// SCM_CREDENTIALS socket control messages are enabled.
//
// Only supported on Unix sockets.
type PasscredOption int

// Route is a row in the routing table. It specifies through which NIC (and
// gateway) sets of packets should be routed. A row is considered viable if the
// masked target address matches the destination adddress in the row.
type Route struct {
	// Destination is the address that must be matched against the masked
	// target address to check if this row is viable.
	Destination Address

	// Mask specifies which bits of the Destination and the target address
	// must match for this row to be viable.
	Mask Address

	// Gateway is the gateway to be used if this row is viable.
	Gateway Address

	// NIC is the id of the nic to be used if this row is viable.
	NIC NICID
}

// Match determines if r is viable for the given destination address.
func (r *Route) Match(addr Address) bool {
	if len(addr) != len(r.Destination) {
		return false
	}

	for i := 0; i < len(r.Destination); i++ {
		if (addr[i] & r.Mask[i]) != r.Destination[i] {
			return false
		}
	}

	return true
}

// LinkEndpointID represents a data link layer endpoint.
type LinkEndpointID uint64

// TransportProtocolNumber is the number of a transport protocol.
type TransportProtocolNumber uint32

// NetworkProtocolNumber is the number of a network protocol.
type NetworkProtocolNumber uint32

// Stack represents a networking stack, with all supported protocols, NICs, and
// route table.
type Stack interface {
	// NewEndpoint creates a new transport layer endpoint of the given
	// protocol.
	NewEndpoint(transport TransportProtocolNumber, network NetworkProtocolNumber, waiterQueue *waiter.Queue) (Endpoint, error)

	// SetRouteTable assigns the route table to be used by this stack. It
	// specifies which NICs to use for given destination address ranges.
	SetRouteTable(table []Route)

	// CreateNIC creates a NIC with the provided id and link-layer sender.
	CreateNIC(id NICID, linkEndpoint LinkEndpointID) error

	// AddAddress adds a new network-layer address to the specified NIC.
	AddAddress(id NICID, protocol NetworkProtocolNumber, addr Address) error

	// Stats returns a snapshot of the current stats.
	// TODO: Make stats available in sentry for debugging/diag.
	Stats() Stats

	// NICSubnets returns a map of NICIDs to their associated subnets.
	NICSubnets() map[NICID][]Subnet

	// CheckNetworkProtocol checks if a given network protocol is enabled in the
	// stack.
	CheckNetworkProtocol(protocol NetworkProtocolNumber) bool
}

// Stats holds statistics about the networking stack.
type Stats struct {
	// UnkownProtocolRcvdPackets is the number of packets received by the
	// stack that were for an unknown or unsupported protocol.
	UnknownProtocolRcvdPackets uint64

	// UnknownNetworkEndpointRcvdPackets is the number of packets received
	// by the stack that were for a supported network protocol, but whose
	// destination address didn't having a matching endpoint.
	UnknownNetworkEndpointRcvdPackets uint64

	// MalformedRcvPackets is the number of packets received by the stack
	// that were deemed malformed.
	MalformedRcvdPackets uint64

	// DroppedPackets is the number of packets dropped due to full queues.
	DroppedPackets uint64
}

// String implements the fmt.Stringer interface.
func (a Address) String() string {
	switch len(a) {
	case 4:
		return fmt.Sprintf("%d.%d.%d.%d", int(a[0]), int(a[1]), int(a[2]), int(a[3]))
	default:
		return fmt.Sprintf("%x", []byte(a))
	}
}

// To4 converts the IPv4 address to a 4-byte representation.
// If the address is not an IPv4 address, To4 returns "".
func (a Address) To4() Address {
	const (
		ipv4len = 4
		ipv6len = 16
	)
	if len(a) == ipv4len {
		return a
	}
	if len(a) == ipv6len &&
		isZeros(a[0:10]) &&
		a[10] == 0xff &&
		a[11] == 0xff {
		return a[12:16]
	}
	return ""
}

// isZeros reports whether a is all zeros.
func isZeros(a Address) bool {
	for i := 0; i < len(a); i++ {
		if a[i] != 0 {
			return false
		}
	}
	return true
}

// LinkAddress is a byte slice cast as a string that represents a link address.
// It is typically a 6-byte MAC address.
type LinkAddress string

// String implements the fmt.Stringer interface.
func (a LinkAddress) String() string {
	switch len(a) {
	case 6:
		return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", a[0], a[1], a[2], a[3], a[4], a[5])
	default:
		return fmt.Sprintf("%x", []byte(a))
	}
}
<|endoftext|>