<commit_before>// Copyright (C) 2016 Space Monkey, Inc.
//
// 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 ir

import "gopkg.in/spacemonkeygo/dbx.v1/ast"

func transformModel(lookup *lookup, model_entry *modelEntry) (err error) {
	model := model_entry.model
	ast_model := model_entry.ast

	model.Name = ast_model.Name
	model.Table = ast_model.Table

	for _, ast_field := range ast_model.Fields {
		field_entry := model_entry.GetField(ast_field.Name)
		if err := transformField(lookup, field_entry); err != nil {
			return err
		}
	}

	if len(ast_model.PrimaryKey.Refs) == 0 {
		return Error.New("%s: no primary key defined", ast_model.Pos)
	}

	for _, ast_fieldref := range ast_model.PrimaryKey.Refs {
		field, err := model_entry.FindField(ast_fieldref)
		if err != nil {
			return err
		}
		if field.Nullable {
			return Error.New("%s: nullable field %q cannot be a primary key",
				ast_fieldref.Pos, ast_fieldref.Field)
		}
		if field.Updatable {
			return Error.New("%s: updatable field %q cannot be a primary key",
				ast_fieldref.Pos, ast_fieldref.Field)
		}
		model.PrimaryKey = append(model.PrimaryKey, field)
	}

	for _, ast_unique := range ast_model.Unique {
		fields, err := resolveRelativeFieldRefs(model_entry, ast_unique.Refs)
		if err != nil {
			return err
		}
		model.Unique = append(model.Unique, fields)
	}

	index_names := map[string]*ast.Index{}
	for _, ast_index := range ast_model.Indexes {
		if existing, ok := index_names[ast_index.Name]; ok {
			return Error.New("%s: index %q already defined at %s",
				ast_index.Pos, ast_index.Name, existing.Pos)
		}
		index_names[ast_index.Name] = ast_index

		if ast_index.Fields == nil || len(ast_index.Fields.Refs) < 1 {
			return Error.New("%s: index %q has no fields defined",
				ast_index.Pos, ast_index.Name)
		}

		fields, err := resolveRelativeFieldRefs(
			model_entry, ast_index.Fields.Refs)
		if err != nil {
			return err
		}
		model.Indexes = append(model.Indexes, &Index{
			Name:   ast_index.Name,
			Model:  fields[0].Model,
			Fields: fields,
			Unique: ast_index.Unique,
		})
	}

	return nil
}
<commit_msg>fix minor bug if primary key isnt set<commit_after>// Copyright (C) 2016 Space Monkey, Inc.
//
// 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 ir

import "gopkg.in/spacemonkeygo/dbx.v1/ast"

