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

package goga

import (
	"sort"

	"github.com/cpmech/gosl/io"
)

// Population holds all individuals
type Population []*Individual

// NewPopFloatChromo allocates a population made entirely of float point numbers
//  Input:
//   genes -- all genes of all individuals [ninds][ngenes]
func NewPopFloatChromo(nbases int, genes [][]float64) (pop Population) {
	ninds := len(genes)
	pop = make([]*Individual, ninds)
	for i := 0; i < ninds; i++ {
		pop[i] = new(Individual)
		pop[i].InitChromo(nbases, genes[i])
	}
	return
}

// NewPopReference creates a population based on a reference individual
func NewPopReference(ninds int, ref *Individual) (pop Population) {
	pop = make([]*Individual, ninds)
	for i := 0; i < ninds; i++ {
		pop[i] = ref.GetCopy()
	}
	return
}

// Len returns the length of the population == number of individuals
func (o Population) Len() int {
	return len(o)
}

// Swap swaps two individuals
func (o Population) Swap(i, j int) {
	o[i], o[j] = o[j], o[i]
}

// Less returns true if 'i' is "less bad" than 'j'; therefore it can be used
// to sort the population in decreasing fitness order: best => worst
func (o Population) Less(i, j int) bool {
	return o[i].Fitness > o[j].Fitness
}

// Sort sorts the population from best to worst individuals; i.e. decreasing fitness values
func (o *Population) Sort() {
	sort.Sort(o)
}

// Output generates a nice table with population data
//  Input:
//  fmts -- formats for     int,     flt, string, byte,  bytes, and func
//          use fmts == nil to choose default ones
func (o Population) Output(fmts []string) (l string) {

	// check
	if len(o) < 1 {
		return
	}
	if len(o[0].Chromo) < 1 {
		return
	}

	// compute sizes and generate formats list
	nfields := o[0].Chromo[0].Nfields()
	ngenes := len(o[0].Chromo)
	sizes := make([]int, 6)
	if fmts == nil {
		fmts = make([]string, 6)
		for _, ind := range o {
			sz := ind.GetStringSizes()
			for i := 0; i < 6; i++ {
				sizes[i] = imax(sizes[i], sz[i])
				if nfields == 1 {
					if sz[i] > 0 {
						if sizes[i]*ngenes < 5 { // 5 ==> len("Genes")
							sizes[i] = 3
							if ngenes == 1 {
								sizes[i] = 5
							}
						}
					}
				}
			}
		}
		for i, str := range []string{"d", "g", "s", "x", "s", "s"} {
			fmts[i] = io.Sf("%%%d%s", sizes[i]+1, str)
		}
	}

	// compute sizes of header items
	nOvl, nFit := 0, 0
	for _, ind := range o {
		nOvl = imax(nOvl, len(io.Sf("%g", ind.ObjValue)))
		nFit = imax(nFit, len(io.Sf("%g", ind.Fitness)))
	}
	nChr := nfields * ngenes // spaces between fields
	for i := 0; i < 6; i++ {
		nChr += sizes[i] * ngenes
	}
	if nfields > 1 {
		nChr += ngenes*2 + (ngenes - 1) // 2 ==> "(" and ")" and (ngens-1) ==> space between
	}
	nChr = imax(5, nChr) // 5 ==> len("Genes")

	// print individuals
	nOvl = imax(nOvl, 6)        // 6 ==> len("ObjVal")
	nFit = imax(nFit, 7)        // 7 ==> len("Fitness")
	n := nOvl + nFit + nChr + 3 // 3 ==> spaces beeen "ObjVal", "Fitness" and "Genes"
	fmtOvl := io.Sf("%%%d", nOvl+1)
	fmtFit := io.Sf("%%%d", nFit+1)
	fmtChr := io.Sf("%%%ds", nChr)
	l += printThickLine(n)
	l += io.Sf(fmtOvl+"s", "ObjVal")
	l += io.Sf(fmtFit+"s", "Fitness") + " "
	l += io.Sf(fmtChr, "Genes")
	l += "\n" + printThinLine(n)
	fmtOvl += "g"
	fmtFit += "g"
	for _, ind := range o {
		l += io.Sf(fmtOvl, ind.ObjValue) + io.Sf(fmtFit, ind.Fitness) + " " + ind.Output(fmts) + "\n"
	}
	l += printThickLine(n)
	return
}

// OutFloatBases print bases of float genes
func (o Population) OutFloatBases(numFmt string) (l string) {
	for _, ind := range o {
		for _, g := range ind.Chromo {
			l += io.Sf(numFmt, g.Fbases)
		}
		l += "\n"
	}
	return
}
<commit_msg>table fixed<commit_after>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"bytes"
	"sort"

	"github.com/cpmech/gosl/io"
)

// Population holds all individuals
type Population []*Individual

// NewPopFloatChromo allocates a population made entirely of float point numbers
//  Input:
//   genes -- all genes of all individuals [ninds][ngenes]
func NewPopFloatChromo(nbases int, genes [][]float64) (pop Population) {
	ninds := len(genes)
	pop = make([]*Individual, ninds)
	for i := 0; i < ninds; i++ {
		pop[i] = new(Individual)
		pop[i].InitChromo(nbases, genes[i])
	}
	return
}

// NewPopReference creates a population based on a reference individual
func NewPopReference(ninds int, ref *Individual) (pop Population) {
	pop = make([]*Individual, ninds)
	for i := 0; i < ninds; i++ {
		pop[i] = ref.GetCopy()
	}
	return
}

// Len returns the length of the population == number of individuals
func (o Population) Len() int {
	return len(o)
}

// Swap swaps two individuals
func (o Population) Swap(i, j int) {
	o[i], o[j] = o[j], o[i]
}

// Less returns true if 'i' is "less bad" than 'j'; therefore it can be used
// to sort the population in decreasing fitness order: best => worst
func (o Population) Less(i, j int) bool {
	return o[i].Fitness > o[j].Fitness
}

// Sort sorts the population from best to worst individuals; i.e. decreasing fitness values
func (o *Population) Sort() {
	sort.Sort(o)
}

// Output generates a nice table with population data
//  Input:
//  fmts -- formats for int, flt, string, byte, bytes, and func
//          use fmts == nil to choose default ones
func (o Population) Output(fmts []string) (buf *bytes.Buffer) {

	// check
	if len(o) < 1 {
		return
	}
	if len(o[0].Chromo) < 1 {
		return
	}

	// compute sizes and generate formats list
	nfields := o[0].Chromo[0].Nfields()
	ngenes := len(o[0].Chromo)
	sizes := make([]int, 6)
	if fmts == nil {
		fmts = make([]string, 6)
		for _, ind := range o {
			sz := ind.GetStringSizes()
			for i := 0; i < 6; i++ {
				sizes[i] = imax(sizes[i], sz[i])
				if nfields == 1 {
					if sz[i] > 0 {
						if sizes[i]*ngenes < 5 { // 5 ==> len("Genes")
							sizes[i] = 3
							if ngenes == 1 {
								sizes[i] = 5
							}
						}
					}
				}
			}
		}
		for i, str := range []string{"d", "g", "s", "x", "s", "s"} {
			fmts[i] = io.Sf("%%%d%s", sizes[i]+1, str)
		}
	}

	// compute sizes of header items
	nOvl, nFit := 0, 0
	for _, ind := range o {
		nOvl = imax(nOvl, len(io.Sf("%g", ind.ObjValue)))
		nFit = imax(nFit, len(io.Sf("%g", ind.Fitness)))
	}
	nOvl = imax(nOvl, 6) // 6 ==> len("ObjVal")
	nFit = imax(nFit, 7) // 7 ==> len("Fitness")

	// print individuals
	fmtOvl := io.Sf("%%%d", nOvl+1)
	fmtFit := io.Sf("%%%d", nFit+1)
	line, sza, szb := "", 0, 0
	for i, ind := range o {
		stra := io.Sf(fmtOvl+"g", ind.ObjValue) + io.Sf(fmtFit+"g", ind.Fitness) + " "
		strb := ind.Output(fmts)
		line += stra + strb + "\n"
		if i == 0 {
			sza, szb = len(stra), len(strb)
		}
	}

	// write to buffer
	fmtGen := io.Sf(" %%%ds\n", szb)
	n := sza + szb
	buf = new(bytes.Buffer)
	io.Ff(buf, printThickLine(n))
	io.Ff(buf, fmtOvl+"s", "ObjVal")
	io.Ff(buf, fmtFit+"s", "Fitness")
	io.Ff(buf, fmtGen, "Genes")
	io.Ff(buf, printThinLine(n))
	io.Ff(buf, line)
	io.Ff(buf, printThickLine(n))
	return
}

// OutFloatBases print bases of float genes
func (o Population) OutFloatBases(numFmt string) (l string) {
	for _, ind := range o {
		for _, g := range ind.Chromo {
			l += io.Sf(numFmt, g.Fbases)
		}
		l += "\n"
	}
	return
}
<|endoftext|>


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

package xormadapter

import (
	"runtime"

	"github.com/casbin/casbin/model"
	"github.com/casbin/casbin/persist"
	"github.com/go-xorm/xorm"
	"github.com/lib/pq"
)

type Line struct {
	PType string `xorm:"varchar(100)"`
	V0    string `xorm:"varchar(100)"`
	V1    string `xorm:"varchar(100)"`
	V2    string `xorm:"varchar(100)"`
	V3    string `xorm:"varchar(100)"`
	V4    string `xorm:"varchar(100)"`
	V5    string `xorm:"varchar(100)"`
}

// Adapter represents the MySQL adapter for policy storage.
type Adapter struct {
	driverName     string
	dataSourceName string
	engine         *xorm.Engine
}

// finalizer is the destructor for Adapter.
func finalizer(a *Adapter) {
	a.engine.Close()
}

// NewAdapter is the constructor for Adapter.
func NewAdapter(driverName string, dataSourceName string) *Adapter {
	a := &Adapter{}
	a.driverName = driverName
	a.dataSourceName = dataSourceName

	// Open the DB, create it if not existed.
	a.open()

	// Call the destructor when the object is released.
	runtime.SetFinalizer(a, finalizer)

	return a
}

func (a *Adapter) createDatabase() error {
	var err error
	var engine *xorm.Engine
	if a.driverName == "postgres" {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=postgres")
	} else {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
	}
	if err != nil {
		return err
	}
	defer engine.Close()

	if a.driverName == "postgres" {
		if _, err = engine.Exec("CREATE DATABASE casbin"); err != nil {
			// 42P04 is	duplicate_database
			if err.(*pq.Error).Code == "42P04" {
				return nil
			}
		}
	} else {
		_, err = engine.Exec("CREATE DATABASE IF NOT EXISTS casbin")
	}
	return err
}

func (a *Adapter) open() {
	var err error
	if err = a.createDatabase(); err != nil {
		panic(err)
	}

	var engine *xorm.Engine
	if a.driverName == "postgres" {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=casbin")
	} else {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+"casbin")
	}
	if err != nil {
		panic(err)
	}

	a.engine = engine

	a.createTable()
}

func (a *Adapter) close() {
	a.engine.Close()
	a.engine = nil
}

func (a *Adapter) createTable() {
	err := a.engine.Sync2(new(Line))
	if err != nil {
		panic(err)
	}
}

func (a *Adapter) dropTable() {
	err := a.engine.DropTables(new(Line))
	if err != nil {
		panic(err)
	}
}

func loadPolicyLine(line Line, model model.Model) {
	lineText := line.PType
	if line.V0 != "" {
		lineText += ", " + line.V0
	}
	if line.V1 != "" {
		lineText += ", " + line.V1
	}
	if line.V2 != "" {
		lineText += ", " + line.V2
	}
	if line.V3 != "" {
		lineText += ", " + line.V3
	}
	if line.V4 != "" {
		lineText += ", " + line.V4
	}
	if line.V5 != "" {
		lineText += ", " + line.V5
	}

	persist.LoadPolicyLine(lineText, model)
}

// LoadPolicy loads policy from database.
func (a *Adapter) LoadPolicy(model model.Model) error {
	var lines []Line
	err := a.engine.Table("line").Find(&lines)
	if err != nil {
		return err
	}

	for _, line := range lines {
		loadPolicyLine(line, model)
	}

	return nil
}

