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

// Package firebase is the entry point to the Firebase Admin SDK. It provides functionality for initializing App
// instances, which serve as the central entities that provide access to various other Firebase services exposed
// from the SDK.
package firebase

import (
	"context"
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"

	"cloud.google.com/go/firestore"
	"firebase.google.com/go/v4/appcheck"
	"firebase.google.com/go/v4/auth"
	"firebase.google.com/go/v4/db"
	"firebase.google.com/go/v4/iid"
	"firebase.google.com/go/v4/internal"
	"firebase.google.com/go/v4/messaging"
	"firebase.google.com/go/v4/storage"
	"google.golang.org/api/option"
	"google.golang.org/api/transport"
)

var defaultAuthOverrides = make(map[string]interface{})

// Version of the Firebase Go Admin SDK.
const Version = "4.9.0"

// firebaseEnvName is the name of the environment variable with the Config.
const firebaseEnvName = "FIREBASE_CONFIG"

// An App holds configuration and state common to all Firebase services that are exposed from the SDK.
type App struct {
	authOverride     map[string]interface{}
	dbURL            string
	projectID        string
	serviceAccountID string
	storageBucket    string
	opts             []option.ClientOption
}

// Config represents the configuration used to initialize an App.
type Config struct {
	AuthOverride     *map[string]interface{} `json:"databaseAuthVariableOverride"`
	DatabaseURL      string                  `json:"databaseURL"`
	ProjectID        string                  `json:"projectId"`
	ServiceAccountID string                  `json:"serviceAccountId"`
	StorageBucket    string                  `json:"storageBucket"`
}

// Auth returns an instance of auth.Client.
func (a *App) Auth(ctx context.Context) (*auth.Client, error) {
	conf := &internal.AuthConfig{
		ProjectID:        a.projectID,
		Opts:             a.opts,
		ServiceAccountID: a.serviceAccountID,
		Version:          Version,
	}
	return auth.NewClient(ctx, conf)
}

// Database returns an instance of db.Client to interact with the default Firebase Database
// configured via Config.DatabaseURL.
func (a *App) Database(ctx context.Context) (*db.Client, error) {
	return a.DatabaseWithURL(ctx, a.dbURL)
}

// DatabaseWithURL returns an instance of db.Client to interact with the Firebase Database
// identified by the given URL.
func (a *App) DatabaseWithURL(ctx context.Context, url string) (*db.Client, error) {
	conf := &internal.DatabaseConfig{
		AuthOverride: a.authOverride,
		URL:          url,
		Opts:         a.opts,
		Version:      Version,
	}
	return db.NewClient(ctx, conf)
}

// Storage returns a new instance of storage.Client.
func (a *App) Storage(ctx context.Context) (*storage.Client, error) {
	conf := &internal.StorageConfig{
		Opts:   a.opts,
		Bucket: a.storageBucket,
	}
	return storage.NewClient(ctx, conf)
}

// Firestore returns a new firestore.Client instance from the https://godoc.org/cloud.google.com/go/firestore
// package.
func (a *App) Firestore(ctx context.Context) (*firestore.Client, error) {
	if a.projectID == "" {
		return nil, errors.New("project id is required to access Firestore")
	}
	return firestore.NewClient(ctx, a.projectID, a.opts...)
}

// InstanceID returns an instance of iid.Client.
func (a *App) InstanceID(ctx context.Context) (*iid.Client, error) {
	conf := &internal.InstanceIDConfig{
		ProjectID: a.projectID,
		Opts:      a.opts,
	}
	return iid.NewClient(ctx, conf)
}

// Messaging returns an instance of messaging.Client.
func (a *App) Messaging(ctx context.Context) (*messaging.Client, error) {
	conf := &internal.MessagingConfig{
		ProjectID: a.projectID,
		Opts:      a.opts,
		Version:   Version,
	}
	return messaging.NewClient(ctx, conf)
}

// AppCheck returns an instance of appcheck.Client.
func (a *App) AppCheck(ctx context.Context) (*appcheck.Client, error) {
	conf := &internal.AppCheckConfig{
		ProjectID: a.projectID,
	}
	return appcheck.NewClient(ctx, conf)
}

// NewApp creates a new App from the provided config and client options.
//
// If the client options contain a valid credential (a service account file, a refresh token
// file or an oauth2.TokenSource) the App will be authenticated using that credential. Otherwise,
// NewApp attempts to authenticate the App with Google application default credentials.
// If `config` is nil, the SDK will attempt to load the config options from the
// `FIREBASE_CONFIG` environment variable. If the value in it starts with a `{` it is parsed as a
// JSON object, otherwise it is assumed to be the name of the JSON file containing the options.
func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*App, error) {
	o := []option.ClientOption{option.WithScopes(internal.FirebaseScopes...)}
	o = append(o, opts...)
	if config == nil {
		var err error
		if config, err = getConfigDefaults(); err != nil {
			return nil, err
		}
	}

	pid := getProjectID(ctx, config, o...)
	ao := defaultAuthOverrides
	if config.AuthOverride != nil {
		ao = *config.AuthOverride
	}

	return &App{
		authOverride:     ao,
		dbURL:            config.DatabaseURL,
		projectID:        pid,
		serviceAccountID: config.ServiceAccountID,
		storageBucket:    config.StorageBucket,
		opts:             o,
	}, nil
}

// getConfigDefaults reads the default config file, defined by the FIREBASE_CONFIG
// env variable, used only when options are nil.
func getConfigDefaults() (*Config, error) {
	fbc := &Config{}
	confFileName := os.Getenv(firebaseEnvName)
	if confFileName == "" {
		return fbc, nil
	}
	var dat []byte
	if confFileName[0] == byte('{') {
		dat = []byte(confFileName)
	} else {
		var err error
		if dat, err = ioutil.ReadFile(confFileName); err != nil {
			return nil, err
		}
	}
	if err := json.Unmarshal(dat, fbc); err != nil {
		return nil, err
	}

	// Some special handling necessary for db auth overrides
	var m map[string]interface{}
	if err := json.Unmarshal(dat, &m); err != nil {
		return nil, err
	}
	if ao, ok := m["databaseAuthVariableOverride"]; ok && ao == nil {
		// Auth overrides are explicitly set to null
		var nullMap map[string]interface{}
		fbc.AuthOverride = &nullMap
	}
	return fbc, nil
}

func getProjectID(ctx context.Context, config *Config, opts ...option.ClientOption) string {
	if config.ProjectID != "" {
		return config.ProjectID
	}

	creds, _ := transport.Creds(ctx, opts...)
	if creds != nil && creds.ProjectID != "" {
		return creds.ProjectID
	}

	if pid := os.Getenv("GOOGLE_CLOUD_PROJECT"); pid != "" {
		return pid
	}

	return os.Getenv("GCLOUD_PROJECT")
}
<commit_msg>[chore] Release 4.10.0 Take 2 (#526)<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package firebase is the entry point to the Firebase Admin SDK. It provides functionality for initializing App
// instances, which serve as the central entities that provide access to various other Firebase services exposed
// from the SDK.
package firebase

import (
	"context"
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"

	"cloud.google.com/go/firestore"
	"firebase.google.com/go/v4/appcheck"
	"firebase.google.com/go/v4/auth"
	"firebase.google.com/go/v4/db"
	"firebase.google.com/go/v4/iid"
	"firebase.google.com/go/v4/internal"
	"firebase.google.com/go/v4/messaging"
	"firebase.google.com/go/v4/storage"
	"google.golang.org/api/option"
	"google.golang.org/api/transport"
)

var defaultAuthOverrides = make(map[string]interface{})

// Version of the Firebase Go Admin SDK.
const Version = "4.10.0"

// firebaseEnvName is the name of the environment variable with the Config.
const firebaseEnvName = "FIREBASE_CONFIG"

// An App holds configuration and state common to all Firebase services that are exposed from the SDK.
type App struct {
	authOverride     map[string]interface{}
	dbURL            string
	projectID        string
	serviceAccountID string
	storageBucket    string
	opts             []option.ClientOption
}

// Config represents the configuration used to initialize an App.
type Config struct {
	AuthOverride     *map[string]interface{} `json:"databaseAuthVariableOverride"`
	DatabaseURL      string                  `json:"databaseURL"`
	ProjectID        string                  `json:"projectId"`
	ServiceAccountID string                  `json:"serviceAccountId"`
	StorageBucket    string                  `json:"storageBucket"`
}

// Auth returns an instance of auth.Client.
func (a *App) Auth(ctx context.Context) (*auth.Client, error) {
	conf := &internal.AuthConfig{
		ProjectID:        a.projectID,
		Opts:             a.opts,
		ServiceAccountID: a.serviceAccountID,
		Version:          Version,
	}
	return auth.NewClient(ctx, conf)
}

// Database returns an instance of db.Client to interact with the default Firebase Database
// configured via Config.DatabaseURL.
func (a *App) Database(ctx context.Context) (*db.Client, error) {
	return a.DatabaseWithURL(ctx, a.dbURL)
}

// DatabaseWithURL returns an instance of db.Client to interact with the Firebase Database
// identified by the given URL.
func (a *App) DatabaseWithURL(ctx context.Context, url string) (*db.Client, error) {
	conf := &internal.DatabaseConfig{
		AuthOverride: a.authOverride,
		URL:          url,
		Opts:         a.opts,
		Version:      Version,
	}
	return db.NewClient(ctx, conf)
}

// Storage returns a new instance of storage.Client.
func (a *App) Storage(ctx context.Context) (*storage.Client, error) {
	conf := &internal.StorageConfig{
		Opts:   a.opts,
		Bucket: a.storageBucket,
	}
	return storage.NewClient(ctx, conf)
}

// Firestore returns a new firestore.Client instance from the https://godoc.org/cloud.google.com/go/firestore
// package.
func (a *App) Firestore(ctx context.Context) (*firestore.Client, error) {
	if a.projectID == "" {
		return nil, errors.New("project id is required to access Firestore")
	}
	return firestore.NewClient(ctx, a.projectID, a.opts...)
}

// InstanceID returns an instance of iid.Client.
func (a *App) InstanceID(ctx context.Context) (*iid.Client, error) {
	conf := &internal.InstanceIDConfig{
		ProjectID: a.projectID,
		Opts:      a.opts,
	}
	return iid.NewClient(ctx, conf)
}

// Messaging returns an instance of messaging.Client.
func (a *App) Messaging(ctx context.Context) (*messaging.Client, error) {
	conf := &internal.MessagingConfig{
		ProjectID: a.projectID,
		Opts:      a.opts,
		Version:   Version,
	}
	return messaging.NewClient(ctx, conf)
}

// AppCheck returns an instance of appcheck.Client.
func (a *App) AppCheck(ctx context.Context) (*appcheck.Client, error) {
	conf := &internal.AppCheckConfig{
		ProjectID: a.projectID,
	}
	return appcheck.NewClient(ctx, conf)
}

// NewApp creates a new App from the provided config and client options.
//
// If the client options contain a valid credential (a service account file, a refresh token
// file or an oauth2.TokenSource) the App will be authenticated using that credential. Otherwise,
// NewApp attempts to authenticate the App with Google application default credentials.
// If `config` is nil, the SDK will attempt to load the config options from the
// `FIREBASE_CONFIG` environment variable. If the value in it starts with a `{` it is parsed as a
// JSON object, otherwise it is assumed to be the name of the JSON file containing the options.
func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*App, error) {
	o := []option.ClientOption{option.WithScopes(internal.FirebaseScopes...)}
	o = append(o, opts...)
	if config == nil {
		var err error
		if config, err = getConfigDefaults(); err != nil {
			return nil, err
		}
	}

	pid := getProjectID(ctx, config, o...)
	ao := defaultAuthOverrides
	if config.AuthOverride != nil {
		ao = *config.AuthOverride
	}

	return &App{
		authOverride:     ao,
		dbURL:            config.DatabaseURL,
		projectID:        pid,
		serviceAccountID: config.ServiceAccountID,
		storageBucket:    config.StorageBucket,
		opts:             o,
	}, nil
}