func transformModel(lookup *lookup, model_entry *modelEntry) (err error) {
	model := model_entry.model
	ast_model := model_entry.ast

	model.Name = ast_model.Name
	model.Table = ast_model.Table

	for _, ast_field := range ast_model.Fields {
		field_entry := model_entry.GetField(ast_field.Name)
		if err := transformField(lookup, field_entry); err != nil {
			return err
		}
	}

	if ast_model.PrimaryKey == nil || len(ast_model.PrimaryKey.Refs) == 0 {
		return Error.New("%s: no primary key defined", ast_model.Pos)
	}

	for _, ast_fieldref := range ast_model.PrimaryKey.Refs {
		field, err := model_entry.FindField(ast_fieldref)
		if err != nil {
			return err
		}
		if field.Nullable {
			return Error.New("%s: nullable field %q cannot be a primary key",
				ast_fieldref.Pos, ast_fieldref.Field)
		}
		if field.Updatable {
			return Error.New("%s: updatable field %q cannot be a primary key",
				ast_fieldref.Pos, ast_fieldref.Field)
		}
		model.PrimaryKey = append(model.PrimaryKey, field)
	}

	for _, ast_unique := range ast_model.Unique {
		fields, err := resolveRelativeFieldRefs(model_entry, ast_unique.Refs)
		if err != nil {
			return err
		}
		model.Unique = append(model.Unique, fields)
	}

	index_names := map[string]*ast.Index{}
	for _, ast_index := range ast_model.Indexes {
		if existing, ok := index_names[ast_index.Name]; ok {
			return Error.New("%s: index %q already defined at %s",
				ast_index.Pos, ast_index.Name, existing.Pos)
		}
		index_names[ast_index.Name] = ast_index

		if ast_index.Fields == nil || len(ast_index.Fields.Refs) < 1 {
			return Error.New("%s: index %q has no fields defined",
				ast_index.Pos, ast_index.Name)
		}

		fields, err := resolveRelativeFieldRefs(
			model_entry, ast_index.Fields.Refs)
		if err != nil {
			return err
		}
		model.Indexes = append(model.Indexes, &Index{
			Name:   ast_index.Name,
			Model:  fields[0].Model,
			Fields: fields,
			Unique: ast_index.Unique,
		})
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Derived from pnm/reader.go, which is based on
// the structure of image/gif/reader.go.

// Package xbm implements an X11 Bitmap decoder.
package xbm

import (
	"bufio"
	"errors"

	img "github.com/knieriem/g/image"
	"image"
	"io"
	"strconv"
	"strings"
)

// If the io.Reader does not also have ReadLine, then decode will introduce its own buffering.
type reader interface {
	io.Reader
	ReadLine() (line []byte, isPrefix bool, err error)
}

// decoder is the type used to decode an XBM file.
type decoder struct {
	r reader

	// From header.
	width  int
	height int
	line   []byte
}

// decode reads an X11 bitmap from r and stores the result in d.
func (d *decoder) decode(r io.Reader, configOnly bool) (im *img.Bitmap, err error) {
	defer func() {
		if r := recover(); r != nil {
			if s, ok := r.(string); ok {
				err = errors.New("xbm:" + s)
			} else {
				err = r.(error)
			}
		}
	}()

	// Add buffering if r does not provide ReadByte.
	if rr, ok := r.(reader); ok {
		d.r = rr
	} else {
		d.r = bufio.NewReader(r)
	}

	d.readHeader()
	if configOnly {
		return
	}

	im = img.NewBitmap(d.width, d.height)
	p := im.Pix

	if len(d.line) > 0 {
		p = d.scan(p, d.line)
	}
	for len(p) > 0 {
		line, isPfx, err := d.r.ReadLine()
		if isPfx {
			goto malformed
		}
		if err != nil {
			return nil, err
		}
		p = d.scan(p, line)
	}
	return

malformed:
	return nil, errors.New("xbm: data probably malformed")
}

func (d *decoder) readHeader() {
	var name string

	for {
		line, isPfx, err := d.r.ReadLine()
		if isPfx {
			goto malformed
		}
		if err != nil {
			panic(err)
		}
		linestr := string(line)
		f := strings.Fields(linestr)
		if len(f) < 3 {
			continue
		}
		switch f[0] {
		case "#define":
			if iu := strings.LastIndex(f[1], "_"); iu != -1 {
				s := f[1][:iu]
				switch name {
				case "":
					name = s
				default:
					panic("name mismatch")
				case s:
				}
				val, _ := strconv.Atoi(f[2])
				switch f[1][iu+1:] {
				case "width":
					d.width = val
				case "height":
					d.height = val
				}
			}
		case "static":
			if d.width == 0 || d.height == 0 {
				goto malformed
			}
			if f[1] != "char" && f[2] != "char" {
				panic("data type not supported")
			}
			if bi := strings.Index(linestr, "{"); bi == -1 {
				goto malformed
			} else {
				d.line = line[bi+1:]
			}
			return
		}
	}
malformed:
	panic("probably malformed header")
}

func (d *decoder) scan(dst []byte, line []byte) []byte {
	var ndst = len(dst)
	var n = len(line)
	var di = 0

	for i := 0; i < n && di != ndst; i++ {
		if line[i] == 'x' {
			if n-i < 3 {
				panic("malformed data")
			}
			dst[di] = unhex(line[i+2])<<4 | unhex(line[i+1])
			di++
		}
	}
	return dst[di:]
}

var flipped = []byte{0, 8, 4, 0xC, 2, 0xA, 6, 0xE, 1, 9, 5, 0xD, 3, 0xB, 7, 0xF}

func unhex(h byte) (b uint8) {
	switch {
	case h >= '0' && h <= '9':
		b = h - '0'
	case h >= 'A' && h <= 'F':
		b = 10 + h - 'A'
	case h >= 'a' && h <= 'f':
		b = 10 + h - 'a'
	default:
		panic("malformed data")
	}
	return flipped[b]
}

// Decode reads an XBM image from r and returns the first embedded
// image as an image.Image.
func Decode(r io.Reader) (im image.Image, err error) {
	var d decoder
	return d.decode(r, false)
}

// DecodeConfig returns the color model and dimensions of an XBM image
// without decoding the entire image.
func DecodeConfig(r io.Reader) (ic image.Config, err error) {
	var d decoder
	if _, err = d.decode(r, true); err == nil {
		ic = image.Config{img.BinaryColorModel, d.width, d.height}
	}
	return
}

func init() {
	image.RegisterFormat("xbm", "/*", Decode, DecodeConfig)
	image.RegisterFormat("xbm", "#defi", Decode, DecodeConfig)
}
<commit_msg>Fix XBM to parse #defines correctly.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Derived from pnm/reader.go, which is based on
// the structure of image/gif/reader.go.

// Package xbm implements an X11 Bitmap decoder.
package xbm

import (
	"bufio"
	"errors"

	img "github.com/knieriem/g/image"
	"image"
	"io"
	"strconv"
	"strings"
)

// If the io.Reader does not also have ReadLine, then decode will introduce its own buffering.
type reader interface {
	io.Reader
	ReadLine() (line []byte, isPrefix bool, err error)
}

// decoder is the type used to decode an XBM file.
type decoder struct {
	r reader

	// From header.
	width  int
	height int
	line   []byte
}

// decode reads an X11 bitmap from r and stores the result in d.
func (d *decoder) decode(r io.Reader, configOnly bool) (im *img.Bitmap, err error) {
	defer func() {
		if r := recover(); r != nil {
			if s, ok := r.(string); ok {
				err = errors.New("xbm:" + s)
			} else {
				err = r.(error)
			}
		}
	}()

	// Add buffering if r does not provide ReadByte.
	if rr, ok := r.(reader); ok {
		d.r = rr
	} else {
		d.r = bufio.NewReader(r)
	}

	d.readHeader()
	if configOnly {
		return
	}

	im = img.NewBitmap(d.width, d.height)
	p := im.Pix

	if len(d.line) > 0 {
		p = d.scan(p, d.line)
	}
	for len(p) > 0 {
		line, isPfx, err := d.r.ReadLine()
		if isPfx {
			goto malformed
		}
		if err != nil {
			return nil, err
		}
		p = d.scan(p, line)
	}
	return

malformed:
	return nil, errors.New("xbm: data probably malformed")
}

func (d *decoder) readHeader() {
	var name string

	for {
		line, isPfx, err := d.r.ReadLine()
		if isPfx {
			goto malformed
		}
		if err != nil {
			panic(err)
		}
		linestr := string(line)
		f := strings.Fields(linestr)
		if len(f) < 3 {
			continue
		}
		switch f[0] {
		case "#define":
			if iu := strings.Index(f[1], "_"); iu != -1 {
				s := f[1][:iu]
				switch name {
				case "":
					name = s
				default:
					panic("name mismatch")
				case s:
				}
				val, _ := strconv.Atoi(f[2])
				switch f[1][iu+1:] {
				case "width":
					d.width = val
				case "height":
					d.height = val
				}
			}
		case "static":
			if d.width == 0 || d.height == 0 {
				goto malformed
			}
			if f[1] != "char" && f[2] != "char" {
				panic("data type not supported")
			}
			if bi := strings.Index(linestr, "{"); bi == -1 {
				goto malformed
			} else {
				d.line = line[bi+1:]
			}
			return
		}
	}
malformed:
	panic("probably malformed header")
}

func (d *decoder) scan(dst []byte, line []byte) []byte {
	var ndst = len(dst)
	var n = len(line)
	var di = 0

	for i := 0; i < n && di != ndst; i++ {
		if line[i] == 'x' {
			if n-i < 3 {
				panic("malformed data")
			}
			dst[di] = unhex(line[i+2])<<4 | unhex(line[i+1])
			di++
		}
	}
	return dst[di:]
}

var flipped = []byte{0, 8, 4, 0xC, 2, 0xA, 6, 0xE, 1, 9, 5, 0xD, 3, 0xB, 7, 0xF}

func unhex(h byte) (b uint8) {
	switch {
	case h >= '0' && h <= '9':
		b = h - '0'
	case h >= 'A' && h <= 'F':
		b = 10 + h - 'A'
	case h >= 'a' && h <= 'f':
		b = 10 + h - 'a'
	default:
		panic("malformed data")
	}
	return flipped[b]
}

// Decode reads an XBM image from r and returns the first embedded
// image as an image.Image.
func Decode(r io.Reader) (im image.Image, err error) {
	var d decoder
	return d.decode(r, false)
}

// DecodeConfig returns the color model and dimensions of an XBM image
// without decoding the entire image.
func DecodeConfig(r io.Reader) (ic image.Config, err error) {
	var d decoder
	if _, err = d.decode(r, true); err == nil {
		ic = image.Config{img.BinaryColorModel, d.width, d.height}
	}
	return
}

func init() {
	image.RegisterFormat("xbm", "/*", Decode, DecodeConfig)
	image.RegisterFormat("xbm", "#defi", Decode, DecodeConfig)
}
<|endoftext|>


<commit_before>package listeners

import (
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/getlantern/measured"
)

const (
	rateInterval = 1 * time.Second
)

// MeasuredReportFN is a function that gets called to report stats from the
// measured connection. deltaStats is like stats except that SentTotal and
// RecvTotal are deltas relative to the prior reported stats. final indicates
// whether this is the last call for a connection (i.e. connection has been
// closed).
type MeasuredReportFN func(ctx map[string]interface{}, stats *measured.Stats, deltaStats *measured.Stats,
	final bool)

// Wrapped stateAwareMeasuredListener that generates the wrapped wrapMeasuredConn
type stateAwareMeasuredListener struct {
	net.Listener
	reportInterval time.Duration
	report         MeasuredReportFN
}

func NewMeasuredListener(l net.Listener, reportInterval time.Duration, report MeasuredReportFN) net.Listener {
	return &stateAwareMeasuredListener{l, reportInterval, report}
}

func (l *stateAwareMeasuredListener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	fs := make(chan *measured.Stats)
	wc := &wrapMeasuredConn{
		ctx:        make(map[string]interface{}),
		finalStats: fs,
		Conn: measured.Wrap(c, rateInterval, func(mc measured.Conn) {
			fs <- mc.Stats()
		}),
		WrapConnEmbeddable: c.(WrapConnEmbeddable),
	}
	go wc.track(l.reportInterval, l.report)
	return wc, nil
}

// Wrapped MeasuredConn that supports OnState
type wrapMeasuredConn struct {
	WrapConnEmbeddable
	measured.Conn
	ctx        map[string]interface{}
	ctxMx      sync.RWMutex
	finalStats chan *measured.Stats
}

func (c *wrapMeasuredConn) track(reportInterval time.Duration, report MeasuredReportFN) {
	ticker := time.NewTicker(reportInterval)
	var priorStats *measured.Stats
	applyStats := func(stats *measured.Stats, final bool) {
		deltaStats := stats
		if priorStats != nil {
			deltaStats.SentTotal -= priorStats.SentTotal
			deltaStats.RecvTotal -= priorStats.RecvTotal
		}
		priorStats = stats
		c.ctxMx.RLock()
		ctx := c.ctx
		c.ctxMx.RUnlock()
		report(ctx, stats, deltaStats, final)
	}

	for {
		select {
		case <-ticker.C:
			applyStats(c.Conn.Stats(), false)
		case stats := <-c.finalStats:
			applyStats(stats, true)
			return
		}
	}
}

func (c *wrapMeasuredConn) OnState(s http.ConnState) {
	c.WrapConnEmbeddable.OnState(s)
}

// Responds to the "measured" message type
func (c *wrapMeasuredConn) ControlMessage(msgType string, data interface{}) {
	if msgType == "measured" {
		ctxUpdate := data.(map[string]interface{})
		c.ctxMx.Lock()
		defer c.ctxMx.Unlock()
		newContext := make(map[string]interface{}, len(c.ctx))
		// Copy context
		for key, value := range c.ctx {
			newContext[key] = value
		}
		// Update context
		for key, value := range ctxUpdate {
			newContext[key] = value
		}
		c.ctx = newContext
	}

	// Pass it down too, just in case other wrapper does something with
	c.WrapConnEmbeddable.ControlMessage(msgType, data)
}
<commit_msg>Added back guards for WrapConnEmbeddable<commit_after>package listeners

import (
	"net"
	"net/http"
	"sync"
	"time"

	"github.com/getlantern/measured"
)

const (
	rateInterval = 1 * time.Second
)

// MeasuredReportFN is a function that gets called to report stats from the
// measured connection. deltaStats is like stats except that SentTotal and
// RecvTotal are deltas relative to the prior reported stats. final indicates
// whether this is the last call for a connection (i.e. connection has been
// closed).
type MeasuredReportFN func(ctx map[string]interface{}, stats *measured.Stats, deltaStats *measured.Stats,
	final bool)

// Wrapped stateAwareMeasuredListener that generates the wrapped wrapMeasuredConn
type stateAwareMeasuredListener struct {
	net.Listener
	reportInterval time.Duration
	report         MeasuredReportFN
}

func NewMeasuredListener(l net.Listener, reportInterval time.Duration, report MeasuredReportFN) net.Listener {
	return &stateAwareMeasuredListener{l, reportInterval, report}
}

func (l *stateAwareMeasuredListener) Accept() (c net.Conn, err error) {
	c, err = l.Listener.Accept()
	if err != nil {
		return nil, err
	}
	fs := make(chan *measured.Stats)
	wc := &wrapMeasuredConn{
		Conn: measured.Wrap(c, rateInterval, func(mc measured.Conn) {
			fs <- mc.Stats()
		}),
		ctx:        make(map[string]interface{}),
		finalStats: make(chan *measured.Stats),
	}
	sac, _ := c.(WrapConnEmbeddable)
	wc.WrapConnEmbeddable = sac
	go wc.track(l.reportInterval, l.report)
	return wc, nil
}

// Wrapped MeasuredConn that supports OnState
type wrapMeasuredConn struct {
	WrapConnEmbeddable
	measured.Conn
	ctx        map[string]interface{}
	ctxMx      sync.RWMutex
	finalStats chan *measured.Stats
}

func (c *wrapMeasuredConn) track(reportInterval time.Duration, report MeasuredReportFN) {
	ticker := time.NewTicker(reportInterval)
	var priorStats *measured.Stats
	applyStats := func(stats *measured.Stats, final bool) {
		deltaStats := stats
		if priorStats != nil {
			deltaStats.SentTotal -= priorStats.SentTotal
			deltaStats.RecvTotal -= priorStats.RecvTotal
		}
		priorStats = stats
		c.ctxMx.RLock()
		ctx := c.ctx
		c.ctxMx.RUnlock()
		report(ctx, stats, deltaStats, final)
	}

	for {
		select {
		case <-ticker.C:
			applyStats(c.Conn.Stats(), false)
		case stats := <-c.finalStats:
			applyStats(stats, true)
			return
		}
	}
}

func (c *wrapMeasuredConn) OnState(s http.ConnState) {
	if c.WrapConnEmbeddable != nil {
		c.WrapConnEmbeddable.OnState(s)
	}
}

// Responds to the "measured" message type
func (c *wrapMeasuredConn) ControlMessage(msgType string, data interface{}) {
	if msgType == "measured" {
		ctxUpdate := data.(map[string]interface{})
		c.ctxMx.Lock()
		defer c.ctxMx.Unlock()
		newContext := make(map[string]interface{}, len(c.ctx))
		// Copy context
		for key, value := range c.ctx {
			newContext[key] = value
		}
		// Update context
		for key, value := range ctxUpdate {
			newContext[key] = value
		}
		c.ctx = newContext
	}

	if c.WrapConnEmbeddable != nil {
		// Pass it down too, just in case other wrapper does something with
		c.WrapConnEmbeddable.ControlMessage(msgType, data)
	}
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"os/exec"
	"strings"
	"time"

	"gopkg.in/robfig/cron.v2"

	"github.com/jinzhu/gorm"
	"github.com/mrkaspa/go-helpers"
)

//Task executed recurrently
type Task struct {
	ID          string    `sql:"type:varchar(100)" gorm:"primary_key" json:"id"`
	Periodicity string    `json:"periodicity" validate:"required"`
	CronID      int       `json:"-"`
	Command     string    `json:"command" validate:"required"`
	CreatedAt   time.Time `json:"created_at"`
}

//BeforeCreate callback
func (t *Task) BeforeCreate() {
	if t.ID == "" {
		t.ID = helpers.PseudoUUID()
	}
}

//AfterCreate callback
func (t *Task) AfterCreate(txn *gorm.DB) error {
	fmt.Println("Task AfterCreate")
	return t.Start(txn)
}

//AfterUpdate callback
func (t *Task) AfterUpdate(txn *gorm.DB) error {
	fmt.Println("Task AfterUpdate")
	if err := t.Stop(txn); err != nil {
		return err
	}
	return t.Start(txn)
}

//BeforeDelete callback
func (t *Task) BeforeDelete(txn *gorm.DB) error {
	return t.Stop(txn)
}

func (t *Task) Start(txn *gorm.DB) error {
	pid, err := MasterCron.AddFunc(t.Periodicity, func() {
		commandArr := strings.Split(t.Command, " ")
		command, args := commandArr[0], commandArr[1:]
		exec.Command(command, args...).Run()
	})
	if err != nil {
		return err
	}
	fmt.Println("Cron started")
	return txn.Model(t).UpdateColumn("cron_id", int(pid)).Error
}

func (t *Task) Stop(txn *gorm.DB) error {
	entryID := cron.EntryID(t.CronID)
	fmt.Println("1>> ", t.CronID)
	fmt.Println("2>> ", entryID)
	MasterCron.Remove(entryID)
	fmt.Println("Cron schedules")
	fmt.Println(MasterCron.Entries())
	return txn.Model(t).UpdateColumn("cron_id", 0).Error
}
<commit_msg>deploy<commit_after>package models

import (
	"fmt"
	"os/exec"
	"strings"
	"time"

	"gopkg.in/robfig/cron.v2"

	"github.com/jinzhu/gorm"
	"github.com/mrkaspa/go-helpers"
)

//Task executed recurrently
type Task struct {
	ID          string    `sql:"type:varchar(100)" gorm:"primary_key" json:"id"`
	Periodicity string    `json:"periodicity" validate:"required"`
	CronID      int       `json:"-"`
	Command     string    `json:"command" validate:"required"`
	CreatedAt   time.Time `json:"created_at"`
}

//BeforeCreate callback
func (t *Task) BeforeCreate() {
	if t.ID == "" {
		t.ID = helpers.PseudoUUID()
	}
}

//AfterCreate callback
func (t *Task) AfterCreate(txn *gorm.DB) error {
	return t.Start(txn)
}

//AfterUpdate callback
func (t *Task) AfterUpdate(txn *gorm.DB) error {
	if err := t.Stop(txn); err != nil {
		return err
	}
	return t.Start(txn)
}

//BeforeDelete callback
func (t *Task) BeforeDelete(txn *gorm.DB) error {
	return t.Stop(txn)
}

func (t *Task) Start(txn *gorm.DB) error {
	pid, err := MasterCron.AddFunc(t.Periodicity, func() {
		commandArr := strings.Split(t.Command, " ")
		command, args := commandArr[0], commandArr[1:]
		exec.Command(command, args...).Run()
	})
	if err != nil {
		return err
	}
	fmt.Println("Cron started")
	return txn.Model(t).UpdateColumn("cron_id", int(pid)).Error
}

func (t *Task) Stop(txn *gorm.DB) error {
	entryID := cron.EntryID(t.CronID)
	MasterCron.Remove(entryID)
	return txn.Model(t).UpdateColumn("cron_id", 0).Error
}
<|endoftext|>


<commit_before>package bus

import (
	"io"
	"os"
	"runtime"
	"unsafe"

	"github.com/zyxar/berry/sys"
)

const (
	spiIoctlMAGIC = 'k'
	spiDev0       = "/dev/spidev0.0"
	spiDev1       = "/dev/spidev0.1"
)

type spiIoctlTransfer struct {
	TxBuf, RxBuf          uint64
	Length, SpeedHz       uint32
	DelayUsecs            uint16
	BitsPerWord, CsChange uint8
	_                     uint32
}

type spi struct {
	channel uint8
	speed   uint32
	file    *os.File
}

var spiBPW uint8 = 8

func OpenSPI(channel uint8, speed uint32, mode uint8) (device io.ReadWriteCloser, err error) {
	channel &= 1 // 0 or 1
	mode &= 3    // 0, 1, 2 or 3
	s := &spi{channel: channel, speed: speed}
	defer func() {
		if err != nil {
			s.Close()
		}
	}()
	if channel == 0 {
		if s.file, err = os.OpenFile(spiDev0, os.O_RDWR, 0); err != nil {
			return
		}
	} else {
		if s.file, err = os.OpenFile(spiDev1, os.O_RDWR, 0); err != nil {
			return
		}
	}
	if err = sys.Ioctl(s.file.Fd(), SPI_IOC_WR_MODE(), uintptr(unsafe.Pointer(&mode))); err != nil {
		return
	}
	if err = sys.Ioctl(s.file.Fd(), SPI_IOC_WR_BITS_PER_WORD(), uintptr(unsafe.Pointer(&spiBPW))); err != nil {
		return
	}
	if err = sys.Ioctl(s.file.Fd(), SPI_IOC_WR_MAX_SPEED_HZ(), uintptr(unsafe.Pointer(&speed))); err != nil {
		return
	}
	runtime.SetFinalizer(s, func(this *spi) {
		this.Close()
	})
	device = s
	return
}

func (this *spi) rw(p []byte) (n int, err error) {
	n = len(p)
	var transfer = spiIoctlTransfer{
		TxBuf:       uint64(uintptr(unsafe.Pointer(&p))),
		RxBuf:       uint64(uintptr(unsafe.Pointer(&p))),
		Length:      uint32(n),
		SpeedHz:     this.speed,
		DelayUsecs:  0,
		BitsPerWord: spiBPW,
	}
	err = sys.Ioctl(this.file.Fd(), SPI_IOC_MESSAGE(1), uintptr(unsafe.Pointer(&transfer)))
	return
}

func (this *spi) Read(p []byte) (n int, err error) {
	n, err = this.rw(p)
	return
}

func (this *spi) Write(p []byte) (n int, err error) {
	n, err = this.rw(p)
	return
}

func (this *spi) Close() (err error) {
	if this.file != nil {
		err = this.file.Close()
		this.file = nil
	}
	return
}

// Read of SPI mode (SPI_MODE_0..SPI_MODE_3)
func SPI_IOC_RD_MODE() uintptr {
	return sys.IOR(spiIoctlMAGIC, 1, 1)
}

// Write of SPI mode (SPI_MODE_0..SPI_MODE_3)
func SPI_IOC_WR_MODE() uintptr {
	return sys.IOW(spiIoctlMAGIC, 1, 1)
}

// Read SPI bit justification
func SPI_IOC_RD_LSB_FIRST() uintptr {
	return sys.IOR(spiIoctlMAGIC, 2, 1)
}

// Write SPI bit justification
func SPI_IOC_WR_LSB_FIRST() uintptr {
	return sys.IOW(spiIoctlMAGIC, 2, 1)
}

// Read SPI device word length (1..N)
func SPI_IOC_RD_BITS_PER_WORD() uintptr {
	return sys.IOR(spiIoctlMAGIC, 3, 1)
}

// Write SPI device word length (1..N)
func SPI_IOC_WR_BITS_PER_WORD() uintptr {
	return sys.IOW(spiIoctlMAGIC, 3, 1)
}

// Read SPI device default max speed hz
func SPI_IOC_RD_MAX_SPEED_HZ() uintptr {
	return sys.IOR(spiIoctlMAGIC, 4, 4)
}

// Write SPI device default max speed hz
func SPI_IOC_WR_MAX_SPEED_HZ() uintptr {
	return sys.IOW(spiIoctlMAGIC, 4, 4)
}

// Write custom SPI message
func SPI_IOC_MESSAGE(n uintptr) uintptr {
	return sys.IOW(spiIoctlMAGIC, 0, uintptr(SPI_MESSAGE_SIZE(n)))
}
func SPI_MESSAGE_SIZE(n uintptr) uintptr {
	if (n * unsafe.Sizeof(spiIoctlTransfer{})) < (1 << sys.IOC_SIZEBITS) {
		return (n * unsafe.Sizeof(spiIoctlTransfer{}))
	}
	return 0
}
<commit_msg>Fixes bus/spi<commit_after>package bus

import (
	"io"
	"os"
	"runtime"
	"unsafe"

	"github.com/zyxar/berry/sys"
)

const (
	spiIoctlMAGIC = 'k'
	spiDev0       = "/dev/spidev0.0"
	spiDev1       = "/dev/spidev0.1"
)

type spiIoctlTransfer struct {
	TxBuf, RxBuf          uint64
	Length, SpeedHz       uint32
	DelayUsecs            uint16
	BitsPerWord, CsChange uint8
	_                     uint32
}

type spi struct {
	channel uint8
	speed   uint32
	file    *os.File
}

var spiBPW uint8 = 8

type SPIBus interface {
	io.ReadWriteCloser
	WriteAndRead(p []byte) (n int, err error)
}

func OpenSPI(channel uint8, speed uint32, mode uint8) (device SPIBus, err error) {
	channel &= 1 // 0 or 1
	mode &= 3    // 0, 1, 2 or 3
	s := &spi{channel: channel, speed: speed}
	defer func() {
		if err != nil {
			s.Close()
		}
	}()
	if channel == 0 {
		if s.file, err = os.OpenFile(spiDev0, os.O_RDWR, 0); err != nil {
			return
		}
	} else {
		if s.file, err = os.OpenFile(spiDev1, os.O_RDWR, 0); err != nil {
			return
		}
	}
	if err = sys.Ioctl(s.file.Fd(), SPI_IOC_WR_MODE(), uintptr(unsafe.Pointer(&mode))); err != nil {
		return
	}
	if err = sys.Ioctl(s.file.Fd(), SPI_IOC_WR_BITS_PER_WORD(), uintptr(unsafe.Pointer(&spiBPW))); err != nil {
		return
	}
	if err = sys.Ioctl(s.file.Fd(), SPI_IOC_WR_MAX_SPEED_HZ(), uintptr(unsafe.Pointer(&speed))); err != nil {
		return
	}
	runtime.SetFinalizer(s, func(this *spi) {
		this.Close()
	})
	device = s
	return
}

func (this *spi) WriteAndRead(p []byte) (n int, err error) {
	n = len(p)
	var transfer = spiIoctlTransfer{
		TxBuf:       uint64(uintptr(unsafe.Pointer(&p[0]))),
		RxBuf:       uint64(uintptr(unsafe.Pointer(&p[0]))),
		Length:      uint32(n),
		SpeedHz:     this.speed,
		DelayUsecs:  0,
		BitsPerWord: spiBPW,
	}
	err = sys.Ioctl(this.file.Fd(), SPI_IOC_MESSAGE(1), uintptr(unsafe.Pointer(&transfer)))
	return
}

func (this *spi) Read(p []byte) (n int, err error) {
	n, err = this.WriteAndRead(p)
	return
}

func (this *spi) Write(p []byte) (n int, err error) {
	n, err = this.WriteAndRead(p)
	return
}

func (this *spi) Close() (err error) {
	if this.file != nil {
		err = this.file.Close()
		this.file = nil
	}
	return
}

// Read of SPI mode (SPI_MODE_0..SPI_MODE_3)
func SPI_IOC_RD_MODE() uintptr {
	return sys.IOR(spiIoctlMAGIC, 1, 1)
}

// Write of SPI mode (SPI_MODE_0..SPI_MODE_3)
func SPI_IOC_WR_MODE() uintptr {
	return sys.IOW(spiIoctlMAGIC, 1, 1)
}

// Read SPI bit justification
func SPI_IOC_RD_LSB_FIRST() uintptr {
	return sys.IOR(spiIoctlMAGIC, 2, 1)
}

// Write SPI bit justification
func SPI_IOC_WR_LSB_FIRST() uintptr {
	return sys.IOW(spiIoctlMAGIC, 2, 1)
}

// Read SPI device word length (1..N)
func SPI_IOC_RD_BITS_PER_WORD() uintptr {
	return sys.IOR(spiIoctlMAGIC, 3, 1)
}

// Write SPI device word length (1..N)
func SPI_IOC_WR_BITS_PER_WORD() uintptr {
	return sys.IOW(spiIoctlMAGIC, 3, 1)
}

// Read SPI device default max speed hz
func SPI_IOC_RD_MAX_SPEED_HZ() uintptr {
	return sys.IOR(spiIoctlMAGIC, 4, 4)
}

// Write SPI device default max speed hz
func SPI_IOC_WR_MAX_SPEED_HZ() uintptr {
	return sys.IOW(spiIoctlMAGIC, 4, 4)
}

// Write custom SPI message
func SPI_IOC_MESSAGE(n uintptr) uintptr {
	return sys.IOW(spiIoctlMAGIC, 0, uintptr(SPI_MESSAGE_SIZE(n)))
}
func SPI_MESSAGE_SIZE(n uintptr) uintptr {
	if (n * unsafe.Sizeof(spiIoctlTransfer{})) < (1 << sys.IOC_SIZEBITS) {
		return (n * unsafe.Sizeof(spiIoctlTransfer{}))
	}
	return 0
}
<|endoftext|>


<commit_before>package cache

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
)

var blobNameSHA256 = regexp.MustCompile("^/?(ac/|cas/)?([a-f0-9]{64})$")

// HTTPCache ...
type HTTPCache interface {
	Serve()
}

type httpCache struct {
	addr         string
	cache        Cache
	ensureSpacer EnsureSpacer
}

// NewHTTPCache ...
func NewHTTPCache(listenAddr string, cacheDir string, maxBytes int64, ensureSpacer EnsureSpacer) HTTPCache {
	ensureCacheDir(cacheDir)
	cache := NewCache(cacheDir, maxBytes)
	loadFilesIntoCache(cache)
	return &httpCache{listenAddr, cache, ensureSpacer}
}

// Serve ...
func (h *httpCache) Serve() {
	s := &http.Server{
		Addr:    h.addr,
		Handler: h,
	}
	log.Fatal(s.ListenAndServe())
}

func ensureCacheDir(path string) {
	d, err := os.Open(path)
	if err != nil {
		err := os.MkdirAll(path, os.FileMode(0644))
		if err != nil {
			log.Fatal(err)
		}
	}
	d.Close()
}

func loadFilesIntoCache(cache Cache) {
	filepath.Walk(cache.Dir(), func(name string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			cache.AddFile(filepath.Base(name), info.Size())
		}
		return nil
	})
}