func savePolicyLine(ptype string, rule []string) Line {
	line := Line{}

	line.PType = ptype
	if len(rule) > 0 {
		line.V0 = rule[0]
	}
	if len(rule) > 1 {
		line.V1 = rule[1]
	}
	if len(rule) > 2 {
		line.V2 = rule[2]
	}
	if len(rule) > 3 {
		line.V3 = rule[3]
	}
	if len(rule) > 4 {
		line.V4 = rule[4]
	}
	if len(rule) > 5 {
		line.V5 = rule[5]
	}

	return line
}

// SavePolicy saves policy to database.
func (a *Adapter) SavePolicy(model model.Model) error {
	a.dropTable()
	a.createTable()

	var lines []Line

	for ptype, ast := range model["p"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	for ptype, ast := range model["g"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	_, err := a.engine.Insert(&lines)
	return err
}

func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Insert(line)
	return err
}

func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Delete(line)
	return err
}

func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
	line := Line{}

	line.PType = ptype
	if fieldIndex <= 0 && 0 < fieldIndex + len(fieldValues) {
		line.V0 = fieldValues[0 - fieldIndex]
	}
	if fieldIndex <= 1 && 1 < fieldIndex + len(fieldValues) {
		line.V1 = fieldValues[1 - fieldIndex]
	}
	if fieldIndex <= 2 && 2 < fieldIndex + len(fieldValues) {
		line.V2 = fieldValues[2 - fieldIndex]
	}
	if fieldIndex <= 3 && 3 < fieldIndex + len(fieldValues) {
		line.V3 = fieldValues[3 - fieldIndex]
	}
	if fieldIndex <= 4 && 4 < fieldIndex + len(fieldValues) {
		line.V4 = fieldValues[4 - fieldIndex]
	}
	if fieldIndex <= 5 && 5 < fieldIndex + len(fieldValues) {
		line.V5 = fieldValues[5 - fieldIndex]
	}

	_, err := a.engine.Delete(line)
	return err
}
<commit_msg>Add comments to auto-save functions.<commit_after>// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package xormadapter

import (
	"runtime"

	"github.com/casbin/casbin/model"
	"github.com/casbin/casbin/persist"
	"github.com/go-xorm/xorm"
	"github.com/lib/pq"
)

type Line struct {
	PType string `xorm:"varchar(100)"`
	V0    string `xorm:"varchar(100)"`
	V1    string `xorm:"varchar(100)"`
	V2    string `xorm:"varchar(100)"`
	V3    string `xorm:"varchar(100)"`
	V4    string `xorm:"varchar(100)"`
	V5    string `xorm:"varchar(100)"`
}

// Adapter represents the MySQL adapter for policy storage.
type Adapter struct {
	driverName     string
	dataSourceName string
	engine         *xorm.Engine
}

// finalizer is the destructor for Adapter.
func finalizer(a *Adapter) {
	a.engine.Close()
}

// NewAdapter is the constructor for Adapter.
func NewAdapter(driverName string, dataSourceName string) *Adapter {
	a := &Adapter{}
	a.driverName = driverName
	a.dataSourceName = dataSourceName

	// Open the DB, create it if not existed.
	a.open()

	// Call the destructor when the object is released.
	runtime.SetFinalizer(a, finalizer)

	return a
}

func (a *Adapter) createDatabase() error {
	var err error
	var engine *xorm.Engine
	if a.driverName == "postgres" {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=postgres")
	} else {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName)
	}
	if err != nil {
		return err
	}
	defer engine.Close()

	if a.driverName == "postgres" {
		if _, err = engine.Exec("CREATE DATABASE casbin"); err != nil {
			// 42P04 is	duplicate_database
			if err.(*pq.Error).Code == "42P04" {
				return nil
			}
		}
	} else {
		_, err = engine.Exec("CREATE DATABASE IF NOT EXISTS casbin")
	}
	return err
}

func (a *Adapter) open() {
	var err error
	if err = a.createDatabase(); err != nil {
		panic(err)
	}

	var engine *xorm.Engine
	if a.driverName == "postgres" {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+" dbname=casbin")
	} else {
		engine, err = xorm.NewEngine(a.driverName, a.dataSourceName+"casbin")
	}
	if err != nil {
		panic(err)
	}

	a.engine = engine

	a.createTable()
}

func (a *Adapter) close() {
	a.engine.Close()
	a.engine = nil
}

func (a *Adapter) createTable() {
	err := a.engine.Sync2(new(Line))
	if err != nil {
		panic(err)
	}
}

func (a *Adapter) dropTable() {
	err := a.engine.DropTables(new(Line))
	if err != nil {
		panic(err)
	}
}

func loadPolicyLine(line Line, model model.Model) {
	lineText := line.PType
	if line.V0 != "" {
		lineText += ", " + line.V0
	}
	if line.V1 != "" {
		lineText += ", " + line.V1
	}
	if line.V2 != "" {
		lineText += ", " + line.V2
	}
	if line.V3 != "" {
		lineText += ", " + line.V3
	}
	if line.V4 != "" {
		lineText += ", " + line.V4
	}
	if line.V5 != "" {
		lineText += ", " + line.V5
	}

	persist.LoadPolicyLine(lineText, model)
}

// LoadPolicy loads policy from database.
func (a *Adapter) LoadPolicy(model model.Model) error {
	var lines []Line
	err := a.engine.Table("line").Find(&lines)
	if err != nil {
		return err
	}

	for _, line := range lines {
		loadPolicyLine(line, model)
	}

	return nil
}

func savePolicyLine(ptype string, rule []string) Line {
	line := Line{}

	line.PType = ptype
	if len(rule) > 0 {
		line.V0 = rule[0]
	}
	if len(rule) > 1 {
		line.V1 = rule[1]
	}
	if len(rule) > 2 {
		line.V2 = rule[2]
	}
	if len(rule) > 3 {
		line.V3 = rule[3]
	}
	if len(rule) > 4 {
		line.V4 = rule[4]
	}
	if len(rule) > 5 {
		line.V5 = rule[5]
	}

	return line
}

// SavePolicy saves policy to database.
func (a *Adapter) SavePolicy(model model.Model) error {
	a.dropTable()
	a.createTable()

	var lines []Line

	for ptype, ast := range model["p"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	for ptype, ast := range model["g"] {
		for _, rule := range ast.Policy {
			line := savePolicyLine(ptype, rule)
			lines = append(lines, line)
		}
	}

	_, err := a.engine.Insert(&lines)
	return err
}

// AddPolicy adds a policy rule to the storage.
func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Insert(line)
	return err
}

// RemovePolicy removes a policy rule from the storage.
func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
	line := savePolicyLine(ptype, rule)
	_, err := a.engine.Delete(line)
	return err
}

// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
	line := Line{}

	line.PType = ptype
	if fieldIndex <= 0 && 0 < fieldIndex + len(fieldValues) {
		line.V0 = fieldValues[0 - fieldIndex]
	}
	if fieldIndex <= 1 && 1 < fieldIndex + len(fieldValues) {
		line.V1 = fieldValues[1 - fieldIndex]
	}
	if fieldIndex <= 2 && 2 < fieldIndex + len(fieldValues) {
		line.V2 = fieldValues[2 - fieldIndex]
	}
	if fieldIndex <= 3 && 3 < fieldIndex + len(fieldValues) {
		line.V3 = fieldValues[3 - fieldIndex]
	}
	if fieldIndex <= 4 && 4 < fieldIndex + len(fieldValues) {
		line.V4 = fieldValues[4 - fieldIndex]
	}
	if fieldIndex <= 5 && 5 < fieldIndex + len(fieldValues) {
		line.V5 = fieldValues[5 - fieldIndex]
	}

	_, err := a.engine.Delete(line)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2015 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 validate

import (
	"fmt"
	"io"
	"reflect"
	"strings"

	"github.com/coreos/ignition/config/validate/report"
)

type validator interface {
	Validate() report.Report
}

// AstNode abstracts the differences between yaml and json nodes, providing a
// common interface
type AstNode interface {
	// ValueLineCol returns the line, column, and highlight string of the value of
	// this node in the source.
	ValueLineCol(source io.ReadSeeker) (int, int, string)

	// KeyLineCol returns the line, column, and highlight string of the key for the
	// value of this node in the source.
	KeyLineCol(source io.ReadSeeker) (int, int, string)

	// LiteralValue returns the value of this node.
	LiteralValue() interface{}

	// SliceChild returns the child node at the index specified. If this node is not
	// a slice node, an empty AstNode and false is returned.
	SliceChild(index int) (AstNode, bool)

	// KeyValueMap returns a map of keys and values. If this node is not a mapping
	// node, nil and false are returned.
	KeyValueMap() (map[string]AstNode, bool)

	// Tag returns the struct tag used in the config structure used to unmarshal.
	Tag() string
}

// Validate walks down a struct tree calling Validate on every node that implements it, building
// A report of all the errors, warnings, info, and deprecations it encounters. If checkUnusedKeys
// is true, Validate will generate warnings for unused keys in the ast, otherwise it will not.
func Validate(vObj reflect.Value, ast AstNode, source io.ReadSeeker, checkUnusedKeys bool) (r report.Report) {
	if !vObj.IsValid() {
		return
	}

	line, col, highlight := 0, 0, ""
	if ast != nil {
		line, col, highlight = ast.ValueLineCol(source)
	}

	// See if we A) can call Validate on vObj, and B) should call Validate. Validate should NOT be called
	// when vObj is nil, as it will panic or when vObj is a pointer to a value with Validate implemented with a
	// value receiver. This is to prevent Validate being called twice, as otherwise it would be called on the
	// pointer version (due to go's automatic deferencing) and once when the pointer is deferenced below. The only
	// time Validate should be called on a pointer is when the function is implemented with a pointer reciever.
	if obj, ok := vObj.Interface().(validator); ok &&
		((vObj.Kind() != reflect.Ptr) ||
			(!vObj.IsNil() && !vObj.Elem().Type().Implements(reflect.TypeOf((*validator)(nil)).Elem()))) {
		sub_r := obj.Validate()
		sub_r.AddPosition(line, col, highlight)
		r.Merge(sub_r)

		// Dont recurse on invalid inner nodes, it mostly leads to bogus messages
		if sub_r.IsFatal() {
			return
		}
	}

	switch vObj.Kind() {
	case reflect.Ptr:
		sub_report := Validate(vObj.Elem(), ast, source, checkUnusedKeys)
		sub_report.AddPosition(line, col, "")
		r.Merge(sub_report)
	case reflect.Struct:
		sub_report := validateStruct(vObj, ast, source, checkUnusedKeys)
		sub_report.AddPosition(line, col, "")
		r.Merge(sub_report)
	case reflect.Slice:
		for i := 0; i < vObj.Len(); i++ {
			sub_node := ast
			if ast != nil {
				if n, ok := ast.SliceChild(i); ok {
					sub_node = n
				}
			}
			sub_report := Validate(vObj.Index(i), sub_node, source, checkUnusedKeys)
			sub_report.AddPosition(line, col, "")
			r.Merge(sub_report)
		}
	}
	return
}

func ValidateWithoutSource(cfg reflect.Value) (report report.Report) {
	return Validate(cfg, nil, nil, false)
}

type field struct {
	Type  reflect.StructField
	Value reflect.Value
}

// getFields returns a field of all the fields in the struct, including the fields of
// embedded structs and structs inside interface{}'s
func getFields(vObj reflect.Value) []field {
	if vObj.Kind() != reflect.Struct {
		return nil
	}
	ret := []field{}
	for i := 0; i < vObj.Type().NumField(); i++ {
		if vObj.Type().Field(i).Anonymous {
			// in the case of an embedded type that is an alias to interface, extract the
			// real type contained by the interface
			realObj := reflect.ValueOf(vObj.Field(i).Interface())
			ret = append(ret, getFields(realObj)...)
		} else {
			ret = append(ret, field{Type: vObj.Type().Field(i), Value: vObj.Field(i)})
		}
	}
	return ret
}