// getConfigDefaults reads the default config file, defined by the FIREBASE_CONFIG
// env variable, used only when options are nil.
func getConfigDefaults() (*Config, error) {
	fbc := &Config{}
	confFileName := os.Getenv(firebaseEnvName)
	if confFileName == "" {
		return fbc, nil
	}
	var dat []byte
	if confFileName[0] == byte('{') {
		dat = []byte(confFileName)
	} else {
		var err error
		if dat, err = ioutil.ReadFile(confFileName); err != nil {
			return nil, err
		}
	}
	if err := json.Unmarshal(dat, fbc); err != nil {
		return nil, err
	}

	// Some special handling necessary for db auth overrides
	var m map[string]interface{}
	if err := json.Unmarshal(dat, &m); err != nil {
		return nil, err
	}
	if ao, ok := m["databaseAuthVariableOverride"]; ok && ao == nil {
		// Auth overrides are explicitly set to null
		var nullMap map[string]interface{}
		fbc.AuthOverride = &nullMap
	}
	return fbc, nil
}

func getProjectID(ctx context.Context, config *Config, opts ...option.ClientOption) string {
	if config.ProjectID != "" {
		return config.ProjectID
	}

	creds, _ := transport.Creds(ctx, opts...)
	if creds != nil && creds.ProjectID != "" {
		return creds.ProjectID
	}

	if pid := os.Getenv("GOOGLE_CLOUD_PROJECT"); pid != "" {
		return pid
	}

	return os.Getenv("GCLOUD_PROJECT")
}
<|endoftext|>


<commit_before>package tokenizer

import (
	"fmt"
)

// Document represents the entirety of input text of one logical
// document -- usually a file.
//
// It holds information about its sections, tokens in them, and the
// words that were recognised by other processors.  In case the
// document has associated training annotations, it holds them as
// well.
type Document struct {
	id     string // Must be unique within a run
	input  map[string]string
	tokens map[string][]*TextToken
	words  map[string][]*Word
	annos  map[string][]*Annotation
}

// NewDocument creates and initialises a document with the given
// identifier.
//
// It holds information about its sections: currently title, abstract
// and body.  It also holds information of their constituent words and
// annotations.
func NewDocument(id string) (*Document, error) {
	if id == "" {
		return nil, fmt.Errorf("Empty identifier given")
	}

	d := &Document{}
	d.id = id
	d.input = make(map[string]string, 2)
	d.tokens = make(map[string][]*TextToken, 2)
	d.words = make(map[string][]*Word, 2)
	d.annos = make(map[string][]*Annotation, 2)

	return d, nil
}

// SetInput registers the input text of the given section of the
// document.
func (d *Document) SetInput(sec, input string) error {
	if sec == "" || input == "" {
		return fmt.Errorf("Empty section name or body given.")
	}

	d.input[sec] = input
	return nil
}

// Input answers the registered input text of the given section, if
// one exists.
func (d *Document) Input(sec string) (string, error) {
	if s, ok := d.input[sec]; ok {
		return s, nil
	}

	return "", fmt.Errorf("No input text for section : %s", sec)
}

// Tokenize breaks the text in the title, abstract and body into
// quasi-atomic tokens.
//
// These tokens can be matched against any available annotations.
// They can also be combined into logical words for named entity
// recognition and part of speech recognition purposes.
func (d *Document) Tokenize() {
	var ti *TextTokenIterator
	var err error

	for sec, inp := range d.input {
		ti = NewTextTokenIterator(inp)
		var toks []*TextToken
		for err = ti.MoveNext(); err == nil; err = ti.MoveNext() {
			toks = append(toks, ti.Item())
		}
		d.tokens[sec] = toks
	}
}

// Annotate records the given annotation against the applicable
// sequence of tokens in the appropriate section of the document.
// Currently known sections are "T" for title, "A" for abstract and
// "B" for body.  It creates and stores a `Word` corresponding to the
// text in the annotation.
func (d *Document) Annotate(a *Annotation) error {
	toks, ok := d.tokens[a.Section]
	if !ok {
		return fmt.Errorf("Annotation for unrecognised section : %s", a.Section)
	}

	w, err := d.wordForAnnotation(a, toks)
	if err != nil {
		return err
	}

	words := d.words[a.Section]
	words = append(words, w)
	d.words[a.Section] = words

	annos := d.annos[a.Section]
	annos = append(annos, a)
	d.annos[a.Section] = annos

	return nil
}

//

func (d *Document) wordForAnnotation(a *Annotation, toks []*TextToken) (*Word, error) {
	bidx := -1
	eidx := -1
	for i, t := range toks {
		if t.Begin() == a.Begin {
			bidx = i
			break
		}
	}
	if bidx == -1 {
		return nil, fmt.Errorf("Annotation could not be matched : %v", *a)
	}
	l := len(toks)
	for i := bidx; i < l; i++ {
		if toks[i].End() == a.End {
			eidx = i
			break
		}
	}
	if eidx == -1 {
		return nil, fmt.Errorf("Annotation could not be matched : %v", *a)
	}

	w := NewWord(a.Entity, a.Begin, a.End)
	w.etype = a.Type
	return w, nil
}

// TokensInSection answers any recognised tokens in the given section.
func (d *Document) TokensInSection(sec string) []*TextToken {
	if v, ok := d.tokens[sec]; ok {
		return v
	}

	return nil
}

// SectionTokenCount answers the number of recognised tokens in the
// given section.
func (d *Document) SectionTokenCount(sec string) (int, error) {
	if v, ok := d.tokens[sec]; ok {
		return len(v), nil
	}

	return -1, fmt.Errorf("Unknown section : %s", sec)
}

// WordsInSection answers any recognised words in the given section.
func (d *Document) WordsInSection(sec string) []*Word {
	if v, ok := d.words[sec]; ok {
		return v
	}

	return nil
}

// SectionWordCount answers the number of recognised words in the
// given section.
func (d *Document) SectionWordCount(sec string) (int, error) {
	if v, ok := d.words[sec]; ok {
		return len(v), nil
	}

	return -1, fmt.Errorf("Unknown section : %s", sec)
}

// AnnotationsForSection answers the registered annotations for the
// given section.
func (d *Document) AnnotationsForSection(sec string) []*Annotation {
	if v, ok := d.annos[sec]; ok {
		return v
	}

	return nil
}

// SectionAnnotationCount answers the number of registered annotations
// in the given section.
func (d *Document) SectionAnnotationCount(sec string) (int, error) {
	if v, ok := d.annos[sec]; ok {
		return len(v), nil
	}

	return -1, fmt.Errorf("Unknown section : %s", sec)
}
<commit_msg>Correct comments to make them generic w.r.t. sections<commit_after>package tokenizer

import (
	"fmt"
)

// Document represents the entirety of input text of one logical
// document -- usually a file.
//
// It holds information about its sections, tokens in them, and the
// words and sentences that were recognised by other processors.  In
// case the document has associated training annotations, it holds
// them as well.
type Document struct {
	id     string // Must be unique within a run
	input  map[string]string
	tokens map[string][]*TextToken
	words  map[string][]*Word
	annos  map[string][]*Annotation
}

// NewDocument creates and initialises a document with the given
// identifier.
//
// It holds information about its sections.  It also holds information
// of their constituent tokens, words, sentences and annotations.
func NewDocument(id string) (*Document, error) {
	if id == "" {
		return nil, fmt.Errorf("Empty identifier given")
	}

	d := &Document{}
	d.id = id
	d.input = make(map[string]string, 2)
	d.tokens = make(map[string][]*TextToken, 2)
	d.words = make(map[string][]*Word, 2)
	d.annos = make(map[string][]*Annotation, 2)

	return d, nil
}

// SetInput registers the input text of the given section of the
// document.
func (d *Document) SetInput(sec, input string) error {
	if sec == "" || input == "" {
		return fmt.Errorf("Empty section name or body given.")
	}

	d.input[sec] = input
	return nil
}

// Input answers the registered input text of the given section, if
// one exists.
func (d *Document) Input(sec string) (string, error) {
	if s, ok := d.input[sec]; ok {
		return s, nil
	}

	return "", fmt.Errorf("No input text for section : %s", sec)
}

// Tokenize breaks the text in the various sections of the document
// into quasi-atomic tokens.
//
// These tokens can be matched against any available annotations.
// They can also be combined into logical words for named entity
// recognition and part of speech recognition purposes.
func (d *Document) Tokenize() {
	var ti *TextTokenIterator
	var err error

	for sec, inp := range d.input {
		ti = NewTextTokenIterator(inp)
		var toks []*TextToken
		for err = ti.MoveNext(); err == nil; err = ti.MoveNext() {
			toks = append(toks, ti.Item())
		}
		d.tokens[sec] = toks
	}
}

// Annotate records the given annotation against the applicable
// sequence of tokens in the appropriate section of the document.  It
// creates and stores a `Word` corresponding to the text in the
// annotation.
func (d *Document) Annotate(a *Annotation) error {
	toks, ok := d.tokens[a.Section]
	if !ok {
		return fmt.Errorf("Annotation for unrecognised section : %s", a.Section)
	}

	w, err := d.wordForAnnotation(a, toks)
	if err != nil {
		return err
	}

	words := d.words[a.Section]
	words = append(words, w)
	d.words[a.Section] = words

	annos := d.annos[a.Section]
	annos = append(annos, a)
	d.annos[a.Section] = annos

	return nil
}

//

func (d *Document) wordForAnnotation(a *Annotation, toks []*TextToken) (*Word, error) {
	bidx := -1
	eidx := -1
	for i, t := range toks {
		if t.Begin() == a.Begin {
			bidx = i
			break
		}
	}
	if bidx == -1 {
		return nil, fmt.Errorf("Annotation could not be matched : %v", *a)
	}
	l := len(toks)
	for i := bidx; i < l; i++ {
		if toks[i].End() == a.End {
			eidx = i
			break
		}
	}
	if eidx == -1 {
		return nil, fmt.Errorf("Annotation could not be matched : %v", *a)
	}

	w := NewWord(a.Entity, a.Begin, a.End)
	w.etype = a.Type
	return w, nil
}

// TokensInSection answers any recognised tokens in the given section.
func (d *Document) TokensInSection(sec string) []*TextToken {
	if v, ok := d.tokens[sec]; ok {
		return v
	}

	return nil
}

// SectionTokenCount answers the number of recognised tokens in the
// given section.
func (d *Document) SectionTokenCount(sec string) (int, error) {
	if v, ok := d.tokens[sec]; ok {
		return len(v), nil
	}

	return -1, fmt.Errorf("Unknown section : %s", sec)
}

// WordsInSection answers any recognised words in the given section.
func (d *Document) WordsInSection(sec string) []*Word {
	if v, ok := d.words[sec]; ok {
		return v
	}

	return nil
}

