<commit_before>package execution

import (
	"fmt"
	"pfi/sensorbee/sensorbee/bql/parser"
	"pfi/sensorbee/sensorbee/bql/udf"
	"pfi/sensorbee/sensorbee/core"
	"pfi/sensorbee/sensorbee/data"
	"strings"
)

/*
The functions in this file transform an AST as returned by
`parser.bqlParser.ParseStmt()` into a physical execution plan.
This works similar to the way it is done in Spark SQL as
outlined on
  https://databricks.com/blog/2015/04/13/deep-dive-into-spark-sqls-catalyst-optimizer.html
in three phases:
- Analyze
- LogicalOptimize
- MakePhysicalPlan
*/

type LogicalPlan struct {
	GroupingStmt bool
	parser.EmitterAST
	Projections []aliasedExpression
	parser.WindowedFromAST
	Filter    FlatExpression
	GroupList []FlatExpression
	parser.HavingAST
}

// ExecutionPlan is a physical interface that is capable of
// computing the data that needs to be emitted into an output
// stream when a new tuple arrives in the input stream.
type ExecutionPlan interface {
	// Process must be called whenever a new tuple arrives in
	// the input stream. It will return a list of data.Map
	// items where each of these items is to be emitted as
	// a tuple. It is the caller's task to create those tuples
	// and set appropriate meta information such as timestamps.
	//
	// NB. Process is not thread-safe, i.e., it must be called in
	// a single-threaded context.
	Process(input *core.Tuple) ([]data.Map, error)
}

func Analyze(s parser.SelectStmt, reg udf.FunctionRegistry) (*LogicalPlan, error) {
	/*
	   In Spark, this does the following:

	   > - Looking up relations by name from the catalog.
	   > - Mapping named attributes, such as col, to the input provided
	   >   given operator’s children.
	   > - Determining which attributes refer to the same value to give
	   >   them a unique ID (which later allows optimization of expressions
	   >   such as col = col).
	   > - Propagating and coercing types through expressions: for
	   >   example, we cannot know the return type of 1 + col until we
	   >   have resolved col and possibly casted its subexpressions to a
	   >   compatible types.
	*/

	if err := makeRelationAliases(&s); err != nil {
		return nil, err
	}

	if err := validateReferences(&s); err != nil {
		return nil, err
	}

	return flattenExpressions(&s, reg)
}

// isAggregateFunc is a helper function to check if one of
// the parameters of the given function is an aggregate
// parameter.
func isAggregateFunc(f udf.UDF, arity int) bool {
	agg := false
	for k := 0; k < arity; k++ {
		if f.IsAggregationParameter(k) {
			agg = true
		}
	}
	return agg
}

// flattenExpressions separates the aggregate and non-aggregate
// part in a statement and returns with an error if there are
// aggregates in structures that may not have some
func flattenExpressions(s *parser.SelectStmt, reg udf.FunctionRegistry) (*LogicalPlan, error) {
	// groupingMode is active when aggregate functions or the
	// GROUP BY clause are used
	groupingMode := false

	flatProjExprs := make([]aliasedExpression, len(s.Projections))
	numAggParams := 0
	for i, expr := range s.Projections {
		// convert the parser Expression to a FlatExpression
		flatExpr, aggrs, err := ParserExprToMaybeAggregate(expr, numAggParams, reg)
		numAggParams += len(aggrs)
		if err != nil {
			return nil, err
		}
		// remember if we have aggregates at all
		if len(aggrs) > 0 {
			groupingMode = true
		}
		// compute column name
		colHeader := fmt.Sprintf("col_%v", i+1)
		switch projType := expr.(type) {
		case parser.RowMeta:
			if projType.MetaType == parser.TimestampMeta {
				colHeader = "ts"
			}
		case parser.RowValue:
			colHeader = projType.Column
		case parser.AliasAST:
			colHeader = projType.Alias
		case parser.FuncAppAST:
			colHeader = string(projType.Function)
		case parser.Wildcard:
			// The wildcard projection (without AS) is very special in that
			// it is the only case where the BQL user does not determine
			// the output key names (implicitly or explicitly). The
			// Evaluator interface is designed such that Evaluator
			// has 100% control over the returned value, but 0% control
			// over how it is named, therefore the wildcard evaluation
			// requires handling in multiple locations.
			// As a workaround, we will return the complete Map from
			// the wildcard Evaluator, nest it under a hard-coded key
			// called "*" and flatten them later (this is done correctly
			// by the assignOutputValue function).
			// Note that if it is desired at some point that there are
			// more evaluators with that behavior, we should change the
			// Evaluator.Eval interface.
			colHeader = "*"
		}
		flatProjExprs[i] = aliasedExpression{colHeader, flatExpr, aggrs}
	}

	if s.Having != nil {
		// convert the parser Expression to a FlatExpression
		flatExpr, aggrs, err := ParserExprToMaybeAggregate(s.Having, numAggParams, reg)
		if err != nil {
			return nil, err
		}
		// use a special column name
		colHeader := ":having:"
		flatProjExprs = append(flatProjExprs,
			aliasedExpression{colHeader, flatExpr, aggrs})
		// we are definitely in grouping mode
		groupingMode = true
	}

	var filterExpr FlatExpression
	if s.Filter != nil {
		filterFlatExpr, err := ParserExprToFlatExpr(s.Filter, reg)
		if err != nil {
			// return a prettier error message
			if strings.HasPrefix(err.Error(), "you cannot use aggregate") {
				err = fmt.Errorf("aggregates not allowed in WHERE clause")
			}
			return nil, err
		}
		filterExpr = filterFlatExpr
	}

	groupCols := make([]RowValue, len(s.GroupList))
	flatGroupExprs := make([]FlatExpression, len(s.GroupList))
	for i, expr := range s.GroupList {
		// convert the parser Expression to a FlatExpression
		flatExpr, err := ParserExprToFlatExpr(expr, reg)
		if err != nil {
			// return a prettier error message
			if strings.HasPrefix(err.Error(), "you cannot use aggregate") {
				err = fmt.Errorf("aggregates not allowed in GROUP BY clause")
			}
			return nil, err
		}
		// at the moment we only support grouping by single columns,
		// not expressions
		col, ok := flatExpr.(RowValue)
		if !ok {
			err := fmt.Errorf("grouping by expressions is not supported yet")
			return nil, err
		}
		groupCols[i] = col
		flatGroupExprs[i] = flatExpr
	}
	groupingMode = groupingMode || len(flatGroupExprs) > 0

	// check if grouping is done correctly
	if groupingMode {
		for _, expr := range flatProjExprs {
			// all columns mentioned outside of an aggregate
			// function must be in the GROUP BY clause
			if rm, ok := expr.expr.(RowMeta); ok {
				err := fmt.Errorf("using metadata '%s' in GROUP BY statements is "+
					"not supported yet", rm.MetaType)
				return nil, err
			}
			usedCols := expr.expr.Columns()
			for _, usedCol := range usedCols {
				// look for this col in the GROUP BY clause
				mentioned := false
				for _, groupCol := range groupCols {
					if usedCol == groupCol {
						mentioned = true
						break
					}
				}
				if !mentioned {
					err := fmt.Errorf("column \"%s\" must appear in the GROUP BY "+
						"clause or be used in an aggregate function", usedCol.Repr())
					return nil, err
				}
			}
		}
	}

	return &LogicalPlan{
		groupingMode,
		s.EmitterAST,
		flatProjExprs,
		s.WindowedFromAST,
		filterExpr,
		flatGroupExprs,
		s.HavingAST,
	}, nil
}

// makeRelationAliases will assign an internal alias to every relation
// does not yet have one (given by the user). It will also detect if
// there is a conflict between aliases.
func makeRelationAliases(s *parser.SelectStmt) error {
	relNames := make(map[string]parser.AliasedStreamWindowAST, len(s.Relations))
	newRels := make([]parser.AliasedStreamWindowAST, len(s.Relations))
	for i, aliasedRel := range s.Relations {
		// if the relation does not yet have an internal alias, use
		// the relation name itself
		if aliasedRel.Alias == "" {
			aliasedRel.Alias = aliasedRel.Name
		}
		otherRel, exists := relNames[aliasedRel.Alias]
		if exists {
			return fmt.Errorf("cannot use relations '%s' and '%s' with the "+
				"same alias '%s'", aliasedRel.Name, otherRel.Name, aliasedRel.Alias)
		} else {
			relNames[aliasedRel.Alias] = aliasedRel
		}
		newRels[i] = aliasedRel
	}
	s.Relations = newRels
	return nil
}

// validateReferences checks if the references to input relations
// in SELECT, WHERE, GROUP BY and HAVING clauses of the given
// statement are matching the relations mentioned in the FROM
// clause.
func validateReferences(s *parser.SelectStmt) error {

	/* We want to check if we can access all relations properly.
	   If there is just one input relation, we ask that none of the
	   RowValue structs has a Relation string different from ""
	   (as in `SELECT col FROM stream`).
	   If there are multiple input relations, we ask that *all*
	   RowValue structs have a Relation string that matches one of
	   the input relations (as in `SELECT a.col, b.col FROM a, b`).
	*/

	// collect the referenced relations in SELECT, WHERE, GROUP BY clauses
	// and store them in the given map
	refRels := map[string]bool{}
	for _, proj := range s.Projections {
		for rel := range proj.ReferencedRelations() {
			refRels[rel] = true
		}
	}
	if s.Filter != nil {
		for rel := range s.Filter.ReferencedRelations() {
			refRels[rel] = true
		}
	}
	for _, group := range s.GroupList {
		for rel := range group.ReferencedRelations() {
			refRels[rel] = true
		}
	}
	if s.Having != nil {
		for rel := range s.Having.ReferencedRelations() {
			refRels[rel] = true
		}
	}

	// do the correctness check for SELECT, WHERE, GROUP BY clauses
	if len(s.Relations) == 0 {
		// Sample: SELECT a (no FROM clause)
		// this case should never happen due to parser setup
		return fmt.Errorf("need at least one relation to select from")

	} else if len(s.Relations) == 1 {
		inputRel := s.Relations[0].Alias
		if len(refRels) == 1 {
			// Sample: SELECT a FROM b // SELECT b.a FROM b
			// check if the one map item is either "" or the name of
			// the input relation
			for rel := range refRels {
				if rel != "" && rel != inputRel {
					err := fmt.Errorf("cannot refer to relation '%s' "+
						"when using only '%s'", rel, inputRel)
					return err
				}
			}
			// we need to make the references more explicit,
			// i.e., change all "" references to the name
			// of the only input relation
			newProjs := make([]parser.Expression, len(s.Projections))
			for i, proj := range s.Projections {
				newProjs[i] = proj.RenameReferencedRelation("", inputRel)
			}
			s.Projections = newProjs
			if s.Filter != nil {
				s.Filter = s.Filter.RenameReferencedRelation("", inputRel)
			}
			newGroup := make([]parser.Expression, len(s.GroupList))
			for i, group := range s.GroupList {
				newGroup[i] = group.RenameReferencedRelation("", inputRel)
			}
			s.GroupList = newGroup
			if s.Having != nil {
				s.Having = s.Having.RenameReferencedRelation("", inputRel)
			}

		} else if len(refRels) > 1 {
			// Sample: SELECT a, b.a FROM b // SELECT b.a, x.a FROM b
			// this is an invalid statement
			failRels := make([]string, 0, len(refRels))
			for rel := range refRels {
				failRels = append(failRels, fmt.Sprintf("'%s'", rel))
			}
			failRelsStr := strings.Join(failRels, ", ")
			err := fmt.Errorf("cannot refer to relations %s "+
				"when using only '%s'", failRelsStr, inputRel)
			return err
		}
		// if we arrive here, the only referenced relation is valid or
		// we do not actually reference anything

	} else if len(s.Relations) > 1 {
		// Sample: SELECT b.a, c.d FROM b, c
		// check if all referenced relations are actually listed in FROM
		for rel := range refRels {
			found := false
			for _, inputRel := range s.Relations {
				if rel == inputRel.Alias {
					found = true
					break
				}
			}
			if !found {
				prettyRels := make([]string, 0, len(s.Relations))
				for _, rel_ := range s.Relations {
					prettyRels = append(prettyRels, fmt.Sprintf("'%s'", rel_.Alias))
				}
				prettyRelsStr := strings.Join(prettyRels, ", ")
				err := fmt.Errorf("cannot reference relation '%s' "+
					"when using input relations %v", rel, prettyRelsStr)
				return err
			}
		}
		// if we arrive here, all referenced relations exist in the
		// FROM clause -> OK
	}

	return nil
}

func (lp *LogicalPlan) LogicalOptimize() (*LogicalPlan, error) {
	/*
	   In Spark, this does the following:

	   > These include constant folding, predicate pushdown, projection
	   > pruning, null propagation, Boolean expression simplification,
	   > and other rules.
	*/
	return lp, nil
}