func validateStruct(vObj reflect.Value, ast AstNode, source io.ReadSeeker, checkUnusedKeys bool) report.Report {
	r := report.Report{}

	// isFromObject will be true if this struct was unmarshalled from a JSON object.
	keys, isFromObject := map[string]AstNode{}, false
	if ast != nil {
		keys, isFromObject = ast.KeyValueMap()
	}

	// Maintain a set of key's that have been used.
	usedKeys := map[string]struct{}{}

	// Maintain a list of all the tags in the struct for fuzzy matching later.
	tags := []string{}

	for _, f := range getFields(vObj) {
		// Default to nil AstNode if the field's corrosponding node cannot be found.
		var sub_node AstNode
		// Default to passing a nil source if the field's corrosponding node cannot be found.
		// This ensures the line numbers reported from all sub-structs are 0 and will be changed by AddPosition
		var src io.ReadSeeker

		// Try to determine the json.Node that corrosponds with the struct field
		if isFromObject {
			tag := strings.SplitN(f.Type.Tag.Get(ast.Tag()), ",", 2)[0]
			// Save the tag so we have a list of all the tags in the struct
			tags = append(tags, tag)
			// mark that this key was used
			usedKeys[tag] = struct{}{}

			if sub, ok := keys[tag]; ok {
				// Found it
				sub_node = sub
				src = source
			}
		}

		// Default to deepest node if the node's type isn't an object,
		// such as when a json string actually unmarshal to structs (like with version)
		line, col := 0, 0
		if ast != nil {
			line, col, _ = ast.ValueLineCol(src)
		}

		// If there's a Validate<Name> func for the given field, call it
		funct := vObj.MethodByName("Validate" + f.Type.Name)
		if funct.IsValid() {
			res := funct.Call(nil)
			sub_report := res[0].Interface().(report.Report)
			sub_report.AddPosition(line, col, "")
			r.Merge(sub_report)
		}

		sub_report := Validate(f.Value, sub_node, src, checkUnusedKeys)
		sub_report.AddPosition(line, col, "")
		r.Merge(sub_report)
	}
	if !isFromObject || !checkUnusedKeys {
		// If this struct was not unmarshalled from a JSON object, there cannot be unused keys.
		return r
	}

	for k, v := range keys {
		if _, hasKey := usedKeys[k]; hasKey {
			continue
		}
		line, col, highlight := v.KeyLineCol(source)
		typo := similar(k, tags)

		r.Add(report.Entry{
			Kind:      report.EntryWarning,
			Message:   fmt.Sprintf("Config has unrecognized key: %s", k),
			Line:      line,
			Column:    col,
			Highlight: highlight,
		})

		if typo != "" {
			r.Add(report.Entry{
				Kind:      report.EntryInfo,
				Message:   fmt.Sprintf("Did you mean %s instead of %s", typo, k),
				Line:      line,
				Column:    col,
				Highlight: highlight,
			})
		}
	}

	return r
}

// similar returns a string in candidates that is similar to str. Currently it just does case
// insensitive comparison, but it should be updated to use levenstein distances to catch typos
func similar(str string, candidates []string) string {
	for _, candidate := range candidates {
		if strings.EqualFold(str, candidate) {
			return candidate
		}
	}
	return ""
}
<commit_msg>config/validate: report better line/col info<commit_after>// Copyright 2015 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 validate

import (
	"fmt"
	"io"
	"reflect"
	"strings"

	"github.com/coreos/ignition/config/validate/report"
)

type validator interface {
	Validate() report.Report
}

// AstNode abstracts the differences between yaml and json nodes, providing a
// common interface
type AstNode interface {
	// ValueLineCol returns the line, column, and highlight string of the value of
	// this node in the source.
	ValueLineCol(source io.ReadSeeker) (int, int, string)

	// KeyLineCol returns the line, column, and highlight string of the key for the
	// value of this node in the source.
	KeyLineCol(source io.ReadSeeker) (int, int, string)

	// LiteralValue returns the value of this node.
	LiteralValue() interface{}

	// SliceChild returns the child node at the index specified. If this node is not
	// a slice node, an empty AstNode and false is returned.
	SliceChild(index int) (AstNode, bool)

	// KeyValueMap returns a map of keys and values. If this node is not a mapping
	// node, nil and false are returned.
	KeyValueMap() (map[string]AstNode, bool)

	// Tag returns the struct tag used in the config structure used to unmarshal.
	Tag() string
}

// Validate walks down a struct tree calling Validate on every node that implements it, building
// A report of all the errors, warnings, info, and deprecations it encounters. If checkUnusedKeys
// is true, Validate will generate warnings for unused keys in the ast, otherwise it will not.
func Validate(vObj reflect.Value, ast AstNode, source io.ReadSeeker, checkUnusedKeys bool) (r report.Report) {
	if !vObj.IsValid() {
		return
	}

	line, col, highlight := 0, 0, ""
	if ast != nil {
		line, col, highlight = ast.ValueLineCol(source)
	}

	// See if we A) can call Validate on vObj, and B) should call Validate. Validate should NOT be called
	// when vObj is nil, as it will panic or when vObj is a pointer to a value with Validate implemented with a
	// value receiver. This is to prevent Validate being called twice, as otherwise it would be called on the
	// pointer version (due to go's automatic deferencing) and once when the pointer is deferenced below. The only
	// time Validate should be called on a pointer is when the function is implemented with a pointer reciever.
	if obj, ok := vObj.Interface().(validator); ok &&
		((vObj.Kind() != reflect.Ptr) ||
			(!vObj.IsNil() && !vObj.Elem().Type().Implements(reflect.TypeOf((*validator)(nil)).Elem()))) {
		sub_r := obj.Validate()
		sub_r.AddPosition(line, col, highlight)
		r.Merge(sub_r)

		// Dont recurse on invalid inner nodes, it mostly leads to bogus messages
		if sub_r.IsFatal() {
			return
		}
	}

	switch vObj.Kind() {
	case reflect.Ptr:
		sub_report := Validate(vObj.Elem(), ast, source, checkUnusedKeys)
		sub_report.AddPosition(line, col, "")
		r.Merge(sub_report)
	case reflect.Struct:
		sub_report := validateStruct(vObj, ast, source, checkUnusedKeys)
		sub_report.AddPosition(line, col, "")
		r.Merge(sub_report)
	case reflect.Slice:
		for i := 0; i < vObj.Len(); i++ {
			sub_node := ast
			if ast != nil {
				if n, ok := ast.SliceChild(i); ok {
					sub_node = n
				}
			}
			sub_report := Validate(vObj.Index(i), sub_node, source, checkUnusedKeys)
			sub_report.AddPosition(line, col, "")
			r.Merge(sub_report)
		}
	}
	return
}

func ValidateWithoutSource(cfg reflect.Value) (report report.Report) {
	return Validate(cfg, nil, nil, false)
}

type field struct {
	Type  reflect.StructField
	Value reflect.Value
}

// getFields returns a field of all the fields in the struct, including the fields of
// embedded structs and structs inside interface{}'s
func getFields(vObj reflect.Value) []field {
	if vObj.Kind() != reflect.Struct {
		return nil
	}
	ret := []field{}
	for i := 0; i < vObj.Type().NumField(); i++ {
		if vObj.Type().Field(i).Anonymous {
			// in the case of an embedded type that is an alias to interface, extract the
			// real type contained by the interface
			realObj := reflect.ValueOf(vObj.Field(i).Interface())
			ret = append(ret, getFields(realObj)...)
		} else {
			ret = append(ret, field{Type: vObj.Type().Field(i), Value: vObj.Field(i)})
		}
	}
	return ret
}

func validateStruct(vObj reflect.Value, ast AstNode, source io.ReadSeeker, checkUnusedKeys bool) report.Report {
	r := report.Report{}

	// isFromObject will be true if this struct was unmarshalled from a JSON object.
	keys, isFromObject := map[string]AstNode{}, false
	if ast != nil {
		keys, isFromObject = ast.KeyValueMap()
	}

	// Maintain a set of key's that have been used.
	usedKeys := map[string]struct{}{}

	// Maintain a list of all the tags in the struct for fuzzy matching later.
	tags := []string{}

	for _, f := range getFields(vObj) {
		// Default to nil AstNode if the field's corrosponding node cannot be found.
		var sub_node AstNode
		// Default to passing a nil source if the field's corrosponding node cannot be found.
		// This ensures the line numbers reported from all sub-structs are 0 and will be changed by AddPosition
		var src io.ReadSeeker

		// Try to determine the json.Node that corrosponds with the struct field
		if isFromObject {
			tag := strings.SplitN(f.Type.Tag.Get(ast.Tag()), ",", 2)[0]
			// Save the tag so we have a list of all the tags in the struct
			tags = append(tags, tag)
			// mark that this key was used
			usedKeys[tag] = struct{}{}

			if sub, ok := keys[tag]; ok {
				// Found it
				sub_node = sub
				src = source
			}
		}

		// Default to deepest node if the node's type isn't an object,
		// such as when a json string actually unmarshal to structs (like with version)
		line, col := 0, 0
		if ast != nil {
			line, col, _ = ast.ValueLineCol(src)
		}

		// If there's a Validate<Name> func for the given field, call it
		funct := vObj.MethodByName("Validate" + f.Type.Name)
		if funct.IsValid() {
			if sub_node != nil {
				// if sub_node is non-nil, we can get better line/col info
				line, col, _ = sub_node.ValueLineCol(src)
			}
			res := funct.Call(nil)
			sub_report := res[0].Interface().(report.Report)
			sub_report.AddPosition(line, col, "")
			r.Merge(sub_report)
		}

		sub_report := Validate(f.Value, sub_node, src, checkUnusedKeys)
		sub_report.AddPosition(line, col, "")
		r.Merge(sub_report)
	}
	if !isFromObject || !checkUnusedKeys {
		// If this struct was not unmarshalled from a JSON object, there cannot be unused keys.
		return r
	}

	for k, v := range keys {
		if _, hasKey := usedKeys[k]; hasKey {
			continue
		}
		line, col, highlight := v.KeyLineCol(source)
		typo := similar(k, tags)

		r.Add(report.Entry{
			Kind:      report.EntryWarning,
			Message:   fmt.Sprintf("Config has unrecognized key: %s", k),
			Line:      line,
			Column:    col,
			Highlight: highlight,
		})

		if typo != "" {
			r.Add(report.Entry{
				Kind:      report.EntryInfo,
				Message:   fmt.Sprintf("Did you mean %s instead of %s", typo, k),
				Line:      line,
				Column:    col,
				Highlight: highlight,
			})
		}
	}

	return r
}

// similar returns a string in candidates that is similar to str. Currently it just does case
// insensitive comparison, but it should be updated to use levenstein distances to catch typos
func similar(str string, candidates []string) string {
	for _, candidate := range candidates {
		if strings.EqualFold(str, candidate) {
			return candidate
		}
	}
	return ""
}
<|endoftext|>


<commit_before><commit_msg>fix parameter naming<commit_after><|endoftext|>


<commit_before>package peppy

import (
	"database/sql"
	"fmt"
	"strings"
	"time"

	"git.zxq.co/ripple/rippleapi/common"
	"git.zxq.co/x/getrank"
	"github.com/gin-gonic/gin"
	"gopkg.in/thehowl/go-osuapi.v1"
)

// GetUserRecent retrieves an user's recent scores.
func GetUserRecent(c *gin.Context, db *sql.DB) {
	getUserX(c, db, "ORDER BY scores.time DESC", common.InString(1, c.Query("limit"), 50, 10))
}

// GetUserBest retrieves an user's best scores.
func GetUserBest(c *gin.Context, db *sql.DB) {
	var sb string
	if genmodei(c.Query("m")) == 0 {
		sb = "scores.pp"
	} else {
		sb = "scores.score"
	}
	getUserX(c, db, "ORDER BY "+sb+" DESC", common.InString(1, c.Query("limit"), 100, 10))
}