// SectionWordCount answers the number of recognised words in the
// given section.
func (d *Document) SectionWordCount(sec string) (int, error) {
	if v, ok := d.words[sec]; ok {
		return len(v), nil
	}

	return -1, fmt.Errorf("Unknown section : %s", sec)
}

// AnnotationsForSection answers the registered annotations for the
// given section.
func (d *Document) AnnotationsForSection(sec string) []*Annotation {
	if v, ok := d.annos[sec]; ok {
		return v
	}

	return nil
}

// SectionAnnotationCount answers the number of registered annotations
// in the given section.
func (d *Document) SectionAnnotationCount(sec string) (int, error) {
	if v, ok := d.annos[sec]; ok {
		return len(v), nil
	}

	return -1, fmt.Errorf("Unknown section : %s", sec)
}
<|endoftext|>


<commit_before>package quic

import (
	"fmt"
	"io"
	"sync"

	"github.com/lucas-clemente/quic-go/flowcontrol"
	"github.com/lucas-clemente/quic-go/frames"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/utils"
)

// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface
//
// Read() and Write() may be called concurrently, but multiple calls to Read() or Write() individually must be synchronized manually.
type stream struct {
	mutex sync.Mutex

	streamID protocol.StreamID
	onData   func()

	readPosInFrame int
	writeOffset    protocol.ByteCount
	readOffset     protocol.ByteCount

	// Once set, the errors must not be changed!
	err error

	cancelled       utils.AtomicBool
	finishedReading utils.AtomicBool
	finishedWriting utils.AtomicBool
	resetLocally    utils.AtomicBool

	frameQueue        *streamFrameSorter
	newFrameOrErrCond sync.Cond

	dataForWriting       []byte
	finSent              bool
	doneWritingOrErrCond sync.Cond

	flowControlManager flowcontrol.FlowControlManager
}

// newStream creates a new Stream
func newStream(StreamID protocol.StreamID, onData func(), flowControlManager flowcontrol.FlowControlManager) (*stream, error) {
	s := &stream{
		onData:             onData,
		streamID:           StreamID,
		flowControlManager: flowControlManager,
		frameQueue:         newStreamFrameSorter(),
	}

	s.newFrameOrErrCond.L = &s.mutex
	s.doneWritingOrErrCond.L = &s.mutex

	return s, nil
}

// Read implements io.Reader. It is not thread safe!
func (s *stream) Read(p []byte) (int, error) {
	if s.cancelled.Get() || s.resetLocally.Get() {
		return 0, s.err
	}
	if s.finishedReading.Get() {
		return 0, io.EOF
	}

	bytesRead := 0
	for bytesRead < len(p) {
		s.mutex.Lock()
		frame := s.frameQueue.Head()

		if frame == nil && bytesRead > 0 {
			s.mutex.Unlock()
			return bytesRead, s.err
		}

		var err error
		for {
			// Stop waiting on errors
			if s.err != nil {
				err = s.err
				break
			}
			if frame != nil {
				s.readPosInFrame = int(s.readOffset - frame.Offset)
				break
			}
			s.newFrameOrErrCond.Wait()
			frame = s.frameQueue.Head()
		}
		s.mutex.Unlock()
		// Here, either frame != nil xor err != nil

		// fmt.Printf("err: %#v, frame: %#v\n", err, frame)

		if frame == nil {
			s.finishedReading.Set(true)
			// We have an err and no data, return the error
			return bytesRead, err
		}

		m := utils.Min(len(p)-bytesRead, int(frame.DataLen())-s.readPosInFrame)

		if bytesRead > len(p) {
			return bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p))
		}
		if s.readPosInFrame > int(frame.DataLen()) {
			return bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, frame.DataLen())
		}
		copy(p[bytesRead:], frame.Data[s.readPosInFrame:])

		s.readPosInFrame += m
		bytesRead += m
		s.readOffset += protocol.ByteCount(m)

		s.flowControlManager.AddBytesRead(s.streamID, protocol.ByteCount(m))
		s.onData() // so that a possible WINDOW_UPDATE is sent

		if s.readPosInFrame >= int(frame.DataLen()) {
			fin := frame.FinBit
			s.mutex.Lock()
			s.frameQueue.Pop()
			s.mutex.Unlock()
			if fin {
				s.finishedReading.Set(true)
				return bytesRead, io.EOF
			}
		}
	}

	return bytesRead, nil
}

func (s *stream) Write(p []byte) (int, error) {
	if s.resetLocally.Get() {
		return 0, s.err
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.err != nil {
		return 0, s.err
	}

	if len(p) == 0 {
		return 0, nil
	}

	s.dataForWriting = make([]byte, len(p))
	copy(s.dataForWriting, p)

	s.onData()

	for s.dataForWriting != nil && s.err == nil {
		s.doneWritingOrErrCond.Wait()
	}

	if s.err != nil {
		return 0, s.err
	}

	return len(p), nil
}

func (s *stream) lenOfDataForWriting() protocol.ByteCount {
	s.mutex.Lock()
	var l protocol.ByteCount
	if s.err == nil {
		l = protocol.ByteCount(len(s.dataForWriting))
	}
	s.mutex.Unlock()
	return l
}

func (s *stream) getDataForWriting(maxBytes protocol.ByteCount) []byte {
	s.mutex.Lock()
	if s.err != nil {
		s.mutex.Unlock()
		return nil
	}
	if s.dataForWriting == nil {
		s.mutex.Unlock()
		return nil
	}
	var ret []byte
	if protocol.ByteCount(len(s.dataForWriting)) > maxBytes {
		ret = s.dataForWriting[:maxBytes]
		s.dataForWriting = s.dataForWriting[maxBytes:]
	} else {
		ret = s.dataForWriting
		s.dataForWriting = nil
		s.doneWritingOrErrCond.Signal()
	}
	s.writeOffset += protocol.ByteCount(len(ret))
	s.mutex.Unlock()
	return ret
}

// Close implements io.Closer
func (s *stream) Close() error {
	s.finishedWriting.Set(true)
	s.onData()
	return nil
}

func (s *stream) shouldSendFin() bool {
	s.mutex.Lock()
	res := s.finishedWriting.Get() && !s.finSent && s.err == nil && s.dataForWriting == nil
	s.mutex.Unlock()
	return res
}

func (s *stream) sentFin() {
	s.mutex.Lock()
	s.finSent = true
	s.mutex.Unlock()
}

// AddStreamFrame adds a new stream frame
func (s *stream) AddStreamFrame(frame *frames.StreamFrame) error {
	maxOffset := frame.Offset + frame.DataLen()
	err := s.flowControlManager.UpdateHighestReceived(s.streamID, maxOffset)
	if err != nil {
		return err
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()
	err = s.frameQueue.Push(frame)
	if err != nil && err != errDuplicateStreamData {
		return err
	}
	s.newFrameOrErrCond.Signal()
	return nil
}

// CloseRemote makes the stream receive a "virtual" FIN stream frame at a given offset
func (s *stream) CloseRemote(offset protocol.ByteCount) {
	s.AddStreamFrame(&frames.StreamFrame{FinBit: true, Offset: offset})
}

// Cancel is called by session to indicate that an error occurred
// The stream should will be closed immediately
func (s *stream) Cancel(err error) {
	s.finishedReading.Set(true)
	s.finishedWriting.Set(true)
	s.cancelled.Set(true)

	s.mutex.Lock()
	// errors must not be changed!
	if s.err == nil {
		s.err = err
		s.newFrameOrErrCond.Signal()
		s.doneWritingOrErrCond.Signal()
	}
	// if s.writeErr == nil {
	// 	s.writeErr = err
	// }
	s.mutex.Unlock()
}

// resets the stream locally
func (s *stream) Reset(err error) {
	s.finishedReading.Set(true)
	s.resetLocally.Set(true)
	s.mutex.Lock()
	// errors must not be changed!
	if s.err == nil {
		s.err = err
		s.newFrameOrErrCond.Signal()
		s.doneWritingOrErrCond.Signal()
	}
	s.mutex.Unlock()
}

// resets the stream remotely
func (s *stream) RegisterRemoteError(err error) {
	s.finishedWriting.Set(true)
	s.mutex.Lock()
	// errors must not be changed!
	if s.err == nil {
		s.err = err
		s.doneWritingOrErrCond.Signal()
	}
	s.mutex.Unlock()
}

func (s *stream) finishedRead() bool {
	return s.finishedReading.Get()
}

func (s *stream) finishedWriteAndSentFin() bool {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.err != nil || (s.finishedWriting.Get() && s.finSent)
}

func (s *stream) finished() bool {
	return s.finishedRead() && s.finishedWriteAndSentFin()
}

func (s *stream) StreamID() protocol.StreamID {
	return s.streamID
}
<commit_msg>stream code cleanup<commit_after>package quic

import (
	"fmt"
	"io"
	"sync"

	"github.com/lucas-clemente/quic-go/flowcontrol"
	"github.com/lucas-clemente/quic-go/frames"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/utils"
)

// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface
//
// Read() and Write() may be called concurrently, but multiple calls to Read() or Write() individually must be synchronized manually.
type stream struct {
	mutex sync.Mutex

	streamID protocol.StreamID
	onData   func()

	readPosInFrame int
	writeOffset    protocol.ByteCount
	readOffset     protocol.ByteCount

	// Once set, the errors must not be changed!
	err error

	// cancelled is set when Cancel() is called
	cancelled utils.AtomicBool
	// finishedReading is set once we read a frame with a FinBit
	finishedReading utils.AtomicBool
	// finisedWriting is set once Close() is called
	finishedWriting utils.AtomicBool
	// resetLocally is set if Reset() is called
	resetLocally utils.AtomicBool
	// resetRemotely is set if RegisterRemoteError() is called
	resetRemotely utils.AtomicBool

	frameQueue        *streamFrameSorter
	newFrameOrErrCond sync.Cond

	dataForWriting       []byte
	finSent              utils.AtomicBool
	doneWritingOrErrCond sync.Cond

	flowControlManager flowcontrol.FlowControlManager
}

// newStream creates a new Stream
func newStream(StreamID protocol.StreamID, onData func(), flowControlManager flowcontrol.FlowControlManager) (*stream, error) {
	s := &stream{
		onData:             onData,
		streamID:           StreamID,
		flowControlManager: flowControlManager,
		frameQueue:         newStreamFrameSorter(),
	}

	s.newFrameOrErrCond.L = &s.mutex
	s.doneWritingOrErrCond.L = &s.mutex

	return s, nil
}

// Read implements io.Reader. It is not thread safe!
func (s *stream) Read(p []byte) (int, error) {
	if s.cancelled.Get() || s.resetLocally.Get() {
		return 0, s.err
	}
	if s.finishedReading.Get() {
		return 0, io.EOF
	}

	bytesRead := 0
	for bytesRead < len(p) {
		s.mutex.Lock()
		frame := s.frameQueue.Head()

		if frame == nil && bytesRead > 0 {
			s.mutex.Unlock()
			return bytesRead, s.err
		}

		var err error
		for {
			// Stop waiting on errors
			if s.resetLocally.Get() || s.cancelled.Get() {
				err = s.err
				break
			}
			if frame != nil {
				s.readPosInFrame = int(s.readOffset - frame.Offset)
				break
			}
			s.newFrameOrErrCond.Wait()
			frame = s.frameQueue.Head()
		}
		s.mutex.Unlock()

		if err != nil {
			return bytesRead, err
		}

		m := utils.Min(len(p)-bytesRead, int(frame.DataLen())-s.readPosInFrame)

		if bytesRead > len(p) {
			return bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p))
		}
		if s.readPosInFrame > int(frame.DataLen()) {
			return bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, frame.DataLen())
		}
		copy(p[bytesRead:], frame.Data[s.readPosInFrame:])

		s.readPosInFrame += m
		bytesRead += m
		s.readOffset += protocol.ByteCount(m)

		s.flowControlManager.AddBytesRead(s.streamID, protocol.ByteCount(m))
		s.onData() // so that a possible WINDOW_UPDATE is sent

		if s.readPosInFrame >= int(frame.DataLen()) {
			fin := frame.FinBit
			s.mutex.Lock()
			s.frameQueue.Pop()
			s.mutex.Unlock()
			if fin {
				s.finishedReading.Set(true)
				return bytesRead, io.EOF
			}
		}
	}

	return bytesRead, nil
}