func (lp *LogicalPlan) MakePhysicalPlan(reg udf.FunctionRegistry) (ExecutionPlan, error) {
	/*
	   In Spark, this does the following:

	   > In the physical planning phase, Spark SQL takes a logical plan
	   > and generates one or more physical plans, using physical operators
	   > that match the Spark execution engine.
	*/
	if CanBuildDefaultSelectExecutionPlan(lp, reg) {
		return NewDefaultSelectExecutionPlan(lp, reg)
	} else if CanBuildGroupbyExecutionPlan(lp, reg) {
		return NewGroupbyExecutionPlan(lp, reg)
	}
	return nil, fmt.Errorf("no plan can deal with such a statement")
}
<commit_msg>validate emitter parameter when creating a plan<commit_after>package execution

import (
	"fmt"
	"pfi/sensorbee/sensorbee/bql/parser"
	"pfi/sensorbee/sensorbee/bql/udf"
	"pfi/sensorbee/sensorbee/core"
	"pfi/sensorbee/sensorbee/data"
	"strings"
)

/*
The functions in this file transform an AST as returned by
`parser.bqlParser.ParseStmt()` into a physical execution plan.
This works similar to the way it is done in Spark SQL as
outlined on
  https://databricks.com/blog/2015/04/13/deep-dive-into-spark-sqls-catalyst-optimizer.html
in three phases:
- Analyze
- LogicalOptimize
- MakePhysicalPlan
*/

type LogicalPlan struct {
	GroupingStmt bool
	EmitterType  parser.Emitter
	EmitterLimit int64
	Projections  []aliasedExpression
	parser.WindowedFromAST
	Filter    FlatExpression
	GroupList []FlatExpression
	parser.HavingAST
}

// ExecutionPlan is a physical interface that is capable of
// computing the data that needs to be emitted into an output
// stream when a new tuple arrives in the input stream.
type ExecutionPlan interface {
	// Process must be called whenever a new tuple arrives in
	// the input stream. It will return a list of data.Map
	// items where each of these items is to be emitted as
	// a tuple. It is the caller's task to create those tuples
	// and set appropriate meta information such as timestamps.
	//
	// NB. Process is not thread-safe, i.e., it must be called in
	// a single-threaded context.
	Process(input *core.Tuple) ([]data.Map, error)
}

func Analyze(s parser.SelectStmt, reg udf.FunctionRegistry) (*LogicalPlan, error) {
	/*
	   In Spark, this does the following:

	   > - Looking up relations by name from the catalog.
	   > - Mapping named attributes, such as col, to the input provided
	   >   given operator’s children.
	   > - Determining which attributes refer to the same value to give
	   >   them a unique ID (which later allows optimization of expressions
	   >   such as col = col).
	   > - Propagating and coercing types through expressions: for
	   >   example, we cannot know the return type of 1 + col until we
	   >   have resolved col and possibly casted its subexpressions to a
	   >   compatible types.
	*/

	if err := makeRelationAliases(&s); err != nil {
		return nil, err
	}

	if err := validateReferences(&s); err != nil {
		return nil, err
	}

	return flattenExpressions(&s, reg)
}

// isAggregateFunc is a helper function to check if one of
// the parameters of the given function is an aggregate
// parameter.
func isAggregateFunc(f udf.UDF, arity int) bool {
	agg := false
	for k := 0; k < arity; k++ {
		if f.IsAggregationParameter(k) {
			agg = true
		}
	}
	return agg
}

// flattenExpressions separates the aggregate and non-aggregate
// part in a statement and returns with an error if there are
// aggregates in structures that may not have some
func flattenExpressions(s *parser.SelectStmt, reg udf.FunctionRegistry) (*LogicalPlan, error) {
	// groupingMode is active when aggregate functions or the
	// GROUP BY clause are used
	groupingMode := false

	flatProjExprs := make([]aliasedExpression, len(s.Projections))
	numAggParams := 0
	for i, expr := range s.Projections {
		// convert the parser Expression to a FlatExpression
		flatExpr, aggrs, err := ParserExprToMaybeAggregate(expr, numAggParams, reg)
		numAggParams += len(aggrs)
		if err != nil {
			return nil, err
		}
		// remember if we have aggregates at all
		if len(aggrs) > 0 {
			groupingMode = true
		}
		// compute column name
		colHeader := fmt.Sprintf("col_%v", i+1)
		switch projType := expr.(type) {
		case parser.RowMeta:
			if projType.MetaType == parser.TimestampMeta {
				colHeader = "ts"
			}
		case parser.RowValue:
			colHeader = projType.Column
		case parser.AliasAST:
			colHeader = projType.Alias
		case parser.FuncAppAST:
			colHeader = string(projType.Function)
		case parser.Wildcard:
			// The wildcard projection (without AS) is very special in that
			// it is the only case where the BQL user does not determine
			// the output key names (implicitly or explicitly). The
			// Evaluator interface is designed such that Evaluator
			// has 100% control over the returned value, but 0% control
			// over how it is named, therefore the wildcard evaluation
			// requires handling in multiple locations.
			// As a workaround, we will return the complete Map from
			// the wildcard Evaluator, nest it under a hard-coded key
			// called "*" and flatten them later (this is done correctly
			// by the assignOutputValue function).
			// Note that if it is desired at some point that there are
			// more evaluators with that behavior, we should change the
			// Evaluator.Eval interface.
			colHeader = "*"
		}
		flatProjExprs[i] = aliasedExpression{colHeader, flatExpr, aggrs}
	}

	if s.Having != nil {
		// convert the parser Expression to a FlatExpression
		flatExpr, aggrs, err := ParserExprToMaybeAggregate(s.Having, numAggParams, reg)
		if err != nil {
			return nil, err
		}
		// use a special column name
		colHeader := ":having:"
		flatProjExprs = append(flatProjExprs,
			aliasedExpression{colHeader, flatExpr, aggrs})
		// we are definitely in grouping mode
		groupingMode = true
	}

	var filterExpr FlatExpression
	if s.Filter != nil {
		filterFlatExpr, err := ParserExprToFlatExpr(s.Filter, reg)
		if err != nil {
			// return a prettier error message
			if strings.HasPrefix(err.Error(), "you cannot use aggregate") {
				err = fmt.Errorf("aggregates not allowed in WHERE clause")
			}
			return nil, err
		}
		filterExpr = filterFlatExpr
	}

	groupCols := make([]RowValue, len(s.GroupList))
	flatGroupExprs := make([]FlatExpression, len(s.GroupList))
	for i, expr := range s.GroupList {
		// convert the parser Expression to a FlatExpression
		flatExpr, err := ParserExprToFlatExpr(expr, reg)
		if err != nil {
			// return a prettier error message
			if strings.HasPrefix(err.Error(), "you cannot use aggregate") {
				err = fmt.Errorf("aggregates not allowed in GROUP BY clause")
			}
			return nil, err
		}
		// at the moment we only support grouping by single columns,
		// not expressions
		col, ok := flatExpr.(RowValue)
		if !ok {
			err := fmt.Errorf("grouping by expressions is not supported yet")
			return nil, err
		}
		groupCols[i] = col
		flatGroupExprs[i] = flatExpr
	}
	groupingMode = groupingMode || len(flatGroupExprs) > 0

	// check if grouping is done correctly
	if groupingMode {
		for _, expr := range flatProjExprs {
			// all columns mentioned outside of an aggregate
			// function must be in the GROUP BY clause
			if rm, ok := expr.expr.(RowMeta); ok {
				err := fmt.Errorf("using metadata '%s' in GROUP BY statements is "+
					"not supported yet", rm.MetaType)
				return nil, err
			}
			usedCols := expr.expr.Columns()
			for _, usedCol := range usedCols {
				// look for this col in the GROUP BY clause
				mentioned := false
				for _, groupCol := range groupCols {
					if usedCol == groupCol {
						mentioned = true
						break
					}
				}
				if !mentioned {
					err := fmt.Errorf("column \"%s\" must appear in the GROUP BY "+
						"clause or be used in an aggregate function", usedCol.Repr())
					return nil, err
				}
			}
		}
	}

	// validate the emitter parameters
	emitLimit := int64(-1)
	for _, opt := range s.EmitterAST.EmitterOptions {
		switch obj := opt.(type) {
		default:
			return nil, fmt.Errorf("unknown emitter options: %+v", obj)
		case parser.EmitterLimit:
			l := obj.Limit
			if l < 0 {
				return nil, fmt.Errorf("LIMIT parameter must have a "+
					"positive value, not %d", l)
			}
			emitLimit = l
		}
	}

	return &LogicalPlan{
		groupingMode,
		s.EmitterAST.EmitterType,
		emitLimit,
		flatProjExprs,
		s.WindowedFromAST,
		filterExpr,
		flatGroupExprs,
		s.HavingAST,
	}, nil
}

// makeRelationAliases will assign an internal alias to every relation
// does not yet have one (given by the user). It will also detect if
// there is a conflict between aliases.
func makeRelationAliases(s *parser.SelectStmt) error {
	relNames := make(map[string]parser.AliasedStreamWindowAST, len(s.Relations))
	newRels := make([]parser.AliasedStreamWindowAST, len(s.Relations))
	for i, aliasedRel := range s.Relations {
		// if the relation does not yet have an internal alias, use
		// the relation name itself
		if aliasedRel.Alias == "" {
			aliasedRel.Alias = aliasedRel.Name
		}
		otherRel, exists := relNames[aliasedRel.Alias]
		if exists {
			return fmt.Errorf("cannot use relations '%s' and '%s' with the "+
				"same alias '%s'", aliasedRel.Name, otherRel.Name, aliasedRel.Alias)
		} else {
			relNames[aliasedRel.Alias] = aliasedRel
		}
		newRels[i] = aliasedRel
	}
	s.Relations = newRels
	return nil
}

// validateReferences checks if the references to input relations
// in SELECT, WHERE, GROUP BY and HAVING clauses of the given
// statement are matching the relations mentioned in the FROM
// clause.
func validateReferences(s *parser.SelectStmt) error {

	/* We want to check if we can access all relations properly.
	   If there is just one input relation, we ask that none of the
	   RowValue structs has a Relation string different from ""
	   (as in `SELECT col FROM stream`).
	   If there are multiple input relations, we ask that *all*
	   RowValue structs have a Relation string that matches one of
	   the input relations (as in `SELECT a.col, b.col FROM a, b`).
	*/

	// collect the referenced relations in SELECT, WHERE, GROUP BY clauses
	// and store them in the given map
	refRels := map[string]bool{}
	for _, proj := range s.Projections {
		for rel := range proj.ReferencedRelations() {
			refRels[rel] = true
		}
	}
	if s.Filter != nil {
		for rel := range s.Filter.ReferencedRelations() {
			refRels[rel] = true
		}
	}
	for _, group := range s.GroupList {
		for rel := range group.ReferencedRelations() {
			refRels[rel] = true
		}
	}
	if s.Having != nil {
		for rel := range s.Having.ReferencedRelations() {
			refRels[rel] = true
		}
	}

	// do the correctness check for SELECT, WHERE, GROUP BY clauses
	if len(s.Relations) == 0 {
		// Sample: SELECT a (no FROM clause)
		// this case should never happen due to parser setup
		return fmt.Errorf("need at least one relation to select from")

	} else if len(s.Relations) == 1 {
		inputRel := s.Relations[0].Alias
		if len(refRels) == 1 {
			// Sample: SELECT a FROM b // SELECT b.a FROM b
			// check if the one map item is either "" or the name of
			// the input relation
			for rel := range refRels {
				if rel != "" && rel != inputRel {
					err := fmt.Errorf("cannot refer to relation '%s' "+
						"when using only '%s'", rel, inputRel)
					return err
				}
			}
			// we need to make the references more explicit,
			// i.e., change all "" references to the name
			// of the only input relation
			newProjs := make([]parser.Expression, len(s.Projections))
			for i, proj := range s.Projections {
				newProjs[i] = proj.RenameReferencedRelation("", inputRel)
			}
			s.Projections = newProjs
			if s.Filter != nil {
				s.Filter = s.Filter.RenameReferencedRelation("", inputRel)
			}
			newGroup := make([]parser.Expression, len(s.GroupList))
			for i, group := range s.GroupList {
				newGroup[i] = group.RenameReferencedRelation("", inputRel)
			}
			s.GroupList = newGroup
			if s.Having != nil {
				s.Having = s.Having.RenameReferencedRelation("", inputRel)
			}

		} else if len(refRels) > 1 {
			// Sample: SELECT a, b.a FROM b // SELECT b.a, x.a FROM b
			// this is an invalid statement
			failRels := make([]string, 0, len(refRels))
			for rel := range refRels {
				failRels = append(failRels, fmt.Sprintf("'%s'", rel))
			}
			failRelsStr := strings.Join(failRels, ", ")
			err := fmt.Errorf("cannot refer to relations %s "+
				"when using only '%s'", failRelsStr, inputRel)
			return err
		}
		// if we arrive here, the only referenced relation is valid or
		// we do not actually reference anything

	} else if len(s.Relations) > 1 {
		// Sample: SELECT b.a, c.d FROM b, c
		// check if all referenced relations are actually listed in FROM
		for rel := range refRels {
			found := false
			for _, inputRel := range s.Relations {
				if rel == inputRel.Alias {
					found = true
					break
				}
			}
			if !found {
				prettyRels := make([]string, 0, len(s.Relations))
				for _, rel_ := range s.Relations {
					prettyRels = append(prettyRels, fmt.Sprintf("'%s'", rel_.Alias))
				}
				prettyRelsStr := strings.Join(prettyRels, ", ")
				err := fmt.Errorf("cannot reference relation '%s' "+
					"when using input relations %v", rel, prettyRelsStr)
				return err
			}
		}
		// if we arrive here, all referenced relations exist in the
		// FROM clause -> OK
	}

	return nil
}