func getUserX(c *gin.Context, db *sql.DB, orderBy string, limit int) {
	whereClause, p := genUser(c, db)
	query := fmt.Sprintf(
		`SELECT
			beatmaps.beatmap_id, scores.score, scores.max_combo,
			scores.300_count, scores.100_count, scores.50_count,
			scores.gekis_count, scores.katus_count, scores.misses_count,
			scores.full_combo, scores.mods, users.id, scores.time,
			scores.pp, scores.accuracy
		FROM scores
		LEFT JOIN beatmaps ON beatmaps.beatmap_md5 = scores.beatmap_md5
		LEFT JOIN users ON scores.username = users.username
		WHERE %s AND scores.play_mode = ?
		%s
		LIMIT %d`, whereClause, orderBy, limit,
	)
	scores := make([]osuapi.GUSScore, 0, limit)
	m := genmodei(c.Query("m"))
	rows, err := db.Query(query, p, m)
	if err != nil {
		c.JSON(200, defaultResponse)
		c.Error(err)
		return
	}
	for rows.Next() {
		var (
			curscore osuapi.GUSScore
			rawTime  string
			acc      float64
			fc       bool
			mods     int
		)
		err := rows.Scan(
			&curscore.BeatmapID, &curscore.Score.Score, &curscore.MaxCombo,
			&curscore.Count300, &curscore.Count100, &curscore.Count50,
			&curscore.CountGeki, &curscore.CountKatu, &curscore.CountMiss,
			&fc, &mods, &curscore.UserID, &rawTime,
			&curscore.PP, &acc,
		)
		if err != nil {
			c.JSON(200, defaultResponse)
			c.Error(err)
			return
		}
		curscore.FullCombo = osuapi.OsuBool(fc)
		curscore.Mods = osuapi.Mods(mods)
		t, err := time.Parse(common.OsuTimeFormat, rawTime)
		if err != nil {
			c.JSON(200, defaultResponse)
			c.Error(err)
			return
		}
		curscore.Date = osuapi.MySQLDate(t)
		curscore.Rank = strings.ToUpper(getrank.GetRank(
			osuapi.Mode(m),
			curscore.Mods,
			acc,
			curscore.Count300,
			curscore.Count100,
			curscore.Count50,
			curscore.CountMiss,
		))
		scores = append(scores, curscore)
	}
	c.JSON(200, scores)
}
<commit_msg>fixes<commit_after>package peppy

import (
	"database/sql"
	"fmt"
	"strings"
	"time"

	"git.zxq.co/ripple/rippleapi/common"
	"git.zxq.co/x/getrank"
	"github.com/gin-gonic/gin"
	"gopkg.in/thehowl/go-osuapi.v1"
)

// GetUserRecent retrieves an user's recent scores.
func GetUserRecent(c *gin.Context, db *sql.DB) {
	getUserX(c, db, "ORDER BY scores.time DESC", common.InString(1, c.Query("limit"), 50, 10))
}

// GetUserBest retrieves an user's best scores.
func GetUserBest(c *gin.Context, db *sql.DB) {
	var sb string
	if genmodei(c.Query("m")) == 0 {
		sb = "scores.pp"
	} else {
		sb = "scores.score"
	}
	getUserX(c, db, "AND completed = '3' ORDER BY "+sb+" DESC", common.InString(1, c.Query("limit"), 100, 10))
}

func getUserX(c *gin.Context, db *sql.DB, orderBy string, limit int) {
	whereClause, p := genUser(c, db)
	query := fmt.Sprintf(
		`SELECT
			beatmaps.beatmap_id, scores.score, scores.max_combo,
			scores.300_count, scores.100_count, scores.50_count,
			scores.gekis_count, scores.katus_count, scores.misses_count,
			scores.full_combo, scores.mods, users.id, scores.time,
			scores.pp, scores.accuracy
		FROM scores
		LEFT JOIN beatmaps ON beatmaps.beatmap_md5 = scores.beatmap_md5
		LEFT JOIN users ON scores.username = users.username
		WHERE %s AND scores.play_mode = ?
		%s
		LIMIT %d`, whereClause, orderBy, limit,
	)
	scores := make([]osuapi.GUSScore, 0, limit)
	m := genmodei(c.Query("m"))
	rows, err := db.Query(query, p, m)
	if err != nil {
		c.JSON(200, defaultResponse)
		c.Error(err)
		return
	}
	for rows.Next() {
		var (
			curscore osuapi.GUSScore
			rawTime  string
			acc      float64
			fc       bool
			mods     int
			bid      *int
		)
		err := rows.Scan(
			&bid, &curscore.Score.Score, &curscore.MaxCombo,
			&curscore.Count300, &curscore.Count100, &curscore.Count50,
			&curscore.CountGeki, &curscore.CountKatu, &curscore.CountMiss,
			&fc, &mods, &curscore.UserID, &rawTime,
			&curscore.PP, &acc,
		)
		if err != nil {
			c.JSON(200, defaultResponse)
			c.Error(err)
			return
		}
		if bid == nil {
			curscore.BeatmapID = 0
		} else {
			curscore.BeatmapID = *bid
		}
		curscore.FullCombo = osuapi.OsuBool(fc)
		curscore.Mods = osuapi.Mods(mods)
		t, err := time.Parse(common.OsuTimeFormat, rawTime)
		if err != nil {
			c.JSON(200, defaultResponse)
			c.Error(err)
			return
		}
		curscore.Date = osuapi.MySQLDate(t)
		curscore.Rank = strings.ToUpper(getrank.GetRank(
			osuapi.Mode(m),
			curscore.Mods,
			acc,
			curscore.Count300,
			curscore.Count100,
			curscore.Count50,
			curscore.CountMiss,
		))
		scores = append(scores, curscore)
	}
	c.JSON(200, scores)
}
<|endoftext|>


<commit_before>package misc

import (
	. "jvmgo/any"
	"jvmgo/jvm/rtda"
	rtc "jvmgo/jvm/rtda/class"
	"jvmgo/util"
	"sync/atomic"
	"unsafe"
)