func (h *httpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	parts, err := parseURL(r.URL.Path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var hash string
	var verifyHash bool
	if len(parts) == 1 {
		// For backwards compatibiliy with older Bazel version's that don't
		// support {cas,actioncache} prefixes.
		verifyHash = false
		hash = parts[0]
	} else {
		verifyHash = parts[0] == "cas/"
		hash = parts[1]
	}

	switch m := r.Method; m {
	case http.MethodGet:
		if !h.cache.ContainsFile(hash) {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		http.ServeFile(w, r, h.filePath(hash))
	case http.MethodPut:
		if h.cache.ContainsFile(hash) {
			w.WriteHeader(http.StatusOK)
			return
		}
		if !h.ensureSpacer.EnsureSpace(h.cache, r.ContentLength) {
			http.Error(w, "Cache full.", http.StatusInsufficientStorage)
			return
		}
		written, err := h.saveToDisk(r.Body, hash, verifyHash)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		h.cache.AddFile(hash, written)
		w.WriteHeader(http.StatusOK)
	case http.MethodHead:
		if !h.cache.ContainsFile(hash) {
			http.Error(w, err.Error(), http.StatusNotFound)
		}
		w.WriteHeader(http.StatusOK)
	default:
		msg := fmt.Sprintf("Method '%s' not supported.", m)
		http.Error(w, msg, http.StatusMethodNotAllowed)
	}
}

func parseURL(url string) ([]string, error) {
	m := blobNameSHA256.FindStringSubmatch(url)
	if m == nil {
		msg := fmt.Sprintf("Resource name must be a SHA256 hash in hex. "+
			"Got '%s'.", url)
		return nil, errors.New(msg)
	}
	return m[1:], nil
}

func (h *httpCache) saveToDisk(content io.Reader, hash string, verifyHash bool) (written int64, err error) {
	f, err := ioutil.TempFile(h.cache.Dir(), "upload")
	if err != nil {
		return 0, err
	}
	tmpName := f.Name()
	if verifyHash {
		hasher := sha256.New()
		written, err = io.Copy(io.MultiWriter(f, hasher), content)
		actualHash := hex.EncodeToString(hasher.Sum(nil))
		if hash != actualHash {
			os.Remove(tmpName)
			msg := fmt.Sprintf("Hashes don't match. Provided '%s', Actual '%s'.",
				hash, actualHash)
			return 0, errors.New(msg)
		}
	} else {
		written, err = io.Copy(f, content)
	}
	if err != nil {
		return 0, err
	}
	f.Close()
	err2 := os.Rename(tmpName, h.filePath(hash))
	if err2 != nil {
		return 0, err2
	}
	return written, nil
}

func (h httpCache) filePath(hash string) string {
	return fmt.Sprintf("%s%c%s", h.cache.Dir(), os.PathSeparator, hash)
}
<commit_msg>http: flush contents to disk<commit_after>package cache

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
)

var blobNameSHA256 = regexp.MustCompile("^/?(ac/|cas/)?([a-f0-9]{64})$")

// HTTPCache ...
type HTTPCache interface {
	Serve()
}

type httpCache struct {
	addr         string
	cache        Cache
	ensureSpacer EnsureSpacer
}

// NewHTTPCache ...
func NewHTTPCache(listenAddr string, cacheDir string, maxBytes int64, ensureSpacer EnsureSpacer) HTTPCache {
	ensureCacheDir(cacheDir)
	cache := NewCache(cacheDir, maxBytes)
	loadFilesIntoCache(cache)
	return &httpCache{listenAddr, cache, ensureSpacer}
}

// Serve ...
func (h *httpCache) Serve() {
	s := &http.Server{
		Addr:    h.addr,
		Handler: h,
	}
	log.Fatal(s.ListenAndServe())
}

func ensureCacheDir(path string) {
	d, err := os.Open(path)
	if err != nil {
		err := os.MkdirAll(path, os.FileMode(0644))
		if err != nil {
			log.Fatal(err)
		}
	}
	d.Close()
}

func loadFilesIntoCache(cache Cache) {
	filepath.Walk(cache.Dir(), func(name string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			cache.AddFile(filepath.Base(name), info.Size())
		}
		return nil
	})
}