func (lp *LogicalPlan) LogicalOptimize() (*LogicalPlan, error) {
	/*
	   In Spark, this does the following:

	   > These include constant folding, predicate pushdown, projection
	   > pruning, null propagation, Boolean expression simplification,
	   > and other rules.
	*/
	return lp, nil
}

func (lp *LogicalPlan) MakePhysicalPlan(reg udf.FunctionRegistry) (ExecutionPlan, error) {
	/*
	   In Spark, this does the following:

	   > In the physical planning phase, Spark SQL takes a logical plan
	   > and generates one or more physical plans, using physical operators
	   > that match the Spark execution engine.
	*/
	if CanBuildDefaultSelectExecutionPlan(lp, reg) {
		return NewDefaultSelectExecutionPlan(lp, reg)
	} else if CanBuildGroupbyExecutionPlan(lp, reg) {
		return NewGroupbyExecutionPlan(lp, reg)
	}
	return nil, fmt.Errorf("no plan can deal with such a statement")
}
<|endoftext|>


<commit_before>// Copyright 2014 Akamai Technologies, Inc.
// Please see the accompanying LICENSE file for licensing information.
//
// Note: this file is a derivative work of
//
// https://github.com/share/ShareJS/blob/0.6/src/client/ace.coffee
//
// Copyright 2011 Joseph Gentle
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package ace

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"unicode/utf8"

	"github.com/gopherjs/gopherjs/js"

	"github.com/mstone/focus/js/alert"
	"github.com/mstone/focus/msg"
	"github.com/mstone/focus/ot"
)

type Lengther interface {
	Length() int
}

type Sender interface {
	Send(msg []byte)
}

type Adapter struct {
	mu       sync.Mutex
	conn     Sender
	session  Lengther
	doc      Document
	state    *ot.Controller
	suppress bool
	fd       int
}

func NewAdapter() *Adapter {
	return &Adapter{
		mu: sync.Mutex{},
	}
}

func (a *Adapter) Suppress(suppress bool) {
	a.suppress = suppress
}

func (a *Adapter) IsSuppressed() bool {
	return a.suppress
}

func (a *Adapter) Send(rev int, hash string, ops ot.Ops) {
	if !a.suppress {
		jsOps, _ := json.Marshal(msg.Msg{
			Cmd:  msg.C_WRITE,
			Fd:   a.fd,
			Rev:  rev,
			Hash: hash,
			Ops:  ops,
		})
		alert.Golang(fmt.Sprintf("sending jsops: %s", jsOps))
		a.conn.Send(jsOps)
	}
}

func (a *Adapter) Recv(ops ot.Ops) {
	a.mu.Lock()
	defer a.mu.Unlock()

	pos := 0
	alert.String(fmt.Sprintf("recv(%s)", ops.String()))

	a.suppress = true
	defer func() {
		a.suppress = false
	}()

	for _, op := range ops {
		if op.Len() == 0 {
			continue
		}
		switch {
		case op.IsRetain():
			alert.String(fmt.Sprintf("retain(%d)", op.Size))
			pos += op.Size
			continue
		case op.IsInsert():
			rowcol := NewRowCol(a.doc, pos)
			alert.String(fmt.Sprintf("insert(%d, %q)", pos, op.Body.String()))
			// alert.JSON(rowcol)
			a.doc.Insert(rowcol, ot.AsString(op.Body))
			pos += op.Len()
			continue
		case op.IsDelete():
			startEnd := NewStartEnd(a.doc, pos, pos-op.Size)
			alert.String(fmt.Sprintf("remove(%d, %d)", pos, pos-op.Size))
			a.doc.Remove(startEnd)
		}
	}
}

func (a *Adapter) AttachFd(fd int) {
	a.fd = fd
}

func (a *Adapter) AttachEditor(session Lengther, doc Document) {
	a.session = session
	a.doc = doc
	doc.SetOnChange(a.OnChange)
}

func (a *Adapter) AttachSocket(state *ot.Controller, conn Sender) {
	a.state = state
	a.conn = conn
}

// RowCall(...)
//   -> getAllLines() -> Array[Line]
// StartEnd(...)
//   -> RowCol
// NewRange(...)
//   -> getLines(a, b)  -> Array[Line]  (.Get("length").Int())
// On("change")
// Insert()
// Remove()

func (a *Adapter) OnChange(change *js.Object) bool {
	if a.IsSuppressed() {
		alert.String("change SUPPRESSED")
		return true
	}

	a.mu.Lock()
	defer a.mu.Unlock()

	length := a.session.Length()
	alert.String(fmt.Sprintf("session len: %d", length))

	data := change.Get("data")
	alert.JSON(data)

	action := data.Get("action").String()
	textRange := NewRange(a.doc, NewJSStartEnd(data.Get("range")))

	start := textRange.Start()
	end := textRange.End()

	var ops ot.Ops
	switch action {
	case "insertText":
		str := data.Get("text").String()
		oldLen := length - (end - start)
		alert.String(fmt.Sprintf("newInsert(%d, %d, %q)", oldLen, start, str))
		ops = ot.NewInsert(oldLen, start, str)
	case "removeText":
		// BUG(mistone): end is bogus after delete when \n in text...
		// Repro: abc\ndef<<<^H
		text := data.Get("text").String()
		textLen := len(text)
		oldLen := length + textLen
		alert.String(fmt.Sprintf("newDelete(%d, %d, %d)", oldLen, start, textLen))
		ops = ot.NewDelete(oldLen, start, textLen)
	case "insertLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length - numRunes
		ops = ot.NewInsert(oldLen, start, str)
	case "removeLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length + numRunes
		ops = ot.NewDelete(oldLen, start, numRunes)
	}

	alert.String("sending ops")
	alert.Golang(ops)

	go a.state.OnClientWrite(ops)
	return true
}
<commit_msg>Hack up ACE adapter; add *minimal* tree support.<commit_after>// Copyright 2014 Akamai Technologies, Inc.
// Please see the accompanying LICENSE file for licensing information.
//
// Note: this file is a derivative work of
//
// https://github.com/share/ShareJS/blob/0.6/src/client/ace.coffee
//
// Copyright 2011 Joseph Gentle
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package ace

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"unicode/utf8"

	"github.com/gopherjs/gopherjs/js"

	"github.com/mstone/focus/js/alert"
	"github.com/mstone/focus/msg"
	"github.com/mstone/focus/ot"
)

type Lengther interface {
	Length() int
}

type Sender interface {
	Send(msg []byte)
}

type Adapter struct {
	mu       sync.Mutex
	conn     Sender
	session  Lengther
	doc      Document
	state    *ot.Controller
	suppress bool
	fd       int
}

func NewAdapter() *Adapter {
	return &Adapter{
		mu: sync.Mutex{},
	}
}

func (a *Adapter) Suppress(suppress bool) {
	a.suppress = suppress
}

func (a *Adapter) IsSuppressed() bool {
	return a.suppress
}

func (a *Adapter) Send(rev int, hash string, ops ot.Ops) {
	if !a.suppress {
		jsOps, _ := json.Marshal(msg.Msg{
			Cmd:  msg.C_WRITE,
			Fd:   a.fd,
			Rev:  rev,
			Hash: hash,
			Ops:  ops,
		})
		alert.Golang(fmt.Sprintf("sending jsops: %s", jsOps))
		a.conn.Send(jsOps)
	}
}

func (a *Adapter) Recv(ops ot.Ops) {
	a.mu.Lock()
	defer a.mu.Unlock()

	pos := 0
	alert.String(fmt.Sprintf("recv(%s)", ops.String()))

	a.suppress = true
	defer func() {
		a.suppress = false
	}()

	if len(ops) != 1 || !ops[0].IsWith() {
		alert.String("recv err; ops len != 1 || ops[0] != With(...)")
		panic(1)
	}
	o := ops[0]

	for _, op := range o.Kids {
		switch {
		case op.IsZero():
			continue
		case op.IsInsert():
			rowcol := NewRowCol(a.doc, pos)
			alert.String(fmt.Sprintf("insert(%d, %q)", pos, op.Body.String()))
			a.doc.Insert(rowcol, ot.AsString([]rune{op.Body.Leaf}))
			pos += op.Len()
			continue
		case op.IsRetain():
			alert.String(fmt.Sprintf("retain(%d)", op.Size))
			pos += op.Size
			continue
		case op.IsDelete():
			startEnd := NewStartEnd(a.doc, pos, pos-op.Size)
			alert.String(fmt.Sprintf("remove(%d, %d)", pos, pos-op.Size))
			a.doc.Remove(startEnd)
		case op.IsWith():
			alert.String("recv err; got inner with op; exiting")
			panic(2)
		}
	}
}

func (a *Adapter) AttachFd(fd int) {
	a.fd = fd
}

func (a *Adapter) AttachEditor(session Lengther, doc Document) {
	a.session = session
	a.doc = doc
	doc.SetOnChange(a.OnChange)
}

func (a *Adapter) AttachSocket(state *ot.Controller, conn Sender) {
	a.state = state
	a.conn = conn
}

// RowCall(...)
//   -> getAllLines() -> Array[Line]
// StartEnd(...)
//   -> RowCol
// NewRange(...)
//   -> getLines(a, b)  -> Array[Line]  (.Get("length").Int())
// On("change")
// Insert()
// Remove()

func (a *Adapter) OnChange(change *js.Object) bool {
	if a.IsSuppressed() {
		alert.String("change SUPPRESSED")
		return true
	}

	a.mu.Lock()
	defer a.mu.Unlock()

	length := a.session.Length()
	alert.String(fmt.Sprintf("session len: %d", length))

	data := change.Get("data")
	alert.JSON(data)

	action := data.Get("action").String()
	textRange := NewRange(a.doc, NewJSStartEnd(data.Get("range")))

	start := textRange.Start()
	end := textRange.End()

	var ops ot.Ops
	switch action {
	case "insertText":
		str := data.Get("text").String()
		oldLen := length - (end - start)
		alert.String(fmt.Sprintf("newInsert(%d, %d, %q)", oldLen, start, str))
		ops = ot.NewInsert(oldLen, start, str)
	case "removeText":
		// BUG(mistone): end is bogus after delete when \n in text...
		// Repro: abc\ndef<<<^H
		text := data.Get("text").String()
		textLen := len(text)
		oldLen := length + textLen
		alert.String(fmt.Sprintf("newDelete(%d, %d, %d)", oldLen, start, textLen))
		ops = ot.NewDelete(oldLen, start, textLen)
	case "insertLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length - numRunes
		ops = ot.NewInsert(oldLen, start, str)
	case "removeLines":
		linesObj := data.Get("lines")
		numLines := linesObj.Length()
		lines := make([]string, numLines)
		for i := 0; i < numLines; i++ {
			lines[i] = linesObj.Index(i).String() + "\n"
		}
		str := strings.Join(lines, "")
		numRunes := utf8.RuneCountInString(str)
		oldLen := length + numRunes
		ops = ot.NewDelete(oldLen, start, numRunes)
	}

	alert.String("sending ops")
	alert.Golang(ops)

	go a.state.OnClientWrite(ops)
	return true
}
<|endoftext|>


<commit_before>// Copyright ©2016 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package native

import (
	"math"

	"github.com/gonum/blas"
	"github.com/gonum/blas/blas64"
	"github.com/gonum/lapack"
)