func (s *stream) Write(p []byte) (int, error) {
	if s.resetLocally.Get() {
		return 0, s.err
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.err != nil {
		return 0, s.err
	}

	if len(p) == 0 {
		return 0, nil
	}

	s.dataForWriting = make([]byte, len(p))
	copy(s.dataForWriting, p)

	s.onData()

	for s.dataForWriting != nil && s.err == nil {
		s.doneWritingOrErrCond.Wait()
	}

	if s.err != nil {
		return 0, s.err
	}

	return len(p), nil
}

func (s *stream) lenOfDataForWriting() protocol.ByteCount {
	s.mutex.Lock()
	var l protocol.ByteCount
	if s.err == nil {
		l = protocol.ByteCount(len(s.dataForWriting))
	}
	s.mutex.Unlock()
	return l
}

func (s *stream) getDataForWriting(maxBytes protocol.ByteCount) []byte {
	s.mutex.Lock()
	if s.err != nil {
		s.mutex.Unlock()
		return nil
	}
	if s.dataForWriting == nil {
		s.mutex.Unlock()
		return nil
	}
	var ret []byte
	if protocol.ByteCount(len(s.dataForWriting)) > maxBytes {
		ret = s.dataForWriting[:maxBytes]
		s.dataForWriting = s.dataForWriting[maxBytes:]
	} else {
		ret = s.dataForWriting
		s.dataForWriting = nil
		s.doneWritingOrErrCond.Signal()
	}
	s.writeOffset += protocol.ByteCount(len(ret))
	s.mutex.Unlock()
	return ret
}

// Close implements io.Closer
func (s *stream) Close() error {
	s.finishedWriting.Set(true)
	s.onData()
	return nil
}

func (s *stream) shouldSendFin() bool {
	s.mutex.Lock()
	res := s.finishedWriting.Get() && !s.finSent.Get() && s.err == nil && s.dataForWriting == nil
	s.mutex.Unlock()
	return res
}

func (s *stream) sentFin() {
	s.finSent.Set(true)
}

// AddStreamFrame adds a new stream frame
func (s *stream) AddStreamFrame(frame *frames.StreamFrame) error {
	maxOffset := frame.Offset + frame.DataLen()
	err := s.flowControlManager.UpdateHighestReceived(s.streamID, maxOffset)
	if err != nil {
		return err
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()
	err = s.frameQueue.Push(frame)
	if err != nil && err != errDuplicateStreamData {
		return err
	}
	s.newFrameOrErrCond.Signal()
	return nil
}

// CloseRemote makes the stream receive a "virtual" FIN stream frame at a given offset
func (s *stream) CloseRemote(offset protocol.ByteCount) {
	s.AddStreamFrame(&frames.StreamFrame{FinBit: true, Offset: offset})
}

// Cancel is called by session to indicate that an error occurred
// The stream should will be closed immediately
func (s *stream) Cancel(err error) {
	s.cancelled.Set(true)

	s.mutex.Lock()
	// errors must not be changed!
	if s.err == nil {
		s.err = err
		s.newFrameOrErrCond.Signal()
		s.doneWritingOrErrCond.Signal()
	}
	s.mutex.Unlock()
}

// resets the stream locally
func (s *stream) Reset(err error) {
	s.resetLocally.Set(true)
	s.mutex.Lock()
	// errors must not be changed!
	if s.err == nil {
		s.err = err
		s.newFrameOrErrCond.Signal()
		s.doneWritingOrErrCond.Signal()
	}
	s.mutex.Unlock()
}

// resets the stream remotely
func (s *stream) RegisterRemoteError(err error) {
	s.resetRemotely.Set(true)
	s.mutex.Lock()
	// errors must not be changed!
	if s.err == nil {
		s.err = err
		s.doneWritingOrErrCond.Signal()
	}
	s.mutex.Unlock()
}

func (s *stream) finishedWriteAndSentFin() bool {
	return s.finishedWriting.Get() && s.finSent.Get()
}

func (s *stream) finished() bool {
	return s.cancelled.Get() || (s.finishedReading.Get() && s.finishedWriteAndSentFin())
}

func (s *stream) StreamID() protocol.StreamID {
	return s.streamID
}
<|endoftext|>


<commit_before>// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
// Use of this source code is governed by the license that can be
// found in the LICENSE file.

package yara

import (
	"io"
	"unsafe"
)

// #include <string.h>
import "C"

//export streamRead
func streamRead(ptr unsafe.Pointer, size, nmemb C.size_t, userData unsafe.Pointer) C.size_t {
	if size == 0 || nmemb == 0 {
		return nmemb
	}
	dst := uintptr(ptr)
	buf := make([]byte, size)
	rd := (*io.Reader)(userData)
	for i := 0; i < int(nmemb); i++ {
		rc, err := (*rd).Read(buf)
		if err != nil || rc < int(size) {
			return C.size_t(i)
		}
		C.memcpy(unsafe.Pointer(dst+uintptr(i)*uintptr(size)), unsafe.Pointer(&buf[0]), size)
	}
	return nmemb
}

//export streamWrite
func streamWrite(ptr unsafe.Pointer, size, nmemb C.size_t, userData unsafe.Pointer) C.size_t {
	if size == 0 || nmemb == 0 {
		return nmemb
	}
	src := uintptr(ptr)
	buf := make([]byte, size)
	wr := (*io.Writer)(userData)
	for i := 0; i < int(nmemb); i++ {
		C.memcpy(unsafe.Pointer(&buf[0]), unsafe.Pointer(src+uintptr(i)*uintptr(size)), size)
		rc, err := (*wr).Write(buf)
		if err != nil || rc < int(size) {
			return C.size_t(i)
		}
	}
	return nmemb
}
<commit_msg>stream: Work around readers/writers that can't process the buffer size in one call<commit_after>// Copyright © 2015 Hilko Bengen <bengen@hilluzination.de>. All rights reserved.
// Use of this source code is governed by the license that can be
// found in the LICENSE file.

package yara

import (
	"io"
	"unsafe"
)

// #include <string.h>
import "C"

//export streamRead
func streamRead(ptr unsafe.Pointer, size, nmemb C.size_t, userData unsafe.Pointer) C.size_t {
	if size == 0 || nmemb == 0 {
		return nmemb
	}
	dst := uintptr(ptr)
	buf := make([]byte, size)
	rd := (*io.Reader)(userData)
	for i := 0; i < int(nmemb); i++ {
		var sz int
		for offset := 0; offset < int(size); offset += sz {
			var err error
			if sz, err = (*rd).Read(buf[offset:]); err != nil {
				return C.size_t(i)
			}
		}
		C.memcpy(unsafe.Pointer(dst+uintptr(i)*uintptr(size)), unsafe.Pointer(&buf[0]), size)
	}
	return nmemb
}

//export streamWrite
func streamWrite(ptr unsafe.Pointer, size, nmemb C.size_t, userData unsafe.Pointer) C.size_t {
	if size == 0 || nmemb == 0 {
		return nmemb
	}
	src := uintptr(ptr)
	buf := make([]byte, size)
	wr := (*io.Writer)(userData)
	for i := 0; i < int(nmemb); i++ {
		C.memcpy(unsafe.Pointer(&buf[0]), unsafe.Pointer(src+uintptr(i)*uintptr(size)), size)
		var sz int
		for offset := 0; offset < int(size); offset += sz {
			var err error
			if sz, err = (*wr).Write(buf[offset:]); err != nil {
				return C.size_t(i)
			}
		}
	}
	return nmemb
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/nsf/termbox-go"
)

// Stream updates a StreamDisplay with new data updates
type Stream struct {
	display  *StreamDisplay
	speed    int
	length   int
	headPos  int
	tailPos  int
	stopCh   chan bool
	headDone bool
}

func (s *Stream) run() {
	var lastRune rune
	for {
		select {
		case <-s.stopCh:
			log.Printf("Stream on SD %d was stopped.\n", s.display.column)
			goto done
		case <-time.After(time.Duration(s.speed) * time.Millisecond):
			// add a new rune if there is space in the stream
			if !s.headDone && s.headPos <= curSizes.height {
				newRune := characters[rand.Intn(len(characters))]
				termbox.SetCell(s.display.column, s.headPos-1, lastRune, termbox.ColorGreen, termbox.ColorBlack)
				termbox.SetCell(s.display.column, s.headPos, newRune, termbox.ColorWhite, termbox.ColorBlack)
				lastRune = newRune
				s.headPos++
			} else {
				s.headDone = true
			}

			// clear rune at the tail of the stream
			if s.tailPos > 0 || s.headPos >= s.length {
				if s.tailPos == 0 {
					// tail is being incremented for the first time. there is space for a new stream
					s.display.newStream <- true
				}
				if s.tailPos < curSizes.height {
					termbox.SetCell(s.display.column, s.tailPos, ' ', termbox.ColorBlack, termbox.ColorBlack) //'\uFF60'
					s.tailPos++
				} else {
					goto done
				}
			}
		}
	}
done:
	delete(s.display.streams, s)
}

// StreamDisplay represents a horizontal line in the terminal on which `Stream`s are displayed.
// StreamDisplay also creates the Streams themselves
type StreamDisplay struct {
	column      int
	stopCh      chan bool
	streams     map[*Stream]bool
	streamsLock sync.Mutex
	newStream   chan bool
}

func (sd *StreamDisplay) run() {
	for {
		select {
		case <-sd.stopCh:
			// lock this SD forever
			sd.streamsLock.Lock()

			// stop streams for this SD
			for s, _ := range sd.streams {
				s.stopCh <- true
			}

			// log that SD has closed
			log.Printf("StreamDisplay on column %d stopped.\n", sd.column)

			// close this goroutine
			return

		case <-sd.newStream:
			// have some wait before the first stream starts..
			// <-time.After(time.Duration(rand.Intn(9000)) * time.Millisecond) //++ TODO: .After or .Sleep??
			time.Sleep(time.Duration(rand.Intn(9000)) * time.Millisecond)

			// lock map
			sd.streamsLock.Lock()

			// crekate new stream instance
			s := &Stream{
				display: sd,
				stopCh:  make(chan bool),
				speed:   30 + rand.Intn(110),
				length:  6 + rand.Intn(6), // length of a stream is between 6 and 12 runes
			}

			// store in streams map
			sd.streams[s] = true

			// run the stream in a goroutine
			go s.run()

			// unlock map
			sd.streamsLock.Unlock()
		}
	}
}
<commit_msg>Mixing bright characters into the streams<commit_after>package main

import (
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/nsf/termbox-go"
)

// Stream updates a StreamDisplay with new data updates
type Stream struct {
	display  *StreamDisplay
	speed    int
	length   int
	headPos  int
	tailPos  int
	stopCh   chan bool
	headDone bool
}

func (s *Stream) run() {
	var lastRune rune
	for {
		select {
		case <-s.stopCh:
			log.Printf("Stream on SD %d was stopped.\n", s.display.column)
			goto done
		case <-time.After(time.Duration(s.speed) * time.Millisecond):
			// add a new rune if there is space in the stream
			if !s.headDone && s.headPos <= curSizes.height {
				newRune := characters[rand.Intn(len(characters))]

				// Making most of the green characters bright/bold...
				if rand.Intn(100) < 66 {
					termbox.SetCell(s.display.column, s.headPos-1, lastRune, termbox.ColorGreen|termbox.AttrBold, termbox.ColorBlack)
				} else {
					termbox.SetCell(s.display.column, s.headPos-1, lastRune, termbox.ColorGreen, termbox.ColorBlack)
				}

				// ...and turning about a third of the heads from gray to white
				if rand.Intn(100) < 33 {
					termbox.SetCell(s.display.column, s.headPos, newRune, termbox.ColorWhite|termbox.AttrBold, termbox.ColorBlack)
				} else {
					termbox.SetCell(s.display.column, s.headPos, newRune, termbox.ColorWhite, termbox.ColorBlack)
				}
				lastRune = newRune
				s.headPos++
			} else {
				s.headDone = true
			}

			// clear rune at the tail of the stream
			if s.tailPos > 0 || s.headPos >= s.length {
				if s.tailPos == 0 {
					// tail is being incremented for the first time. there is space for a new stream
					s.display.newStream <- true
				}
				if s.tailPos < curSizes.height {
					termbox.SetCell(s.display.column, s.tailPos, ' ', termbox.ColorBlack, termbox.ColorBlack) //'\uFF60'
					s.tailPos++
				} else {
					goto done
				}
			}
		}
	}
done:
	delete(s.display.streams, s)
}

// StreamDisplay represents a horizontal line in the terminal on which `Stream`s are displayed.
// StreamDisplay also creates the Streams themselves
type StreamDisplay struct {
	column      int
	stopCh      chan bool
	streams     map[*Stream]bool
	streamsLock sync.Mutex
	newStream   chan bool
}

func (sd *StreamDisplay) run() {
	for {
		select {
		case <-sd.stopCh:
			// lock this SD forever
			sd.streamsLock.Lock()

			// stop streams for this SD
			for s, _ := range sd.streams {
				s.stopCh <- true
			}

			// log that SD has closed
			log.Printf("StreamDisplay on column %d stopped.\n", sd.column)

			// close this goroutine
			return

		case <-sd.newStream:
			// have some wait before the first stream starts..
			// <-time.After(time.Duration(rand.Intn(9000)) * time.Millisecond) //++ TODO: .After or .Sleep??
			time.Sleep(time.Duration(rand.Intn(9000)) * time.Millisecond)

			// lock map
			sd.streamsLock.Lock()

			// crekate new stream instance
			s := &Stream{
				display: sd,
				stopCh:  make(chan bool),
				speed:   30 + rand.Intn(110),
				length:  6 + rand.Intn(6), // length of a stream is between 6 and 12 runes
			}

			// store in streams map
			sd.streams[s] = true

			// run the stream in a goroutine
			go s.run()

			// unlock map
			sd.streamsLock.Unlock()
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mattbaird/elastigo/api"
	"github.com/mattbaird/elastigo/core"
)

// Storage is the target storage for an Index operation.
type Storage int

const (
	// StoreSnapshot corresponds to the non-expiring index (that is, the index
	// that always holds the latest version of all GitHub items). This index
	// only stores snapshotted data.
	StoreSnapshot Storage = iota

	// StoreCurrentState corresponds to the periocally rolling index (that is,
	// the index that gets archived and renewed at a regular interval). This
	// index only stores snapshotted data.
	StoreCurrentState

	// StoreLiveEvent corresponds to the rolling index of events.
	StoreLiveEvent
)

// blobStore is capable of storing a blob into a backend.
type blobStore interface {
	// Index stores the blob into the specified storage under the provided id
	// for a given repository.
	Index(Storage, *Repository, *Blob) error
}

// transformingBlobStore applies transformations before forwarding the
// resulting blob to a simpleBlobStore.
type transformations struct {
	transformations Transformations
}

// NewTransformingBlobStore creates a new transformingBlobStore backed by a
// simpleBlobStore.
func NewTransformingBlobStore(transformations Transformations) blobStore {
	return &transformingBlobStore{
		impl:            NewSimpleBlobStore(),
		transformations: transformations,
	}
}

// transformingBlobStore implements blobStore by applying transformations
// before forwarding the resulting blob to a backing blobStore instance.
type transformingBlobStore struct {
	impl            blobStore
	transformations Transformations
}

// Index stores the blob into the specified storage under the provided id for
// a given repository.
func (b *transformingBlobStore) Index(storage Storage, repo *Repository, blob *Blob) error {
	if trans := b.getTransformation(storage, repo, blob.Type); trans != nil {
		t, err := trans.Apply(blob)
		if err != nil {
			return fmt.Errorf("applying transformation to event %q: %v", blob.Type, err)
		}
		blob = t
	}

	// Forward to the backing implementation.
	return b.impl.Index(storage, repo, blob)
}

func (b *transformingBlobStore) getTransformation(storage Storage, repo *Repository, event string) *Transformation {
	// Live and snapshot data have overlapping types: we can received a
	// "pull_request" live event for a new pull request being opened, as well
	// as a "pull_request" snapshot during a sync operation.
	//
	// This is problematic, because different transformations have to be
	// applies, which is why the storage type contributes to the transformation
	// election.
	if storage == StoreLiveEvent {
		return repo.EventSet[event]
	}

	// This is not a live event: we have hardcoded transformations for the
	// issues and pull requests data types.
	switch event {
	case GithubTypeIssue:
		// [transformation.pull_request] is mandatory
		return b.transformations[GithubTypeIssue]
	case GithubTypePullRequest:
		// [transformation.issue] is mandatory
		return b.transformations[GithubTypePullRequest]
	default:
		// No transformation for that event type.
		return nil
	}
}

// NewSimpleBlobStore creates a new simpleBlobStore.
func NewSimpleBlobStore() blobStore {
	return &simpleBlobStore{}
}

// simpleBlobStore provides basic facilities for writing into Elastic Search.
type simpleBlobStore struct {
}

// Index stores the blob into the specified storage under the provided id for
// a given repository.
func (b *simpleBlobStore) Index(storage Storage, repo *Repository, blob *Blob) error {
	switch storage {
	// Live is an index containing the webhook events. In this particular case,
	// we use the delivery id as the document index.
	//
	// When storing a live event, we always update the next two indices.
	case StoreLiveEvent:
		liveIndex := repo.LiveIndexForTimestamp(blob.Timestamp)
		log.Debugf("store live event to %s/%s/%s", liveIndex, blob.Type, blob.ID)
		if _, err := index(liveIndex, blob); err != nil {
			return fmt.Errorf("store live event %s data: %v", blob.ID, err)
		}
		// Before falling through, replace the blob with the snapshot data from
		// the event, if any.
		if blob = blob.Snapshot(); blob == nil {
			return nil
		}
		fallthrough
	// Current state is an index containing the last version of items at a
	// given moment in time, and is updated at a frequency configured by the
	// user.
	//
	// When storing a current state, we always update the next index.
	case StoreCurrentState:
		stateIndex := repo.StateIndexForTimestamp(blob.Timestamp)
		log.Debugf("store current state to %s/%s/%s", stateIndex, blob.Type, blob.ID)
		if _, err := index(stateIndex, blob); err != nil {
			return fmt.Errorf("store current state %s data: %v", blob.ID, err)
		}
		fallthrough
	// Snapshot is an index containing the last version of all items, opened or
	// closed.
	case StoreSnapshot:
		log.Debugf("store snapshot to %s/%s/%s", repo.SnapshotIndex(), blob.Type, blob.ID)
		if _, err := index(repo.SnapshotIndex(), blob); err != nil {
			return fmt.Errorf("store snapshot %s data: %v", blob.ID, err)
		}
	}
	return nil
}

func index(index string, blob *Blob) (api.BaseResponse, error) {
	timestamp := blob.Timestamp.Format(time.RFC3339)
	//log.Warnf("Index [%s] add [%s] [%s] [%#v]\n", index, blob.ID, timestamp, *blob.Data)
	return core.IndexWithParameters(
		index, blob.Type, blob.ID,
		"" /* parentId */, 0 /* version */, "" /* op_type */, "", /* routing */
		timestamp,
		0 /* ttl */, "" /* percolate */, "" /* timeout */, false /* refresh */, map[string]interface{}{}, /* args */
		blob.Data)
}
<commit_msg>Timezone fix<commit_after>package main

import (
	"fmt"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mattbaird/elastigo/api"
	"github.com/mattbaird/elastigo/core"
)

// Storage is the target storage for an Index operation.
type Storage int

const (
	// StoreSnapshot corresponds to the non-expiring index (that is, the index
	// that always holds the latest version of all GitHub items). This index
	// only stores snapshotted data.
	StoreSnapshot Storage = iota

	// StoreCurrentState corresponds to the periocally rolling index (that is,
	// the index that gets archived and renewed at a regular interval). This
	// index only stores snapshotted data.
	StoreCurrentState

	// StoreLiveEvent corresponds to the rolling index of events.
	StoreLiveEvent
)

// blobStore is capable of storing a blob into a backend.
type blobStore interface {
	// Index stores the blob into the specified storage under the provided id
	// for a given repository.
	Index(Storage, *Repository, *Blob) error
}

// transformingBlobStore applies transformations before forwarding the
// resulting blob to a simpleBlobStore.
type transformations struct {
	transformations Transformations
}

// NewTransformingBlobStore creates a new transformingBlobStore backed by a
// simpleBlobStore.
func NewTransformingBlobStore(transformations Transformations) blobStore {
	return &transformingBlobStore{
		impl:            NewSimpleBlobStore(),
		transformations: transformations,
	}
}

// transformingBlobStore implements blobStore by applying transformations
// before forwarding the resulting blob to a backing blobStore instance.
type transformingBlobStore struct {
	impl            blobStore
	transformations Transformations
}

// Index stores the blob into the specified storage under the provided id for
// a given repository.
func (b *transformingBlobStore) Index(storage Storage, repo *Repository, blob *Blob) error {
	if trans := b.getTransformation(storage, repo, blob.Type); trans != nil {
		t, err := trans.Apply(blob)
		if err != nil {
			return fmt.Errorf("applying transformation to event %q: %v", blob.Type, err)
		}
		blob = t
	}

	// Forward to the backing implementation.
	return b.impl.Index(storage, repo, blob)
}

func (b *transformingBlobStore) getTransformation(storage Storage, repo *Repository, event string) *Transformation {
	// Live and snapshot data have overlapping types: we can received a
	// "pull_request" live event for a new pull request being opened, as well
	// as a "pull_request" snapshot during a sync operation.
	//
	// This is problematic, because different transformations have to be
	// applies, which is why the storage type contributes to the transformation
	// election.
	if storage == StoreLiveEvent {
		return repo.EventSet[event]
	}

	// This is not a live event: we have hardcoded transformations for the
	// issues and pull requests data types.
	switch event {
	case GithubTypeIssue:
		// [transformation.pull_request] is mandatory
		return b.transformations[GithubTypeIssue]
	case GithubTypePullRequest:
		// [transformation.issue] is mandatory
		return b.transformations[GithubTypePullRequest]
	default:
		// No transformation for that event type.
		return nil
	}
}

// NewSimpleBlobStore creates a new simpleBlobStore.
func NewSimpleBlobStore() blobStore {
	return &simpleBlobStore{}
}

// simpleBlobStore provides basic facilities for writing into Elastic Search.
type simpleBlobStore struct {
}

// Index stores the blob into the specified storage under the provided id for
// a given repository.
func (b *simpleBlobStore) Index(storage Storage, repo *Repository, blob *Blob) error {
	switch storage {
	// Live is an index containing the webhook events. In this particular case,
	// we use the delivery id as the document index.
	//
	// When storing a live event, we always update the next two indices.
	case StoreLiveEvent:
		liveIndex := repo.LiveIndexForTimestamp(blob.Timestamp)
		log.Debugf("store live event to %s/%s/%s", liveIndex, blob.Type, blob.ID)
		if _, err := index(liveIndex, blob); err != nil {
			return fmt.Errorf("store live event %s data: %v", blob.ID, err)
		}
		// Before falling through, replace the blob with the snapshot data from
		// the event, if any.
		if blob = blob.Snapshot(); blob == nil {
			return nil
		}
		fallthrough
	// Current state is an index containing the last version of items at a
	// given moment in time, and is updated at a frequency configured by the
	// user.
	//
	// When storing a current state, we always update the next index.
	case StoreCurrentState:
		stateIndex := repo.StateIndexForTimestamp(blob.Timestamp)
		log.Debugf("store current state to %s/%s/%s", stateIndex, blob.Type, blob.ID)
		if _, err := index(stateIndex, blob); err != nil {
			return fmt.Errorf("store current state %s data: %v", blob.ID, err)
		}
		fallthrough
	// Snapshot is an index containing the last version of all items, opened or
	// closed.
	case StoreSnapshot:
		log.Debugf("store snapshot to %s/%s/%s", repo.SnapshotIndex(), blob.Type, blob.ID)
		if _, err := index(repo.SnapshotIndex(), blob); err != nil {
			return fmt.Errorf("store snapshot %s data: %v", blob.ID, err)
		}
	}
	return nil
}

func index(index string, blob *Blob) (api.BaseResponse, error) {
	// Apparently Elastic Search don't like timezone specifiers other than Z.
	timestamp := blob.Timestamp.UTC().Format(time.RFC3339)
	//log.Warnf("Index [%s] add [%s] [%s] [%#v]\n", index, blob.ID, timestamp, *blob.Data)
	return core.IndexWithParameters(
		index, blob.Type, blob.ID,
		"" /* parentId */, 0 /* version */, "" /* op_type */, "", /* routing */
		timestamp,
		0 /* ttl */, "" /* percolate */, "" /* timeout */, false /* refresh */, map[string]interface{}{}, /* args */
		blob.Data)
}
<|endoftext|>


<commit_before>// Copyright 2017 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"html/template"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"sync"
	"testing"
	"time"

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

func testRequest(t *testing.T, url string) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}
	client := &http.Client{Transport: tr}

	resp, err := client.Get(url)
	assert.NoError(t, err)
	defer resp.Body.Close()

	body, ioerr := ioutil.ReadAll(resp.Body)
	assert.NoError(t, ioerr)
	assert.Equal(t, "it worked", string(body), "resp body should match")
	assert.Equal(t, "200 OK", resp.Status, "should get a 200")
}

func TestRunEmpty(t *testing.T) {
	os.Setenv("PORT", "")
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.Run())
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.Run(":8080"))
	testRequest(t, "http://localhost:8080/example")
}