func (h *httpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	parts, err := parseURL(r.URL.Path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var hash string
	var verifyHash bool
	if len(parts) == 1 {
		// For backwards compatibiliy with older Bazel version's that don't
		// support {cas,actioncache} prefixes.
		verifyHash = false
		hash = parts[0]
	} else {
		verifyHash = parts[0] == "cas/"
		hash = parts[1]
	}

	switch m := r.Method; m {
	case http.MethodGet:
		if !h.cache.ContainsFile(hash) {
			w.WriteHeader(http.StatusNotFound)
			return
		}
		http.ServeFile(w, r, h.filePath(hash))
	case http.MethodPut:
		if h.cache.ContainsFile(hash) {
			w.WriteHeader(http.StatusOK)
			return
		}
		if !h.ensureSpacer.EnsureSpace(h.cache, r.ContentLength) {
			http.Error(w, "Cache full.", http.StatusInsufficientStorage)
			return
		}
		written, err := h.saveToDisk(r.Body, hash, verifyHash)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		h.cache.AddFile(hash, written)
		w.WriteHeader(http.StatusOK)
	case http.MethodHead:
		if !h.cache.ContainsFile(hash) {
			http.Error(w, err.Error(), http.StatusNotFound)
		}
		w.WriteHeader(http.StatusOK)
	default:
		msg := fmt.Sprintf("Method '%s' not supported.", m)
		http.Error(w, msg, http.StatusMethodNotAllowed)
	}
}

func parseURL(url string) ([]string, error) {
	m := blobNameSHA256.FindStringSubmatch(url)
	if m == nil {
		msg := fmt.Sprintf("Resource name must be a SHA256 hash in hex. "+
			"Got '%s'.", url)
		return nil, errors.New(msg)
	}
	return m[1:], nil
}

func (h *httpCache) saveToDisk(content io.Reader, hash string, verifyHash bool) (written int64, err error) {
	f, err := ioutil.TempFile(h.cache.Dir(), "upload")
	if err != nil {
		return 0, err
	}
	tmpName := f.Name()
	if verifyHash {
		hasher := sha256.New()
		written, err = io.Copy(io.MultiWriter(f, hasher), content)
		actualHash := hex.EncodeToString(hasher.Sum(nil))
		if hash != actualHash {
			os.Remove(tmpName)
			msg := fmt.Sprintf("Hashes don't match. Provided '%s', Actual '%s'.",
				hash, actualHash)
			return 0, errors.New(msg)
		}
	} else {
		written, err = io.Copy(f, content)
	}
	if err != nil {
		return 0, err
	}
	err = f.Sync()
	if err != nil {
		log.Fatal(err)
	}
	f.Close()
	err2 := os.Rename(tmpName, h.filePath(hash))
	if err2 != nil {
		return 0, err2
	}
	return written, nil
}

func (h httpCache) filePath(hash string) string {
	return fmt.Sprintf("%s%c%s", h.cache.Dir(), os.PathSeparator, hash)
}
<|endoftext|>


<commit_before>package anidb

import (
	"github.com/Kovensky/go-fscache"
	"strconv"
	"time"
)

// These are all pointers because they're not
// sent at all if they're nil
type MyListSet struct {
	State    *MyListState
	Watched  *bool
	ViewDate *time.Time
	Source   *string
	Storage  *string
	Other    *string
}

func (set *MyListSet) toParamMap() (pm paramMap) {
	pm = paramMap{}
	if set == nil {
		return
	}

	if set.State != nil {
		pm["state"] = *set.State
	}
	if set.Watched != nil {
		pm["viewed"] = *set.Watched
	}
	if set.ViewDate != nil {
		if set.ViewDate.IsZero() {
			pm["viewdate"] = 0
		} else {
			pm["viewdate"] = int(int32(set.ViewDate.Unix()))
		}
	}
	if set.Source != nil {
		pm["source"] = *set.Source
	}
	if set.Storage != nil {
		pm["storage"] = *set.Storage
	}
	if set.Other != nil {
		pm["other"] = *set.Other
	}
	return
}

func (set *MyListSet) update(uid UID, f *File, lid LID) {
	if f.LID[uid] != lid {
		f.LID[uid] = lid
		Cache.Set(f, "fid", f.FID)
		Cache.Chtime(f.Cached, "fid", f.FID)
	}

	mla := uid.MyListAnime(f.AID)
	if mla == nil {
		mla = &MyListAnime{
			EpisodesWithState: MyListStateMap{},
			EpisodesPerGroup:  GroupEpisodes{},
		}
	}
	// We only ever add, not remove -- we don't know if other files also satisfy the list
	eg := mla.EpisodesPerGroup[f.GID]
	eg.Add(f.EpisodeNumber)
	mla.EpisodesPerGroup[f.GID] = eg

	if set.State != nil {
		es := mla.EpisodesWithState[*set.State]
		es.Add(f.EpisodeNumber)
		mla.EpisodesWithState[*set.State] = es
	}

	if set.Watched != nil && *set.Watched ||
		set.ViewDate != nil && !set.ViewDate.IsZero() {
		mla.WatchedEpisodes.Add(f.EpisodeNumber)
	}

	Cache.Set(mla, "mylist-anime", uid, f.AID)
	Cache.Chtime(mla.Cached, "mylist-anime", uid, f.AID)

	if set.ViewDate == nil && set.Watched == nil && set.State == nil &&
		set.Source == nil && set.Storage == nil && set.Other == nil {
		return
	}

	e := lid.MyListEntry()
	if set.ViewDate != nil {
		e.DateWatched = *set.ViewDate
	} else if set.Watched != nil {
		if *set.Watched {
			e.DateWatched = time.Now()
		} else {
			e.DateWatched = time.Time{}
		}
	}
	if set.State != nil {
		e.MyListState = *set.State
	}
	if set.Source != nil {
		e.Source = *set.Source
	}
	if set.Storage != nil {
		e.Storage = *set.Storage
	}
	if set.Other != nil {
		e.Other = *set.Other
	}
	Cache.Set(e, "mylist", lid)
	Cache.Chtime(e.Cached, "mylist", lid)
}

func (adb *AniDB) MyListAdd(f *File, set *MyListSet) <-chan LID {
	ch := make(chan LID, 1)
	if f == nil {
		ch <- 0
		close(ch)
		return ch
	}

	go func() {
		user := <-adb.GetCurrentUser()
		if user == nil || user.UID < 1 {
			ch <- 0
			close(ch)
			return
		}

		// for the intent map; doesn't get cached
		key := []fscache.CacheKey{"mylist-add", user.UID, f.FID}

		ic := make(chan notification, 1)
		go func() { ch <- (<-ic).(LID); close(ch) }()
		if intentMap.Intent(ic, key...) {
			return
		}

		pm := set.toParamMap()
		pm["fid"] = f.FID

		reply := <-adb.udp.SendRecv("MYLISTADD", pm)

		lid := LID(0)

		switch reply.Code() {
		case 310:
			e := adb.parseMylistReply(reply)
			if e != nil {
				lid = e.LID
			}
		case 210:
			id, _ := strconv.ParseInt(reply.Lines()[1], 10, 64)
			lid = LID(id)

			// the 310 case does this in parseMylistReply
			set.update(user.UID, f, lid)
		}

		intentMap.NotifyClose(lid, key...)
	}()

	return ch
}

func (adb *AniDB) MyListAddByEd2kSize(ed2k string, size int64, set *MyListSet) <-chan LID {
	ch := make(chan LID, 1)
	if size < 1 || !validEd2kHash.MatchString(ed2k) {
		ch <- 0
		close(ch)
		return ch
	}

	go func() {
		ch <- <-adb.MyListAdd(<-adb.FileByEd2kSize(ed2k, size), set)
		close(ch)
	}()
	return ch
}

func (adb *AniDB) MyListEdit(f *File, set *MyListSet) <-chan bool {
	ch := make(chan bool, 1)
	if f == nil {
		ch <- false
		close(ch)
		return ch
	}

	go func() {
		user := <-adb.GetCurrentUser()
		if user == nil || user.UID < 1 {
			ch <- false
			close(ch)
			return
		}

		// for the intent map; doesn't get cached
		key := []fscache.CacheKey{"mylist-edit", user.UID, f.FID}

		ic := make(chan notification, 1)
		go func() { ch <- (<-ic).(bool); close(ch) }()
		if intentMap.Intent(ic, key...) {
			return
		}

		pm := set.toParamMap()
		pm["edit"] = 1
		if lid := f.LID[user.UID]; lid > 0 {
			pm["lid"] = lid
		} else {
			pm["fid"] = f.FID
		}

		reply := <-adb.udp.SendRecv("MYLISTADD", pm)

		switch reply.Code() {
		case 311:
			intentMap.NotifyClose(true, key...)

			set.update(user.UID, f, 0)
		default:
			intentMap.NotifyClose(false, key...)
		}
	}()

	return ch
}

func (adb *AniDB) MyListDel(f *File) <-chan bool {
	ch := make(chan bool)
	if f == nil {
		ch <- false
		close(ch)
		return ch
	}

	go func() {
		user := <-adb.GetCurrentUser()
		if user == nil || user.UID < 1 {
			ch <- false
			close(ch)
			return
		}

		// for the intent map; doesn't get cached
		key := []fscache.CacheKey{"mylist-del", user.UID, f.FID}

		ic := make(chan notification, 1)
		go func() { ch <- (<-ic).(bool); close(ch) }()
		if intentMap.Intent(ic, key...) {
			return
		}

		pm := paramMap{}
		if lid := f.LID[user.UID]; lid > 0 {
			pm["lid"] = lid
		} else {
			pm["fid"] = f.FID
		}

		reply := <-adb.udp.SendRecv("MYLISTDEL", pm)

		switch reply.Code() {
		case 211:
			delete(f.LID, user.UID)
			Cache.Set(f, "fid", f.FID)
			Cache.Chtime(f.Cached, "fid", f.FID)

			intentMap.NotifyClose(true, key...)
		default:
			intentMap.NotifyClose(false, key...)
		}
	}()

	return ch
}
<commit_msg>anidb: Fix crash when MyListAdd/MyListEdit get a nil MyListSet<commit_after>package anidb

import (
	"github.com/Kovensky/go-fscache"
	"strconv"
	"time"
)

// These are all pointers because they're not
// sent at all if they're nil
type MyListSet struct {
	State    *MyListState
	Watched  *bool
	ViewDate *time.Time
	Source   *string
	Storage  *string
	Other    *string
}

func (set *MyListSet) toParamMap() (pm paramMap) {
	pm = paramMap{}
	if set == nil {
		return
	}

	if set.State != nil {
		pm["state"] = *set.State
	}
	if set.Watched != nil {
		pm["viewed"] = *set.Watched
	}
	if set.ViewDate != nil {
		if set.ViewDate.IsZero() {
			pm["viewdate"] = 0
		} else {
			pm["viewdate"] = int(int32(set.ViewDate.Unix()))
		}
	}
	if set.Source != nil {
		pm["source"] = *set.Source
	}
	if set.Storage != nil {
		pm["storage"] = *set.Storage
	}
	if set.Other != nil {
		pm["other"] = *set.Other
	}
	return
}

func (set *MyListSet) update(uid UID, f *File, lid LID) {
	if f.LID[uid] != lid {
		f.LID[uid] = lid
		Cache.Set(f, "fid", f.FID)
		Cache.Chtime(f.Cached, "fid", f.FID)
	}

	mla := uid.MyListAnime(f.AID)
	if mla == nil {
		mla = &MyListAnime{
			EpisodesWithState: MyListStateMap{},
			EpisodesPerGroup:  GroupEpisodes{},
		}
	}
	// We only ever add, not remove -- we don't know if other files also satisfy the list
	eg := mla.EpisodesPerGroup[f.GID]
	eg.Add(f.EpisodeNumber)
	mla.EpisodesPerGroup[f.GID] = eg

	if set != nil {
		if set.State != nil {
			es := mla.EpisodesWithState[*set.State]
			es.Add(f.EpisodeNumber)
			mla.EpisodesWithState[*set.State] = es
		}

		if set.Watched != nil && *set.Watched ||
			set.ViewDate != nil && !set.ViewDate.IsZero() {
			mla.WatchedEpisodes.Add(f.EpisodeNumber)
		}
	}

	Cache.Set(mla, "mylist-anime", uid, f.AID)
	Cache.Chtime(mla.Cached, "mylist-anime", uid, f.AID)

	if set == nil ||
		(set.ViewDate == nil && set.Watched == nil && set.State == nil &&
			set.Source == nil && set.Storage == nil && set.Other == nil) {
		return
	}

	e := lid.MyListEntry()
	if set.ViewDate != nil {
		e.DateWatched = *set.ViewDate
	} else if set.Watched != nil {
		if *set.Watched {
			e.DateWatched = time.Now()
		} else {
			e.DateWatched = time.Time{}
		}
	}
	if set.State != nil {
		e.MyListState = *set.State
	}
	if set.Source != nil {
		e.Source = *set.Source
	}
	if set.Storage != nil {
		e.Storage = *set.Storage
	}
	if set.Other != nil {
		e.Other = *set.Other
	}
	Cache.Set(e, "mylist", lid)
	Cache.Chtime(e.Cached, "mylist", lid)
}

func (adb *AniDB) MyListAdd(f *File, set *MyListSet) <-chan LID {
	ch := make(chan LID, 1)
	if f == nil {
		ch <- 0
		close(ch)
		return ch
	}

	go func() {
		user := <-adb.GetCurrentUser()
		if user == nil || user.UID < 1 {
			ch <- 0
			close(ch)
			return
		}

		// for the intent map; doesn't get cached
		key := []fscache.CacheKey{"mylist-add", user.UID, f.FID}

		ic := make(chan notification, 1)
		go func() { ch <- (<-ic).(LID); close(ch) }()
		if intentMap.Intent(ic, key...) {
			return
		}

		pm := set.toParamMap()
		pm["fid"] = f.FID

		reply := <-adb.udp.SendRecv("MYLISTADD", pm)

		lid := LID(0)

		switch reply.Code() {
		case 310:
			e := adb.parseMylistReply(reply)
			if e != nil {
				lid = e.LID
			}
		case 210:
			id, _ := strconv.ParseInt(reply.Lines()[1], 10, 64)
			lid = LID(id)

			// the 310 case does this in parseMylistReply
			set.update(user.UID, f, lid)
		}

		intentMap.NotifyClose(lid, key...)
	}()

	return ch
}

func (adb *AniDB) MyListAddByEd2kSize(ed2k string, size int64, set *MyListSet) <-chan LID {
	ch := make(chan LID, 1)
	if size < 1 || !validEd2kHash.MatchString(ed2k) {
		ch <- 0
		close(ch)
		return ch
	}

	go func() {
		ch <- <-adb.MyListAdd(<-adb.FileByEd2kSize(ed2k, size), set)
		close(ch)
	}()
	return ch
}

func (adb *AniDB) MyListEdit(f *File, set *MyListSet) <-chan bool {
	ch := make(chan bool, 1)
	if f == nil {
		ch <- false
		close(ch)
		return ch
	}

	go func() {
		user := <-adb.GetCurrentUser()
		if user == nil || user.UID < 1 {
			ch <- false
			close(ch)
			return
		}

		// for the intent map; doesn't get cached
		key := []fscache.CacheKey{"mylist-edit", user.UID, f.FID}

		ic := make(chan notification, 1)
		go func() { ch <- (<-ic).(bool); close(ch) }()
		if intentMap.Intent(ic, key...) {
			return
		}

		pm := set.toParamMap()
		pm["edit"] = 1
		if lid := f.LID[user.UID]; lid > 0 {
			pm["lid"] = lid
		} else {
			pm["fid"] = f.FID
		}

		reply := <-adb.udp.SendRecv("MYLISTADD", pm)

		switch reply.Code() {
		case 311:
			intentMap.NotifyClose(true, key...)

			set.update(user.UID, f, 0)
		default:
			intentMap.NotifyClose(false, key...)
		}
	}()

	return ch
}

func (adb *AniDB) MyListDel(f *File) <-chan bool {
	ch := make(chan bool)
	if f == nil {
		ch <- false
		close(ch)
		return ch
	}

	go func() {
		user := <-adb.GetCurrentUser()
		if user == nil || user.UID < 1 {
			ch <- false
			close(ch)
			return
		}

		// for the intent map; doesn't get cached
		key := []fscache.CacheKey{"mylist-del", user.UID, f.FID}

		ic := make(chan notification, 1)
		go func() { ch <- (<-ic).(bool); close(ch) }()
		if intentMap.Intent(ic, key...) {
			return
		}

		pm := paramMap{}
		if lid := f.LID[user.UID]; lid > 0 {
			pm["lid"] = lid
		} else {
			pm["fid"] = f.FID
		}

		reply := <-adb.udp.SendRecv("MYLISTDEL", pm)

		switch reply.Code() {
		case 211:
			delete(f.LID, user.UID)
			Cache.Set(f, "fid", f.FID)
			Cache.Chtime(f.Cached, "fid", f.FID)

			intentMap.NotifyClose(true, key...)
		default:
			intentMap.NotifyClose(false, key...)
		}
	}()

	return ch
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "io/ioutil"
    "os"
)

func saveHandler(w http.ResponseWriter, r *http.Request) {
	folder := "/PRODUCTION/EXPERIMENT/web/savedfiles/"
    filename := generateRandomURL()
    path := folder + filename

    if _, err := os.Stat(path); err != nil {
	    if os.IsNotExist(err) {
		    http.Error(w, err.Error(), http.StatusInternalServerError)
	    }
    }

    r.ParseForm()
    text := r.Form.Get("text")
	ioutil.WriteFile(path, []byte(text), 0400)

	http.Redirect(w, r, "/"+filename, http.StatusCreated)
}

func generateRandomURL() string {
	return "1234556"
}

func main() {
	http.HandleFunc("/save", saveHandler)
    http.ListenAndServe(":8080", nil)
}

<commit_msg>Return error if file already exists<commit_after>package main

import (
    "net/http"
    "io/ioutil"
    "os"
)

func saveHandler(w http.ResponseWriter, r *http.Request) {
	folder := "/PRODUCTION/EXPERIMENT/web/savedfiles/"
    filename := generateRandomURL()
    path := folder + filename

    if _, err := os.Stat(path); err != nil {
	    if os.IsNotExist(err) {
		    http.Error(w, err.Error(), http.StatusInternalServerError)
		    return
	    }
    }

    r.ParseForm()
    text := r.Form.Get("text")
	ioutil.WriteFile(path, []byte(text), 0400)

	http.Redirect(w, r, "/"+filename, http.StatusCreated)
}

func generateRandomURL() string {
	return "1234556"
}

func main() {
	http.HandleFunc("/save", saveHandler)
    http.ListenAndServe(":8080", nil)
}

<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// 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 misc

import (
	"fmt"
	"regexp"
	"time"

	"github.com/coreos/mantle/kola/cluster"
	"github.com/coreos/mantle/kola/register"
	"github.com/coreos/mantle/platform"
)

func init() {
	register.Register(&register.Test{
		Run:         RebootIntoUSRB,
		ClusterSize: 1,
		Name:        "coreos.update.reboot",
	})
	register.Register(&register.Test{
		Run:         RecoverBadVerity,
		ClusterSize: 1,
		Name:        "coreos.update.badverity",
		Flags:       []register.Flag{register.NoEmergencyShellCheck},
	})
	register.Register(&register.Test{
		Run:         RecoverBadUsr,
		ClusterSize: 1,
		Name:        "coreos.update.badusr",
		Flags:       []register.Flag{register.NoEmergencyShellCheck},
	})
}

// Simulate update scenarios

// Check that we can reprioritize and boot into USR-B. This largely
// validates the other tests in this file.
func RebootIntoUSRB(c cluster.TestCluster) {
	m := c.Machines()[0]

	assertBootedUsr(c, m, "USR-A")

	// copy USR-A to USR-B
	c.MustSSH(m, "sudo dd if=/dev/disk/by-partlabel/USR-A of=/dev/disk/by-partlabel/USR-B bs=10M status=none")

	// copy kernel
	c.MustSSH(m, "sudo cp /boot/coreos/vmlinuz-a /boot/coreos/vmlinuz-b")

	prioritizeUsr(c, m, "USR-B")
	if err := m.Reboot(); err != nil {
		c.Fatalf("couldn't reboot: %v", err)
	}
	assertBootedUsr(c, m, "USR-B")
}

// Verify that we reboot into the old image after the new image fails a
// verity check.
func RecoverBadVerity(c cluster.TestCluster) {
	m := c.Machines()[0]

	skipUnlessVerity(c, m)

	assertBootedUsr(c, m, "USR-A")

	// copy USR-A to USR-B
	c.MustSSH(m, "sudo dd if=/dev/disk/by-partlabel/USR-A of=/dev/disk/by-partlabel/USR-B bs=10M status=none")

	// copy kernel
	c.MustSSH(m, "sudo cp /boot/coreos/vmlinuz-a /boot/coreos/vmlinuz-b")

	// invalidate verity hash on B kernel
	c.MustSSH(m, fmt.Sprintf("sudo dd of=/boot/coreos/vmlinuz-b bs=1 seek=%d count=64 conv=notrunc status=none <<<0000000000000000000000000000000000000000000000000000000000000000", getKernelVerityHashOffset(c)))

	prioritizeUsr(c, m, "USR-B")
	rebootWithEmergencyShellTimeout(c, m)
	assertBootedUsr(c, m, "USR-A")
}

// Verify that we reboot into the old image when the new image is an
// unreasonable filesystem (an empty one) that passes verity.
func RecoverBadUsr(c cluster.TestCluster) {
	m := c.Machines()[0]

	assertBootedUsr(c, m, "USR-A")

	// create filesystem for USR-B
	c.MustSSH(m, "sudo mkfs.ext4 -q -b 4096 /dev/disk/by-partlabel/USR-B 25600")

	// create verity metadata for USR-B
	output := c.MustSSH(m, "sudo veritysetup format --hash=sha256 "+
		"--data-block-size 4096 --hash-block-size 4096 --data-blocks 25600 --hash-offset 104857600 "+
		"/dev/disk/by-partlabel/USR-B /dev/disk/by-partlabel/USR-B")

	// extract root hash for USR-B
	match := regexp.MustCompile("\nRoot hash:\\s+([0-9a-f]+)").FindSubmatch(output)
	if match == nil {
		c.Fatalf("Couldn't obtain new root hash; output %s", output)
	}
	verityHash := match[1]

	// copy kernel
	c.MustSSH(m, "sudo cp /boot/coreos/vmlinuz-a /boot/coreos/vmlinuz-b")

	// update verity hash on B kernel
	c.MustSSH(m, fmt.Sprintf("sudo dd of=/boot/coreos/vmlinuz-b bs=1 seek=%d count=64 conv=notrunc status=none <<<%s", getKernelVerityHashOffset(c), verityHash))

	prioritizeUsr(c, m, "USR-B")
	rebootWithEmergencyShellTimeout(c, m)
	assertBootedUsr(c, m, "USR-A")
}

func assertBootedUsr(c cluster.TestCluster, m platform.Machine, usr string) {
	usrdev := getUsrDeviceNode(c, m)
	target := c.MustSSH(m, "readlink -f /dev/disk/by-partlabel/"+usr)
	if usrdev != string(target) {
		c.Fatalf("Expected /usr to be %v (%s) but it is %v", usr, target, usrdev)
	}
}

func prioritizeUsr(c cluster.TestCluster, m platform.Machine, usr string) {
	c.MustSSH(m, "sudo cgpt repair /dev/disk/by-partlabel/"+usr)
	c.MustSSH(m, "sudo cgpt add -S0 -T1 /dev/disk/by-partlabel/"+usr)
	c.MustSSH(m, "sudo cgpt prioritize /dev/disk/by-partlabel/"+usr)
}

// reboot, waiting extra-long for the 5-minute emergency shell timeout
func rebootWithEmergencyShellTimeout(c cluster.TestCluster, m platform.Machine) {
	// reboot; wait extra 5 minutes; check machine
	// this defeats some of the machinery in m.Reboot()
	if err := platform.StartReboot(m); err != nil {
		c.Fatal(err)
	}
	time.Sleep(5 * time.Minute)
	if err := platform.CheckMachine(m); err != nil {
		c.Fatal(err)
	}
}
<commit_msg>kola/tests/misc/update: mask update-engine<commit_after>// Copyright 2017 CoreOS, Inc.
//
// 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 misc

import (
	"fmt"
	"regexp"
	"time"

	"github.com/coreos/mantle/kola/cluster"
	"github.com/coreos/mantle/kola/register"
	"github.com/coreos/mantle/platform"
	"github.com/coreos/mantle/platform/conf"
)

var (
	// prevents a race where update-engine sets the boot partition back to
	// USR-A after the test sets it to USR-B
	disableUpdateEngine = conf.ContainerLinuxConfig(`systemd:
  units:
    - name: update-engine.service
      mask: true
    - name: locksmithd.service
      mask: true`)
)

func init() {
	register.Register(&register.Test{
		Run:         RebootIntoUSRB,
		ClusterSize: 1,
		Name:        "coreos.update.reboot",
		UserData:    disableUpdateEngine,
	})
	register.Register(&register.Test{
		Run:         RecoverBadVerity,
		ClusterSize: 1,
		Name:        "coreos.update.badverity",
		Flags:       []register.Flag{register.NoEmergencyShellCheck},
		UserData:    disableUpdateEngine,
	})
	register.Register(&register.Test{
		Run:         RecoverBadUsr,
		ClusterSize: 1,
		Name:        "coreos.update.badusr",
		Flags:       []register.Flag{register.NoEmergencyShellCheck},
		UserData:    disableUpdateEngine,
	})
}

// Simulate update scenarios

// Check that we can reprioritize and boot into USR-B. This largely
// validates the other tests in this file.
func RebootIntoUSRB(c cluster.TestCluster) {
	m := c.Machines()[0]

	assertBootedUsr(c, m, "USR-A")

	// copy USR-A to USR-B
	c.MustSSH(m, "sudo dd if=/dev/disk/by-partlabel/USR-A of=/dev/disk/by-partlabel/USR-B bs=10M status=none")

	// copy kernel
	c.MustSSH(m, "sudo cp /boot/coreos/vmlinuz-a /boot/coreos/vmlinuz-b")

	prioritizeUsr(c, m, "USR-B")
	if err := m.Reboot(); err != nil {
		c.Fatalf("couldn't reboot: %v", err)
	}
	assertBootedUsr(c, m, "USR-B")
}

// Verify that we reboot into the old image after the new image fails a
// verity check.
func RecoverBadVerity(c cluster.TestCluster) {
	m := c.Machines()[0]

	skipUnlessVerity(c, m)

	assertBootedUsr(c, m, "USR-A")

	// copy USR-A to USR-B
	c.MustSSH(m, "sudo dd if=/dev/disk/by-partlabel/USR-A of=/dev/disk/by-partlabel/USR-B bs=10M status=none")

	// copy kernel
	c.MustSSH(m, "sudo cp /boot/coreos/vmlinuz-a /boot/coreos/vmlinuz-b")

	// invalidate verity hash on B kernel
	c.MustSSH(m, fmt.Sprintf("sudo dd of=/boot/coreos/vmlinuz-b bs=1 seek=%d count=64 conv=notrunc status=none <<<0000000000000000000000000000000000000000000000000000000000000000", getKernelVerityHashOffset(c)))

	prioritizeUsr(c, m, "USR-B")
	rebootWithEmergencyShellTimeout(c, m)
	assertBootedUsr(c, m, "USR-A")
}

// Verify that we reboot into the old image when the new image is an
// unreasonable filesystem (an empty one) that passes verity.
func RecoverBadUsr(c cluster.TestCluster) {
	m := c.Machines()[0]

	assertBootedUsr(c, m, "USR-A")

	// create filesystem for USR-B
	c.MustSSH(m, "sudo mkfs.ext4 -q -b 4096 /dev/disk/by-partlabel/USR-B 25600")

	// create verity metadata for USR-B
	output := c.MustSSH(m, "sudo veritysetup format --hash=sha256 "+
		"--data-block-size 4096 --hash-block-size 4096 --data-blocks 25600 --hash-offset 104857600 "+
		"/dev/disk/by-partlabel/USR-B /dev/disk/by-partlabel/USR-B")

	// extract root hash for USR-B
	match := regexp.MustCompile("\nRoot hash:\\s+([0-9a-f]+)").FindSubmatch(output)
	if match == nil {
		c.Fatalf("Couldn't obtain new root hash; output %s", output)
	}
	verityHash := match[1]

	// copy kernel
	c.MustSSH(m, "sudo cp /boot/coreos/vmlinuz-a /boot/coreos/vmlinuz-b")

	// update verity hash on B kernel
	c.MustSSH(m, fmt.Sprintf("sudo dd of=/boot/coreos/vmlinuz-b bs=1 seek=%d count=64 conv=notrunc status=none <<<%s", getKernelVerityHashOffset(c), verityHash))

	prioritizeUsr(c, m, "USR-B")
	rebootWithEmergencyShellTimeout(c, m)
	assertBootedUsr(c, m, "USR-A")
}

func assertBootedUsr(c cluster.TestCluster, m platform.Machine, usr string) {
	usrdev := getUsrDeviceNode(c, m)
	target := c.MustSSH(m, "readlink -f /dev/disk/by-partlabel/"+usr)
	if usrdev != string(target) {
		c.Fatalf("Expected /usr to be %v (%s) but it is %v", usr, target, usrdev)
	}
}

func prioritizeUsr(c cluster.TestCluster, m platform.Machine, usr string) {
	c.MustSSH(m, "sudo cgpt repair /dev/disk/by-partlabel/"+usr)
	c.MustSSH(m, "sudo cgpt add -S0 -T1 /dev/disk/by-partlabel/"+usr)
	c.MustSSH(m, "sudo cgpt prioritize /dev/disk/by-partlabel/"+usr)
}

// reboot, waiting extra-long for the 5-minute emergency shell timeout
func rebootWithEmergencyShellTimeout(c cluster.TestCluster, m platform.Machine) {
	// reboot; wait extra 5 minutes; check machine
	// this defeats some of the machinery in m.Reboot()
	if err := platform.StartReboot(m); err != nil {
		c.Fatal(err)
	}
	time.Sleep(5 * time.Minute)
	if err := platform.CheckMachine(m); err != nil {
		c.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package inet

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/misc"
	"github.com/aws/aws-xray-sdk-go/xray"
)

// HTTPRequest defines all of the parameters necessary for making an outgoing HTTP request using the DoHTTP function.
type HTTPRequest struct {
	TimeoutSec  int                       // Read timeout for response (default to 30)
	Method      string                    // HTTP method (default to GET)
	Header      http.Header               // Additional request header (default to nil)
	ContentType string                    // Content type header (default to "application/x-www-form-urlencoded")
	Body        io.Reader                 // HTTPRequest body (default to nil)
	RequestFunc func(*http.Request) error // Manipulate the HTTP request at will (default to nil)
	MaxBytes    int                       // MaxBytes is the maximum number of bytes of response body to read (default to 4MB)
	MaxRetry    int                       // MaxRetry is the maximum number of attempts to make the same request in case of an IO error, 4xx, or 5xx response (default to 3).
}

// FillBlanks gives sets the parameters of the HTTP request using sensible default values.
func (req *HTTPRequest) FillBlanks() {
	if req.TimeoutSec <= 0 {
		req.TimeoutSec = 30
	}
	if req.Method == "" {
		req.Method = "GET"
	}
	if req.ContentType == "" {
		req.ContentType = "application/x-www-form-urlencoded"
	}
	if req.MaxBytes <= 0 {
		req.MaxBytes = 4 * 1048576
	}
	if req.MaxRetry < 1 {
		req.MaxRetry = 3
	}
}

// HTTPResponse encapsulates the response code, header, and response body in its entirety.
type HTTPResponse struct {
	StatusCode int
	Header     http.Header
	Body       []byte
}

// Non2xxToError returns an error only if the HTTP response status is not 2xx.
func (resp *HTTPResponse) Non2xxToError() error {
	// Avoid showing the entire HTTP (quite likely HTML) response to end-user
	compactBody := resp.Body
	if compactBody == nil {
		compactBody = []byte("<IO error prior to response>")
	} else if len(compactBody) > 256 {
		compactBody = compactBody[:256]
	} else if len(compactBody) == 0 {
		compactBody = []byte("<empty response>")
	}

	if resp.StatusCode/200 != 1 {
		return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(compactBody))
	} else {
		return nil
	}
}

// GetBodyUpTo returns response body but only up to the specified number of bytes.
func (resp *HTTPResponse) GetBodyUpTo(nBytes int) []byte {
	if resp.Body == nil {
		return []byte{}
	}
	ret := resp.Body
	if len(resp.Body) > nBytes {
		ret = resp.Body[:nBytes]
	}
	return ret
}

// doHTTPRequestUsingClient makes an HTTP request via the input HTTP client.Placeholders in the URL template must always use %s.
func doHTTPRequestUsingClient(ctx context.Context, client *http.Client, reqParam HTTPRequest, urlTemplate string, urlValues ...interface{}) (HTTPResponse, error) {
	reqParam.FillBlanks()
	client.Timeout = time.Duration(reqParam.TimeoutSec) * time.Second
	defer client.CloseIdleConnections()
	// Encode values in URL path
	encodedURLValues := make([]interface{}, len(urlValues))
	for i, val := range urlValues {
		encodedURLValues[i] = url.QueryEscape(fmt.Sprint(val))
	}
	fullURL := fmt.Sprintf(urlTemplate, encodedURLValues...)
	// Retain a copy of request body for retry
	reqBodyCopy := new(bytes.Buffer)
	var lastHTTPErr error
	var lastResponse HTTPResponse
	// Send the request away, and retry in case of error.
	for retry := 0; retry < reqParam.MaxRetry; retry++ {
		var reqBodyReader io.Reader
		if reqParam.Body != nil {
			if retry == 0 {
				// Retain a copy of the request body in memory
				reqBodyReader = io.TeeReader(reqParam.Body, reqBodyCopy)
			} else {
				// Use the in-memory copy of request body from now as the original stream has already been drained
				reqBodyReader = bytes.NewReader(reqBodyCopy.Bytes())
			}
		}
		req, err := http.NewRequestWithContext(ctx, reqParam.Method, fullURL, reqBodyReader)
		if err != nil {
			return HTTPResponse{}, err
		}
		if reqParam.Header != nil {
			req.Header = reqParam.Header
		}
		// Use the input function to further customise the HTTP request
		if reqParam.RequestFunc != nil {
			if err := reqParam.RequestFunc(req); err != nil {
				return HTTPResponse{}, err
			}
		}
		req.Header.Set("Content-Type", reqParam.ContentType)
		if len(reqParam.Header) > 0 {
			if contentType := reqParam.Header.Get("Content-Type"); contentType != "" {
				req.Header.Set("Content-Type", contentType)
			}
		}
		var httpResp *http.Response
		httpResp, lastHTTPErr = client.Do(req)
		if lastHTTPErr == nil {
			lastResponse = HTTPResponse{
				Header:     httpResp.Header,
				StatusCode: httpResp.StatusCode,
			}
			lastResponse.Body, lastHTTPErr = misc.ReadAllUpTo(httpResp.Body, reqParam.MaxBytes)
			lalog.DefaultLogger.MaybeMinorError(httpResp.Body.Close())
			if lastHTTPErr == nil && httpResp.StatusCode/400 != 1 && httpResp.StatusCode/500 != 1 {
				// Return the response upon success
				if retry > 0 {
					// Let operator know that this URL endpoint may not be quite reliable
					lalog.DefaultLogger.Info("DoHTTP", urlTemplate, nil, "took %d retries to complete this %s request", retry, reqParam.Method)
				}
				return lastResponse, nil
			}
		}
		// Retry in case of IO error, 4xx, and 5xx responses.
		time.Sleep(1 * time.Second)
	}
	// Having exhausted all attempts, return the status code, body, etc, that belong to the latest response.
	return lastResponse, lastHTTPErr
}

// DoHTTP makes an HTTP request and returns its HTTP response. Placeholders in the URL template must always use %s.
func DoHTTP(ctx context.Context, reqParam HTTPRequest, urlTemplate string, urlValues ...interface{}) (resp HTTPResponse, err error) {
	client := &http.Client{}
	// Integrate the decorated handler with AWS x-ray. The crucial x-ray daemon program seems to be only capable of running on AWS compute resources.
	if misc.EnableAWSIntegration && IsAWS() {
		client = xray.Client(client)
	}
	return doHTTPRequestUsingClient(ctx, client, reqParam, urlTemplate, urlValues...)
}
<commit_msg>use context instead of the legacy timeout field of http client<commit_after>package inet

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/misc"
	"github.com/aws/aws-xray-sdk-go/xray"
)