// Dlaexc swaps two adjacent diagonal blocks of order 1 or 2 in an n×n upper
// quasi-triangular matrix T by an orthogonal similarity transformation.
//
// T must be in Schur canonical form, that is, block upper triangular with 1×1
// and 2×2 diagonal blocks; each 2×2 diagonal block has its diagonal elements
// equal and its off-diagonal elements of opposite sign. On return, T will
// contain the updated matrix again in Schur canonical form.
//
// If wantq is true, the transformation is accumulated in the n×n matrix Q,
// otherwise Q is not referenced.
//
// j1 is the index of the first row of the first block. n1 and n2 are the order
// of the first and second block, respectively.
//
// work much have length at least n, otherwise Dlaexc will panic.
//
// If ok is false, the transformed matrix T would be too far from Schur form.
// The blocks are not swapped, and T and Q are not modified.
func (impl Implementation) Dlaexc(wantq bool, n int, t []float64, ldt int, q []float64, ldq int, j1, n1, n2 int, work []float64) (ok bool) {
	checkMatrix(n, n, t, ldt)
	if wantq {
		checkMatrix(n, n, q, ldq)
	}
	if j1 < 0 || n <= j1 {
		panic("lapack: index j1 out of range")
	}
	if len(work) < n {
		panic(badWork)
	}
	if n1 < 0 || 2 < n1 {
		panic("lapack: invalid value of n1")
	}
	if n2 < 0 || 2 < n2 {
		panic("lapack: invalid value of n2")
	}

	if n == 0 || n1 == 0 || n2 == 0 {
		return true
	}
	if j1+n1 >= n {
		// TODO(vladimir-ch): Reference LAPACK does this check whether
		// the start of the second block is in the matrix T. It returns
		// true if it is not and moreover it does not check whether the
		// whole second block fits into T. This does not feel
		// satisfactory. The only caller of Dlaexc is Dtrexc, so if the
		// caller makes sure that this does not happen, we could be
		// stricter here.
		return true
	}

	j2 := j1 + 1
	j3 := j1 + 2

	bi := blas64.Implementation()

	if n1 == 1 && n2 == 1 {
		// Swap two 1×1 blocks.
		t11 := t[j1*ldt+j1]
		t22 := t[j2*ldt+j2]

		// Determine the transformation to perform the interchange.
		cs, sn, _ := impl.Dlartg(t[j1*ldt+j2], t22-t11)

		// Apply transformation to the matrix T.
		if n-j3 > 0 {
			bi.Drot(n-j3, t[j1*ldt+j3:], 1, t[j2*ldt+j3:], 1, cs, sn)
		}
		if j1 > 0 {
			bi.Drot(j1, t[j1:], ldt, t[j2:], ldt, cs, sn)
		}

		t[j1*ldt+j1] = t22
		t[j2*ldt+j2] = t11

		if wantq {
			// Accumulate transformation in the matrix Q.
			bi.Drot(n, q[j1:], ldq, q[j2:], ldq, cs, sn)
		}

		return true
	}

	// Swapping involves at least one 2×2 block.
	//
	// Copy the diagonal block of order n1+n2 to the local array d and
	// compute its norm.
	nd := n1 + n2
	var d [16]float64
	const ldd = 4
	impl.Dlacpy(blas.All, nd, nd, t[j1*ldt+j1:], ldt, d[:], ldd)
	dnorm := impl.Dlange(lapack.MaxAbs, nd, nd, d[:], ldd, work)

	// Compute machine-dependent threshold for test for accepting swap.
	eps := dlamchP
	thresh := math.Max(10*eps*dnorm, dlamchS/eps)

	// Solve T11*X - X*T22 = scale*T12 for X.
	var x [4]float64
	const ldx = 2
	scale, _, _ := impl.Dlasy2(false, false, -1, n1, n2, d[:], ldd, d[n1*ldd+n1:], ldd, d[n1+1:], ldd, x[:], ldx)

	// Swap the adjacent diagonal blocks.
	switch {
	case n1 == 1 && n2 == 2:
		// Generate elementary reflector H so that
		//  ( scale, X11, X12 ) H = ( 0, 0, * )
		u := [3]float64{scale, x[0], 1}
		_, tau := impl.Dlarfg(3, x[1], u[:2], 1)
		t11 := t[j1*ldt+j1]

		// Perform swap provisionally on diagonal block in d.
		impl.Dlarfx(blas.Left, 3, 3, u[:], tau, d[:], ldd, work)
		impl.Dlarfx(blas.Right, 3, 3, u[:], tau, d[:], ldd, work)

		// Test whether to reject swap.
		if math.Max(math.Abs(d[2*ldd]), math.Max(math.Abs(d[2*ldd+1]), math.Abs(d[2*ldd+2]-t11))) > thresh {
			return false
		}

		// Accept swap: apply transformation to the entire matrix T.
		impl.Dlarfx(blas.Left, 3, n-j1, u[:], tau, t[j1*ldt+j1:], ldt, work)
		impl.Dlarfx(blas.Right, j2+1, 3, u[:], tau, t[j1:], ldt, work)

		t[j3*ldt+j1] = 0
		t[j3*ldt+j2] = 0
		t[j3*ldt+j3] = t11

		if wantq {
			// Accumulate transformation in the matrix Q.
			impl.Dlarfx(blas.Right, n, 3, u[:], tau, q[j1:], ldq, work)
		}

	case n1 == 2 && n2 == 1:
		//  Generate elementary reflector H so that:
		//   H (  -X11 ) = ( * )
		//     (  -X21 ) = ( 0 )
		//     ( scale ) = ( 0 )
		u := [3]float64{1, -x[ldx], scale}
		_, tau := impl.Dlarfg(3, -x[0], u[1:], 1)
		t33 := t[j3*ldt+j3]

		// Perform swap provisionally on diagonal block in D.
		impl.Dlarfx(blas.Left, 3, 3, u[:], tau, d[:], ldd, work)
		impl.Dlarfx(blas.Right, 3, 3, u[:], tau, d[:], ldd, work)

		// Test whether to reject swap.
		if math.Max(math.Abs(d[ldd]), math.Max(math.Abs(d[2*ldd]), math.Abs(d[0]-t33))) > thresh {
			return false
		}

		// Accept swap: apply transformation to the entire matrix T.
		impl.Dlarfx(blas.Right, j3+1, 3, u[:], tau, t[j1:], ldt, work)
		impl.Dlarfx(blas.Left, 3, n-j1-1, u[:], tau, t[j1*ldt+j2:], ldt, work)

		t[j1*ldt+j1] = t33
		t[j2*ldt+j1] = 0
		t[j3*ldt+j1] = 0

		if wantq {
			// Accumulate transformation in the matrix Q.
			impl.Dlarfx(blas.Right, n, 3, u[:], tau, q[j1:], ldq, work)
		}

	default: // n1 == 2 && n2 == 2
		// Generate elementary reflectors H_1 and H_2 so that:
		//  H_2 H_1 (  -X11  -X12 ) = (  *  * )
		//          (  -X21  -X22 )   (  0  * )
		//          ( scale    0  )   (  0  0 )
		//          (    0  scale )   (  0  0 )
		u1 := [3]float64{1, -x[ldx], scale}
		_, tau1 := impl.Dlarfg(3, -x[0], u1[1:], 1)

		temp := -tau1 * (x[1] + u1[1]*x[ldx+1])
		u2 := [3]float64{1, -temp * u1[2], scale}
		_, tau2 := impl.Dlarfg(3, -temp*u1[1]-x[ldx+1], u2[1:], 1)

		// Perform swap provisionally on diagonal block in D.
		impl.Dlarfx(blas.Left, 3, 4, u1[:], tau1, d[:], ldd, work)
		impl.Dlarfx(blas.Right, 4, 3, u1[:], tau1, d[:], ldd, work)
		impl.Dlarfx(blas.Left, 3, 4, u2[:], tau2, d[ldd:], ldd, work)
		impl.Dlarfx(blas.Right, 4, 3, u2[:], tau2, d[1:], ldd, work)

		// Test whether to reject swap.
		m1 := math.Max(math.Abs(d[2*ldd]), math.Abs(d[2*ldd+1]))
		m2 := math.Max(math.Abs(d[3*ldd]), math.Abs(d[3*ldd+1]))
		if math.Max(m1, m2) > thresh {
			return false
		}

		// Accept swap: apply transformation to the entire matrix T.
		j4 := j1 + 3
		impl.Dlarfx(blas.Left, 3, n-j1, u1[:], tau1, t[j1*ldt+j1:], ldt, work)
		impl.Dlarfx(blas.Right, j4+1, 3, u1[:], tau1, t[j1:], ldt, work)
		impl.Dlarfx(blas.Left, 3, n-j1, u2[:], tau2, t[j2*ldt+j1:], ldt, work)
		impl.Dlarfx(blas.Right, j4+1, 3, u2[:], tau2, t[j2:], ldt, work)

		t[j3*ldt+j1] = 0
		t[j3*ldt+j2] = 0
		t[j4*ldt+j1] = 0
		t[j4*ldt+j2] = 0

		if wantq {
			// Accumulate transformation in the matrix Q.
			impl.Dlarfx(blas.Right, n, 3, u1[:], tau1, q[j1:], ldq, work)
			impl.Dlarfx(blas.Right, n, 3, u2[:], tau2, q[j2:], ldq, work)
		}
	}

	if n2 == 2 {
		// Standardize new 2×2 block T11.
		a, b := t[j1*ldt+j1], t[j1*ldt+j2]
		c, d := t[j2*ldt+j1], t[j2*ldt+j2]
		var cs, sn float64
		t[j1*ldt+j1], t[j1*ldt+j2], t[j2*ldt+j1], t[j2*ldt+j2], _, _, _, _, cs, sn = impl.Dlanv2(a, b, c, d)
		if n-j1-2 > 0 {
			bi.Drot(n-j1-2, t[j1*ldt+j1+2:], 1, t[j2*ldt+j1+2:], 1, cs, sn)
		}
		if j1 > 0 {
			bi.Drot(j1, t[j1:], ldt, t[j2:], ldt, cs, sn)
		}
		if wantq {
			bi.Drot(n, q[j1:], ldq, q[j2:], ldq, cs, sn)
		}
	}
	if n1 == 2 {
		// Standardize new 2×2 block T22.
		j3 := j1 + n2
		j4 := j3 + 1
		a, b := t[j3*ldt+j3], t[j3*ldt+j4]
		c, d := t[j4*ldt+j3], t[j4*ldt+j4]
		var cs, sn float64
		t[j3*ldt+j3], t[j3*ldt+j4], t[j4*ldt+j3], t[j4*ldt+j4], _, _, _, _, cs, sn = impl.Dlanv2(a, b, c, d)
		if n-j3-2 > 0 {
			bi.Drot(n-j3-2, t[j3*ldt+j3+2:], 1, t[j4*ldt+j3+2:], 1, cs, sn)
		}
		bi.Drot(j3, t[j3:], ldt, t[j4:], ldt, cs, sn)
		if wantq {
			bi.Drot(n, q[j3:], ldq, q[j4:], ldq, cs, sn)
		}
	}

	return true
}
<commit_msg>native: fix off-by-1 indexing error in Dlaexc<commit_after>// Copyright ©2016 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package native

import (
	"math"

	"github.com/gonum/blas"
	"github.com/gonum/blas/blas64"
	"github.com/gonum/lapack"
)