func TestRunTLS(t *testing.T) {
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

		assert.NoError(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	}()

	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	testRequest(t, "https://localhost:8443/example")
}

func TestPusher(t *testing.T) {
	var html = template.Must(template.New("https").Parse(`

  <title>Https Test</title>

Welcome, Ginner!

`))

	router := New()
	router.Static("./assets", "./assets")
	router.SetHTMLTemplate(html)

	go func() {
		router.GET("/pusher", func(c *Context) {
			if pusher := c.Writer.Pusher(); pusher != nil {
				err := pusher.Push("/assets/app.js", nil)
				assert.NoError(t, err)
			}
			c.String(http.StatusOK, "it worked")
		})

		assert.NoError(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	}()

	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	testRequest(t, "https://localhost:8449/pusher")
}

func TestRunEmptyWithEnv(t *testing.T) {
	os.Setenv("PORT", "3123")
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.Run())
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.Run(":3123"))
	testRequest(t, "http://localhost:3123/example")
}

func TestRunTooMuchParams(t *testing.T) {
	router := New()
	assert.Panics(t, func() {
		assert.NoError(t, router.Run("2", "2"))
	})
}

func TestRunWithPort(t *testing.T) {
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.Run(":5150"))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.Run(":5150"))
	testRequest(t, "http://localhost:5150/example")
}