// HTTPRequest defines all of the parameters necessary for making an outgoing HTTP request using the DoHTTP function.
type HTTPRequest struct {
	TimeoutSec  int                       // Read timeout for response (default to 30)
	Method      string                    // HTTP method (default to GET)
	Header      http.Header               // Additional request header (default to nil)
	ContentType string                    // Content type header (default to "application/x-www-form-urlencoded")
	Body        io.Reader                 // HTTPRequest body (default to nil)
	RequestFunc func(*http.Request) error // Manipulate the HTTP request at will (default to nil)
	MaxBytes    int                       // MaxBytes is the maximum number of bytes of response body to read (default to 4MB)
	MaxRetry    int                       // MaxRetry is the maximum number of attempts to make the same request in case of an IO error, 4xx, or 5xx response (default to 3).
}

// FillBlanks gives sets the parameters of the HTTP request using sensible default values.
func (req *HTTPRequest) FillBlanks() {
	if req.TimeoutSec <= 0 {
		req.TimeoutSec = 30
	}
	if req.Method == "" {
		req.Method = "GET"
	}
	if req.ContentType == "" {
		req.ContentType = "application/x-www-form-urlencoded"
	}
	if req.MaxBytes <= 0 {
		req.MaxBytes = 4 * 1048576
	}
	if req.MaxRetry < 1 {
		req.MaxRetry = 3
	}
}