// Dlaexc swaps two adjacent diagonal blocks of order 1 or 2 in an n×n upper
// quasi-triangular matrix T by an orthogonal similarity transformation.
//
// T must be in Schur canonical form, that is, block upper triangular with 1×1
// and 2×2 diagonal blocks; each 2×2 diagonal block has its diagonal elements
// equal and its off-diagonal elements of opposite sign. On return, T will
// contain the updated matrix again in Schur canonical form.
//
// If wantq is true, the transformation is accumulated in the n×n matrix Q,
// otherwise Q is not referenced.
//
// j1 is the index of the first row of the first block. n1 and n2 are the order
// of the first and second block, respectively.
//
// work much have length at least n, otherwise Dlaexc will panic.
//
// If ok is false, the transformed matrix T would be too far from Schur form.
// The blocks are not swapped, and T and Q are not modified.
func (impl Implementation) Dlaexc(wantq bool, n int, t []float64, ldt int, q []float64, ldq int, j1, n1, n2 int, work []float64) (ok bool) {
	checkMatrix(n, n, t, ldt)
	if wantq {
		checkMatrix(n, n, q, ldq)
	}
	if j1 < 0 || n <= j1 {
		panic("lapack: index j1 out of range")
	}
	if len(work) < n {
		panic(badWork)
	}
	if n1 < 0 || 2 < n1 {
		panic("lapack: invalid value of n1")
	}
	if n2 < 0 || 2 < n2 {
		panic("lapack: invalid value of n2")
	}

	if n == 0 || n1 == 0 || n2 == 0 {
		return true
	}
	if j1+n1 >= n {
		// TODO(vladimir-ch): Reference LAPACK does this check whether
		// the start of the second block is in the matrix T. It returns
		// true if it is not and moreover it does not check whether the
		// whole second block fits into T. This does not feel
		// satisfactory. The only caller of Dlaexc is Dtrexc, so if the
		// caller makes sure that this does not happen, we could be
		// stricter here.
		return true
	}

	j2 := j1 + 1
	j3 := j1 + 2

	bi := blas64.Implementation()

	if n1 == 1 && n2 == 1 {
		// Swap two 1×1 blocks.
		t11 := t[j1*ldt+j1]
		t22 := t[j2*ldt+j2]

		// Determine the transformation to perform the interchange.
		cs, sn, _ := impl.Dlartg(t[j1*ldt+j2], t22-t11)

		// Apply transformation to the matrix T.
		if n-j3 > 0 {
			bi.Drot(n-j3, t[j1*ldt+j3:], 1, t[j2*ldt+j3:], 1, cs, sn)
		}
		if j1 > 0 {
			bi.Drot(j1, t[j1:], ldt, t[j2:], ldt, cs, sn)
		}

		t[j1*ldt+j1] = t22
		t[j2*ldt+j2] = t11

		if wantq {
			// Accumulate transformation in the matrix Q.
			bi.Drot(n, q[j1:], ldq, q[j2:], ldq, cs, sn)
		}

		return true
	}

	// Swapping involves at least one 2×2 block.
	//
	// Copy the diagonal block of order n1+n2 to the local array d and
	// compute its norm.
	nd := n1 + n2
	var d [16]float64
	const ldd = 4
	impl.Dlacpy(blas.All, nd, nd, t[j1*ldt+j1:], ldt, d[:], ldd)
	dnorm := impl.Dlange(lapack.MaxAbs, nd, nd, d[:], ldd, work)

	// Compute machine-dependent threshold for test for accepting swap.
	eps := dlamchP
	thresh := math.Max(10*eps*dnorm, dlamchS/eps)

	// Solve T11*X - X*T22 = scale*T12 for X.
	var x [4]float64
	const ldx = 2
	scale, _, _ := impl.Dlasy2(false, false, -1, n1, n2, d[:], ldd, d[n1*ldd+n1:], ldd, d[n1:], ldd, x[:], ldx)

	// Swap the adjacent diagonal blocks.
	switch {
	case n1 == 1 && n2 == 2:
		// Generate elementary reflector H so that
		//  ( scale, X11, X12 ) H = ( 0, 0, * )
		u := [3]float64{scale, x[0], 1}
		_, tau := impl.Dlarfg(3, x[1], u[:2], 1)
		t11 := t[j1*ldt+j1]

		// Perform swap provisionally on diagonal block in d.
		impl.Dlarfx(blas.Left, 3, 3, u[:], tau, d[:], ldd, work)
		impl.Dlarfx(blas.Right, 3, 3, u[:], tau, d[:], ldd, work)

		// Test whether to reject swap.
		if math.Max(math.Abs(d[2*ldd]), math.Max(math.Abs(d[2*ldd+1]), math.Abs(d[2*ldd+2]-t11))) > thresh {
			return false
		}

		// Accept swap: apply transformation to the entire matrix T.
		impl.Dlarfx(blas.Left, 3, n-j1, u[:], tau, t[j1*ldt+j1:], ldt, work)
		impl.Dlarfx(blas.Right, j2+1, 3, u[:], tau, t[j1:], ldt, work)

		t[j3*ldt+j1] = 0
		t[j3*ldt+j2] = 0
		t[j3*ldt+j3] = t11

		if wantq {
			// Accumulate transformation in the matrix Q.
			impl.Dlarfx(blas.Right, n, 3, u[:], tau, q[j1:], ldq, work)
		}

	case n1 == 2 && n2 == 1:
		//  Generate elementary reflector H so that:
		//   H (  -X11 ) = ( * )
		//     (  -X21 ) = ( 0 )
		//     ( scale ) = ( 0 )
		u := [3]float64{1, -x[ldx], scale}
		_, tau := impl.Dlarfg(3, -x[0], u[1:], 1)
		t33 := t[j3*ldt+j3]

		// Perform swap provisionally on diagonal block in D.
		impl.Dlarfx(blas.Left, 3, 3, u[:], tau, d[:], ldd, work)
		impl.Dlarfx(blas.Right, 3, 3, u[:], tau, d[:], ldd, work)

		// Test whether to reject swap.
		if math.Max(math.Abs(d[ldd]), math.Max(math.Abs(d[2*ldd]), math.Abs(d[0]-t33))) > thresh {
			return false
		}

		// Accept swap: apply transformation to the entire matrix T.
		impl.Dlarfx(blas.Right, j3+1, 3, u[:], tau, t[j1:], ldt, work)
		impl.Dlarfx(blas.Left, 3, n-j1-1, u[:], tau, t[j1*ldt+j2:], ldt, work)

		t[j1*ldt+j1] = t33
		t[j2*ldt+j1] = 0
		t[j3*ldt+j1] = 0

		if wantq {
			// Accumulate transformation in the matrix Q.
			impl.Dlarfx(blas.Right, n, 3, u[:], tau, q[j1:], ldq, work)
		}

	default: // n1 == 2 && n2 == 2
		// Generate elementary reflectors H_1 and H_2 so that:
		//  H_2 H_1 (  -X11  -X12 ) = (  *  * )
		//          (  -X21  -X22 )   (  0  * )
		//          ( scale    0  )   (  0  0 )
		//          (    0  scale )   (  0  0 )
		u1 := [3]float64{1, -x[ldx], scale}
		_, tau1 := impl.Dlarfg(3, -x[0], u1[1:], 1)

		temp := -tau1 * (x[1] + u1[1]*x[ldx+1])
		u2 := [3]float64{1, -temp * u1[2], scale}
		_, tau2 := impl.Dlarfg(3, -temp*u1[1]-x[ldx+1], u2[1:], 1)

		// Perform swap provisionally on diagonal block in D.
		impl.Dlarfx(blas.Left, 3, 4, u1[:], tau1, d[:], ldd, work)
		impl.Dlarfx(blas.Right, 4, 3, u1[:], tau1, d[:], ldd, work)
		impl.Dlarfx(blas.Left, 3, 4, u2[:], tau2, d[ldd:], ldd, work)
		impl.Dlarfx(blas.Right, 4, 3, u2[:], tau2, d[1:], ldd, work)

		// Test whether to reject swap.
		m1 := math.Max(math.Abs(d[2*ldd]), math.Abs(d[2*ldd+1]))
		m2 := math.Max(math.Abs(d[3*ldd]), math.Abs(d[3*ldd+1]))
		if math.Max(m1, m2) > thresh {
			return false
		}

		// Accept swap: apply transformation to the entire matrix T.
		j4 := j1 + 3
		impl.Dlarfx(blas.Left, 3, n-j1, u1[:], tau1, t[j1*ldt+j1:], ldt, work)
		impl.Dlarfx(blas.Right, j4+1, 3, u1[:], tau1, t[j1:], ldt, work)
		impl.Dlarfx(blas.Left, 3, n-j1, u2[:], tau2, t[j2*ldt+j1:], ldt, work)
		impl.Dlarfx(blas.Right, j4+1, 3, u2[:], tau2, t[j2:], ldt, work)

		t[j3*ldt+j1] = 0
		t[j3*ldt+j2] = 0
		t[j4*ldt+j1] = 0
		t[j4*ldt+j2] = 0

		if wantq {
			// Accumulate transformation in the matrix Q.
			impl.Dlarfx(blas.Right, n, 3, u1[:], tau1, q[j1:], ldq, work)
			impl.Dlarfx(blas.Right, n, 3, u2[:], tau2, q[j2:], ldq, work)
		}
	}

	if n2 == 2 {
		// Standardize new 2×2 block T11.
		a, b := t[j1*ldt+j1], t[j1*ldt+j2]
		c, d := t[j2*ldt+j1], t[j2*ldt+j2]
		var cs, sn float64
		t[j1*ldt+j1], t[j1*ldt+j2], t[j2*ldt+j1], t[j2*ldt+j2], _, _, _, _, cs, sn = impl.Dlanv2(a, b, c, d)
		if n-j1-2 > 0 {
			bi.Drot(n-j1-2, t[j1*ldt+j1+2:], 1, t[j2*ldt+j1+2:], 1, cs, sn)
		}
		if j1 > 0 {
			bi.Drot(j1, t[j1:], ldt, t[j2:], ldt, cs, sn)
		}
		if wantq {
			bi.Drot(n, q[j1:], ldq, q[j2:], ldq, cs, sn)
		}
	}
	if n1 == 2 {
		// Standardize new 2×2 block T22.
		j3 := j1 + n2
		j4 := j3 + 1
		a, b := t[j3*ldt+j3], t[j3*ldt+j4]
		c, d := t[j4*ldt+j3], t[j4*ldt+j4]
		var cs, sn float64
		t[j3*ldt+j3], t[j3*ldt+j4], t[j4*ldt+j3], t[j4*ldt+j4], _, _, _, _, cs, sn = impl.Dlanv2(a, b, c, d)
		if n-j3-2 > 0 {
			bi.Drot(n-j3-2, t[j3*ldt+j3+2:], 1, t[j4*ldt+j3+2:], 1, cs, sn)
		}
		bi.Drot(j3, t[j3:], ldt, t[j4:], ldt, cs, sn)
		if wantq {
			bi.Drot(n, q[j3:], ldq, q[j4:], ldq, cs, sn)
		}
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright 2016 Joel Scoble and The JoeFriday 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 stat handles processing of network information: /proc/net/dev
package info

import (
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/SermoDigital/helpers"
	joe "github.com/mohae/joefriday"
	"github.com/mohae/joefriday/net/structs"
)

// The proc file used by the Profiler.
const ProcFile = "/proc/net/dev"

// Profiler is used to process the /proc/net/dev file.
type Profiler struct {
	*joe.Proc
}

// Returns an initialized Profiler; ready to use.
func New() (prof *Profiler, err error) {
	proc, err := joe.New(ProcFile)
	if err != nil {
		return nil, err
	}
	return &Profiler{Proc: proc}, nil
}

// Get returns the current network information.
func (prof *Profiler) Get() (*structs.Info, error) {
	var (
		l, i, pos, fieldNum int
		n                   uint64
		v                   byte
	)
	err := prof.Reset()
	if err != nil {
		return nil, err
	}
	// there's always at least 2 interfaces (I think)
	inf := &structs.Info{Timestamp: time.Now().UTC().UnixNano(), Interfaces: make([]structs.Interface, 0, 2)}
	for {
		prof.Val = prof.Val[:0]
		prof.Line, err = prof.Buf.ReadSlice('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, fmt.Errorf("error reading output bytes: %s", err)
		}
		l++
		if l < 3 {
			continue
		}
		var iInfo structs.Interface
		// first grab the interface name (everything up to the ':')
		for i, v = range prof.Line {
			if v == 0x3A {
				pos = i + 1
				break
			}
			// skip spaces
			if v == 0x20 {
				continue
			}
			prof.Val = append(prof.Val, v)
		}
		iInfo.Name = string(prof.Val[:])
		fieldNum = 0
		// process the rest of the line
		for {
			fieldNum++
			// skip all spaces
			for i, v = range prof.Line[pos:] {
				if v != 0x20 {
					pos += i
					break
				}
			}

			// grab the numbers
			for i, v = range prof.Line[pos:] {
				if v == 0x20 || v == '\n' {

					break
				}
			}
			// any conversion error results in 0
			n, err = helpers.ParseUint(prof.Line[pos : pos+i])
			pos += i
			if err != nil {
				return nil, fmt.Errorf("%s: %s", iInfo.Name, err)
			}
			if fieldNum < 9 {
				if fieldNum < 5 {
					if fieldNum < 3 {
						if fieldNum == 1 {
							iInfo.RBytes = int64(n)
							continue
						}
						iInfo.RPackets = int64(n) // must be 2
						continue
					}
					if fieldNum == 3 {
						iInfo.RErrs = int64(n)
						continue
					}
					iInfo.RDrop = int64(n) // must be 4
					continue
				}
				if fieldNum < 7 {
					if fieldNum == 5 {
						iInfo.RFIFO = int64(n)
						continue
					}
					iInfo.RFrame = int64(n) // must be 6
					continue
				}
				if fieldNum == 7 {
					iInfo.RCompressed = int64(n)
					continue
				}
				iInfo.RMulticast = int64(n) // must be 8
				continue
			}
			if fieldNum < 13 {
				if fieldNum < 11 {
					if fieldNum == 9 {
						iInfo.TBytes = int64(n)
						continue
					}
					iInfo.TPackets = int64(n) // must be 10
					continue
				}
				if fieldNum == 11 {
					iInfo.TErrs = int64(n)
					continue
				}
				iInfo.TDrop = int64(n) // must be 12
				continue
			}
			if fieldNum < 15 {
				if fieldNum == 13 {
					iInfo.TFIFO = int64(n)
					continue
				}
				iInfo.TColls = int64(n) // must be 14
				continue
			}
			if fieldNum == 15 {
				iInfo.TCarrier = int64(n)
				continue
			}
			if fieldNum == 16 {
				iInfo.TCompressed = int64(n)
				break
			}
		}
		inf.Interfaces = append(inf.Interfaces, iInfo)
	}
	return inf, nil
}

var std *Profiler
var stdMu sync.Mutex

// Get returns the current network information using the package's global
// Profiler.
func Get() (inf *structs.Info, err error) {
	stdMu.Lock()
	defer stdMu.Unlock()
	if std == nil {
		std, err = New()
		if err != nil {
			return nil, err
		}
	}
	return std.Get()
}

// Ticker processes meminfo information on a ticker.  The generated data is
// sent to the out channel.  Any errors encountered are sent to the errs
// channel.  Processing ends when a done signal is received.
//
// It is the callers responsibility to close the done and errs channels.
func (prof *Profiler) Ticker(interval time.Duration, out chan *structs.Info, done chan struct{}, errs chan error) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()
	defer close(out)

	for {
		select {
		case <-done:
			return
		case <-ticker.C:
			info, err := prof.Get()
			if err != nil {
				errs <- err
				continue
			}
			out <- info
		}
	}
}

// Ticker gathers information on a ticker using the specified interval.
// This uses a local Profiler as using the global doesn't make sense for
// an ongoing ticker.
func Ticker(interval time.Duration, out chan *structs.Info, done chan struct{}, errs chan error) {
	prof, err := New()
	if err != nil {
		errs <- err
		close(out)
		return
	}
	prof.Ticker(interval, out, done, errs)
}
<commit_msg>net/info move structs.Interface declaration outside of the loop<commit_after>// Copyright 2016 Joel Scoble and The JoeFriday 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 stat handles processing of network information: /proc/net/dev
package info

import (
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/SermoDigital/helpers"
	joe "github.com/mohae/joefriday"
	"github.com/mohae/joefriday/net/structs"
)

// The proc file used by the Profiler.
const ProcFile = "/proc/net/dev"

// Profiler is used to process the /proc/net/dev file.
type Profiler struct {
	*joe.Proc
}

// Returns an initialized Profiler; ready to use.
func New() (prof *Profiler, err error) {
	proc, err := joe.New(ProcFile)
	if err != nil {
		return nil, err
	}
	return &Profiler{Proc: proc}, nil
}

// Get returns the current network information.
func (prof *Profiler) Get() (*structs.Info, error) {
	var (
		l, i, pos, fieldNum int
		n                   uint64
		v                   byte
		iInfo               structs.Interface
	)
	err := prof.Reset()
	if err != nil {
		return nil, err
	}
	// there's always at least 2 interfaces (I think)
	inf := &structs.Info{Timestamp: time.Now().UTC().UnixNano(), Interfaces: make([]structs.Interface, 0, 2)}
	for {
		prof.Line, err = prof.Buf.ReadSlice('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, fmt.Errorf("error reading output bytes: %s", err)
		}
		l++
		if l < 3 {
			continue
		}
		prof.Val = prof.Val[:0]
		// first grab the interface name (everything up to the ':')
		for i, v = range prof.Line {
			if v == 0x3A {
				pos = i + 1
				break
			}
			// skip spaces
			if v == 0x20 {
				continue
			}
			prof.Val = append(prof.Val, v)
		}
		iInfo.Name = string(prof.Val[:])
		fieldNum = 0
		// process the rest of the line
		for {
			fieldNum++
			// skip all spaces
			for i, v = range prof.Line[pos:] {
				if v != 0x20 {
					pos += i
					break
				}
			}

			// grab the numbers
			for i, v = range prof.Line[pos:] {
				if v == 0x20 || v == '\n' {
					break
				}
			}
			// any conversion error results in 0
			n, err = helpers.ParseUint(prof.Line[pos : pos+i])
			pos += i
			if err != nil {
				return nil, fmt.Errorf("%s: %s", iInfo.Name, err)
			}
			if fieldNum < 9 {
				if fieldNum < 5 {
					if fieldNum < 3 {
						if fieldNum == 1 {
							iInfo.RBytes = int64(n)
							continue
						}
						iInfo.RPackets = int64(n) // must be 2
						continue
					}
					if fieldNum == 3 {
						iInfo.RErrs = int64(n)
						continue
					}
					iInfo.RDrop = int64(n) // must be 4
					continue
				}
				if fieldNum < 7 {
					if fieldNum == 5 {
						iInfo.RFIFO = int64(n)
						continue
					}
					iInfo.RFrame = int64(n) // must be 6
					continue
				}
				if fieldNum == 7 {
					iInfo.RCompressed = int64(n)
					continue
				}
				iInfo.RMulticast = int64(n) // must be 8
				continue
			}
			if fieldNum < 13 {
				if fieldNum < 11 {
					if fieldNum == 9 {
						iInfo.TBytes = int64(n)
						continue
					}
					iInfo.TPackets = int64(n) // must be 10
					continue
				}
				if fieldNum == 11 {
					iInfo.TErrs = int64(n)
					continue
				}
				iInfo.TDrop = int64(n) // must be 12
				continue
			}
			if fieldNum < 15 {
				if fieldNum == 13 {
					iInfo.TFIFO = int64(n)
					continue
				}
				iInfo.TColls = int64(n) // must be 14
				continue
			}
			if fieldNum == 15 {
				iInfo.TCarrier = int64(n)
				continue
			}
			if fieldNum == 16 {
				iInfo.TCompressed = int64(n)
				break
			}
		}
		inf.Interfaces = append(inf.Interfaces, iInfo)
	}
	return inf, nil
}

var std *Profiler
var stdMu sync.Mutex

// Get returns the current network information using the package's global
// Profiler.
func Get() (inf *structs.Info, err error) {
	stdMu.Lock()
	defer stdMu.Unlock()
	if std == nil {
		std, err = New()
		if err != nil {
			return nil, err
		}
	}
	return std.Get()
}

// Ticker processes meminfo information on a ticker.  The generated data is
// sent to the out channel.  Any errors encountered are sent to the errs
// channel.  Processing ends when a done signal is received.
//
// It is the callers responsibility to close the done and errs channels.
func (prof *Profiler) Ticker(interval time.Duration, out chan *structs.Info, done chan struct{}, errs chan error) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()
	defer close(out)

	for {
		select {
		case <-done:
			return
		case <-ticker.C:
			info, err := prof.Get()
			if err != nil {
				errs <- err
				continue
			}
			out <- info
		}
	}
}