func TestUnixSocket(t *testing.T) {
	router := New()

	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.RunUnix("/tmp/unix_unit_test"))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	c, err := net.Dial("unix", "/tmp/unix_unit_test")
	assert.NoError(t, err)

	fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n")
	scanner := bufio.NewScanner(c)
	var response string
	for scanner.Scan() {
		response += scanner.Text()
	}
	assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
	assert.Contains(t, response, "it worked", "resp body should match")
}

func TestBadUnixSocket(t *testing.T) {
	router := New()
	assert.Error(t, router.RunUnix("#/tmp/unix_unit_test"))
}

func TestFileDescriptor(t *testing.T) {
	router := New()

	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	assert.NoError(t, err)
	listener, err := net.ListenTCP("tcp", addr)
	assert.NoError(t, err)
	socketFile, err := listener.File()
	assert.NoError(t, err)

	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.RunFd(int(socketFile.Fd())))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	c, err := net.Dial("tcp", listener.Addr().String())
	assert.NoError(t, err)

	fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
	scanner := bufio.NewScanner(c)
	var response string
	for scanner.Scan() {
		response += scanner.Text()
	}
	assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
	assert.Contains(t, response, "it worked", "resp body should match")
}

func TestBadFileDescriptor(t *testing.T) {
	router := New()
	assert.Error(t, router.RunFd(0))
}

func TestListener(t *testing.T) {
	router := New()
	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	assert.NoError(t, err)
	listener, err := net.ListenTCP("tcp", addr)
	assert.NoError(t, err)
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.RunListener(listener))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	c, err := net.Dial("tcp", listener.Addr().String())
	assert.NoError(t, err)

	fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
	scanner := bufio.NewScanner(c)
	var response string
	for scanner.Scan() {
		response += scanner.Text()
	}
	assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
	assert.Contains(t, response, "it worked", "resp body should match")
}

func TestBadListener(t *testing.T) {
	router := New()
	addr, err := net.ResolveTCPAddr("tcp", "localhost:10086")
	assert.NoError(t, err)
	listener, err := net.ListenTCP("tcp", addr)
	assert.NoError(t, err)
	listener.Close()
	assert.Error(t, router.RunListener(listener))
}

func TestWithHttptestWithAutoSelectedPort(t *testing.T) {
	router := New()
	router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

	ts := httptest.NewServer(router)
	defer ts.Close()

	testRequest(t, ts.URL+"/example")
}

func TestConcurrentHandleContext(t *testing.T) {
	router := New()
	router.GET("/", func(c *Context) {
		c.Request.URL.Path = "/example"
		router.HandleContext(c)
	})
	router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

	var wg sync.WaitGroup
	iterations := 200
	wg.Add(iterations)
	for i := 0; i < iterations; i++ {
		go func() {
			testGetRequestHandler(t, router, "/")
			wg.Done()
		}()
	}
	wg.Wait()
}

// func TestWithHttptestWithSpecifiedPort(t *testing.T) {
// 	router := New()
// 	router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

// 	l, _ := net.Listen("tcp", ":8033")
// 	ts := httptest.Server{
// 		Listener: l,
// 		Config:   &http.Server{Handler: router},
// 	}
// 	ts.Start()
// 	defer ts.Close()

// 	testRequest(t, "http://localhost:8033/example")
// }

func testGetRequestHandler(t *testing.T, h http.Handler, url string) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	assert.NoError(t, err)

	w := httptest.NewRecorder()
	h.ServeHTTP(w, req)

	assert.Equal(t, "it worked", w.Body.String(), "resp body should match")
	assert.Equal(t, 200, w.Code, "should get a 200")
}
<commit_msg>Update gin_integration_test.go (#2341)<commit_after>// Copyright 2017 Manu Martinez-Almeida.  All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"html/template"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"os"
	"sync"
	"testing"
	"time"

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

func testRequest(t *testing.T, url string) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}
	client := &http.Client{Transport: tr}

	resp, err := client.Get(url)
	assert.NoError(t, err)
	defer resp.Body.Close()

	body, ioerr := ioutil.ReadAll(resp.Body)
	assert.NoError(t, ioerr)
	assert.Equal(t, "it worked", string(body), "resp body should match")
	assert.Equal(t, "200 OK", resp.Status, "should get a 200")
}

func TestRunEmpty(t *testing.T) {
	os.Setenv("PORT", "")
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.Run())
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.Run(":8080"))
	testRequest(t, "http://localhost:8080/example")
}

func TestRunTLS(t *testing.T) {
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

		assert.NoError(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	}()

	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	testRequest(t, "https://localhost:8443/example")
}

func TestPusher(t *testing.T) {
	var html = template.Must(template.New("https").Parse(`

  <title>Https Test</title>

Welcome, Ginner!

`))

	router := New()
	router.Static("./assets", "./assets")
	router.SetHTMLTemplate(html)

	go func() {
		router.GET("/pusher", func(c *Context) {
			if pusher := c.Writer.Pusher(); pusher != nil {
				err := pusher.Push("/assets/app.js", nil)
				assert.NoError(t, err)
			}
			c.String(http.StatusOK, "it worked")
		})

		assert.NoError(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	}()

	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
	testRequest(t, "https://localhost:8449/pusher")
}

func TestRunEmptyWithEnv(t *testing.T) {
	os.Setenv("PORT", "3123")
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.Run())
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.Run(":3123"))
	testRequest(t, "http://localhost:3123/example")
}

func TestRunTooMuchParams(t *testing.T) {
	router := New()
	assert.Panics(t, func() {
		assert.NoError(t, router.Run("2", "2"))
	})
}

func TestRunWithPort(t *testing.T) {
	router := New()
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.Run(":5150"))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	assert.Error(t, router.Run(":5150"))
	testRequest(t, "http://localhost:5150/example")
}

func TestUnixSocket(t *testing.T) {
	router := New()

	unixTestSocket := "/tmp/unix_unit_test"

	defer os.Remove(unixTestSocket)

	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.RunUnix(unixTestSocket))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	c, err := net.Dial("unix", unixTestSocket)
	assert.NoError(t, err)

	fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n")
	scanner := bufio.NewScanner(c)
	var response string
	for scanner.Scan() {
		response += scanner.Text()
	}
	assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
	assert.Contains(t, response, "it worked", "resp body should match")
}