// HTTPResponse encapsulates the response code, header, and response body in its entirety.
type HTTPResponse struct {
	StatusCode int
	Header     http.Header
	Body       []byte
}

// Non2xxToError returns an error only if the HTTP response status is not 2xx.
func (resp *HTTPResponse) Non2xxToError() error {
	// Avoid showing the entire HTTP (quite likely HTML) response to end-user
	compactBody := resp.Body
	if compactBody == nil {
		compactBody = []byte("<IO error prior to response>")
	} else if len(compactBody) > 256 {
		compactBody = compactBody[:256]
	} else if len(compactBody) == 0 {
		compactBody = []byte("<empty response>")
	}

	if resp.StatusCode/200 != 1 {
		return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(compactBody))
	} else {
		return nil
	}
}

// GetBodyUpTo returns response body but only up to the specified number of bytes.
func (resp *HTTPResponse) GetBodyUpTo(nBytes int) []byte {
	if resp.Body == nil {
		return []byte{}
	}
	ret := resp.Body
	if len(resp.Body) > nBytes {
		ret = resp.Body[:nBytes]
	}
	return ret
}

// doHTTPRequestUsingClient makes an HTTP request via the input HTTP client.Placeholders in the URL template must always use %s.
func doHTTPRequestUsingClient(ctx context.Context, client *http.Client, reqParam HTTPRequest, urlTemplate string, urlValues ...interface{}) (HTTPResponse, error) {
	reqParam.FillBlanks()
	ctx = context.WithTimeout(ctx, time.Duration(reqParam.TimeoutSec)*time.Second)
	defer client.CloseIdleConnections()
	// Encode values in URL path
	encodedURLValues := make([]interface{}, len(urlValues))
	for i, val := range urlValues {
		encodedURLValues[i] = url.QueryEscape(fmt.Sprint(val))
	}
	fullURL := fmt.Sprintf(urlTemplate, encodedURLValues...)
	// Retain a copy of request body for retry
	reqBodyCopy := new(bytes.Buffer)
	var lastHTTPErr error
	var lastResponse HTTPResponse
	// Send the request away, and retry in case of error.
	for retry := 0; retry < reqParam.MaxRetry; retry++ {
		var reqBodyReader io.Reader
		if reqParam.Body != nil {
			if retry == 0 {
				// Retain a copy of the request body in memory
				reqBodyReader = io.TeeReader(reqParam.Body, reqBodyCopy)
			} else {
				// Use the in-memory copy of request body from now as the original stream has already been drained
				reqBodyReader = bytes.NewReader(reqBodyCopy.Bytes())
			}
		}
		req, err := http.NewRequestWithContext(ctx, reqParam.Method, fullURL, reqBodyReader)
		if err != nil {
			return HTTPResponse{}, err
		}
		if reqParam.Header != nil {
			req.Header = reqParam.Header
		}
		// Use the input function to further customise the HTTP request
		if reqParam.RequestFunc != nil {
			if err := reqParam.RequestFunc(req); err != nil {
				return HTTPResponse{}, err
			}
		}
		req.Header.Set("Content-Type", reqParam.ContentType)
		if len(reqParam.Header) > 0 {
			if contentType := reqParam.Header.Get("Content-Type"); contentType != "" {
				req.Header.Set("Content-Type", contentType)
			}
		}
		var httpResp *http.Response
		httpResp, lastHTTPErr = client.Do(req)
		if lastHTTPErr == nil {
			lastResponse = HTTPResponse{
				Header:     httpResp.Header,
				StatusCode: httpResp.StatusCode,
			}
			lastResponse.Body, lastHTTPErr = misc.ReadAllUpTo(httpResp.Body, reqParam.MaxBytes)
			lalog.DefaultLogger.MaybeMinorError(httpResp.Body.Close())
			if lastHTTPErr == nil && httpResp.StatusCode/400 != 1 && httpResp.StatusCode/500 != 1 {
				// Return the response upon success
				if retry > 0 {
					// Let operator know that this URL endpoint may not be quite reliable
					lalog.DefaultLogger.Info("DoHTTP", urlTemplate, nil, "took %d retries to complete this %s request", retry, reqParam.Method)
				}
				return lastResponse, nil
			}
		}
		// Retry in case of IO error, 4xx, and 5xx responses.
		time.Sleep(1 * time.Second)
	}
	// Having exhausted all attempts, return the status code, body, etc, that belong to the latest response.
	return lastResponse, lastHTTPErr
}