// Ticker gathers information on a ticker using the specified interval.
// This uses a local Profiler as using the global doesn't make sense for
// an ongoing ticker.
func Ticker(interval time.Duration, out chan *structs.Info, done chan struct{}, errs chan error) {
	prof, err := New()
	if err != nil {
		errs <- err
		close(out)
		return
	}
	prof.Ticker(interval, out, done, errs)
}
<|endoftext|>


<commit_before>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"errors"
	"fmt"
	"model"
	"os"
	"regexp"
	"time"

	"github.com/gorilla/schema"
	"github.com/polaris1119/logger"
	"golang.org/x/net/context"
)

var schemaDecoder = schema.NewDecoder()

func init() {
	schemaDecoder.SetAliasTag("json")
	schemaDecoder.IgnoreUnknownKeys(true)
}

var NotModifyAuthorityErr = errors.New("没有修改权限")

func GetLogger(ctx context.Context) *logger.Logger {
	if ctx == nil {
		return logger.New(os.Stdout)
	}

	_logger, ok := ctx.Value("logger").(*logger.Logger)
	if ok {
		return _logger
	}

	return logger.New(os.Stdout)
}

// parseAtUser 解析 @某人
func parseAtUser(ctx context.Context, content string) string {
	reg := regexp.MustCompile(`@([^\s@]{4,20})`)
	return reg.ReplaceAllStringFunc(content, func(matched string) string {
		username := matched[1:]

		// 校验 username 是否存在
		user := DefaultUser.FindOne(ctx, "username", username)
		if user.Username != username {
			return matched
		}
		return fmt.Sprintf(`<a href="/user/%s" title="%s">%s</a>`, username, matched, matched)
	})
}