func TestBadUnixSocket(t *testing.T) {
	router := New()
	assert.Error(t, router.RunUnix("#/tmp/unix_unit_test"))
}

func TestFileDescriptor(t *testing.T) {
	router := New()

	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	assert.NoError(t, err)
	listener, err := net.ListenTCP("tcp", addr)
	assert.NoError(t, err)
	socketFile, err := listener.File()
	assert.NoError(t, err)

	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.RunFd(int(socketFile.Fd())))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	c, err := net.Dial("tcp", listener.Addr().String())
	assert.NoError(t, err)

	fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
	scanner := bufio.NewScanner(c)
	var response string
	for scanner.Scan() {
		response += scanner.Text()
	}
	assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
	assert.Contains(t, response, "it worked", "resp body should match")
}

func TestBadFileDescriptor(t *testing.T) {
	router := New()
	assert.Error(t, router.RunFd(0))
}

func TestListener(t *testing.T) {
	router := New()
	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	assert.NoError(t, err)
	listener, err := net.ListenTCP("tcp", addr)
	assert.NoError(t, err)
	go func() {
		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
		assert.NoError(t, router.RunListener(listener))
	}()
	// have to wait for the goroutine to start and run the server
	// otherwise the main thread will complete
	time.Sleep(5 * time.Millisecond)

	c, err := net.Dial("tcp", listener.Addr().String())
	assert.NoError(t, err)

	fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
	scanner := bufio.NewScanner(c)
	var response string
	for scanner.Scan() {
		response += scanner.Text()
	}
	assert.Contains(t, response, "HTTP/1.0 200", "should get a 200")
	assert.Contains(t, response, "it worked", "resp body should match")
}

func TestBadListener(t *testing.T) {
	router := New()
	addr, err := net.ResolveTCPAddr("tcp", "localhost:10086")
	assert.NoError(t, err)
	listener, err := net.ListenTCP("tcp", addr)
	assert.NoError(t, err)
	listener.Close()
	assert.Error(t, router.RunListener(listener))
}

func TestWithHttptestWithAutoSelectedPort(t *testing.T) {
	router := New()
	router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

	ts := httptest.NewServer(router)
	defer ts.Close()

	testRequest(t, ts.URL+"/example")
}

func TestConcurrentHandleContext(t *testing.T) {
	router := New()
	router.GET("/", func(c *Context) {
		c.Request.URL.Path = "/example"
		router.HandleContext(c)
	})
	router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

	var wg sync.WaitGroup
	iterations := 200
	wg.Add(iterations)
	for i := 0; i < iterations; i++ {
		go func() {
			testGetRequestHandler(t, router, "/")
			wg.Done()
		}()
	}
	wg.Wait()
}

// func TestWithHttptestWithSpecifiedPort(t *testing.T) {
// 	router := New()
// 	router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })

// 	l, _ := net.Listen("tcp", ":8033")
// 	ts := httptest.Server{
// 		Listener: l,
// 		Config:   &http.Server{Handler: router},
// 	}
// 	ts.Start()
// 	defer ts.Close()

// 	testRequest(t, "http://localhost:8033/example")
// }

func testGetRequestHandler(t *testing.T, h http.Handler, url string) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	assert.NoError(t, err)

	w := httptest.NewRecorder()
	h.ServeHTTP(w, req)

	assert.Equal(t, "it worked", w.Body.String(), "resp body should match")
	assert.Equal(t, 200, w.Code, "should get a 200")
}
<|endoftext|>


<commit_before>package fate

import (
	"math/rand"
	"sort"
)

// tokset maintains a set of tokens in a slice. Use Add to insert a
// token and use the slice itself to get the tokens.
type tokset struct {
	t1 []uint8
	t2 []uint16
	t3 []byte
	t4 []uint32
}

// Add inserts tok into this set if not already present.
//
// Returns a bool signaling whether the token was already in the set
// (similar logic to map lookups).
func (t *tokset) Add(tok token) bool {
	var found bool

	switch {
	case tok <= 0xFF:
		t.t1, found = add1(t.t1, tok)
		return found
	case tok <= 0xFFFF:
		t.t2, found = add2(t.t2, tok)
		return found
	case tok <= 0xFFFFFF:
		t.t3, found = add3(t.t3, tok)
		return found
	case tok <= 0xFFFFFFFF:
		t.t4, found = add4(t.t4, tok)
		return found
	}

	panic("oops")
}

func add1(buf []uint8, tok token) ([]byte, bool) {
	val := uint8(tok)

	size := len(buf)
	if size == 0 {
		return append(buf, val), false
	}

	loc := sort.Search(size, func(i int) bool { return buf[i] >= val })
	if loc < size && buf[loc] == val {
		return buf, true
	}

	buf = append(buf, 0)
	copy(buf[loc+1:], buf[loc:])
	buf[loc] = val

	return buf, false
}

func add2(buf []uint16, tok token) ([]uint16, bool) {
	val := uint16(tok)

	size := len(buf)
	if size == 0 {
		return append(buf, val), false
	}

	loc := sort.Search(size, func(i int) bool { return buf[i] >= val })
	if loc < len(buf) && buf[loc] == val {
		return buf, true
	}

	buf = append(buf, 0)
	copy(buf[loc+1:], buf[loc:])
	buf[loc] = val

	return buf, false
}

func add3(buf []byte, tok token) ([]byte, bool) {
	val := make([]byte, 3)
	put3(val, tok)

	size := len(buf) / 3
	if size == 0 {
		return append(buf, val...), false
	}

	loc := sort.Search(size, func(i int) bool { return unpack3(buf[3*i:]) >= tok })

	if 3*loc < len(buf) && unpack3(buf[3*loc:]) == tok {
		return buf, true
	}

	buf = append(buf, 0, 0, 0)
	copy(buf[3*(loc+1):], buf[3*loc:])
	copy(buf[3*loc:], val)

	return buf, false
}

func add4(buf []uint32, tok token) ([]uint32, bool) {
	val := uint32(tok)

	size := len(buf)
	if size == 0 {
		return append(buf, val), false
	}

	loc := sort.Search(size, func(i int) bool { return buf[i] >= val })
	if loc < len(buf) && buf[loc] == val {
		return buf, true
	}

	buf = append(buf, 0)
	copy(buf[loc+1:], buf[loc:])
	buf[loc] = val

	return buf, false
}

func (t *tokset) Len() int {
	if t == nil {
		return 0
	}

	return len(t.t1) + len(t.t2) + len(t.t3)/3 + len(t.t4)
}

func (t *tokset) Tokens() []token {
	if t == nil {
		return nil
	}

	var tokens []token

	for _, val := range t.t1 {
		tokens = append(tokens, token(val))
	}
	for _, val := range t.t2 {
		tokens = append(tokens, token(val))
	}
	for i := 0; i < len(t.t3); i += 3 {
		tokens = append(tokens, unpack3(t.t3[i:]))
	}
	for _, val := range t.t4 {
		tokens = append(tokens, token(val))
	}

	return tokens
}

func put3(buf []byte, tok token) {
	buf[0] = byte(tok)
	buf[1] = byte(tok >> 8)
	buf[2] = byte(tok >> 16)
}

func unpack3(buf []byte) token {
	return token(buf[0]) | token(buf[1])<<8 | token(buf[2])<<16
}

func (t tokset) Choice(r *rand.Rand) token {
	index := r.Intn(t.Len())

	switch {
	case index < len(t.t1):
		return token(t.t1[index])
	case index < len(t.t1)+len(t.t2):
		return token(t.t2[index-len(t.t1)])
	case index < len(t.t1)+len(t.t2)+len(t.t3):
		return unpack3(t.t3[index-len(t.t1)-len(t.t2):])
	case index < len(t.t1)+len(t.t2)+len(t.t3)+len(t.t4):
		return token(t.t4[index-len(t.t1)-len(t.t2)-len(t.t3)])
	}

	panic("oops")
}
<commit_msg>Switch to a single byte buffer in tokset<commit_after>package fate

import (
	"math/rand"
	"sort"
)

// tokset maintains a set of tokens as a sorted slice of integers.
//
// 1-byte tokens (<= 0xFF) are in buf[0:t2]
// 2-byte tokens (<= 0xFFFF) are in buf[t2:t3]
// 3-byte tokens (<= 0xFFFFFF) are in buf[t3:]
//
// They're stored little-endian. Adds are O(log N). Choosing a random
// token in the set is O(1).
//
// tokens greater than 0xFFFFFF are not currently supported. This is
// enough token space to handle the Web 1T corpus.
type tokset struct {
	buf []byte

	// count of 2-byte tokens, count of 1-byte tokens
	c2 uint16
	c1 uint8
}

func (t *tokset) Add(tok token) bool {
	size := len(t.buf)

	switch {
	case tok <= 0xFF:
		if size == 0 {
			t.buf = append(t.buf, byte(tok))
			t.c1++
			return false
		}

		return t.add1(tok)
	case tok <= 0xFFFF:
		if size == 0 {
			t.buf = append(t.buf, byte(tok), byte(tok>>8))
			t.c2++
			return false
		}

		return t.add2(tok)
	case tok <= 0xFFFFFF:
		if size == 0 {
			t.buf = append(t.buf, byte(tok), byte(tok>>8), byte(tok>>16))
			return false
		}

		return t.add3(tok)
	}

	panic("oops")
}

func (t *tokset) span1() []byte {
	return t.buf[0:t.c1]
}

func (t *tokset) span2() []byte {
	return t.buf[int(t.c1) : int(t.c1)+2*int(t.c2)]
}

func (t *tokset) span3() []byte {
	return t.buf[int(t.c1)+2*int(t.c2):]
}

func (t *tokset) add1(tok token) bool {
	span := t.span1()
	loc := sort.Search(len(span), func(i int) bool {
		return token(span[i]) >= tok
	})

	if loc < len(span) && token(span[loc]) == tok {
		return true
	}

	t.buf = append(t.buf, 0)
	copy(t.buf[loc+1:], t.buf[loc:])
	t.buf[loc] = byte(tok)

	t.c1++

	return false
}

func (t *tokset) add2(tok token) bool {
	span := t.span2()
	idx := sort.Search(len(span)/2, func(i int) bool {
		return unpack2(span[2*i:]) >= tok
	})

	if idx < len(span)/2 && unpack2(span[2*idx:]) == tok {
		return true
	}

	t.buf = append(t.buf, 0, 0)

	loc := int(t.c1) + 2*idx
	copy(t.buf[loc+2:], t.buf[loc:])
	put2(t.buf[loc:], tok)

	t.c2++

	return false
}
func (t *tokset) add3(tok token) bool {
	span := t.span3()
	idx := sort.Search(len(span)/3, func(i int) bool {
		return unpack3(span[3*i:]) >= tok
	})

	if idx < len(span)/3 && unpack3(span[3*idx:]) == tok {
		return true
	}

	t.buf = append(t.buf, 0, 0, 0)

	loc := int(t.c1) + 2*int(t.c2) + 3*idx
	copy(t.buf[loc+3:], t.buf[loc:])
	put3(t.buf[loc:], tok)

	return false
}

func (t *tokset) Len() int {
	if t == nil {
		return 0
	}

	return int(t.c1) + int(t.c2) + len(t.span3())/3
}