func init() {
	_unsafe(compareAndSwapInt, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z")
	_unsafe(compareAndSwapLong, "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z")
	_unsafe(compareAndSwapObject, "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z")
	_unsafe(putObject, "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V")
	_unsafe(getObject, "getObject", "(Ljava/lang/Object;J)Ljava/lang/Object;")
	_unsafe(putObjectVolatile, "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V")
	_unsafe(getObjectVolatile, "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;")
	_unsafe(putOrderedObject, "putOrderedObject", "(Ljava/lang/Object;JLjava/lang/Object;)V")
	_unsafe(getOrderedObject, "getOrderedObject", "(Ljava/lang/Object;J)Ljava/lang/Object;")
}

// public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
// (Ljava/lang/Object;JII)Z
func compareAndSwapInt(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)
	expected := vars.GetInt(4)
	newVal := vars.GetInt(5)

	if anys, ok := fields.([]Any); ok {
		// object
		swapped := util.CasInt32(anys[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else if ints, ok := fields.([]int32); ok {
		// int[]
		swapped := atomic.CompareAndSwapInt32(&ints[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else {
		// todo
		panic("todo: compareAndSwapInt!")
	}
}

// public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
// (Ljava/lang/Object;JJJ)Z
func compareAndSwapLong(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)
	expected := vars.GetLong(4)
	newVal := vars.GetLong(6)

	if anys, ok := fields.([]Any); ok {
		// object
		swapped := util.CasInt64(anys[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else if ints, ok := fields.([]int64); ok {
		// long[]
		swapped := atomic.CompareAndSwapInt64(&ints[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else {
		// todo
		panic("todo: compareAndSwapLong!")
	}
}

// public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x)
// (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
func compareAndSwapObject(frame *rtda.Frame) {
	vars := frame.LocalVars()
	obj := vars.GetRef(1)
	fields := obj.Fields()
	offset := vars.GetLong(2)
	expected := vars.GetRef(4)
	newVal := vars.GetRef(5)

	// todo
	if anys, ok := fields.([]Any); ok {
		// object
		swapped := _casObj(obj, anys, offset, expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else if objs, ok := fields.([]*rtc.Obj); ok {
		// ref[]
		swapped := _casArr(objs, offset, expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else {
		// todo
		panic("todo: compareAndSwapObject!")
	}
}
func _casObj(obj *rtc.Obj, fields []Any, offset int64, expected, newVal *rtc.Obj) bool {
	// todo
	obj.LockState()
	defer obj.UnlockState()

	var current *rtc.Obj
	if f := fields[offset]; f != nil {
	 	current = f.(*rtc.Obj)
	} else {
		current = nil
	}

	if current == expected {
		fields[offset] = newVal
		return true
	} else {
		return false
	}
}
func _casArr(objs []*rtc.Obj, offset int64, expected, newVal *rtc.Obj) bool {
	// cast to []unsafe.Pointer
	ps := *((*[]unsafe.Pointer)(unsafe.Pointer(&objs)))

	addr := &ps[offset]
	old := unsafe.Pointer(expected)
	_new := unsafe.Pointer(newVal)
	return atomic.CompareAndSwapPointer(addr, old, _new)
}

// public native void putObject(Object o, long offset, Object x);
// (Ljava/lang/Object;JLjava/lang/Object;)V
func putObject(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)
	x := vars.GetRef(4)

	switch fields.(type) {
	case []Any: // object
		fields.([]Any)[offset] = x
	case []*rtc.Obj: // array of ref
		fields.([]*rtc.Obj)[offset] = x
	}
}

// public native Object getObject(Object o, long offset);
// (Ljava/lang/Object;J)Ljava/lang/Object;
func getObject(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)

	var x *rtc.Obj
	switch fields.(type) {
	case []Any: // object
		x = fields.([]Any)[offset].(*rtc.Obj)
	case []*rtc.Obj: // array of ref
		x = fields.([]*rtc.Obj)[offset]
	}

	stack := frame.OperandStack()
	stack.PushRef(x)
}

// public native void putObjectVolatile(Object o, long offset, Object x);
// (Ljava/lang/Object;JLjava/lang/Object;)V
func putObjectVolatile(frame *rtda.Frame) {
	putObject(frame) // todo
}

// public native Object getObjectVolatile(Object o, long offset);
//(Ljava/lang/Object;J)Ljava/lang/Object;
func getObjectVolatile(frame *rtda.Frame) {
	getObject(frame) // todo
}

// public native void putOrderedObject(Object o, long offset, Object x);
// (Ljava/lang/Object;JLjava/lang/Object;)V
func putOrderedObject(frame *rtda.Frame) {
	putObjectVolatile(frame) // todo
}

// public native Object getOrderedObject(Object o, long offset);
//(Ljava/lang/Object;J)Ljava/lang/Object;
func getOrderedObject(frame *rtda.Frame) {
	getObjectVolatile(frame) // todo
}
<commit_msg>code refactor<commit_after>package misc

import (
	. "jvmgo/any"
	"jvmgo/jvm/rtda"
	rtc "jvmgo/jvm/rtda/class"
	"jvmgo/util"
	"sync/atomic"
	"unsafe"
)

func init() {
	_unsafe(compareAndSwapInt, "compareAndSwapInt", "(Ljava/lang/Object;JII)Z")
	_unsafe(compareAndSwapLong, "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z")
	_unsafe(compareAndSwapObject, "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z")
	_unsafe(putObject, "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V")
	_unsafe(getObject, "getObject", "(Ljava/lang/Object;J)Ljava/lang/Object;")
	_unsafe(putObjectVolatile, "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V")
	_unsafe(getObjectVolatile, "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;")
	_unsafe(putOrderedObject, "putOrderedObject", "(Ljava/lang/Object;JLjava/lang/Object;)V")
	_unsafe(getOrderedObject, "getOrderedObject", "(Ljava/lang/Object;J)Ljava/lang/Object;")
}

// public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
// (Ljava/lang/Object;JII)Z
func compareAndSwapInt(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)
	expected := vars.GetInt(4)
	newVal := vars.GetInt(5)

	if anys, ok := fields.([]Any); ok {
		// object
		swapped := util.CasInt32(anys[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else if ints, ok := fields.([]int32); ok {
		// int[]
		swapped := atomic.CompareAndSwapInt32(&ints[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else {
		// todo
		panic("todo: compareAndSwapInt!")
	}
}

// public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
// (Ljava/lang/Object;JJJ)Z
func compareAndSwapLong(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)
	expected := vars.GetLong(4)
	newVal := vars.GetLong(6)

	if anys, ok := fields.([]Any); ok {
		// object
		swapped := util.CasInt64(anys[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else if ints, ok := fields.([]int64); ok {
		// long[]
		swapped := atomic.CompareAndSwapInt64(&ints[offset], expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else {
		// todo
		panic("todo: compareAndSwapLong!")
	}
}

// public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x)
// (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
func compareAndSwapObject(frame *rtda.Frame) {
	vars := frame.LocalVars()
	obj := vars.GetRef(1)
	fields := obj.Fields()
	offset := vars.GetLong(2)
	expected := vars.GetRef(4)
	newVal := vars.GetRef(5)

	// todo
	if anys, ok := fields.([]Any); ok {
		// object
		swapped := _casObj(obj, anys, offset, expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else if objs, ok := fields.([]*rtc.Obj); ok {
		// ref[]
		swapped := _casArr(objs, offset, expected, newVal)
		frame.OperandStack().PushBoolean(swapped)
	} else {
		// todo
		panic("todo: compareAndSwapObject!")
	}
}
func _casObj(obj *rtc.Obj, fields []Any, offset int64, expected, newVal *rtc.Obj) bool {
	// todo
	obj.LockState()
	defer obj.UnlockState()

	var current *rtc.Obj
	if f := fields[offset]; f != nil {
	 	current = f.(*rtc.Obj)
	} else {
		current = nil
	}

	if current == expected {
		fields[offset] = newVal
		return true
	} else {
		return false
	}
}
func _casArr(objs []*rtc.Obj, offset int64, expected, newVal *rtc.Obj) bool {
	// cast to []unsafe.Pointer
	ps := *((*[]unsafe.Pointer)(unsafe.Pointer(&objs)))

	addr := &ps[offset]
	old := unsafe.Pointer(expected)
	_new := unsafe.Pointer(newVal)
	return atomic.CompareAndSwapPointer(addr, old, _new)
}

// public native void putObject(Object o, long offset, Object x);
// (Ljava/lang/Object;JLjava/lang/Object;)V
func putObject(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)
	x := vars.GetRef(4)

	if anys, ok := fields.([]Any); ok {
		// object
		anys[offset] = x
	} else if objs, ok := fields.([]*rtc.Obj); ok {
		// ref[]
		objs[offset] = x
	} else {
		panic("putObject!")
	}
}

// public native Object getObject(Object o, long offset);
// (Ljava/lang/Object;J)Ljava/lang/Object;
func getObject(frame *rtda.Frame) {
	vars := frame.LocalVars()
	fields := vars.GetRef(1).Fields()
	offset := vars.GetLong(2)

	var x *rtc.Obj
	switch fields.(type) {
	case []Any: // object
		x = fields.([]Any)[offset].(*rtc.Obj)
	case []*rtc.Obj: // array of ref
		x = fields.([]*rtc.Obj)[offset]
	}

	stack := frame.OperandStack()
	stack.PushRef(x)
}

// public native void putObjectVolatile(Object o, long offset, Object x);
// (Ljava/lang/Object;JLjava/lang/Object;)V
func putObjectVolatile(frame *rtda.Frame) {
	putObject(frame) // todo
}

// public native Object getObjectVolatile(Object o, long offset);
//(Ljava/lang/Object;J)Ljava/lang/Object;
func getObjectVolatile(frame *rtda.Frame) {
	getObject(frame) // todo
}

// public native void putOrderedObject(Object o, long offset, Object x);
// (Ljava/lang/Object;JLjava/lang/Object;)V
func putOrderedObject(frame *rtda.Frame) {
	putObjectVolatile(frame) // todo
}

// public native Object getOrderedObject(Object o, long offset);
//(Ljava/lang/Object;J)Ljava/lang/Object;
func getOrderedObject(frame *rtda.Frame) {
	getObjectVolatile(frame) // todo
}
<|endoftext|>


<commit_before>package acme

import (
	"crypto/x509"
	"errors"
	"fmt"
	"math"
	"time"

	"github.com/tommie/acme-go/protocol"
)

var (
	ErrCanceled   = errors.New("operation canceled")
	ErrUnsolvable = errors.New("unsolvable challenge")
)

// A CertificateIssuer can authorize and issue certificates in one
// go. A currently running issuer can be canceled.
type CertificateIssuer struct {
	ia IssuingAccount

	cancel chan struct{}
}

func NewCertificateIssuer(ia IssuingAccount) *CertificateIssuer {
	return &CertificateIssuer{ia, make(chan struct{})}
}

// AuthorizeAndIssue issues a certificate based on a signing request
// after completing any necessary identity authorization
// challenges. The certificate will be tied to the issuing account on
// success. The solvers are used to solve challenges. Solvers are
// chosen to minimize the solver cost. Note that the function does not
// care about the cost unit, but it needs to be consistent across all
// solvers.
//
// If one solver instance is used for multiple types, and the server
// requests solving all types, they may be lumped together in the same
// call to Solve.
func (ci *CertificateIssuer) AuthorizeAndIssue(csr []byte, s Solver) (*Certificate, error) {
	as, err := ci.authorizeIdentities(csr)
	if err != nil {
		return nil, err
	}

	if len(as) > 0 {
		cs, err := bestChallenges(s, as)
		if err != nil {
			return nil, err
		}

		stop, err := ci.startSolver(s, cs)
		if err != nil {
			return nil, err
		}
		defer stop()

		if err := ci.waitAuthorizations(as); err != nil {
			return nil, err
		}
	}

	return ci.ia.IssueCertificate(csr)
}

// authorizeIdentities requests new challenges for the given X.509
// CSR. Only pending authorizations are returned. If any authorization
// is invalid, the call fails.
func (ci *CertificateIssuer) authorizeIdentities(csr []byte) ([]*Authorization, error) {
	pcsr, err := x509.ParseCertificateRequest(csr)
	if err != nil {
		return nil, err
	}

	// De-duplicate names.
	names := make(map[string]Identifier, 1+len(pcsr.DNSNames))
	for _, n := range append([]string{pcsr.Subject.CommonName}, pcsr.DNSNames...) {
		names[n] = DNSIdentifier(n)
	}

	var ret []*Authorization
	for n, id := range names {
		if ci.isCanceled() {
			return nil, ErrCanceled
		}

		// TODO: Check for existing valid (and pending) authorizations first?
		// Whether an existing authz is useful or not depends on how long it
		// will remain useful, and we don't have that information.
		a, err := ci.ia.AuthorizeIdentity(id)
		if err != nil {
			return nil, err
		}
		switch a.Status {
		case protocol.StatusPending:
			ret = append(ret, a)

		case protocol.StatusInvalid:
			return nil, fmt.Errorf("authorization invalid for %q", n)

		case protocol.StatusValid:
			// nothing

		default:
			return nil, fmt.Errorf("unknown authorization status for %q: %v", n, a.Status)
		}
	}

	return ret, nil
}

// bestChallenges picks challenges with lowest cost to solve.
func bestChallenges(s Solver, as []*Authorization) ([]protocol.Challenge, error) {
	var ret []protocol.Challenge
	for _, a := range as {
		cs, err := bestCombination(s, a)
		if err != nil {
			return nil, err
		}
		ret = append(ret, cs...)
	}

	// We have combined challenges. Make sure we can solve them together.
	_, err := s.Cost(ret)
	return ret, err
}

// bestCombination finds the challenge combination with a lowest
// cost. Returns ErrUnsolvable if no solvable combination exists.
func bestCombination(s Solver, a *Authorization) ([]protocol.Challenge, error) {
	var errs []error
	var ret []protocol.Challenge
	bestCost := math.Inf(1)

	for _, cis := range a.Combinations {
		var cs []protocol.Challenge
		for _, ci := range cis {
			cs = append(cs, a.Challenges[ci])
		}
		cost, err := s.Cost(cs)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		if cost < bestCost {
			// Replace the previous best combination.
			bestCost = cost
			ret = cs
		}
	}

	if math.IsInf(bestCost, 1) {
		if len(errs) > 0 {
			return nil, errs[len(errs)-1]
		}
		return nil, ErrUnsolvable
	}

	return ret, nil
}

// startSolver instantiates the solver and informs the ACME server.
func (ci *CertificateIssuer) startSolver(s Solver, cs []protocol.Challenge) (func() error, error) {
	resps, stop, err := s.Solve(cs)
	if err != nil {
		return nil, err
	}
	errStop := stop
	defer func() {
		errStop()
	}()

	if len(resps) != len(cs) {
		return nil, fmt.Errorf("solver was given %d challenges, but returned %d responses (the solver code is broken)", len(cs), len(resps))
	}

	// Tell the ACME server the challenge was accepted.
	for i, ch := range cs {
		if ci.isCanceled() {
			return nil, ErrCanceled
		}

		ch, err = ci.ia.ValidateChallenge(ch.GetURI(), resps[i])
		if err != nil {
			return nil, err
		}
		if ch.GetStatus() == protocol.StatusInvalid {
			return nil, fmt.Errorf("challenge validation failed: %+v", ch)
		}
	}

	errStop = func() error { return nil }
	return stop, nil
}

// waitAuthorizations waits for authorization requests to complete.
func (ci *CertificateIssuer) waitAuthorizations(as []*Authorization) error {
	// It doesn't matter in which order we do this since all of
	// them must complete. So we think of rem as a stack, for
	// simplicity.
	for len(as) != 0 {
		if ci.isCanceled() {
			return ErrCanceled
		}

		a, err := ci.ia.Authorization(as[len(as)-1].URI)
		if err != nil {
			return err
		}
		switch a.Status {
		case protocol.StatusValid:
			as = as[:len(as)-1]
			a.RetryAfter = 0

		case protocol.StatusInvalid:
			return fmt.Errorf("authorization validation failed: %+v", a)
		}

		select {
		case <-time.After(a.RetryAfter):
			break

		case <-ci.cancel:
			return ErrCanceled
		}
	}

	return nil
}

// Cancel stops any running invocation of AuthorizeAndIssue and causes
// new invocations to fail early. A canceled issuer should not be reused.
func (ci *CertificateIssuer) Cancel() {
	close(ci.cancel)
}

func (ci *CertificateIssuer) isCanceled() bool {
	select {
	case <-ci.cancel:
		return true

	default:
		return false
	}
}

// An IssuingAccount is an interface to something that can issue ACME
// certificates given a registered account. A ClientAccount fulfills
// this interface.
type IssuingAccount interface {
	AuthorizeIdentity(id Identifier) (*Authorization, error)
	Authorization(uri string) (*Authorization, error)
	ValidateChallenge(uri string, resp protocol.Response) (protocol.Challenge, error)
	IssueCertificate(csr []byte) (*Certificate, error)
}

// Solver is a way to produce responses to one or more
// challenges. Solver object functions must be concurrency-safe.
type Solver interface {
	// Cost describes the cost to solve the set of challenges. The
	// returned cost is a number in some (consistent) unit. It
	// should be fast to evaluate the cost. The function should
	// return ErrUnsolvable if any challenge cannot be solved.
	Cost([]protocol.Challenge) (float64, error)

	// Solve is a function that starts a solver for some
	// challenges. The returned stop function will be called to
	// stop the solver and release resources. The same solver can
	// be used to solve multiple challenges at once. Each returned
	// response must correspond to the challenge of the same
	// index and len(ch) == len(resps).
	//
	// If err != nil, the stop function must not be called.
	Solve([]protocol.Challenge) (resps []protocol.Response, stop func() error, err error)
}
<commit_msg>Decorate authorization errors with the Authorization objects.<commit_after>package acme

import (
	"crypto/x509"
	"errors"
	"fmt"
	"math"
	"strings"
	"time"

	"github.com/tommie/acme-go/protocol"
)

var (
	ErrCanceled   = errors.New("operation canceled")
	ErrUnsolvable = errors.New("unsolvable challenge")
)

// An AuthorizationError wraps another error and adds information about what
// authorizations were being attempted.
type AuthorizationError struct {
	Err            error
	Authorizations []*Authorization
}

func (e *AuthorizationError) Error() string {
	var auths []string
	for _, a := range e.Authorizations {
		auth := fmt.Sprintf("Authorization of %s, one of: ", a.Identifier)
		for _, comb := range a.Combinations {
			var chals []string
			for _, i := range comb {
				chals = append(chals, string(a.Challenges[i].GetType()))
			}
			auth += fmt.Sprintf("(%s)", strings.Join(chals, ", "))
		}
		auths = append(auths, auth)
	}
	return fmt.Sprintf("%s (authorizations %s)", e.Err, strings.Join(auths, ", "))
}

// A CertificateIssuer can authorize and issue certificates in one
// go. A currently running issuer can be canceled.
type CertificateIssuer struct {
	ia IssuingAccount

	cancel chan struct{}
}

func NewCertificateIssuer(ia IssuingAccount) *CertificateIssuer {
	return &CertificateIssuer{ia, make(chan struct{})}
}

// AuthorizeAndIssue issues a certificate based on a signing request
// after completing any necessary identity authorization
// challenges. The certificate will be tied to the issuing account on
// success. The solvers are used to solve challenges. Solvers are
// chosen to minimize the solver cost. Note that the function does not
// care about the cost unit, but it needs to be consistent across all
// solvers.
//
// If one solver instance is used for multiple types, and the server
// requests solving all types, they may be lumped together in the same
// call to Solve.
func (ci *CertificateIssuer) AuthorizeAndIssue(csr []byte, s Solver) (*Certificate, error) {
	as, err := ci.authorizeIdentities(csr)
	if err != nil {
		return nil, err
	}

	if len(as) > 0 {
		cs, err := bestChallenges(s, as)
		if err != nil {
			return nil, &AuthorizationError{err, as}
		}

		stop, err := ci.startSolver(s, cs)
		if err != nil {
			return nil, err
		}
		defer stop()

		if err := ci.waitAuthorizations(as); err != nil {
			return nil, err
		}
	}

	return ci.ia.IssueCertificate(csr)
}

// authorizeIdentities requests new challenges for the given X.509
// CSR. Only pending authorizations are returned. If any authorization
// is invalid, the call fails.
func (ci *CertificateIssuer) authorizeIdentities(csr []byte) ([]*Authorization, error) {
	pcsr, err := x509.ParseCertificateRequest(csr)
	if err != nil {
		return nil, err
	}

	// De-duplicate names.
	names := make(map[string]Identifier, 1+len(pcsr.DNSNames))
	for _, n := range append([]string{pcsr.Subject.CommonName}, pcsr.DNSNames...) {
		names[n] = DNSIdentifier(n)
	}

	var ret []*Authorization
	for n, id := range names {
		if ci.isCanceled() {
			return nil, ErrCanceled
		}

		// TODO: Check for existing valid (and pending) authorizations first?
		// Whether an existing authz is useful or not depends on how long it
		// will remain useful, and we don't have that information.
		a, err := ci.ia.AuthorizeIdentity(id)
		if err != nil {
			return nil, err
		}
		switch a.Status {
		case protocol.StatusPending:
			ret = append(ret, a)

		case protocol.StatusInvalid:
			return nil, fmt.Errorf("authorization invalid for %q", n)

		case protocol.StatusValid:
			// nothing

		default:
			return nil, fmt.Errorf("unknown authorization status for %q: %v", n, a.Status)
		}
	}

	return ret, nil
}

// bestChallenges picks challenges with lowest cost to solve.
func bestChallenges(s Solver, as []*Authorization) ([]protocol.Challenge, error) {
	var ret []protocol.Challenge
	for _, a := range as {
		cs, err := bestCombination(s, a)
		if err != nil {
			return nil, err
		}
		ret = append(ret, cs...)
	}

	// We have combined challenges. Make sure we can solve them together.
	_, err := s.Cost(ret)
	return ret, err
}

// bestCombination finds the challenge combination with a lowest
// cost. Returns ErrUnsolvable if no solvable combination exists.
func bestCombination(s Solver, a *Authorization) ([]protocol.Challenge, error) {
	var errs []error
	var ret []protocol.Challenge
	bestCost := math.Inf(1)

	for _, cis := range a.Combinations {
		var cs []protocol.Challenge
		for _, ci := range cis {
			cs = append(cs, a.Challenges[ci])
		}
		cost, err := s.Cost(cs)
		if err != nil {
			errs = append(errs, err)
			continue
		}

		if cost < bestCost {
			// Replace the previous best combination.
			bestCost = cost
			ret = cs
		}
	}

	if math.IsInf(bestCost, 1) {
		if len(errs) > 0 {
			return nil, errs[len(errs)-1]
		}
		return nil, ErrUnsolvable
	}

	return ret, nil
}

// startSolver instantiates the solver and informs the ACME server.
func (ci *CertificateIssuer) startSolver(s Solver, cs []protocol.Challenge) (func() error, error) {
	resps, stop, err := s.Solve(cs)
	if err != nil {
		return nil, err
	}
	errStop := stop
	defer func() {
		errStop()
	}()

	if len(resps) != len(cs) {
		return nil, fmt.Errorf("solver was given %d challenges, but returned %d responses (the solver code is broken)", len(cs), len(resps))
	}

	// Tell the ACME server the challenge was accepted.
	for i, ch := range cs {
		if ci.isCanceled() {
			return nil, ErrCanceled
		}

		ch, err = ci.ia.ValidateChallenge(ch.GetURI(), resps[i])
		if err != nil {
			return nil, err
		}
		if ch.GetStatus() == protocol.StatusInvalid {
			return nil, fmt.Errorf("challenge validation failed: %+v", ch)
		}
	}

	errStop = func() error { return nil }
	return stop, nil
}

// waitAuthorizations waits for authorization requests to complete.
func (ci *CertificateIssuer) waitAuthorizations(as []*Authorization) error {
	// It doesn't matter in which order we do this since all of
	// them must complete. So we think of rem as a stack, for
	// simplicity.
	for len(as) != 0 {
		if ci.isCanceled() {
			return ErrCanceled
		}

		a, err := ci.ia.Authorization(as[len(as)-1].URI)
		if err != nil {
			return err
		}
		switch a.Status {
		case protocol.StatusValid:
			as = as[:len(as)-1]
			a.RetryAfter = 0

		case protocol.StatusInvalid:
			return fmt.Errorf("authorization validation failed: %+v", a)
		}

		select {
		case <-time.After(a.RetryAfter):
			break

		case <-ci.cancel:
			return ErrCanceled
		}
	}

	return nil
}

// Cancel stops any running invocation of AuthorizeAndIssue and causes
// new invocations to fail early. A canceled issuer should not be reused.
func (ci *CertificateIssuer) Cancel() {
	close(ci.cancel)
}

func (ci *CertificateIssuer) isCanceled() bool {
	select {
	case <-ci.cancel:
		return true

	default:
		return false
	}
}

// An IssuingAccount is an interface to something that can issue ACME
// certificates given a registered account. A ClientAccount fulfills
// this interface.
type IssuingAccount interface {
	AuthorizeIdentity(id Identifier) (*Authorization, error)
	Authorization(uri string) (*Authorization, error)
	ValidateChallenge(uri string, resp protocol.Response) (protocol.Challenge, error)
	IssueCertificate(csr []byte) (*Certificate, error)
}

// Solver is a way to produce responses to one or more
// challenges. Solver object functions must be concurrency-safe.
type Solver interface {
	// Cost describes the cost to solve the set of challenges. The
	// returned cost is a number in some (consistent) unit. It
	// should be fast to evaluate the cost. The function should
	// return ErrUnsolvable if any challenge cannot be solved.
	Cost([]protocol.Challenge) (float64, error)

	// Solve is a function that starts a solver for some
	// challenges. The returned stop function will be called to
	// stop the solver and release resources. The same solver can
	// be used to solve multiple challenges at once. Each returned
	// response must correspond to the challenge of the same
	// index and len(ch) == len(resps).
	//
	// If err != nil, the stop function must not be called.
	Solve([]protocol.Challenge) (resps []protocol.Response, stop func() error, err error)
}
<|endoftext|>


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

package binarylog

import (
	"bufio"
	"encoding/binary"
	"io"
	"sync"
	"time"

	"github.com/golang/protobuf/proto"
	pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
)

var (
	// DefaultSink is the sink where the logs will be written to. It's exported
	// for the binarylog package to update.
	DefaultSink Sink = &noopSink{} // TODO(blog): change this default (file in /tmp).
)

// Sink writes log entry into the binary log sink.
//
// sink is a copy of the exported binarylog.Sink, to avoid circular dependency.
type Sink interface {
	// Write will be called to write the log entry into the sink.
	//
	// It should be thread-safe so it can be called in parallel.
	Write(*pb.GrpcLogEntry) error
	// Close will be called when the Sink is replaced by a new Sink.
	Close() error
}

type noopSink struct{}

func (ns *noopSink) Write(*pb.GrpcLogEntry) error { return nil }
func (ns *noopSink) Close() error                 { return nil }

// newWriterSink creates a binary log sink with the given writer.
//
// Write() marshals the proto message and writes it to the given writer. Each
// message is prefixed with a 4 byte big endian unsigned integer as the length.
//
// No buffer is done, Close() doesn't try to close the writer.
func newWriterSink(w io.Writer) Sink {
	return &writerSink{out: w}
}

type writerSink struct {
	out io.Writer
}

func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
	b, err := proto.Marshal(e)
	if err != nil {
		grpclogLogger.Infof("binary logging: failed to marshal proto message: %v", err)
	}
	hdr := make([]byte, 4)
	binary.BigEndian.PutUint32(hdr, uint32(len(b)))
	if _, err := ws.out.Write(hdr); err != nil {
		return err
	}
	if _, err := ws.out.Write(b); err != nil {
		return err
	}
	return nil
}

func (ws *writerSink) Close() error { return nil }

type bufferedSink struct {
	mu     sync.Mutex
	closer io.Closer
	out    Sink          // out is built on buf.
	buf    *bufio.Writer // buf is kept for flush.

	writeStartOnce sync.Once
	writeTicker    *time.Ticker
}

func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
	// Start the write loop when Write is called.
	fs.writeStartOnce.Do(fs.startFlushGoroutine)
	fs.mu.Lock()
	if err := fs.out.Write(e); err != nil {
		fs.mu.Unlock()
		return err
	}
	fs.mu.Unlock()
	return nil
}

const (
	bufFlushDuration = 60 * time.Second
)

func (fs *bufferedSink) startFlushGoroutine() {
	fs.writeTicker = time.NewTicker(bufFlushDuration)
	go func() {
		for range fs.writeTicker.C {
			fs.mu.Lock()
			if err := fs.buf.Flush(); err != nil {
				grpclogLogger.Warningf("failed to flush to Sink: %v", err)
			}
			fs.mu.Unlock()
		}
	}()
}

func (fs *bufferedSink) Close() error {
	if fs.writeTicker != nil {
		fs.writeTicker.Stop()
	}
	fs.mu.Lock()
	if err := fs.buf.Flush(); err != nil {
		grpclogLogger.Warningf("failed to flush to Sink: %v", err)
	}
	if err := fs.closer.Close(); err != nil {
		grpclogLogger.Warningf("failed to close the underlying WriterCloser: %v", err)
	}
	if err := fs.out.Close(); err != nil {
		grpclogLogger.Warningf("failed to close the Sink: %v", err)
	}
	fs.mu.Unlock()
	return nil
}

// NewBufferedSink creates a binary log sink with the given WriteCloser.
//
// Write() marshals the proto message and writes it to the given writer. Each
// message is prefixed with a 4 byte big endian unsigned integer as the length.
//
// Content is kept in a buffer, and is flushed every 60 seconds.
//
// Close closes the WriteCloser.
func NewBufferedSink(o io.WriteCloser) Sink {
	bufW := bufio.NewWriter(o)
	return &bufferedSink{
		closer: o,
		out:    newWriterSink(bufW),
		buf:    bufW,
	}
}
<commit_msg>binarylog: Don't leak the flusher goroutine when closing a Sink (#4583)<commit_after>/*
 *
 * Copyright 2018 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package binarylog

import (
	"bufio"
	"encoding/binary"
	"io"
	"sync"
	"time"

	"github.com/golang/protobuf/proto"
	pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
)

var (
	// DefaultSink is the sink where the logs will be written to. It's exported
	// for the binarylog package to update.
	DefaultSink Sink = &noopSink{} // TODO(blog): change this default (file in /tmp).
)

// Sink writes log entry into the binary log sink.
//
// sink is a copy of the exported binarylog.Sink, to avoid circular dependency.
type Sink interface {
	// Write will be called to write the log entry into the sink.
	//
	// It should be thread-safe so it can be called in parallel.
	Write(*pb.GrpcLogEntry) error
	// Close will be called when the Sink is replaced by a new Sink.
	Close() error
}

type noopSink struct{}

func (ns *noopSink) Write(*pb.GrpcLogEntry) error { return nil }
func (ns *noopSink) Close() error                 { return nil }

// newWriterSink creates a binary log sink with the given writer.
//
// Write() marshals the proto message and writes it to the given writer. Each
// message is prefixed with a 4 byte big endian unsigned integer as the length.
//
// No buffer is done, Close() doesn't try to close the writer.
func newWriterSink(w io.Writer) Sink {
	return &writerSink{out: w}
}

type writerSink struct {
	out io.Writer
}

func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
	b, err := proto.Marshal(e)
	if err != nil {
		grpclogLogger.Infof("binary logging: failed to marshal proto message: %v", err)
	}
	hdr := make([]byte, 4)
	binary.BigEndian.PutUint32(hdr, uint32(len(b)))
	if _, err := ws.out.Write(hdr); err != nil {
		return err
	}
	if _, err := ws.out.Write(b); err != nil {
		return err
	}
	return nil
}

func (ws *writerSink) Close() error { return nil }

type bufferedSink struct {
	mu     sync.Mutex
	closer io.Closer
	out    Sink          // out is built on buf.
	buf    *bufio.Writer // buf is kept for flush.

	writeStartOnce sync.Once
	writeTicker    *time.Ticker
	done           chan struct{}
}

func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
	// Start the write loop when Write is called.
	fs.writeStartOnce.Do(fs.startFlushGoroutine)
	fs.mu.Lock()
	if err := fs.out.Write(e); err != nil {
		fs.mu.Unlock()
		return err
	}
	fs.mu.Unlock()
	return nil
}

const (
	bufFlushDuration = 60 * time.Second
)

func (fs *bufferedSink) startFlushGoroutine() {
	fs.writeTicker = time.NewTicker(bufFlushDuration)
	go func() {
		for {
			select {
			case <-fs.done:
				return
			case <-fs.writeTicker.C:
			}
			fs.mu.Lock()
			if err := fs.buf.Flush(); err != nil {
				grpclogLogger.Warningf("failed to flush to Sink: %v", err)
			}
			fs.mu.Unlock()
		}
	}()
}

func (fs *bufferedSink) Close() error {
	if fs.writeTicker != nil {
		fs.writeTicker.Stop()
	}
	close(fs.done)
	fs.mu.Lock()
	if err := fs.buf.Flush(); err != nil {
		grpclogLogger.Warningf("failed to flush to Sink: %v", err)
	}
	if err := fs.closer.Close(); err != nil {
		grpclogLogger.Warningf("failed to close the underlying WriterCloser: %v", err)
	}
	if err := fs.out.Close(); err != nil {
		grpclogLogger.Warningf("failed to close the Sink: %v", err)
	}
	fs.mu.Unlock()
	return nil
}

// NewBufferedSink creates a binary log sink with the given WriteCloser.
//
// Write() marshals the proto message and writes it to the given writer. Each
// message is prefixed with a 4 byte big endian unsigned integer as the length.
//
// Content is kept in a buffer, and is flushed every 60 seconds.
//
// Close closes the WriteCloser.
func NewBufferedSink(o io.WriteCloser) Sink {
	bufW := bufio.NewWriter(o)
	return &bufferedSink{
		closer: o,
		out:    newWriterSink(bufW),
		buf:    bufW,
		done:   make(chan struct{}),
	}
}
<|endoftext|>


<commit_before>// Package user implements an abstraction for a basic user system. Users can be
// created, have properties set on them, disabled, deleted, and more. All data
// is persisted to a redis instance or cluster, and all methods are compeletely
// thread-safe. No data is sanitized by this package, and no authentication is
// done (although authentication mechanisims are provided)
package user

import (
	"encoding/hex"
	"fmt"
	"strings"
	"time"

	"github.com/mediocregopher/mediocre-api/common"
	"github.com/mediocregopher/radix.v2/redis"
	"github.com/mediocregopher/radix.v2/util"
	"golang.org/x/crypto/bcrypt"
)

// Errors which can be expected from various methods in this package
var (
	ErrUserExists = common.ExpectedErr{Code: 400, Err: "user exists"}
	ErrNotFound   = common.ExpectedErr{Code: 404, Err: "user not found"}
	ErrBadAuth    = common.ExpectedErr{Code: 400, Err: "could not authenticate user"}
	ErrDisabled   = common.ExpectedErr{Code: 400, Err: "user account is disabled"}
)

// Functions which return errors based on the related field names
var (
	ErrFieldUnknown = func(f string) error {
		return common.ExpectedErrf(400, "unknown field %q", f)
	}
	ErrFieldUneditable = func(f string) error {
		return common.ExpectedErrf(400, "field %q not editable", f)
	}
)

// HMSETXX key fieldWhichExists field value [field value...]
// Calls HMSET but only if fieldWhichExists is already set on the hash. Returns
// 1 if set was successful, 0 if it failed due to the xx condition
var hmsetxx = `
	if not redis.call('HGET', KEYS[1], ARGV[1]) then
		return 0
	end
	for i=2,#ARGV,2 do
		redis.call('HSET', KEYS[1], ARGV[i], ARGV[i+1])
	end
	return 1
`

// FieldFlag is used to indicate different behaviors for different fields, such
// as preventing them from being returned in certain circumstances, and allowing
// them to be manually edited.
type FieldFlag uint64

const (
	// Public fields will always be returned when calling Get
	Public FieldFlag = 1 << iota

	// Private fields are those that should only be shown to a verified entity,
	// and may contain private user information. Generally, only shown to the
	// logged in user
	Private

	// Hidden fields are never shown anywhere except in specific circumstances.
	Hidden

	// Editable indicates that this field is allowed to be modified manually
	Editable
)

// Field is a struct which describes a single field of a user map. A field's
// value is inherently a string.
type Field struct {

	// The name of the field. This is the key it will appear under in the user
	// map
	Name string

	// If optionally specified this will be the key the field is stored as in
	// redis (can be shorter than Name to save space)
	Key string

	// Used to determine the behavior of this field. This *must* be set to a
	// value greater than zero
	Flags FieldFlag
}

// Info represents information for a single user in the system. The fields in
// the map correspond to the fields added by AddField
type Info map[string]string

// System holds on to a Cmder and uses it to implement a basic user system. By
// default user maps have the following fields:
// * Name
// * TSCreated
// * Email (private, editable)
// * TSModified (private)
// * Disabled (private)
// * PasswordHash (hidden)
type System struct {
	c util.Cmder

	// The cost parameter to use when creating new password hashes. This
	// defaults to 11 and can be set right after instantiation
	BCryptCost int

	fields map[string]Field
}

// New returns a new System which will use the given Cmder as its persistence
// layer
func New(c util.Cmder) *System {
	s := System{
		c:          c,
		BCryptCost: 11,
		fields:     map[string]Field{},
	}
	s.AddField(Field{"Name", "_n", Public})
	s.AddField(Field{"TSCreated", "_t", Public})
	s.AddField(Field{"Email", "_e", Private | Editable})
	s.AddField(Field{"TSModified", "_tm", Private})
	s.AddField(Field{"Disabled", "_d", Private})
	s.AddField(Field{"PasswordHash", "_p", Hidden})
	return &s
}

// AddField can be used just after calling New to add more fields for a single
// user map. For example, if you'd like user maps to include an image field the
// field should be added here and it will appear for appropriate  Get commands
func (s *System) AddField(f Field) {
	if f.Key == "" {
		f.Key = f.Name
	}
	for fieldName := range s.fields {
		if fieldName == f.Name || s.fields[fieldName].Key == f.Key {
			panic(fmt.Sprintf("A field %s/%s already in use", f.Name, f.Key))
		}
	}
	s.fields[f.Name] = f
}

// Key returns a key which can be used to interact with some arbitrary user data
// directly in redis. This is useful if more complicated, lower level operations
// are needed to be done
func (s *System) Key(user string, extra ...string) string {
	k := "user:{" + user + "}"
	if len(extra) > 0 {
		k += ":" + strings.Join(extra, ":")
	}
	return k
}

func marshalTime(t time.Time) string {
	ts, _ := t.UTC().MarshalText()
	return string(ts)
}

func unmarshalTime(ts string) (time.Time, error) {
	var t time.Time
	if ts == "" {
		return t, nil
	}
	err := t.UnmarshalText([]byte(ts))
	return t.UTC(), err
}

// Create attempts to create a new user with the given email and password. If
// the user already exists ErrUserExists will be returned. If not the password
// will be hashed and stored
func (s *System) Create(user, email, password string) error {
	key := s.Key(user)
	nowS := marshalTime(time.Now())
	tsCreatedFieldKey := s.fields["TSCreated"].Key
	i, err := s.c.Cmd("HSETNX", key, tsCreatedFieldKey, nowS).Int()
	if err != nil {
		return err
	} else if i == 0 {
		return ErrUserExists
	}

	hash, err := s.generatePassword(password)
	if err != nil {
		return err
	}

	if err = s.set(user,
		"Name", user,
		"PasswordHash", hash,
		"Email", email,
	); err != nil {
		return err
	}

	return nil
}

func (s *System) generatePassword(password string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), s.BCryptCost)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash), nil
}

// ChangePassword changes an existing user's password to be the given one
func (s *System) ChangePassword(user, newPassword string) error {
	hash, err := s.generatePassword(newPassword)
	if err != nil {
		return err
	}

	return s.setExists(user, "PasswordHash", hash)
}

func (s *System) set(user string, keyvals ...interface{}) error {
	args := make([]interface{}, 0, len(keyvals)+3)
	args = append(args, s.Key(user))
	args, err := s.appendKeyvalsToArgs(user, keyvals, args)
	if err != nil {
		return err
	}

	return s.c.Cmd("HMSET", args...).Err
}

// same as set, but only if the user exists. Returns ErrNotFound if user doesn't
// exist
func (s *System) setExists(user string, keyvals ...interface{}) error {
	args := make([]interface{}, 0, len(keyvals)+4)
	args = append(args, s.Key(user))
	args = append(args, s.fields["Name"].Key)
	args, err := s.appendKeyvalsToArgs(user, keyvals, args)
	if err != nil {
		return err
	}

	i, err := util.LuaEval(s.c, hmsetxx, 1, args...).Int()
	if err != nil {
		return err
	}
	if i == 0 {
		return ErrNotFound
	}
	return nil
}

// Given a set of key/value pairs, keys being field names and values being what
// they want to be set to, checks that all the fields are legitimate and adds on
// a set for the TSModified field, appending all this to the passed in args
// slice and returning the new slice
func (s *System) appendKeyvalsToArgs(
	user string, keyvals []interface{}, args []interface{},
) (
	[]interface{}, error,
) {
	tsModifiedFieldKey := s.fields["TSModified"].Key
	nowS := marshalTime(time.Now())
	args = append(args, tsModifiedFieldKey, nowS)

	for i := 0; i < len(keyvals); i += 2 {
		k := keyvals[i].(string)
		kf := s.fields[k].Key
		if kf == "" {
			return nil, ErrFieldUnknown(k)
		}
		args = append(args, kf, keyvals[i+1])
	}

	return args, nil
}

func (s *System) unset(user string, fields ...string) error {
	if len(fields) == 0 {
		return nil
	}
	keys := make([]string, len(fields))
	for i, f := range fields {
		keys[i] = s.fields[f].Key
		if keys[i] == "" {
			return ErrFieldUnknown(f)
		}
	}

	return s.c.Cmd("HDEL", s.Key(user), keys).Err
}

// Authenticate attempts to authenticate the user with the given password.
// Returns nil on success. Can return ErrDisabled or ErrBadAuth
func (s *System) Authenticate(user, password string) error {
	u, err := s.Get(user, Hidden|Private)
	if err != nil {
		return err
	}

	if u["Disabled"] != "" {
		return ErrDisabled
	}

	p, err := hex.DecodeString(u["PasswordHash"])
	if err != nil {
		return err
	}

	match := bcrypt.CompareHashAndPassword([]byte(p), []byte(password)) == nil
	if !match {
		return ErrBadAuth
	}
	return nil
}

func (s *System) getFromResp(r *redis.Resp, filters FieldFlag) (Info, error) {
	m, err := r.Map()
	if err != nil {
		return nil, err
	}
	if len(m) == 0 {
		return nil, ErrNotFound
	}

	rm := Info{}
	for f := range s.fields {
		filt := s.fields[f].Flags
		if filt != Public && filt&filters == 0 {
			continue
		}
		rm[f] = m[s.fields[f].Key]
	}
	return rm, nil
}

// Get returns the Info for the given user, or ErrNotFound if the user couldn't
// be found
func (s *System) Get(user string, filters FieldFlag) (Info, error) {
	key := s.Key(user)
	return s.getFromResp(s.c.Cmd("HGETALL", key), filters)
}

// Disable marks the user as being disabled, meaning they have effectively
// deleted their account without actually deleting any data. They cannot log in
// and do not show up anywhere
func (s *System) Disable(user string) error {
	return s.set(user, "Disabled", "1")
}

// Enable marks the user as having an account enabled. Accounts are enabled by
// default when created, this only really has an effect when an accound was
// previously Disable'd
func (s *System) Enable(user string) error {
	return s.unset(user, "Disabled")
}

// Set is used to manually modify a user's fields. The Info argument need only
// be filled with the fields which are desired to be changed. All fields given
// in that argument must be Editable.
func (s *System) Set(user string, i Info) error {
	keyvals := make([]interface{}, 0, len(i)*2)
	for fieldName, value := range i {
		flags := s.fields[fieldName].Flags

		if flags == 0 {
			return ErrFieldUnknown(fieldName)

		} else if flags&Editable == 0 {
			return ErrFieldUneditable(fieldName)
		}

		keyvals = append(keyvals, fieldName, value)
	}

	return s.setExists(user, keyvals...)
}
<commit_msg>make user.Create a bit safer<commit_after>// Package user implements an abstraction for a basic user system. Users can be
// created, have properties set on them, disabled, deleted, and more. All data
// is persisted to a redis instance or cluster, and all methods are compeletely
// thread-safe. No data is sanitized by this package, and no authentication is
// done (although authentication mechanisims are provided)
package user

import (
	"encoding/hex"
	"fmt"
	"strings"
	"time"

	"github.com/mediocregopher/mediocre-api/common"
	"github.com/mediocregopher/radix.v2/redis"
	"github.com/mediocregopher/radix.v2/util"
	"golang.org/x/crypto/bcrypt"
)

// Errors which can be expected from various methods in this package
var (
	ErrUserExists = common.ExpectedErr{Code: 400, Err: "user exists"}
	ErrNotFound   = common.ExpectedErr{Code: 404, Err: "user not found"}
	ErrBadAuth    = common.ExpectedErr{Code: 400, Err: "could not authenticate user"}
	ErrDisabled   = common.ExpectedErr{Code: 400, Err: "user account is disabled"}
)

// Functions which return errors based on the related field names
var (
	ErrFieldUnknown = func(f string) error {
		return common.ExpectedErrf(400, "unknown field %q", f)
	}
	ErrFieldUneditable = func(f string) error {
		return common.ExpectedErrf(400, "field %q not editable", f)
	}
)

// HMSETXX key fieldWhichExists field value [field value...]
// Calls HMSET but only if fieldWhichExists is already set on the hash. Returns
// 1 if set was successful, 0 if it failed due to the xx condition
var hmsetxx = `
	if not redis.call('HGET', KEYS[1], ARGV[1]) then
		return 0
	end
	for i=2,#ARGV,2 do
		redis.call('HSET', KEYS[1], ARGV[i], ARGV[i+1])
	end
	return 1
`

// HMSETNX key field value [field value...]
// Calls HMSET but only if the key is currently empty. Returns 1 if set was
// successful, 0 if it failed due to the nx condition
var hmsetnx = `
	if redis.call('HLEN', KEYS[1]) > 0 then
		return 0
	end

	redis.call('HMSET', KEYS[1], unpack(ARGV))
	return 1
`

// FieldFlag is used to indicate different behaviors for different fields, such
// as preventing them from being returned in certain circumstances, and allowing
// them to be manually edited.
type FieldFlag uint64

const (
	// Public fields will always be returned when calling Get
	Public FieldFlag = 1 << iota

	// Private fields are those that should only be shown to a verified entity,
	// and may contain private user information. Generally, only shown to the
	// logged in user
	Private

	// Hidden fields are never shown anywhere except in specific circumstances.
	Hidden

	// Editable indicates that this field is allowed to be modified manually
	Editable
)

// Field is a struct which describes a single field of a user map. A field's
// value is inherently a string.
type Field struct {

	// The name of the field. This is the key it will appear under in the user
	// map
	Name string

	// If optionally specified this will be the key the field is stored as in
	// redis (can be shorter than Name to save space)
	Key string

	// Used to determine the behavior of this field. This *must* be set to a
	// value greater than zero
	Flags FieldFlag
}

// Info represents information for a single user in the system. The fields in
// the map correspond to the fields added by AddField
type Info map[string]string

// System holds on to a Cmder and uses it to implement a basic user system. By
// default user maps have the following fields:
// * Name
// * TSCreated
// * Email (private, editable)
// * TSModified (private)
// * Disabled (private)
// * PasswordHash (hidden)
type System struct {
	c util.Cmder

	// The cost parameter to use when creating new password hashes. This
	// defaults to 11 and can be set right after instantiation
	BCryptCost int

	fields map[string]Field
}

// New returns a new System which will use the given Cmder as its persistence
// layer
func New(c util.Cmder) *System {
	s := System{
		c:          c,
		BCryptCost: 11,
		fields:     map[string]Field{},
	}
	s.AddField(Field{"Name", "_n", Public})
	s.AddField(Field{"TSCreated", "_t", Public})
	s.AddField(Field{"Email", "_e", Private | Editable})
	s.AddField(Field{"TSModified", "_tm", Private})
	s.AddField(Field{"Disabled", "_d", Private})
	s.AddField(Field{"PasswordHash", "_p", Hidden})
	return &s
}

// AddField can be used just after calling New to add more fields for a single
// user map. For example, if you'd like user maps to include an image field the
// field should be added here and it will appear for appropriate  Get commands
func (s *System) AddField(f Field) {
	if f.Key == "" {
		f.Key = f.Name
	}
	for fieldName := range s.fields {
		if fieldName == f.Name || s.fields[fieldName].Key == f.Key {
			panic(fmt.Sprintf("A field %s/%s already in use", f.Name, f.Key))
		}
	}
	s.fields[f.Name] = f
}

// Key returns a key which can be used to interact with some arbitrary user data
// directly in redis. This is useful if more complicated, lower level operations
// are needed to be done
func (s *System) Key(user string, extra ...string) string {
	k := "user:{" + user + "}"
	if len(extra) > 0 {
		k += ":" + strings.Join(extra, ":")
	}
	return k
}

func marshalTime(t time.Time) string {
	ts, _ := t.UTC().MarshalText()
	return string(ts)
}

func unmarshalTime(ts string) (time.Time, error) {
	var t time.Time
	if ts == "" {
		return t, nil
	}
	err := t.UnmarshalText([]byte(ts))
	return t.UTC(), err
}

// Create attempts to create a new user with the given email and password. If
// the user already exists ErrUserExists will be returned. If not the password
// will be hashed and stored
func (s *System) Create(user, email, password string) error {
	key := s.Key(user)
	nowS := marshalTime(time.Now())

	hash, err := s.generatePassword(password)
	if err != nil {
		return err
	}

	args := make([]interface{}, 0, 9)
	args = append(args, key)
	args, err = s.appendKeyvalsToArgs([]interface{}{
		"Name", user,
		"PasswordHash", hash,
		"Email", email,
		"TSCreated", nowS,
	}, args)
	if err != nil {
		return err
	}

	i, err := util.LuaEval(s.c, hmsetnx, 1, args...).Int()
	if err != nil {
		return err
	} else if i == 0 {
		return ErrUserExists
	}
	return nil
}

func (s *System) generatePassword(password string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), s.BCryptCost)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash), nil
}

// ChangePassword changes an existing user's password to be the given one
func (s *System) ChangePassword(user, newPassword string) error {
	hash, err := s.generatePassword(newPassword)
	if err != nil {
		return err
	}

	return s.setExists(user, "PasswordHash", hash)
}

func (s *System) set(user string, keyvals ...interface{}) error {
	args := make([]interface{}, 0, len(keyvals)+3)
	args = append(args, s.Key(user))
	args, err := s.appendKeyvalsToArgs(keyvals, args)
	if err != nil {
		return err
	}

	return s.c.Cmd("HMSET", args...).Err
}

// same as set, but only if the user exists. Returns ErrNotFound if user doesn't
// exist
func (s *System) setExists(user string, keyvals ...interface{}) error {
	args := make([]interface{}, 0, len(keyvals)+4)
	args = append(args, s.Key(user))
	args = append(args, s.fields["Name"].Key)
	args, err := s.appendKeyvalsToArgs(keyvals, args)
	if err != nil {
		return err
	}

	i, err := util.LuaEval(s.c, hmsetxx, 1, args...).Int()
	if err != nil {
		return err
	} else if i == 0 {
		return ErrNotFound
	}
	return nil
}

// Given a set of key/value pairs, keys being field names and values being what
// they want to be set to, checks that all the fields are legitimate and adds on
// a set for the TSModified field, appending all this to the passed in args
// slice and returning the new slice
func (s *System) appendKeyvalsToArgs(
	keyvals []interface{}, args []interface{},
) (
	[]interface{}, error,
) {
	tsModifiedFieldKey := s.fields["TSModified"].Key
	nowS := marshalTime(time.Now())
	args = append(args, tsModifiedFieldKey, nowS)

	for i := 0; i < len(keyvals); i += 2 {
		k := keyvals[i].(string)
		kf := s.fields[k].Key
		if kf == "" {
			return nil, ErrFieldUnknown(k)
		}
		args = append(args, kf, keyvals[i+1])
	}

	return args, nil
}

func (s *System) unset(user string, fields ...string) error {
	if len(fields) == 0 {
		return nil
	}
	keys := make([]string, len(fields))
	for i, f := range fields {
		keys[i] = s.fields[f].Key
		if keys[i] == "" {
			return ErrFieldUnknown(f)
		}
	}

	return s.c.Cmd("HDEL", s.Key(user), keys).Err
}

// Authenticate attempts to authenticate the user with the given password.
// Returns nil on success. Can return ErrDisabled or ErrBadAuth
func (s *System) Authenticate(user, password string) error {
	u, err := s.Get(user, Hidden|Private)
	if err != nil {
		return err
	}

	if u["Disabled"] != "" {
		return ErrDisabled
	}

	p, err := hex.DecodeString(u["PasswordHash"])
	if err != nil {
		return err
	}

	match := bcrypt.CompareHashAndPassword([]byte(p), []byte(password)) == nil
	if !match {
		return ErrBadAuth
	}
	return nil
}

func (s *System) getFromResp(r *redis.Resp, filters FieldFlag) (Info, error) {
	m, err := r.Map()
	if err != nil {
		return nil, err
	}
	if len(m) == 0 {
		return nil, ErrNotFound
	}

	rm := Info{}
	for f := range s.fields {
		filt := s.fields[f].Flags
		if filt != Public && filt&filters == 0 {
			continue
		}
		rm[f] = m[s.fields[f].Key]
	}
	return rm, nil
}

// Get returns the Info for the given user, or ErrNotFound if the user couldn't
// be found
func (s *System) Get(user string, filters FieldFlag) (Info, error) {
	key := s.Key(user)
	return s.getFromResp(s.c.Cmd("HGETALL", key), filters)
}

// Disable marks the user as being disabled, meaning they have effectively
// deleted their account without actually deleting any data. They cannot log in
// and do not show up anywhere
func (s *System) Disable(user string) error {
	return s.set(user, "Disabled", "1")
}

// Enable marks the user as having an account enabled. Accounts are enabled by
// default when created, this only really has an effect when an accound was
// previously Disable'd
func (s *System) Enable(user string) error {
	return s.unset(user, "Disabled")
}

// Set is used to manually modify a user's fields. The Info argument need only
// be filled with the fields which are desired to be changed. All fields given
// in that argument must be Editable.
func (s *System) Set(user string, i Info) error {
	keyvals := make([]interface{}, 0, len(i)*2)
	for fieldName, value := range i {
		flags := s.fields[fieldName].Flags

		if flags == 0 {
			return ErrFieldUnknown(fieldName)

		} else if flags&Editable == 0 {
			return ErrFieldUneditable(fieldName)
		}

		keyvals = append(keyvals, fieldName, value)
	}

	return s.setExists(user, keyvals...)
}
<|endoftext|>