// CanEdit 判断能否编辑
func CanEdit(me *model.Me, curModel interface{}) bool {
	if me == nil {
		return false
	}

	if me.IsAdmin {
		return true
	}

	canEditTime := time.Duration(UserSetting["can_edit_time"]) * time.Second

	switch entity := curModel.(type) {
	case *model.Topic:
		if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime {
			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case *model.Article:
		// 文章的能编辑时间是15天
		if time.Now().Sub(time.Time(entity.Ctime)) > 15*86400*time.Second {
			return false
		}

		if me.Username == entity.Author {
			return true
		}
	case *model.Resource:
		if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime {
			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case *model.OpenProject:
		// 开源项目的能编辑时间是30天
		if time.Now().Sub(time.Time(entity.Ctime)) > 30*86400*time.Second {
			return false
		}

		if me.Username == entity.Username {
			return true
		}
	case *model.Wiki:
		if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime {
			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case *model.Book:
		if time.Now().Sub(time.Time(entity.CreatedAt)) > canEditTime {
			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case map[string]interface{}:
		if ctime, ok := entity["ctime"]; ok {
			if time.Now().Sub(time.Time(ctime.(model.OftenTime))) > canEditTime {
				return false
			}
		}

		if createdAt, ok := entity["created_at"]; ok {
			if time.Now().Sub(time.Time(createdAt.(model.OftenTime))) > canEditTime {
				return false
			}
		}

		if uid, ok := entity["uid"]; ok {
			if me.Uid == uid.(int) {
				return true
			}
		}

		if username, ok := entity["username"]; ok {
			if me.Username == username.(string) {
				return true
			}
		}
	}

	return false
}

func CanPublish(dauAuth, objtype int) bool {
	if dauAuth == 0 {
		return true
	}

	switch objtype {
	case model.TypeTopic:
		return (dauAuth & model.DauAuthTopic) == model.DauAuthTopic
	case model.TypeArticle:
		return (dauAuth & model.DauAuthArticle) == model.DauAuthArticle
	case model.TypeResource:
		return (dauAuth & model.DauAuthResource) == model.DauAuthResource
	case model.TypeProject:
		return (dauAuth & model.DauAuthProject) == model.DauAuthProject
	case model.TypeWiki:
		return (dauAuth & model.DauAuthWiki) == model.DauAuthWiki
	case model.TypeBook:
		return (dauAuth & model.DauAuthBook) == model.DauAuthBook
	case model.TypeComment:
		return (dauAuth & model.DauAuthComment) == model.DauAuthComment
	case model.TypeTop:
		return (dauAuth & model.DauAuthTop) == model.DauAuthTop
	default:
		return true
	}
}

func website() string {
	host := "http://"
	if WebsiteSetting.OnlyHttps {
		host = "https://"
	}
	return host + WebsiteSetting.Domain
}
<commit_msg>是否能编辑修改<commit_after>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"errors"
	"fmt"
	"model"
	"os"
	"regexp"
	"time"

	"github.com/gorilla/schema"
	"github.com/polaris1119/logger"
	"golang.org/x/net/context"
)

var schemaDecoder = schema.NewDecoder()

func init() {
	schemaDecoder.SetAliasTag("json")
	schemaDecoder.IgnoreUnknownKeys(true)
}

var NotModifyAuthorityErr = errors.New("没有修改权限")

func GetLogger(ctx context.Context) *logger.Logger {
	if ctx == nil {
		return logger.New(os.Stdout)
	}

	_logger, ok := ctx.Value("logger").(*logger.Logger)
	if ok {
		return _logger
	}

	return logger.New(os.Stdout)
}

// parseAtUser 解析 @某人
func parseAtUser(ctx context.Context, content string) string {
	reg := regexp.MustCompile(`@([^\s@]{4,20})`)
	return reg.ReplaceAllStringFunc(content, func(matched string) string {
		username := matched[1:]

		// 校验 username 是否存在
		user := DefaultUser.FindOne(ctx, "username", username)
		if user.Username != username {
			return matched
		}
		return fmt.Sprintf(`<a href="/user/%s" title="%s">%s</a>`, username, matched, matched)
	})
}

// CanEdit 判断能否编辑
func CanEdit(me *model.Me, curModel interface{}) bool {
	if me == nil {
		return false
	}

	canEditTime := time.Duration(UserSetting["can_edit_time"]) * time.Second

	switch entity := curModel.(type) {
	case *model.Topic:
		if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime {
			if me.Uid != entity.Uid && me.IsAdmin {
				return true
			}

			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case *model.Article:
		if me.IsAdmin {
			return true
		}

		// 文章的能编辑时间是15天
		if time.Now().Sub(time.Time(entity.Ctime)) > 15*86400*time.Second {
			return false
		}

		if me.Username == entity.Author {
			return true
		}
	case *model.Resource:
		if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime {
			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case *model.OpenProject:
		if me.IsAdmin {
			return true
		}

		// 开源项目的能编辑时间是30天
		if time.Now().Sub(time.Time(entity.Ctime)) > 30*86400*time.Second {
			return false
		}

		if me.Username == entity.Username {
			return true
		}
	case *model.Wiki:
		if me.IsAdmin {
			return true
		}
		if time.Now().Sub(time.Time(entity.Ctime)) > canEditTime {
			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case *model.Book:
		if me.IsAdmin {
			return true
		}
		if time.Now().Sub(time.Time(entity.CreatedAt)) > canEditTime {
			return false
		}

		if me.Uid == entity.Uid {
			return true
		}
	case map[string]interface{}:
		if ctime, ok := entity["ctime"]; ok {
			if time.Now().Sub(time.Time(ctime.(model.OftenTime))) > canEditTime {
				return false
			}
		}

		if createdAt, ok := entity["created_at"]; ok {
			if time.Now().Sub(time.Time(createdAt.(model.OftenTime))) > canEditTime {
				return false
			}
		}

		if uid, ok := entity["uid"]; ok {
			if me.Uid == uid.(int) {
				return true
			}
		}

		if username, ok := entity["username"]; ok {
			if me.Username == username.(string) {
				return true
			}
		}
	}

	return false
}

func CanPublish(dauAuth, objtype int) bool {
	if dauAuth == 0 {
		return true
	}

	switch objtype {
	case model.TypeTopic:
		return (dauAuth & model.DauAuthTopic) == model.DauAuthTopic
	case model.TypeArticle:
		return (dauAuth & model.DauAuthArticle) == model.DauAuthArticle
	case model.TypeResource:
		return (dauAuth & model.DauAuthResource) == model.DauAuthResource
	case model.TypeProject:
		return (dauAuth & model.DauAuthProject) == model.DauAuthProject
	case model.TypeWiki:
		return (dauAuth & model.DauAuthWiki) == model.DauAuthWiki
	case model.TypeBook:
		return (dauAuth & model.DauAuthBook) == model.DauAuthBook
	case model.TypeComment:
		return (dauAuth & model.DauAuthComment) == model.DauAuthComment
	case model.TypeTop:
		return (dauAuth & model.DauAuthTop) == model.DauAuthTop
	default:
		return true
	}
}

func website() string {
	host := "http://"
	if WebsiteSetting.OnlyHttps {
		host = "https://"
	}
	return host + WebsiteSetting.Domain
}
<|endoftext|>


<commit_before>package gou

import (
	"bytes"
	"encoding/json"
	"math"
	"reflect"
	"strconv"
	"strings"
)

// Convert a slice of bytes into an array by ensuring it is wrapped
//  with []
func MakeJsonList(b []byte) []byte {
	if !bytes.HasPrefix(b, []byte{'['}) {
		b = append([]byte{'['}, b...)
		b = append(b, ']')
	}
	return b
}

func JsonString(v interface{}) string {
	b, _ := json.Marshal(v)
	return string(b)
}

type JsonRawWriter struct {
	bytes.Buffer
}

func (m *JsonRawWriter) MarshalJSON() ([]byte, error) {
	return m.Bytes(), nil
}

func (m *JsonRawWriter) Raw() json.RawMessage {
	return json.RawMessage(m.Bytes())
}

// A simple wrapper tohelp json config files be more easily used
// allows usage such as this
//
//		jh := NewJsonHelper([]byte(`{
//			"name":"string",
//			"ints":[1,5,9,11],
//			"int":1,
//			"int64":1234567890,
//			"MaxSize" : 1048576,
//			"strings":["string1"],
//			"nested":{
//				"nest":"string2",
//				"strings":["string1"],
//				"int":2,
//				"list":["value"],
//				"nest2":{
//					"test":"good"
//				}
//			},
//			"nested2":[
//				{"sub":5}
//			]
//		}`)
//
//		i := jh.Int("nested.int")  // 2
//		i2 := jh.Int("ints[1]")    // 5   array position 1 from [1,5,9,11]
//		s := jh.String("nested.nest")  // "string2"
//
type JsonHelper map[string]interface{}

func NewJsonHelper(b []byte) JsonHelper {
	jh := make(JsonHelper)
	json.Unmarshal(b, &jh)
	return jh
}
func NewJsonHelpers(b []byte) []JsonHelper {
	var jhl []JsonHelper
	json.Unmarshal(MakeJsonList(b), &jhl)
	return jhl
}
func (j JsonHelper) Helper(n string) JsonHelper {
	if v, ok := j[n]; ok {
		switch v.(type) {
		case map[string]interface{}:
			cn := JsonHelper{}
			for n, val := range v.(map[string]interface{}) {
				cn[n] = val
			}
			return cn
		case map[string]string:
			cn := JsonHelper{}
			for n, val := range v.(map[string]string) {
				cn[n] = val
			}
			return cn
		case JsonHelper:
			return v.(JsonHelper)
		default:
			rv := reflect.ValueOf(v)
			Debug("no map? ", v, rv.String(), rv.Type())
		}
	}
	return nil
}
func jsonList(v interface{}) []interface{} {
	switch v.(type) {
	case []interface{}:
		return v.([]interface{})
	}
	return nil
}
func jsonEntry(name string, v interface{}) (interface{}, bool) {
	switch v.(type) {
	case map[string]interface{}:
		if root, ok := v.(map[string]interface{})[name]; ok {
			return root, true
		} else {
			return nil, false
		}
	case JsonHelper:
		return v.(JsonHelper).Get(name), true
	case []interface{}:
		return v, true
	default:
		Debug("no type? ", name, " ", v)
		return nil, false
	}
	return nil, false
}
func (j JsonHelper) Get(n string) interface{} {
	var parts []string
	if strings.Contains(n, "/") {
		parts = strings.Split(n, "/")
		if strings.HasPrefix(n, "/") && len(parts) > 0 {
			parts = parts[1:]
		}
	} else {
		parts = strings.Split(n, ".")
	}
	var root interface{}
	var err error
	var ok, isList, listEntry bool
	var ln, st, idx int
	for ict, name := range parts {
		isList = strings.HasSuffix(name, "[]")
		listEntry = strings.HasSuffix(name, "]") && !isList
		ln, idx = len(name), -1
		if isList || listEntry {
			st = strings.Index(name, "[")
			idx, err = strconv.Atoi(name[st+1 : ln-1])
			name = name[:st]
		}
		if ict == 0 {
			root, ok = j[name]
		} else {
			root, ok = jsonEntry(name, root)
		}
		//Debug(isList, listEntry, " ", name, " ", root, " ", ok, err)
		if !ok {
			return nil
		}
		if isList {
			return jsonList(root)
		} else if listEntry && err == nil {
			if lst := jsonList(root); lst != nil && len(lst) > idx {
				root = lst[idx]
			} else {
				return nil
			}
		}

	}
	return root
}

// Get list of Helpers at given name
func (j JsonHelper) Helpers(n string) []JsonHelper {
	v := j.Get(n)
	if v == nil {
		return nil
	}
	switch v.(type) {
	case []map[string]interface{}:
		hl := make([]JsonHelper, 0)
		for _, val := range v.([]map[string]interface{}) {
			hl = append(hl, val)
		}
		return hl
	case []interface{}:
		if l, ok := v.([]interface{}); ok {
			jhl := make([]JsonHelper, 0)
			for _, item := range l {
				//println(item)
				if jh, ok := item.(map[string]interface{}); ok {
					jhl = append(jhl, jh)
				} else {
					Debug(jh)
				}
			}
			return jhl
		}
	}

	return nil
}
func (j JsonHelper) List(n string) []interface{} {
	v := j.Get(n)
	if l, ok := v.([]interface{}); ok {
		return l
	}
	return nil
}
func (j JsonHelper) Int64(n string) int64 {
	i64, ok := j.Int64Safe(n)
	if !ok {
		return -1
	}
	return i64
}
func (j JsonHelper) String(n string) string {
	if v := j.Get(n); v != nil {
		switch v.(type) {
		case string:
			return v.(string)
		case int:
			return strconv.Itoa(v.(int))
		case int32:
			return strconv.FormatInt(int64(v.(int32)), 10)
		case int64:
			return strconv.FormatInt(v.(int64), 10)
		case uint32:
			return strconv.FormatUint(uint64(v.(uint32)), 10)
		case uint64:
			return strconv.FormatUint(v.(uint64), 10)
		case float64:
			//(v.(float64), 10)
			return strconv.FormatInt(int64(v.(float64)), 10)
		default:
			to := reflect.TypeOf(v)
			Debug("not string type? ", n, " ", v, " ", to.String())
		}
	}
	return ""
}
func (j JsonHelper) Strings(n string) []string {
	if v := j.Get(n); v != nil {
		//Debug(n, " ", v)
		switch v.(type) {
		case string:
			return strings.Split(v.(string), ",")
		case []string:
			//Debug("type []string")
			return v.([]string)
		case []interface{}:
			//Debug("Kind = []interface{} n=", n, "  v=", v)
			sva := make([]string, 0)
			for _, av := range v.([]interface{}) {
				switch av.(type) {
				case string:
					sva = append(sva, av.(string))
				default:
					//Debug("Kind ? ", av)
				}
			}
			return sva
		default:
			//Debug("Kind = ?? ", n, v)
		}
	}
	return nil
}
func (j JsonHelper) Ints(n string) []int {
	v := j.Get(n)
	if v == nil {
		return nil
	}
	if sl, isSlice := v.([]interface{}); isSlice {
		iva := make([]int, 0)
		for _, av := range sl {
			avAsInt, ok := valToInt(av)
			if ok {
				iva = append(iva, avAsInt)
			}
		}
		return iva
	}
	return nil
}
func (j JsonHelper) StringSafe(n string) (string, bool) {
	v := j.Get(n)
	if v != nil {
		if s, ok := v.(string); ok {
			return s, ok
		}
	}
	return "", false
}
func (j JsonHelper) Int(n string) int {
	i, ok := j.IntSafe(n)
	if !ok {
		return -1
	}
	return i
}
func (j JsonHelper) Int64Safe(n string) (int64, bool) {
	v := j.Get(n)
	return valToInt64(v)

}
func (j JsonHelper) IntSafe(n string) (int, bool) {
	v := j.Get(n)
	return valToInt(v)
}

// Given any numeric type (float*, int*, uint*, string) return an int. Returns false if it would
// overflow or if the the argument is not numeric.
func valToInt(i interface{}) (int, bool) {
	i64, ok := valToInt64(i)
	if !ok {
		return -1, false
	}
	if i64 > MaxInt || i64 < MinInt {
		return -1, false
	}
	return int(i64), true
}

// Given any simple type (float*, int*, uint*, string) return an int64. Returns false if it would
// overflow or if the the argument is not numeric.
func valToInt64(i interface{}) (int64, bool) {
	switch x := i.(type) {
	case float32:
		return int64(x), true
	case float64:
		return int64(x), true
	case uint8:
		return int64(x), true
	case uint16:
		return int64(x), true
	case uint32:
		return int64(x), true
	case uint64:
		if x > math.MaxInt64 {
			return 0, false
		}
		return int64(x), true
	case int8:
		return int64(x), true
	case int16:
		return int64(x), true
	case int32:
		return int64(x), true
	case int64:
		return int64(x), true
	case int:
		return int64(x), true
	case uint:
		if uint64(x) > math.MaxInt64 {
			return 0, false
		}
		return int64(x), true
	case string:
		if len(x) > 0 {
			if iv, err := strconv.ParseInt(x, 10, 64); err == nil {
				return iv, true
			}
		}
	}
	return 0, false
}
func (j JsonHelper) Uint64(n string) uint64 {
	v := j.Get(n)
	if v != nil {
		switch v.(type) {
		case int:
			return uint64(v.(int))
		case int64:
			return uint64(v.(int64))
		case uint32:
			return uint64(v.(uint32))
		case uint64:
			return uint64(v.(uint64))
		case float32:
			f := float64(v.(float32))
			return uint64(f)
		case float64:
			f := v.(float64)
			return uint64(f)
		default:
			Debug("no type? ", n, " ", v)
		}
	}
	return 0
}

func (j JsonHelper) BoolSafe(n string) (val bool, ok bool) {
	v := j.Get(n)
	if v != nil {
		switch v.(type) {
		case bool:
			return v.(bool), true
		case string:
			if s := v.(string); len(s) > 0 {
				if b, err := strconv.ParseBool(s); err == nil {
					return b, true
				}
			}
		}
	}
	return false, false
}

func (j JsonHelper) Bool(n string) bool {
	val, ok := j.BoolSafe(n)
	if !ok {
		return false
	}

	return val

}

func (j JsonHelper) MapSafe(n string) (map[string]interface{}, bool) {
	v := j.Get(n)
	if v == nil {
		return nil, false
	}
	m, ok := v.(map[string]interface{})
	if !ok {
		return nil, false
	}
	return m, true
}

// The following consts are from http://code.google.com/p/go-bit/ (Apache licensed). It
// lets us figure out how wide go ints are, and determine their max and min values.

// Note the use of << to create an untyped constant.
const bitsPerWord = 32 << uint(^uint(0)>>63)

// Implementation-specific size of int and uint in bits.
const BitsPerWord = bitsPerWord // either 32 or 64

// Implementation-specific integer limit values.
const (
	MaxInt  = 1<<(BitsPerWord-1) - 1 // either 1<<31 - 1 or 1<<63 - 1
	MinInt  = -MaxInt - 1            // either -1 << 31 or -1 << 63
	MaxUint = 1<<BitsPerWord - 1     // either 1<<32 - 1 or 1<<64 - 1
)
<commit_msg>pretty json<commit_after>package gou

import (
	"bytes"
	"encoding/json"
	"math"
	"reflect"
	"strconv"
	"strings"
)

// Convert a slice of bytes into an array by ensuring it is wrapped
//  with []
func MakeJsonList(b []byte) []byte {
	if !bytes.HasPrefix(b, []byte{'['}) {
		b = append([]byte{'['}, b...)
		b = append(b, ']')
	}
	return b
}

func JsonString(v interface{}) string {
	b, _ := json.Marshal(v)
	return string(b)
}

type JsonRawWriter struct {
	bytes.Buffer
}

func (m *JsonRawWriter) MarshalJSON() ([]byte, error) {
	return m.Bytes(), nil
}

func (m *JsonRawWriter) Raw() json.RawMessage {
	return json.RawMessage(m.Bytes())
}

// A simple wrapper tohelp json config files be more easily used
// allows usage such as this
//
//		jh := NewJsonHelper([]byte(`{
//			"name":"string",
//			"ints":[1,5,9,11],
//			"int":1,
//			"int64":1234567890,
//			"MaxSize" : 1048576,
//			"strings":["string1"],
//			"nested":{
//				"nest":"string2",
//				"strings":["string1"],
//				"int":2,
//				"list":["value"],
//				"nest2":{
//					"test":"good"
//				}
//			},
//			"nested2":[
//				{"sub":5}
//			]
//		}`)
//
//		i := jh.Int("nested.int")  // 2
//		i2 := jh.Int("ints[1]")    // 5   array position 1 from [1,5,9,11]
//		s := jh.String("nested.nest")  // "string2"
//
type JsonHelper map[string]interface{}

func NewJsonHelper(b []byte) JsonHelper {
	jh := make(JsonHelper)
	json.Unmarshal(b, &jh)
	return jh
}
func NewJsonHelpers(b []byte) []JsonHelper {
	var jhl []JsonHelper
	json.Unmarshal(MakeJsonList(b), &jhl)
	return jhl
}
func (j JsonHelper) Helper(n string) JsonHelper {
	if v, ok := j[n]; ok {
		switch v.(type) {
		case map[string]interface{}:
			cn := JsonHelper{}
			for n, val := range v.(map[string]interface{}) {
				cn[n] = val
			}
			return cn
		case map[string]string:
			cn := JsonHelper{}
			for n, val := range v.(map[string]string) {
				cn[n] = val
			}
			return cn
		case JsonHelper:
			return v.(JsonHelper)
		default:
			rv := reflect.ValueOf(v)
			Debug("no map? ", v, rv.String(), rv.Type())
		}
	}
	return nil
}
func jsonList(v interface{}) []interface{} {
	switch v.(type) {
	case []interface{}:
		return v.([]interface{})
	}
	return nil
}
func jsonEntry(name string, v interface{}) (interface{}, bool) {
	switch v.(type) {
	case map[string]interface{}:
		if root, ok := v.(map[string]interface{})[name]; ok {
			return root, true
		} else {
			return nil, false
		}
	case JsonHelper:
		return v.(JsonHelper).Get(name), true
	case []interface{}:
		return v, true
	default:
		Debug("no type? ", name, " ", v)
		return nil, false
	}
	return nil, false
}
func (j JsonHelper) Get(n string) interface{} {
	var parts []string
	if strings.Contains(n, "/") {
		parts = strings.Split(n, "/")
		if strings.HasPrefix(n, "/") && len(parts) > 0 {
			parts = parts[1:]
		}
	} else {
		parts = strings.Split(n, ".")
	}
	var root interface{}
	var err error
	var ok, isList, listEntry bool
	var ln, st, idx int
	for ict, name := range parts {
		isList = strings.HasSuffix(name, "[]")
		listEntry = strings.HasSuffix(name, "]") && !isList
		ln, idx = len(name), -1
		if isList || listEntry {
			st = strings.Index(name, "[")
			idx, err = strconv.Atoi(name[st+1 : ln-1])
			name = name[:st]
		}
		if ict == 0 {
			root, ok = j[name]
		} else {
			root, ok = jsonEntry(name, root)
		}
		//Debug(isList, listEntry, " ", name, " ", root, " ", ok, err)
		if !ok {
			return nil
		}
		if isList {
			return jsonList(root)
		} else if listEntry && err == nil {
			if lst := jsonList(root); lst != nil && len(lst) > idx {
				root = lst[idx]
			} else {
				return nil
			}
		}

	}
	return root
}

// Get list of Helpers at given name
func (j JsonHelper) Helpers(n string) []JsonHelper {
	v := j.Get(n)
	if v == nil {
		return nil
	}
	switch v.(type) {
	case []map[string]interface{}:
		hl := make([]JsonHelper, 0)
		for _, val := range v.([]map[string]interface{}) {
			hl = append(hl, val)
		}
		return hl
	case []interface{}:
		if l, ok := v.([]interface{}); ok {
			jhl := make([]JsonHelper, 0)
			for _, item := range l {
				//println(item)
				if jh, ok := item.(map[string]interface{}); ok {
					jhl = append(jhl, jh)
				} else {
					Debug(jh)
				}
			}
			return jhl
		}
	}

	return nil
}
func (j JsonHelper) List(n string) []interface{} {
	v := j.Get(n)
	if l, ok := v.([]interface{}); ok {
		return l
	}
	return nil
}
func (j JsonHelper) Int64(n string) int64 {
	i64, ok := j.Int64Safe(n)
	if !ok {
		return -1
	}
	return i64
}
func (j JsonHelper) String(n string) string {
	if v := j.Get(n); v != nil {
		switch v.(type) {
		case string:
			return v.(string)
		case int:
			return strconv.Itoa(v.(int))
		case int32:
			return strconv.FormatInt(int64(v.(int32)), 10)
		case int64:
			return strconv.FormatInt(v.(int64), 10)
		case uint32:
			return strconv.FormatUint(uint64(v.(uint32)), 10)
		case uint64:
			return strconv.FormatUint(v.(uint64), 10)
		case float64:
			//(v.(float64), 10)
			return strconv.FormatInt(int64(v.(float64)), 10)
		default:
			to := reflect.TypeOf(v)
			Debug("not string type? ", n, " ", v, " ", to.String())
		}
	}
	return ""
}
func (j JsonHelper) Strings(n string) []string {
	if v := j.Get(n); v != nil {
		//Debug(n, " ", v)
		switch v.(type) {
		case string:
			return strings.Split(v.(string), ",")
		case []string:
			//Debug("type []string")
			return v.([]string)
		case []interface{}:
			//Debug("Kind = []interface{} n=", n, "  v=", v)
			sva := make([]string, 0)
			for _, av := range v.([]interface{}) {
				switch av.(type) {
				case string:
					sva = append(sva, av.(string))
				default:
					//Debug("Kind ? ", av)
				}
			}
			return sva
		default:
			//Debug("Kind = ?? ", n, v)
		}
	}
	return nil
}
func (j JsonHelper) Ints(n string) []int {
	v := j.Get(n)
	if v == nil {
		return nil
	}
	if sl, isSlice := v.([]interface{}); isSlice {
		iva := make([]int, 0)
		for _, av := range sl {
			avAsInt, ok := valToInt(av)
			if ok {
				iva = append(iva, avAsInt)
			}
		}
		return iva
	}
	return nil
}
func (j JsonHelper) StringSafe(n string) (string, bool) {
	v := j.Get(n)
	if v != nil {
		if s, ok := v.(string); ok {
			return s, ok
		}
	}
	return "", false
}
func (j JsonHelper) Int(n string) int {
	i, ok := j.IntSafe(n)
	if !ok {
		return -1
	}
	return i
}
func (j JsonHelper) Int64Safe(n string) (int64, bool) {
	v := j.Get(n)
	return valToInt64(v)

}
func (j JsonHelper) IntSafe(n string) (int, bool) {
	v := j.Get(n)
	return valToInt(v)
}

// Given any numeric type (float*, int*, uint*, string) return an int. Returns false if it would
// overflow or if the the argument is not numeric.
func valToInt(i interface{}) (int, bool) {
	i64, ok := valToInt64(i)
	if !ok {
		return -1, false
	}
	if i64 > MaxInt || i64 < MinInt {
		return -1, false
	}
	return int(i64), true
}

// Given any simple type (float*, int*, uint*, string) return an int64. Returns false if it would
// overflow or if the the argument is not numeric.
func valToInt64(i interface{}) (int64, bool) {
	switch x := i.(type) {
	case float32:
		return int64(x), true
	case float64:
		return int64(x), true
	case uint8:
		return int64(x), true
	case uint16:
		return int64(x), true
	case uint32:
		return int64(x), true
	case uint64:
		if x > math.MaxInt64 {
			return 0, false
		}
		return int64(x), true
	case int8:
		return int64(x), true
	case int16:
		return int64(x), true
	case int32:
		return int64(x), true
	case int64:
		return int64(x), true
	case int:
		return int64(x), true
	case uint:
		if uint64(x) > math.MaxInt64 {
			return 0, false
		}
		return int64(x), true
	case string:
		if len(x) > 0 {
			if iv, err := strconv.ParseInt(x, 10, 64); err == nil {
				return iv, true
			}
		}
	}
	return 0, false
}
func (j JsonHelper) Uint64(n string) uint64 {
	v := j.Get(n)
	if v != nil {
		switch v.(type) {
		case int:
			return uint64(v.(int))
		case int64:
			return uint64(v.(int64))
		case uint32:
			return uint64(v.(uint32))
		case uint64:
			return uint64(v.(uint64))
		case float32:
			f := float64(v.(float32))
			return uint64(f)
		case float64:
			f := v.(float64)
			return uint64(f)
		default:
			Debug("no type? ", n, " ", v)
		}
	}
	return 0
}

func (j JsonHelper) BoolSafe(n string) (val bool, ok bool) {
	v := j.Get(n)
	if v != nil {
		switch v.(type) {
		case bool:
			return v.(bool), true
		case string:
			if s := v.(string); len(s) > 0 {
				if b, err := strconv.ParseBool(s); err == nil {
					return b, true
				}
			}
		}
	}
	return false, false
}

func (j JsonHelper) Bool(n string) bool {
	val, ok := j.BoolSafe(n)
	if !ok {
		return false
	}

	return val

}

func (j JsonHelper) MapSafe(n string) (map[string]interface{}, bool) {
	v := j.Get(n)
	if v == nil {
		return nil, false
	}
	m, ok := v.(map[string]interface{})
	if !ok {
		return nil, false
	}
	return m, true
}

func (j JsonHelper) PrettyJson() []byte {
	jsonPretty, _ := json.MarshalIndent(j, "  ", "  ")
	return jsonPretty
}

// The following consts are from http://code.google.com/p/go-bit/ (Apache licensed). It
// lets us figure out how wide go ints are, and determine their max and min values.

// Note the use of << to create an untyped constant.
const bitsPerWord = 32 << uint(^uint(0)>>63)

// Implementation-specific size of int and uint in bits.
const BitsPerWord = bitsPerWord // either 32 or 64

// Implementation-specific integer limit values.
const (
	MaxInt  = 1<<(BitsPerWord-1) - 1 // either 1<<31 - 1 or 1<<63 - 1
	MinInt  = -MaxInt - 1            // either -1 << 31 or -1 << 63
	MaxUint = 1<<BitsPerWord - 1     // either 1<<32 - 1 or 1<<64 - 1
)
<|endoftext|>


<commit_before>package router

import (
   	"net/http"
   	"techtraits.com/log"
   	"net/url"
   	"strings"
   	"regexp"
)

//A set of routes configured for this application
var routes []Route

// Registers a new handler
// Will check that it does not conflict with a route that is already configured
func Register(path string, method Method, consumes []string, produces []string,
		handler func(Route, map[string]string, url.Values, http.Header)) {
	log.Debug("Route Registered: " + path)

	//Trim trailing slash if it exits
	var trailingSlash , _ = regexp.MatchString(".*/$",path)
	if  trailingSlash {
		path = path[0:len(path)-1]
	}

	route := Route{path, method, consumes, produces, handler}
    routes = append(routes, route)
}

func init() {
    http.HandleFunc("/", handler)
}

//Handles all incomming requests and routes them to registered handlers
func handler(resp http.ResponseWriter, req *http.Request) {

	reqRoute := Route{req.URL.Path, ToMethod(req.Method), strings.Split(strings.Split(req.Header.Get("Content-Type"),";")[0],","),
			strings.Split(strings.Split(req.Header.Get("Accept"),";")[0],","), nil}
	for _, route := range routes {
		var match, pathParams = route.matchRoute(reqRoute)
		if match {
			req.ParseForm()
			route.Handler(route, pathParams, req.Form, req.Header);
			return
		}
	}

	http.Error(resp, "Resource not found", http.StatusNotFound)
}
<commit_msg>Adding checks to prevent duplicate route additions<commit_after>package router

import (
   	"net/http"
   	"techtraits.com/log"
   	"net/url"
   	"strings"
   	"regexp"
)

//A set of routes configured for this application
var routes []Route

// Registers a new handler
// Will check that it does not conflict with a route that is already configured
func Register(path string, method Method, consumes []string, produces []string,
		handler func(Route, map[string]string, url.Values, http.Header))bool {

	//Trim trailing slash if it exits
	var trailingSlash , _ = regexp.MatchString(".*/$", path)
	if  trailingSlash {
		path = path[0:len(path)-1]
	}

	regRoute := Route{path, method, consumes, produces, handler}
	for _, route := range routes {
		var match, _ = route.matchRoute(regRoute)
		if match {
			log.Error("Route %v already Registered, ignoring ",  regRoute)
			return false
		}
	}

	log.Debug("Route %v Registered." , regRoute)
    routes = append(routes, regRoute)
    return true
}

func init() {
    http.HandleFunc("/", handler)
}

//Handles all incomming requests and routes them to registered handlers
func handler(resp http.ResponseWriter, req *http.Request) {

	reqRoute := Route{req.URL.Path, ToMethod(req.Method), strings.Split(strings.Split(req.Header.Get("Content-Type"),";")[0],","),
			strings.Split(strings.Split(req.Header.Get("Accept"),";")[0],","), nil}
	for _, route := range routes {
		var match, pathParams = route.matchRoute(reqRoute)
		if match {
			req.ParseForm()
			route.Handler(route, pathParams, req.Form, req.Header);
			return
		}
	}

	http.Error(resp, "Resource not found", http.StatusNotFound)
}
<|endoftext|>