// DoHTTP makes an HTTP request and returns its HTTP response. Placeholders in the URL template must always use %s.
func DoHTTP(ctx context.Context, reqParam HTTPRequest, urlTemplate string, urlValues ...interface{}) (resp HTTPResponse, err error) {
	client := &http.Client{}
	// Integrate the decorated handler with AWS x-ray. The crucial x-ray daemon program seems to be only capable of running on AWS compute resources.
	if misc.EnableAWSIntegration && IsAWS() {
		client = xray.Client(client)
	}
	return doHTTPRequestUsingClient(ctx, client, reqParam, urlTemplate, urlValues...)
}
<|endoftext|>


<commit_before>package influxql

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
)

// Scanner represents a lexical scanner for InfluxQL.
type Scanner struct {
	r *reader
}

// NewScanner returns a new instance of Scanner.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{r: &reader{r: bufio.NewReader(r)}}
}

// Scan returns the next token from the underlying reader.
// Also returns the position and literal string text read for the token.
func (s *Scanner) Scan() (tok Token, pos Pos, lit string) {
	// Read next code point.
	ch0, pos := s.r.read()

	// If we see whitespace then consume all contiguous whitespace.
	// If we see a letter then consume as an ident or reserved word.
	if isWhitespace(ch0) {
		return s.scanWhitespace()
	} else if isLetter(ch0) {
		return s.scanIdent()
	} else if isDigit(ch0) {
		return s.scanNumber()
	}

	// Otherwise parse individual characters.
	switch ch0 {
	case eof:
		return EOF, pos, ""
	case '"', '\'':
		return s.scanString()
	case '.', '+', '-':
		return s.scanNumber()
	case '*':
		return MUL, pos, ""
	case '/':
		return DIV, pos, ""
	case '=':
		return EQ, pos, ""
	case '!':
		if ch1, _ := s.r.read(); ch1 == '=' {
			return NEQ, pos, ""
		}
		s.r.unread()
		return ILLEGAL, pos, string(ch0)
	case '>':
		if ch1, _ := s.r.read(); ch1 == '=' {
			return GTE, pos, ""
		}
		s.r.unread()
		return GT, pos, ""
	case '<':
		if ch1, _ := s.r.read(); ch1 == '=' {
			return LTE, pos, ""
		}
		s.r.unread()
		return LT, pos, ""
	case '(':
		return LPAREN, pos, ""
	case ')':
		return RPAREN, pos, ""
	case ',':
		return COMMA, pos, ""
	}

	return ILLEGAL, pos, string(ch0)
}

// scanWhitespace consumes the current rune and all contiguous whitespace.
func (s *Scanner) scanWhitespace() (tok Token, pos Pos, lit string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer
	ch, pos := s.r.curr()
	_, _ = buf.WriteRune(ch)

	// Read every subsequent whitespace character into the buffer.
	// Non-whitespace characters and EOF will cause the loop to exit.
	for {
		ch, _ = s.r.read()
		if ch == eof {
			break
		} else if !isWhitespace(ch) {
			s.r.unread()
			break
		} else {
			_, _ = buf.WriteRune(ch)
		}
	}

	return WS, pos, buf.String()
}

// scanIdent consumes the current rune and all contiguous ident runes.
func (s *Scanner) scanIdent() (tok Token, pos Pos, lit string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer
	ch, pos := s.r.curr()
	_, _ = buf.WriteRune(ch)

	// Read every subsequent ident character into the buffer.
	// Non-ident characters and EOF will cause the loop to exit.
	for {
		ch, _ = s.r.read()
		if ch == eof {
			break
		} else if !isLetter(ch) && !isDigit(ch) && ch != '_' {
			s.r.unread()
			break
		} else {
			_, _ = buf.WriteRune(ch)
		}
	}

	// If the string matches a keyword then return that keyword.
	if tok = Lookup(buf.String()); tok != IDENT {
		return tok, pos, ""
	}

	// Otherwise return as a regular identifier.
	return IDENT, pos, buf.String()
}

// scanString consumes a contiguous string of non-quote characters.
// Quote characters can be consumed if they're first escaped with a backslash.
func (s *Scanner) scanString() (tok Token, pos Pos, lit string) {
	ending, pos := s.r.curr()
	var buf bytes.Buffer
	for {
		ch0, pos0 := s.r.read()
		if ch0 == ending {
			return STRING, pos, buf.String()
		} else if ch0 == eof || ch0 == '\n' {
			return BADSTRING, pos, buf.String()
		} else if ch0 == '\\' {
			// If the next character is an escape then write the escaped char.
			// If it's not a valid escape then return a BADESCAPE token.
			ch1, _ := s.r.read()
			if ch1 == 'n' {
				_, _ = buf.WriteRune('\n')
			} else if ch1 == '\\' {
				_, _ = buf.WriteRune('\\')
			} else {
				return BADESCAPE, pos0, string(ch0) + string(ch1)
			}
		} else {
			_, _ = buf.WriteRune(ch0)
		}
	}
}

// scanNumber consumes anything that looks like the start of a number.
// Numbers start with a digit, full stop, plus sign or minus sign.
// This function can return non-number tokens if a scan is a false positive.
// For example, a minus sign followed by a letter will just return a minus sign.
func (s *Scanner) scanNumber() (tok Token, pos Pos, lit string) {
	var buf bytes.Buffer

	// Check if the initial rune is a "+" or "-".
	ch, pos := s.r.curr()
	if ch == '+' || ch == '-' {
		// Peek at the next two runes.
		ch1, _ := s.r.read()
		ch2, _ := s.r.read()
		s.r.unread()
		s.r.unread()

		// This rune must be followed by a digit or a full stop and a digit.
		if isDigit(ch1) || (ch1 == '.' && isDigit(ch2)) {
			_, _ = buf.WriteRune(ch)
		} else if ch == '+' {
			return ADD, pos, ""
		} else if ch == '-' {
			return SUB, pos, ""
		}
	} else if ch == '.' {
		// Peek and see if the next rune is a digit.
		ch1, _ := s.r.read()
		s.r.unread()
		if !isDigit(ch1) {
			return ILLEGAL, pos, "."
		}

		// Unread the full stop so we can read it later.
		s.r.unread()
	} else {
		s.r.unread()
	}

	// Read as many digits as possible.
	_, _ = buf.WriteString(s.scanDigits())

	// If next code points are a full stop and digit then consume them.
	if ch0, _ := s.r.read(); ch0 == '.' {
		if ch1, _ := s.r.read(); isDigit(ch1) {
			_, _ = buf.WriteRune(ch0)
			_, _ = buf.WriteRune(ch1)
			_, _ = buf.WriteString(s.scanDigits())
		} else {
			s.r.unread()
			s.r.unread()
		}
	} else {
		s.r.unread()
	}

	// If the next rune is a duration unit (u,µ,ms,s) then return a duration token
	if ch0, _ := s.r.read(); ch0 == 'u' || ch0 == 'µ' || ch0 == 's' || ch0 == 'h' || ch0 == 'd' || ch0 == 'w' {
		_, _ = buf.WriteRune(ch0)
		return DURATION, pos, buf.String()
	} else if ch0 == 'm' {
		_, _ = buf.WriteRune(ch0)
		if ch1, _ := s.r.read(); ch1 == 's' {
			_, _ = buf.WriteRune(ch1)
		} else {
			s.r.unread()
		}
		return DURATION, pos, buf.String()
	}
	s.r.unread()

	return NUMBER, pos, buf.String()
}

// scanDigits consume a contiguous series of digits.
func (s *Scanner) scanDigits() string {
	var buf bytes.Buffer
	for {
		ch, _ := s.r.read()
		if !isDigit(ch) {
			s.r.unread()
			break
		}
		_, _ = buf.WriteRune(ch)
	}
	return buf.String()
}

// isWhitespace returns true if the rune is a space, tab, or newline.
func isWhitespace(ch rune) bool { return ch == ' ' || ch == '\t' || ch == '\n' }

// isLetter returns true if the rune is a letter.
func isLetter(ch rune) bool { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') }

// isDigit returns true if the rune is a digit.
func isDigit(ch rune) bool { return (ch >= '0' && ch <= '9') }

// reader represents a buffered rune reader used by the scanner.
// It provides a fixed-length circular buffer that can be unread.
type reader struct {
	r   io.RuneScanner
	i   int // buffer index
	n   int // buffer char count
	pos Pos // last read rune position
	buf [3]struct {
		ch  rune
		pos Pos
	}
}

// read reads the next rune from the reader.
func (r *reader) read() (ch rune, pos Pos) {
	// If we have unread characters then read them off the buffer first.
	if r.n > 0 {
		r.n--
		return r.curr()
	}

	// Read next rune from underlying reader.
	// Any error (including io.EOF) should return as EOF.
	ch, _, err := r.r.ReadRune()
	if err != nil {
		ch = eof
	} else if ch == '\r' {
		if ch, _, err := r.r.ReadRune(); err != nil {
			// nop
		} else if ch != '\n' {
			_ = r.r.UnreadRune()
		}
		ch = '\n'
	}

	// Save character and position to the buffer.
	r.i = (r.i + 1) % len(r.buf)
	buf := &r.buf[r.i]
	buf.ch, buf.pos = ch, r.pos

	// Update position.
	if ch == '\n' {
		r.pos.Line++
		r.pos.Char = 0
	} else {
		r.pos.Char++
	}

	return r.curr()
}

// unread pushes the previously read rune back onto the buffer.
func (r *reader) unread() {
	r.n++
}

// curr returns the last read character and position.
func (r *reader) curr() (ch rune, pos Pos) {
	i := (r.i - r.n + len(r.buf)) % len(r.buf)
	buf := &r.buf[i]
	return buf.ch, buf.pos
}

// eof is a marker code point to signify that the reader can't read any more.
const eof = rune(0)

func warn(v ...interface{})              { fmt.Fprintln(os.Stderr, v...) }
func warnf(msg string, v ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", v...) }
<commit_msg>Fix Scanner.Scan() comment.<commit_after>package influxql

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
)

// Scanner represents a lexical scanner for InfluxQL.
type Scanner struct {
	r *reader
}

// NewScanner returns a new instance of Scanner.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{r: &reader{r: bufio.NewReader(r)}}
}

// Scan returns the next token and position from the underlying reader.
// Also returns the literal text read for strings, numbers, and duration tokens
// since these token types can have different literal representations.
func (s *Scanner) Scan() (tok Token, pos Pos, lit string) {
	// Read next code point.
	ch0, pos := s.r.read()

	// If we see whitespace then consume all contiguous whitespace.
	// If we see a letter then consume as an ident or reserved word.
	if isWhitespace(ch0) {
		return s.scanWhitespace()
	} else if isLetter(ch0) {
		return s.scanIdent()
	} else if isDigit(ch0) {
		return s.scanNumber()
	}

	// Otherwise parse individual characters.
	switch ch0 {
	case eof:
		return EOF, pos, ""
	case '"', '\'':
		return s.scanString()
	case '.', '+', '-':
		return s.scanNumber()
	case '*':
		return MUL, pos, ""
	case '/':
		return DIV, pos, ""
	case '=':
		return EQ, pos, ""
	case '!':
		if ch1, _ := s.r.read(); ch1 == '=' {
			return NEQ, pos, ""
		}
		s.r.unread()
		return ILLEGAL, pos, string(ch0)
	case '>':
		if ch1, _ := s.r.read(); ch1 == '=' {
			return GTE, pos, ""
		}
		s.r.unread()
		return GT, pos, ""
	case '<':
		if ch1, _ := s.r.read(); ch1 == '=' {
			return LTE, pos, ""
		}
		s.r.unread()
		return LT, pos, ""
	case '(':
		return LPAREN, pos, ""
	case ')':
		return RPAREN, pos, ""
	case ',':
		return COMMA, pos, ""
	}

	return ILLEGAL, pos, string(ch0)
}

// scanWhitespace consumes the current rune and all contiguous whitespace.
func (s *Scanner) scanWhitespace() (tok Token, pos Pos, lit string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer
	ch, pos := s.r.curr()
	_, _ = buf.WriteRune(ch)

	// Read every subsequent whitespace character into the buffer.
	// Non-whitespace characters and EOF will cause the loop to exit.
	for {
		ch, _ = s.r.read()
		if ch == eof {
			break
		} else if !isWhitespace(ch) {
			s.r.unread()
			break
		} else {
			_, _ = buf.WriteRune(ch)
		}
	}

	return WS, pos, buf.String()
}

// scanIdent consumes the current rune and all contiguous ident runes.
func (s *Scanner) scanIdent() (tok Token, pos Pos, lit string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer
	ch, pos := s.r.curr()
	_, _ = buf.WriteRune(ch)

	// Read every subsequent ident character into the buffer.
	// Non-ident characters and EOF will cause the loop to exit.
	for {
		ch, _ = s.r.read()
		if ch == eof {
			break
		} else if !isLetter(ch) && !isDigit(ch) && ch != '_' {
			s.r.unread()
			break
		} else {
			_, _ = buf.WriteRune(ch)
		}
	}

	// If the string matches a keyword then return that keyword.
	if tok = Lookup(buf.String()); tok != IDENT {
		return tok, pos, ""
	}

	// Otherwise return as a regular identifier.
	return IDENT, pos, buf.String()
}