func (t *tokset) Tokens() []token {
	if t == nil {
		return nil
	}

	var tokens []token
	for _, val := range t.span1() {
		tokens = append(tokens, token(val))
	}

	span2 := t.span2()
	for i := 0; i < len(span2); i += 2 {
		tokens = append(tokens, unpack2(span2[i:]))
	}

	span3 := t.span3()
	for i := 0; i < len(span3); i += 3 {
		tokens = append(tokens, unpack3(span3[i:]))
	}

	return tokens
}

func put2(buf []byte, tok token) {
	buf[0] = byte(tok)
	buf[1] = byte(tok >> 8)
}

func put3(buf []byte, tok token) {
	buf[0] = byte(tok)
	buf[1] = byte(tok >> 8)
	buf[2] = byte(tok >> 16)
}

func unpack2(buf []byte) token {
	return token(buf[0]) | token(buf[1])<<8
}

func unpack3(buf []byte) token {
	return token(buf[0]) | token(buf[1])<<8 | token(buf[2])<<16
}

func (t tokset) Choice(r *rand.Rand) token {
	index := r.Intn(t.Len())

	switch {
	case index < int(t.c1):
		return token(t.buf[index])
	case index < int(t.c1)+int(t.c2):
		span := t.span2()
		return unpack2(span[2*(index-int(t.c1)):])
	case index < t.Len():
		span := t.span3()
		return unpack3(span[3*(index-(int(t.c2)+int(t.c1))):])
	}

	panic("oops")
}
<|endoftext|>


<commit_before>package behavior

import (
	"io/ioutil"
	"os"
	"runtime"

	"github.com/pkg/errors"

	"github.com/havoc-io/mutagen/pkg/filesystem"
)

const (
	// executabilityProbeFileNamePrefix is the prefix used for temporary files
	// created by the executability preservation test.
	executabilityProbeFileNamePrefix = filesystem.TemporaryNamePrefix + "executability-test"
)

// PreservesExecutabilityByPath determines whether or not the filesystem on
// which the directory at the specified path resides preserves POSIX
// executability bits. It allows for the path leaf to be a symbolic link.
func PreservesExecutabilityByPath(path string, probeMode ProbeMode) (bool, error) {
	// Check the filesystem probing mode and see if we can return an assumption.
	if probeMode == ProbeMode_ProbeModeAssume {
		return assumeExecutabilityPreservation, nil
	} else if !probeMode.Supported() {
		panic("invalid probe mode")
	}

	// Check if we have a fast test that will work. If we're on Windows, we
	// enforce that the fast path was used. There is some code below, namely the
	// use of os.File's Chmod method (and possibly the os.File's Stat method,
	// which may be racey on Windows), that won't work on Windows (though it
	// could possibly be adapted in case we add a force-probe probe mode).
	if result, ok := probeExecutabilityPreservationFastByPath(path); ok {
		return result, nil
	} else if runtime.GOOS == "windows" {
		panic("fast path not used on Windows")
	}

	// Create a temporary file.
	file, err := ioutil.TempFile(path, executabilityProbeFileNamePrefix)
	if err != nil {
		return false, errors.Wrap(err, "unable to create test file")
	}

	// Ensure that the file is cleaned up and removed when we're done.
	defer func() {
		file.Close()
		os.Remove(file.Name())
	}()

	// Mark the file as user-executable. We use the os.File-based Chmod here
	// since this code only runs on POSIX systems where this is supported.
	if err = file.Chmod(0700); err != nil {
		return false, errors.Wrap(err, "unable to mark test file as executable")
	}

	// Grab the file statistics and check for executability. We enforce that
	// only the user-executable bit is set, because filesystems that don't
	// preserve executability on POSIX systems (e.g. FAT32 on Darwin) sometimes
	// mark every file as having every executable bit set, which is another type
	// of non-preserving behavior. This behavior is not universal (e.g. FAT32 on
	// Linux marks every file as having no executable bit set), but this test
	// should be.
	if info, err := file.Stat(); err != nil {
		return false, errors.Wrap(err, "unable to check test file executability")
	} else {
		return info.Mode()&0111 == 0100, nil
	}
}

// PreservesExecutability determines whether or not the specified directory (and
// its underlying filesystem) preserves POSIX executability bits.
func PreservesExecutability(directory *filesystem.Directory, probeMode ProbeMode) (bool, error) {
	// Check the filesystem probing mode and see if we can return an assumption.
	if probeMode == ProbeMode_ProbeModeAssume {
		return assumeExecutabilityPreservation, nil
	} else if !probeMode.Supported() {
		panic("invalid probe mode")
	}

	// Check if we have a fast test that will work. If we're on Windows, we
	// enforce that the fast path was used. There is some code below, namely the
	// use of os.File's Chmod method (and possibly the os.File's Stat method,
	// which may be racey on Windows), that won't work on Windows (though it
	// could possibly be adapted in case we add a force-probe probe mode).
	if result, ok := probeExecutabilityPreservationFast(directory); ok {
		return result, nil
	} else if runtime.GOOS == "windows" {
		panic("fast path not used on Windows")
	}

	// Create a temporary file.
	name, file, err := directory.CreateTemporaryFile(executabilityProbeFileNamePrefix)
	if err != nil {
		return false, errors.Wrap(err, "unable to create test file")
	}

	// Ensure that the file is cleaned up and removed when we're done.
	defer func() {
		file.Close()
		directory.RemoveFile(name)
	}()

	// HACK: Convert the file to an os.File object for race-free Chmod and Stat
	// access. This is an acceptable hack since we live inside the same package
	// as the Directory implementation.
	osFile, ok := file.(*os.File)
	if !ok {
		panic("opened file is not an os.File object")
	}

	// Mark the file as user-executable. We use the os.File-based Chmod here
	// since this code only runs on POSIX systems where this is supported.
	if err = osFile.Chmod(0700); err != nil {
		return false, errors.Wrap(err, "unable to mark test file as executable")
	}

	// Grab the file statistics and check for executability. We enforce that
	// only the user-executable bit is set, because filesystems that don't
	// preserve executability on POSIX systems (e.g. FAT32 on Darwin) sometimes
	// mark every file as having every executable bit set, which is another type
	// of non-preserving behavior. This behavior is not universal (e.g. FAT32 on
	// Linux marks every file as having no executable bit set), but this test
	// should be.
	if info, err := osFile.Stat(); err != nil {
		return false, errors.Wrap(err, "unable to check test file executability")
	} else {
		return info.Mode()&0111 == 0100, nil
	}
}
<commit_msg>Clarified reason for fast path requirement on Windows.<commit_after>package behavior

import (
	"io/ioutil"
	"os"
	"runtime"

	"github.com/pkg/errors"

	"github.com/havoc-io/mutagen/pkg/filesystem"
)

const (
	// executabilityProbeFileNamePrefix is the prefix used for temporary files
	// created by the executability preservation test.
	executabilityProbeFileNamePrefix = filesystem.TemporaryNamePrefix + "executability-test"
)

// PreservesExecutabilityByPath determines whether or not the filesystem on
// which the directory at the specified path resides preserves POSIX
// executability bits. It allows for the path leaf to be a symbolic link.
func PreservesExecutabilityByPath(path string, probeMode ProbeMode) (bool, error) {
	// Check the filesystem probing mode and see if we can return an assumption.
	if probeMode == ProbeMode_ProbeModeAssume {
		return assumeExecutabilityPreservation, nil
	} else if !probeMode.Supported() {
		panic("invalid probe mode")
	}

	// Check if we have a fast test that will work. If we're on Windows, we
	// enforce that the fast path was used. There is some code below, namely the
	// use of os.File's Chmod method (and possibly the os.File's Stat method,
	// which may be racey on Windows), that won't work on Windows (though it
	// could possibly be adapted in case we add a force-probe probe mode), which
	// is why we require that the fast path succeeds on Windows.
	if result, ok := probeExecutabilityPreservationFastByPath(path); ok {
		return result, nil
	} else if runtime.GOOS == "windows" {
		panic("fast path not used on Windows")
	}

	// Create a temporary file.
	file, err := ioutil.TempFile(path, executabilityProbeFileNamePrefix)
	if err != nil {
		return false, errors.Wrap(err, "unable to create test file")
	}

	// Ensure that the file is cleaned up and removed when we're done.
	defer func() {
		file.Close()
		os.Remove(file.Name())
	}()

	// Mark the file as user-executable. We use the os.File-based Chmod here
	// since this code only runs on POSIX systems where this is supported.
	if err = file.Chmod(0700); err != nil {
		return false, errors.Wrap(err, "unable to mark test file as executable")
	}

	// Grab the file statistics and check for executability. We enforce that
	// only the user-executable bit is set, because filesystems that don't
	// preserve executability on POSIX systems (e.g. FAT32 on Darwin) sometimes
	// mark every file as having every executable bit set, which is another type
	// of non-preserving behavior. This behavior is not universal (e.g. FAT32 on
	// Linux marks every file as having no executable bit set), but this test
	// should be.
	if info, err := file.Stat(); err != nil {
		return false, errors.Wrap(err, "unable to check test file executability")
	} else {
		return info.Mode()&0111 == 0100, nil
	}
}

// PreservesExecutability determines whether or not the specified directory (and
// its underlying filesystem) preserves POSIX executability bits.
func PreservesExecutability(directory *filesystem.Directory, probeMode ProbeMode) (bool, error) {
	// Check the filesystem probing mode and see if we can return an assumption.
	if probeMode == ProbeMode_ProbeModeAssume {
		return assumeExecutabilityPreservation, nil
	} else if !probeMode.Supported() {
		panic("invalid probe mode")
	}

	// Check if we have a fast test that will work. If we're on Windows, we
	// enforce that the fast path was used. There is some code below, namely the
	// use of os.File's Chmod method (and possibly the os.File's Stat method,
	// which may be racey on Windows), that won't work on Windows (though it
	// could possibly be adapted in case we add a force-probe probe mode), which
	// is why we require that the fast path succeeds on Windows.
	if result, ok := probeExecutabilityPreservationFast(directory); ok {
		return result, nil
	} else if runtime.GOOS == "windows" {
		panic("fast path not used on Windows")
	}

	// Create a temporary file.
	name, file, err := directory.CreateTemporaryFile(executabilityProbeFileNamePrefix)
	if err != nil {
		return false, errors.Wrap(err, "unable to create test file")
	}

	// Ensure that the file is cleaned up and removed when we're done.
	defer func() {
		file.Close()
		directory.RemoveFile(name)
	}()

	// HACK: Convert the file to an os.File object for race-free Chmod and Stat
	// access. This is an acceptable hack since we live inside the same package
	// as the Directory implementation.
	osFile, ok := file.(*os.File)
	if !ok {
		panic("opened file is not an os.File object")
	}

	// Mark the file as user-executable. We use the os.File-based Chmod here
	// since this code only runs on POSIX systems where this is supported.
	if err = osFile.Chmod(0700); err != nil {
		return false, errors.Wrap(err, "unable to mark test file as executable")
	}

	// Grab the file statistics and check for executability. We enforce that
	// only the user-executable bit is set, because filesystems that don't
	// preserve executability on POSIX systems (e.g. FAT32 on Darwin) sometimes
	// mark every file as having every executable bit set, which is another type
	// of non-preserving behavior. This behavior is not universal (e.g. FAT32 on
	// Linux marks every file as having no executable bit set), but this test
	// should be.
	if info, err := osFile.Stat(); err != nil {
		return false, errors.Wrap(err, "unable to check test file executability")
	} else {
		return info.Mode()&0111 == 0100, nil
	}
}
<|endoftext|>