// scanString consumes a contiguous string of non-quote characters.
// Quote characters can be consumed if they're first escaped with a backslash.
func (s *Scanner) scanString() (tok Token, pos Pos, lit string) {
	ending, pos := s.r.curr()
	var buf bytes.Buffer
	for {
		ch0, pos0 := s.r.read()
		if ch0 == ending {
			return STRING, pos, buf.String()
		} else if ch0 == eof || ch0 == '\n' {
			return BADSTRING, pos, buf.String()
		} else if ch0 == '\\' {
			// If the next character is an escape then write the escaped char.
			// If it's not a valid escape then return a BADESCAPE token.
			ch1, _ := s.r.read()
			if ch1 == 'n' {
				_, _ = buf.WriteRune('\n')
			} else if ch1 == '\\' {
				_, _ = buf.WriteRune('\\')
			} else {
				return BADESCAPE, pos0, string(ch0) + string(ch1)
			}
		} else {
			_, _ = buf.WriteRune(ch0)
		}
	}
}

// scanNumber consumes anything that looks like the start of a number.
// Numbers start with a digit, full stop, plus sign or minus sign.
// This function can return non-number tokens if a scan is a false positive.
// For example, a minus sign followed by a letter will just return a minus sign.
func (s *Scanner) scanNumber() (tok Token, pos Pos, lit string) {
	var buf bytes.Buffer

	// Check if the initial rune is a "+" or "-".
	ch, pos := s.r.curr()
	if ch == '+' || ch == '-' {
		// Peek at the next two runes.
		ch1, _ := s.r.read()
		ch2, _ := s.r.read()
		s.r.unread()
		s.r.unread()

		// This rune must be followed by a digit or a full stop and a digit.
		if isDigit(ch1) || (ch1 == '.' && isDigit(ch2)) {
			_, _ = buf.WriteRune(ch)
		} else if ch == '+' {
			return ADD, pos, ""
		} else if ch == '-' {
			return SUB, pos, ""
		}
	} else if ch == '.' {
		// Peek and see if the next rune is a digit.
		ch1, _ := s.r.read()
		s.r.unread()
		if !isDigit(ch1) {
			return ILLEGAL, pos, "."
		}

		// Unread the full stop so we can read it later.
		s.r.unread()
	} else {
		s.r.unread()
	}

	// Read as many digits as possible.
	_, _ = buf.WriteString(s.scanDigits())

	// If next code points are a full stop and digit then consume them.
	if ch0, _ := s.r.read(); ch0 == '.' {
		if ch1, _ := s.r.read(); isDigit(ch1) {
			_, _ = buf.WriteRune(ch0)
			_, _ = buf.WriteRune(ch1)
			_, _ = buf.WriteString(s.scanDigits())
		} else {
			s.r.unread()
			s.r.unread()
		}
	} else {
		s.r.unread()
	}

	// If the next rune is a duration unit (u,µ,ms,s) then return a duration token
	if ch0, _ := s.r.read(); ch0 == 'u' || ch0 == 'µ' || ch0 == 's' || ch0 == 'h' || ch0 == 'd' || ch0 == 'w' {
		_, _ = buf.WriteRune(ch0)
		return DURATION, pos, buf.String()
	} else if ch0 == 'm' {
		_, _ = buf.WriteRune(ch0)
		if ch1, _ := s.r.read(); ch1 == 's' {
			_, _ = buf.WriteRune(ch1)
		} else {
			s.r.unread()
		}
		return DURATION, pos, buf.String()
	}
	s.r.unread()

	return NUMBER, pos, buf.String()
}

// scanDigits consume a contiguous series of digits.
func (s *Scanner) scanDigits() string {
	var buf bytes.Buffer
	for {
		ch, _ := s.r.read()
		if !isDigit(ch) {
			s.r.unread()
			break
		}
		_, _ = buf.WriteRune(ch)
	}
	return buf.String()
}

// isWhitespace returns true if the rune is a space, tab, or newline.
func isWhitespace(ch rune) bool { return ch == ' ' || ch == '\t' || ch == '\n' }

// isLetter returns true if the rune is a letter.
func isLetter(ch rune) bool { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') }

// isDigit returns true if the rune is a digit.
func isDigit(ch rune) bool { return (ch >= '0' && ch <= '9') }

// reader represents a buffered rune reader used by the scanner.
// It provides a fixed-length circular buffer that can be unread.
type reader struct {
	r   io.RuneScanner
	i   int // buffer index
	n   int // buffer char count
	pos Pos // last read rune position
	buf [3]struct {
		ch  rune
		pos Pos
	}
}

// read reads the next rune from the reader.
func (r *reader) read() (ch rune, pos Pos) {
	// If we have unread characters then read them off the buffer first.
	if r.n > 0 {
		r.n--
		return r.curr()
	}

	// Read next rune from underlying reader.
	// Any error (including io.EOF) should return as EOF.
	ch, _, err := r.r.ReadRune()
	if err != nil {
		ch = eof
	} else if ch == '\r' {
		if ch, _, err := r.r.ReadRune(); err != nil {
			// nop
		} else if ch != '\n' {
			_ = r.r.UnreadRune()
		}
		ch = '\n'
	}

	// Save character and position to the buffer.
	r.i = (r.i + 1) % len(r.buf)
	buf := &r.buf[r.i]
	buf.ch, buf.pos = ch, r.pos

	// Update position.
	if ch == '\n' {
		r.pos.Line++
		r.pos.Char = 0
	} else {
		r.pos.Char++
	}

	return r.curr()
}

// unread pushes the previously read rune back onto the buffer.
func (r *reader) unread() {
	r.n++
}

// curr returns the last read character and position.
func (r *reader) curr() (ch rune, pos Pos) {
	i := (r.i - r.n + len(r.buf)) % len(r.buf)
	buf := &r.buf[i]
	return buf.ch, buf.pos
}

// eof is a marker code point to signify that the reader can't read any more.
const eof = rune(0)

func warn(v ...interface{})              { fmt.Fprintln(os.Stderr, v...) }
func warnf(msg string, v ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", v...) }
<|endoftext|>


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

package lease

import (
	"fmt"
	"io"
	"log"
	"os"
	"sync"
)

// A read-write wrapper around a file. Unlike a read lease, this cannot be
// revoked.
//
// All methods are safe for concurrent access.
type ReadWriteLease interface {
	// Methods with semantics matching *os.File.
	io.ReadWriteSeeker
	io.ReaderAt
	io.WriterAt
	Truncate(size int64) (err error)

	// Return the current size of the underlying file.
	Size() (size int64, err error)

	// Downgrade to a read lease, releasing any resources pinned by this lease to
	// the pool that may be revoked, as with any read lease. After downgrading,
	// this lease must not be used again.
	Downgrade() (rl ReadLease)
}

type readWriteLease struct {
	mu sync.Mutex

	/////////////////////////
	// Dependencies
	/////////////////////////

	// The leaser that issued this lease.
	leaser *fileLeaser

	// The underlying file, set to nil once downgraded.
	//
	// GUARDED_BY(mu)
	file *os.File

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The cumulative number of bytes we have reported to the leaser using
	// fileLeaser.addReadWriteByteDelta. When the size changes, we report the
	// difference between the new size and this value.
	//
	// GUARDED_BY(mu)
	reportedSize int64
}

var _ ReadWriteLease = &readWriteLease{}

// size is the size that the leaser has already recorded for us.
func newReadWriteLease(
	leaser *fileLeaser,
	size int64,
	file *os.File) (rwl *readWriteLease) {
	rwl = &readWriteLease{
		leaser:       leaser,
		file:         file,
		reportedSize: size,
	}

	return
}

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

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Read(p []byte) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	n, err = rwl.file.Read(p)
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Write(p []byte) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Ensure that we reconcile our size when we're done.
	defer rwl.reconcileSize()

	// Call through.
	n, err = rwl.file.Write(p)

	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Seek(
	offset int64,
	whence int) (off int64, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	off, err = rwl.file.Seek(offset, whence)
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) ReadAt(p []byte, off int64) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	n, err = rwl.file.ReadAt(p, off)
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) WriteAt(p []byte, off int64) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Ensure that we reconcile our size when we're done.
	defer rwl.reconcileSize()

	// Call through.
	n, err = rwl.file.WriteAt(p, off)

	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Truncate(size int64) (err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Ensure that we reconcile our size when we're done.
	defer rwl.reconcileSize()

	// Call through.
	err = rwl.file.Truncate(size)

	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Size() (size int64, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	size, err = rwl.sizeLocked()
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Downgrade() (rl ReadLease) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Find the current size under the lock.
	size, err := rwl.sizeLocked()
	if err != nil {
		log.Printf("Error obtaining size while downgrading: %v", err)
		return
	}

	// Call the leaser.
	rl = rwl.leaser.downgrade(rwl, size, rwl.file)

	// Ensure that we will crash if used again.
	rwl.file = nil

	return
}

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

// LOCKS_REQUIRED(rwl.mu)
func (rwl *readWriteLease) sizeLocked() (size int64, err error) {
	// Stat the file to get its size.
	fi, err := rwl.file.Stat()
	if err != nil {
		err = fmt.Errorf("Stat: %v", err)
		return
	}

	size = fi.Size()
	return
}

// Notify the leaser if our size has changed. Log errors when we fail to find
// our size.
//
// LOCKS_REQUIRED(rwl.mu)
// LOCKS_EXCLUDED(rwl.leaser.mu)
func (rwl *readWriteLease) reconcileSize() {
	var err error

	// Find our size.
	size, err := rwl.sizeLocked()
	if err != nil {
		log.Println("Error getting size for reconciliation:", err)
		return
	}

	// Let the leaser know about any change.
	delta := size - rwl.reportedSize
	if delta != 0 {
		rwl.leaser.addReadWriteByteDelta(delta)
		rwl.reportedSize = size
	}
}
<commit_msg>Fixed broken error path in readWriteLease.Downgrade.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lease

import (
	"fmt"
	"io"
	"log"
	"os"
	"sync"
)

// A read-write wrapper around a file. Unlike a read lease, this cannot be
// revoked.
//
// All methods are safe for concurrent access.
type ReadWriteLease interface {
	// Methods with semantics matching *os.File.
	io.ReadWriteSeeker
	io.ReaderAt
	io.WriterAt
	Truncate(size int64) (err error)

	// Return the current size of the underlying file.
	Size() (size int64, err error)

	// Downgrade to a read lease, releasing any resources pinned by this lease to
	// the pool that may be revoked, as with any read lease. After downgrading,
	// this lease must not be used again.
	Downgrade() (rl ReadLease)
}

type readWriteLease struct {
	mu sync.Mutex

	/////////////////////////
	// Dependencies
	/////////////////////////

	// The leaser that issued this lease.
	leaser *fileLeaser

	// The underlying file, set to nil once downgraded.
	//
	// GUARDED_BY(mu)
	file *os.File

	/////////////////////////
	// Mutable state
	/////////////////////////

	// The cumulative number of bytes we have reported to the leaser using
	// fileLeaser.addReadWriteByteDelta. When the size changes, we report the
	// difference between the new size and this value.
	//
	// GUARDED_BY(mu)
	reportedSize int64
}

var _ ReadWriteLease = &readWriteLease{}

// size is the size that the leaser has already recorded for us.
func newReadWriteLease(
	leaser *fileLeaser,
	size int64,
	file *os.File) (rwl *readWriteLease) {
	rwl = &readWriteLease{
		leaser:       leaser,
		file:         file,
		reportedSize: size,
	}

	return
}

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

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Read(p []byte) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	n, err = rwl.file.Read(p)
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Write(p []byte) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Ensure that we reconcile our size when we're done.
	defer rwl.reconcileSize()

	// Call through.
	n, err = rwl.file.Write(p)

	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Seek(
	offset int64,
	whence int) (off int64, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	off, err = rwl.file.Seek(offset, whence)
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) ReadAt(p []byte, off int64) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	n, err = rwl.file.ReadAt(p, off)
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) WriteAt(p []byte, off int64) (n int, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Ensure that we reconcile our size when we're done.
	defer rwl.reconcileSize()

	// Call through.
	n, err = rwl.file.WriteAt(p, off)

	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Truncate(size int64) (err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Ensure that we reconcile our size when we're done.
	defer rwl.reconcileSize()

	// Call through.
	err = rwl.file.Truncate(size)

	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Size() (size int64, err error) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	size, err = rwl.sizeLocked()
	return
}

// LOCKS_EXCLUDED(rwl.mu)
func (rwl *readWriteLease) Downgrade() (rl ReadLease) {
	rwl.mu.Lock()
	defer rwl.mu.Unlock()

	// Ensure that we will crash if used again.
	f := rwl.file
	rwl.file = nil

	// On error, log an error then return a read lease that looks like it was
	// born revoked.
	var err error
	defer func() {
		if err != nil {
			log.Printf("Error downgrading: %v", err)
			rl = &alwaysRevokedReadLease{}
		}
	}()

	// Find the current size under the lock.
	size, err := rwl.sizeLocked()
	if err != nil {
		err = fmt.Errorf("sizeLocked: %v", err)
		return
	}

	// Call the leaser.
	rl = rwl.leaser.downgrade(rwl, size, f)

	return
}

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

// LOCKS_REQUIRED(rwl.mu)
func (rwl *readWriteLease) sizeLocked() (size int64, err error) {
	// Stat the file to get its size.
	fi, err := rwl.file.Stat()
	if err != nil {
		err = fmt.Errorf("Stat: %v", err)
		return
	}

	size = fi.Size()
	return
}

// Notify the leaser if our size has changed. Log errors when we fail to find
// our size.
//
// LOCKS_REQUIRED(rwl.mu)
// LOCKS_EXCLUDED(rwl.leaser.mu)
func (rwl *readWriteLease) reconcileSize() {
	var err error

	// Find our size.
	size, err := rwl.sizeLocked()
	if err != nil {
		log.Println("Error getting size for reconciliation:", err)
		return
	}

	// Let the leaser know about any change.
	delta := size - rwl.reportedSize
	if delta != 0 {
		rwl.leaser.addReadWriteByteDelta(delta)
		rwl.reportedSize = size
	}
}
<|endoftext|>