<commit_before>package interp

import (
	"go/ast"
	"go/token"
	"log"
	"reflect"

	"golang.org/x/tools/go/exact"
	"golang.org/x/tools/go/types"
	"golang.org/x/tools/go/types/typeutil"
)

func (env *environ) evalExprs(exprs []ast.Expr) []Object {
	var objs []Object
	if len(exprs) == 1 {
		// Single argument expression, potentially multi-valued
		objs = env.Eval(exprs[0])
	} else {
		// Multiple argument expressions, each single-valued
		objs = make([]Object, len(exprs))
		for i, expr := range exprs {
			objs[i] = env.Eval(expr)[0]
		}
	}
	return objs
}

func isMapIndexExpr(env *environ, expr ast.Expr) bool {
	if e, isIndexExpr := expr.(*ast.IndexExpr); isIndexExpr {
		if _, isMap := env.info.TypeOf(e.X).Underlying().(*types.Map); isMap {
			return true
		}
	}
	return false
}

func (env *environ) Eval(expr ast.Expr) []Object {
	// Check for constant
	tv := env.info.Types[expr]
	if tv.Type == types.Typ[types.UntypedNil] {
		obj := Object{
			Value: tv.Value,
			Typ:   tv.Type,
		}
		return []Object{obj}
	}
	if tv.Value != nil {
		// It's a constant. Just return the exact.Value
		// Note: We actually want to convert it to a reflect.Value if it's a typed constant, since
		//   we know we can represent it and for convenience. But we're not doing that yet.
		if isTyped(tv.Type) {
			// It's a typed constant. Convert to a reflect.Value.
			obj := Object{
				Value: convertExactToReflect(env.interp.typeMap, tv),
				Typ:   tv.Type,
			}
			return []Object{obj}
		}
		// It's an untyped constant. Just return the exact.Value as the value.
		obj := Object{
			Value: tv.Value,
			Typ:   tv.Type,
		}
		return []Object{obj}
	}
	// Not a constant expression, so we have to evaluate it ourselves
	typ := tv.Type
	switch e := expr.(type) {
	case *ast.FuncLit:
		// TODO: Simulated functions, to interact with each other correctly inside the
		// interpreter, should not be "func([]reflect.Value)[]reflect.Value" but instead
		// "func([]Object)[]Object". This is because a simulated function's arguments may
		// themselves be simulated functions! To handle this case correctly, the implementation
		// of the function's body needs to know which arguments are simulated, which means
		// the arguments must be of type Object rather than reflect.Value.
		//
		// Unsimulated functions do not have this problem as long as we guarantee that an
		// unsimulated function type's parameter types will also be unsimulated. We will make
		// sure that this guarantee holds.

		// TODO: avoid simulating function types when possible
		rtyp, sim := getReflectType(env.interp.typeMap, typ)
		if sim {
			// We must simulate the function type we want to create
			f := createSimulatedFunc(env, e)
			rf := reflect.ValueOf(f)
			obj := Object{
				Value: rf,
				Typ:   typ,
				Sim:   true,
			}
			return []Object{obj}
		}
		// We can actually create a function of the right type
		f := createUnsimulatedFunc(env, e, rtyp)
		obj := Object{
			Value: f,
			Typ:   typ,
		}
		return []Object{obj}

	case *ast.StarExpr:
		// Because we have a StarExpr at this point in Eval, we know
		// it is a unary "*" expression rather than a pointer type
		xObj := env.Eval(e.X)[0]
		xVal := xObj.Value.(reflect.Value)
		newVal := xVal.Elem()
		if !newVal.IsValid() {
			// Nil pointer dereference!
			panic("goconsole: Nil pointer dereference")
		}
		obj := Object{
			Value: newVal,
			Typ:   typ,
		}
		return []Object{obj}
	case *ast.UnaryExpr:
		// TODO: implement unary expressions
		switch e.Op {
		case token.AND:
			xObj := env.Eval(e.X)[0]
			xVal := xObj.Value.(reflect.Value)
			newVal := xVal.Addr()
			obj := Object{
				Value: newVal,
				Typ:   typ,
			}
			return []Object{obj}
		case token.ARROW:
			xObj := env.Eval(e.X)[0]
			xVal := xObj.Value.(reflect.Value)
			var valTyp types.Type
			commaOk := false
			switch tup := typ.(type) {
			case *types.Tuple:
				valTyp = tup.At(0).Type()
				commaOk = true
			default:
				valTyp = typ
			}
			newVal, ok := xVal.Recv()
			_, sim := getReflectType(env.interp.typeMap, typ)
			valObj := Object{
				Value: newVal,
				Typ:   valTyp,
				Sim:   sim,
			}
			if commaOk {
				okObj := Object{
					Value: reflect.ValueOf(ok),
					Typ:   types.Typ[types.Bool],
					Sim:   false,
				}
				return []Object{valObj, okObj}
			}
			return []Object{valObj}
		default:
			log.Fatalf("Unary operator %q not implemented", e.Op)
		}
	case *ast.BinaryExpr:
		left := env.Eval(e.X)[0]
		right := env.Eval(e.Y)[0]

		var obj Object

		switch e.Op {
		case token.LSS, token.GTR, token.LEQ, token.GEQ, token.EQL, token.NEQ:
			obj = doBinaryComparisonOp(env, left, right, e.Op, typ)
		default:
			obj = doBinaryOp(env, left, right, e.Op)
		}

		return []Object{obj}
	case *ast.Ident:
		val, _ := env.lookupParent(e.String())
		return []Object{val}
	case *ast.ParenExpr:
		return env.Eval(e.X)
	case *ast.SelectorExpr:
		// TODO: implement selector expressions!
		sel, ok := env.info.Selections[e]
		if !ok {
			// Then this selector expression denotes a package object
			obj := env.info.Uses[e.Sel]
			p := obj.Pkg().Name()
			v, ok := env.interp.pkgs[p].Lookup(obj.Name())
			if !ok {
				log.Fatalf("Package object %q not found", obj)
			}
			return []Object{v}
		}
		switch sel.Kind() {
		case types.FieldVal:
			xo := env.Eval(e.X)[0]
			v := xo.Value.(reflect.Value)
			if sel.Indirect() {
				v = v.Elem()
			}
			obj := Object{
				Value: v.FieldByIndex(sel.Index()),
				Typ:   sel.Type(),
			}
			return []Object{obj}
		case types.MethodVal:
			log.Fatal("Method values not yet implemented:", sel.String())
		case types.MethodExpr:
			log.Fatal("Method expressions not yet implemented:", sel.String())
		}
	case *ast.CallExpr:
		switch env.getCallExprKind(e) {
		case builtinKind:
			return env.evalBuiltinCall(e, false)
		case conversionKind:
			// Get the type we're converting to
			typ := env.info.TypeOf(e.Fun)
			rtyp, sim := getReflectType(env.interp.typeMap, typ)
			if rtyp == nil {
				log.Fatal("Failed to obtain reflect.Type to represent type:", typ)
			}

			// Evaluate the value to be converted
			argObj := env.Eval(e.Args[0])[0]

			var val reflect.Value
			argVal, ok := argObj.Value.(reflect.Value)
			if !ok {
				// This means it's a conversion of nil
				// Use the zero value
				val = reflect.Zero(rtyp)
			} else {
				val = argVal.Convert(rtyp)
			}

			obj := Object{
				Value: val,
				Typ:   typ,
				Sim:   sim,
			}
			return []Object{obj}
		case callKind:
			return env.evalFuncCall(e, false)
		}
	case *ast.IndexExpr:
		resultTyp := typ
		collTyp := env.info.TypeOf(e.X)

		// Figure out which type of object we're indexing
		objTyp := collTyp.Underlying()
		switch objTyp := objTyp.(type) {
		case *types.Array:
			log.Fatal("Array indexing not implemented yet")
		case *types.Map:
			keyObj := env.Eval(e.Index)[0]
			keyVal, ok := keyObj.Value.(reflect.Value)
			if !ok {
				// Must be untyped nil. Use zero value of type.
				rtyp, _ := getReflectType(env.interp.typeMap, objTyp.Key())
				if rtyp == nil {
					log.Fatalf("couldn't get reflect.Type corresponding to %q", objTyp)
				}
				keyVal = reflect.Zero(rtyp)
			}
			commaOk := false
			var boolTyp types.Type
			if tupleTyp, ok := resultTyp.(*types.Tuple); ok {
				// "Comma-ok" map index expression
				resultTyp = tupleTyp.At(0).Type()
				boolTyp = tupleTyp.At(1).Type()
				commaOk = true
			}
			rtyp, sim := getReflectType(env.interp.typeMap, resultTyp)
			if rtyp == nil {
				log.Fatal("Couldn't get reflect.Type of result type of map index expression")
			}
			mapObj := env.Eval(e.X)[0]
			mapVal := mapObj.Value.(reflect.Value)
			resultVal := mapVal.MapIndex(keyVal)
			keyFound := true
			if !resultVal.IsValid() {
				// The key wasn't found in the map (including case where map is nil)
				resultVal = reflect.Zero(rtyp)
				keyFound = false
			}
			resultObj := Object{
				Value: resultVal,
				Typ:   resultTyp,
				Sim:   sim,
			}
			if commaOk {
				foundObj := Object{
					Value: reflect.ValueOf(keyFound),
					Typ:   boolTyp,
					Sim:   false,
				}
				return []Object{resultObj, foundObj}
			}
			return []Object{resultObj}

		case *types.Slice:
			indObj := env.Eval(e.Index)[0]
			ind := int(indObj.Value.(reflect.Value).Int())
			sliceObj := env.Eval(e.X)[0]
			sliceVal := sliceObj.Value.(reflect.Value)
			resultVal := sliceVal.Index(ind)
			rtyp, sim := getReflectType(env.interp.typeMap, resultTyp)
			if rtyp == nil {
				log.Fatal("Couldn't get reflect.Type of result type of slice index expression")
			}
			resultObj := Object{
				Value: resultVal,
				Typ:   resultTyp,
				Sim:   sim,
			}
			return []Object{resultObj}
		case *types.Basic:
			log.Fatal("String indexing not implemented yet")
		case *types.Pointer:
			log.Fatal("Pointer-to-array indexing not implemented yet")
		}

		log.Fatalf("Unhandled expression type: %T", e)
	default:
		log.Fatalf("Unhandled expression type: %T", e)
	}
	return []Object{}
}

func isTyped(typ types.Type) bool {
	t, ok := typ.Underlying().(*types.Basic)
	return !ok || t.Info()&types.IsUntyped == 0
}

func convertExactToReflect(typeMap *typeutil.Map, tv types.TypeAndValue) reflect.Value {
	ev := tv.Value
	rtyp, _ := getReflectType(typeMap, tv.Type)
	if rtyp == nil {
		// This should not happen
		log.Fatal("Couldn't get a reflect.Type from the provided types.Type")
	}
	// TODO: Is it a problem that we're making constants settable?
	rv := reflect.New(rtyp).Elem()
	switch tv.Type.Underlying().(*types.Basic).Kind() {
	case types.Bool:
		rv.SetBool(exact.BoolVal(ev))
	case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
		i64, _ := exact.Int64Val(ev)
		rv.SetInt(i64)
	case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
		ui64, _ := exact.Uint64Val(ev)
		rv.SetUint(ui64)
	case types.Float32, types.Float64:
		f64, _ := exact.Float64Val(ev)
		rv.SetFloat(f64)
	case types.Complex64, types.Complex128:
		real64, _ := exact.Float64Val(exact.Real(ev))
		imag64, _ := exact.Float64Val(exact.Imag(ev))
		c128 := complex(real64, imag64)
		rv.SetComplex(c128)
	case types.String:
		rv.SetString(exact.StringVal(ev))
	default:
		return reflect.Value{}
	}
	return rv
}
<commit_msg>Implemented type assertions, including the "comma-ok" variant.<commit_after>package interp

import (
	"fmt"
	"go/ast"
	"go/token"
	"log"
	"reflect"

	"golang.org/x/tools/go/exact"
	"golang.org/x/tools/go/types"
	"golang.org/x/tools/go/types/typeutil"
)

func (env *environ) evalExprs(exprs []ast.Expr) []Object {
	var objs []Object
	if len(exprs) == 1 {
		// Single argument expression, potentially multi-valued
		objs = env.Eval(exprs[0])
	} else {
		// Multiple argument expressions, each single-valued
		objs = make([]Object, len(exprs))
		for i, expr := range exprs {
			objs[i] = env.Eval(expr)[0]
		}
	}
	return objs
}

func isMapIndexExpr(env *environ, expr ast.Expr) bool {
	if e, isIndexExpr := expr.(*ast.IndexExpr); isIndexExpr {
		if _, isMap := env.info.TypeOf(e.X).Underlying().(*types.Map); isMap {
			return true
		}
	}
	return false
}

func (env *environ) Eval(expr ast.Expr) []Object {
	// Check for constant
	tv := env.info.Types[expr]
	if tv.Type == types.Typ[types.UntypedNil] {
		obj := Object{
			Value: tv.Value,
			Typ:   tv.Type,
		}
		return []Object{obj}
	}
	if tv.Value != nil {
		// It's a constant. Just return the exact.Value
		// Note: We actually want to convert it to a reflect.Value if it's a typed constant, since
		//   we know we can represent it and for convenience. But we're not doing that yet.
		if isTyped(tv.Type) {
			// It's a typed constant. Convert to a reflect.Value.
			obj := Object{
				Value: convertExactToReflect(env.interp.typeMap, tv),
				Typ:   tv.Type,
			}
			return []Object{obj}
		}
		// It's an untyped constant. Just return the exact.Value as the value.
		obj := Object{
			Value: tv.Value,
			Typ:   tv.Type,
		}
		return []Object{obj}
	}
	// Not a constant expression, so we have to evaluate it ourselves
	typ := tv.Type
	switch e := expr.(type) {
	case *ast.FuncLit:
		// TODO: Simulated functions, to interact with each other correctly inside the
		// interpreter, should not be "func([]reflect.Value)[]reflect.Value" but instead
		// "func([]Object)[]Object". This is because a simulated function's arguments may
		// themselves be simulated functions! To handle this case correctly, the implementation
		// of the function's body needs to know which arguments are simulated, which means
		// the arguments must be of type Object rather than reflect.Value.
		//
		// Unsimulated functions do not have this problem as long as we guarantee that an
		// unsimulated function type's parameter types will also be unsimulated. We will make
		// sure that this guarantee holds.

		// TODO: avoid simulating function types when possible
		rtyp, sim := getReflectType(env.interp.typeMap, typ)
		if sim {
			// We must simulate the function type we want to create
			f := createSimulatedFunc(env, e)
			rf := reflect.ValueOf(f)
			obj := Object{
				Value: rf,
				Typ:   typ,
				Sim:   true,
			}
			return []Object{obj}
		}
		// We can actually create a function of the right type
		f := createUnsimulatedFunc(env, e, rtyp)
		obj := Object{
			Value: f,
			Typ:   typ,
		}
		return []Object{obj}

	case *ast.StarExpr:
		// Because we have a StarExpr at this point in Eval, we know
		// it is a unary "*" expression rather than a pointer type
		xObj := env.Eval(e.X)[0]
		xVal := xObj.Value.(reflect.Value)
		newVal := xVal.Elem()
		if !newVal.IsValid() {
			// Nil pointer dereference!
			panic("goconsole: Nil pointer dereference")
		}
		obj := Object{
			Value: newVal,
			Typ:   typ,
		}
		return []Object{obj}
	case *ast.UnaryExpr:
		// TODO: implement unary expressions
		switch e.Op {
		case token.AND:
			xObj := env.Eval(e.X)[0]
			xVal := xObj.Value.(reflect.Value)
			newVal := xVal.Addr()
			obj := Object{
				Value: newVal,
				Typ:   typ,
			}
			return []Object{obj}
		case token.ARROW:
			xObj := env.Eval(e.X)[0]
			xVal := xObj.Value.(reflect.Value)
			var valTyp types.Type
			commaOk := false
			switch tup := typ.(type) {
			case *types.Tuple:
				valTyp = tup.At(0).Type()
				commaOk = true
			default:
				valTyp = typ
			}
			newVal, ok := xVal.Recv()
			_, sim := getReflectType(env.interp.typeMap, typ)
			valObj := Object{
				Value: newVal,
				Typ:   valTyp,
				Sim:   sim,
			}
			if commaOk {
				okObj := Object{
					Value: reflect.ValueOf(ok),
					Typ:   types.Typ[types.Bool],
					Sim:   false,
				}
				return []Object{valObj, okObj}
			}
			return []Object{valObj}
		default:
			log.Fatalf("Unary operator %q not implemented", e.Op)
		}

	case *ast.TypeAssertExpr:
		// type assertion x.(T)

		// if T is interface type:
		//    * assert that x's dynamic type implements T
		//    * if so, value of expr is T(val in x) or [T(val in x), true]
		//    * otherwise, runtime panic or [ zero val of T, false ]

		// if T is of non-interface type:
		//    * assert that x's dynamic type is identical to T
		//    * if so, value of expr is (val in x) or [ (val in x), true]
		//    * otherwise, runtime panic or [ zero val of T, false ]

		// types T and V are identical if and only if all of the following are true:
		//    * values of T are assignable to V,
		//    * both or neither of T and V are named types (that is, isNamed(T) == isNamed(V)),
		//    * (T is not a channel type) OR (dir of T == dir of V)

		eTyp := typ

		// If the expression has tuple type, then it's a "comma, ok" type assertion
		_, commaOk := eTyp.(*types.Tuple)

		toTyp := env.info.TypeOf(e.Type)
		toRtyp, sim := getReflectType(env.interp.typeMap, toTyp)
		if toRtyp == nil {
			log.Fatalf("Couldn't get reflect type: %v", toTyp)
		}

		obj := env.Eval(e.X)[0]
		objVal := obj.Value.(reflect.Value)
		dynamicVal := objVal.Elem()
		dynamicRtyp := dynamicVal.Type()

		var resultObj Object
		var assertSuccess bool

		switch typ.Underlying().(type) {
		case *types.Interface:
			// assert that dynamic type of obj implements typ
			// TODO This should be a runtime panic if not

			assertSuccess = objVal.Type().Implements(toRtyp)

			if assertSuccess {
				resultVal := dynamicVal.Convert(toRtyp)
				resultObj = Object{
					Sim:   sim,
					Typ:   toTyp,
					Value: resultVal,
				}
			} else if !commaOk {
				// TODO this should be a runtime panic
				err := fmt.Errorf("interface conversion: interface is %v, not %v", dynamicRtyp, toRtyp)
				panic(err)
			} else {
				resultObj = Object{
					Sim:   sim,
					Typ:   toTyp,
					Value: reflect.Zero(toRtyp),
				}
			}

		default:
			isNamed := func(t reflect.Type) bool {
				return len(t.Name()) > 0
			}

			areIdentical := func(t1, t2 reflect.Type) bool {
				if !t1.AssignableTo(t2) {
					return false
				}
				if isNamed(t1) != isNamed(t2) {
					return false
				}
				if t1.Kind() == reflect.Chan && t1.ChanDir() != t2.ChanDir() {
					return false
				}
				return true
			}

			assertSuccess = areIdentical(dynamicRtyp, toRtyp)
			if assertSuccess {
				resultVal := dynamicVal.Convert(toRtyp)
				resultObj = Object{
					Sim:   sim,
					Typ:   toTyp,
					Value: resultVal,
				}
			} else if !commaOk {
				// TODO this should be a runtime panic
				err := fmt.Errorf("interface conversion: interface is %v, not %v", dynamicRtyp, toRtyp)
				panic(err)
			} else {
				resultObj = Object{
					Sim:   sim,
					Typ:   toTyp,
					Value: reflect.Zero(toRtyp),
				}
			}
		}

		// Return the appropriate
		if commaOk {
			successObj := Object{
				Sim:   false,
				Typ:   types.Typ[types.Bool],
				Value: reflect.ValueOf(assertSuccess),
			}
			return []Object{resultObj, successObj}
		} else {
			return []Object{resultObj}
		}

	case *ast.BinaryExpr:
		left := env.Eval(e.X)[0]
		right := env.Eval(e.Y)[0]

		var obj Object

		switch e.Op {
		case token.LSS, token.GTR, token.LEQ, token.GEQ, token.EQL, token.NEQ:
			obj = doBinaryComparisonOp(env, left, right, e.Op, typ)
		default:
			obj = doBinaryOp(env, left, right, e.Op)
		}

		return []Object{obj}
	case *ast.Ident:
		val, _ := env.lookupParent(e.String())
		return []Object{val}
	case *ast.ParenExpr:
		return env.Eval(e.X)
	case *ast.SelectorExpr:
		// TODO: implement selector expressions!
		sel, ok := env.info.Selections[e]
		if !ok {
			// Then this selector expression denotes a package object
			obj := env.info.Uses[e.Sel]
			p := obj.Pkg().Name()
			v, ok := env.interp.pkgs[p].Lookup(obj.Name())
			if !ok {
				log.Fatalf("Package object %q not found", obj)
			}
			return []Object{v}
		}
		switch sel.Kind() {
		case types.FieldVal:
			xo := env.Eval(e.X)[0]
			v := xo.Value.(reflect.Value)
			if sel.Indirect() {
				v = v.Elem()
			}
			obj := Object{
				Value: v.FieldByIndex(sel.Index()),
				Typ:   sel.Type(),
			}
			return []Object{obj}
		case types.MethodVal:
			log.Fatal("Method values not yet implemented:", sel.String())
		case types.MethodExpr:
			log.Fatal("Method expressions not yet implemented:", sel.String())
		}
	case *ast.CallExpr:
		switch env.getCallExprKind(e) {
		case builtinKind:
			return env.evalBuiltinCall(e, false)
		case conversionKind:
			// Get the type we're converting to
			typ := env.info.TypeOf(e.Fun)
			rtyp, sim := getReflectType(env.interp.typeMap, typ)
			if rtyp == nil {
				log.Fatal("Failed to obtain reflect.Type to represent type:", typ)
			}

			// Evaluate the value to be converted
			argObj := env.Eval(e.Args[0])[0]

			var val reflect.Value
			argVal, ok := argObj.Value.(reflect.Value)
			if !ok {
				// This means it's a conversion of nil
				// Use the zero value
				val = reflect.Zero(rtyp)
			} else {
				val = argVal.Convert(rtyp)
			}

			obj := Object{
				Value: val,
				Typ:   typ,
				Sim:   sim,
			}
			return []Object{obj}
		case callKind:
			return env.evalFuncCall(e, false)
		}
	case *ast.IndexExpr:
		resultTyp := typ
		collTyp := env.info.TypeOf(e.X)

		// Figure out which type of object we're indexing
		objTyp := collTyp.Underlying()
		switch objTyp := objTyp.(type) {
		case *types.Array:
			log.Fatal("Array indexing not implemented yet")
		case *types.Map:
			keyObj := env.Eval(e.Index)[0]
			keyVal, ok := keyObj.Value.(reflect.Value)
			if !ok {
				// Must be untyped nil. Use zero value of type.
				rtyp, _ := getReflectType(env.interp.typeMap, objTyp.Key())
				if rtyp == nil {
					log.Fatalf("couldn't get reflect.Type corresponding to %q", objTyp)
				}
				keyVal = reflect.Zero(rtyp)
			}
			commaOk := false
			var boolTyp types.Type
			if tupleTyp, ok := resultTyp.(*types.Tuple); ok {
				// "Comma-ok" map index expression
				resultTyp = tupleTyp.At(0).Type()
				boolTyp = tupleTyp.At(1).Type()
				commaOk = true
			}
			rtyp, sim := getReflectType(env.interp.typeMap, resultTyp)
			if rtyp == nil {
				log.Fatal("Couldn't get reflect.Type of result type of map index expression")
			}
			mapObj := env.Eval(e.X)[0]
			mapVal := mapObj.Value.(reflect.Value)
			resultVal := mapVal.MapIndex(keyVal)
			keyFound := true
			if !resultVal.IsValid() {
				// The key wasn't found in the map (including case where map is nil)
				resultVal = reflect.Zero(rtyp)
				keyFound = false
			}
			resultObj := Object{
				Value: resultVal,
				Typ:   resultTyp,
				Sim:   sim,
			}
			if commaOk {
				foundObj := Object{
					Value: reflect.ValueOf(keyFound),
					Typ:   boolTyp,
					Sim:   false,
				}
				return []Object{resultObj, foundObj}
			}
			return []Object{resultObj}

		case *types.Slice:
			indObj := env.Eval(e.Index)[0]
			ind := int(indObj.Value.(reflect.Value).Int())
			sliceObj := env.Eval(e.X)[0]
			sliceVal := sliceObj.Value.(reflect.Value)
			resultVal := sliceVal.Index(ind)
			rtyp, sim := getReflectType(env.interp.typeMap, resultTyp)
			if rtyp == nil {
				log.Fatal("Couldn't get reflect.Type of result type of slice index expression")
			}
			resultObj := Object{
				Value: resultVal,
				Typ:   resultTyp,
				Sim:   sim,
			}
			return []Object{resultObj}
		case *types.Basic:
			log.Fatal("String indexing not implemented yet")
		case *types.Pointer:
			log.Fatal("Pointer-to-array indexing not implemented yet")
		}

		log.Fatalf("Unhandled expression type: %T", e)
	default:
		log.Fatalf("Unhandled expression type: %T", e)
	}
	return []Object{}
}

func isTyped(typ types.Type) bool {
	t, ok := typ.Underlying().(*types.Basic)
	return !ok || t.Info()&types.IsUntyped == 0
}

func convertExactToReflect(typeMap *typeutil.Map, tv types.TypeAndValue) reflect.Value {
	ev := tv.Value
	rtyp, _ := getReflectType(typeMap, tv.Type)
	if rtyp == nil {
		// This should not happen
		log.Fatal("Couldn't get a reflect.Type from the provided types.Type")
	}
	// TODO: Is it a problem that we're making constants settable?
	rv := reflect.New(rtyp).Elem()
	switch tv.Type.Underlying().(*types.Basic).Kind() {
	case types.Bool:
		rv.SetBool(exact.BoolVal(ev))
	case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
		i64, _ := exact.Int64Val(ev)
		rv.SetInt(i64)
	case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
		ui64, _ := exact.Uint64Val(ev)
		rv.SetUint(ui64)
	case types.Float32, types.Float64:
		f64, _ := exact.Float64Val(ev)
		rv.SetFloat(f64)
	case types.Complex64, types.Complex128:
		real64, _ := exact.Float64Val(exact.Real(ev))
		imag64, _ := exact.Float64Val(exact.Imag(ev))
		c128 := complex(real64, imag64)
		rv.SetComplex(c128)
	case types.String:
		rv.SetString(exact.StringVal(ev))
	default:
		return reflect.Value{}
	}
	return rv
}
<|endoftext|>


<commit_before>// Package proctl provides functions for attaching to and manipulating
// a process during the debug session.
package proctl

import (
	"fmt"
	"os"
	"syscall"
)

// Struct representing a debugged process. Holds onto pid, register values,
// process struct and process state.
type DebuggedProcess struct {
	Pid          int
	Regs         *syscall.PtraceRegs
	Process      *os.Process
	ProcessState *os.ProcessState
}

// Returns a new DebuggedProcess struct with sensible defaults.
func NewDebugProcess(pid int) (*DebuggedProcess, error) {
	err := syscall.PtraceAttach(pid)
	if err != nil {
		return nil, err
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		return nil, err
	}

	ps, err := proc.Wait()
	if err != nil {
		return nil, err
	}

	debuggedProc := DebuggedProcess{
		Pid:          pid,
		Regs:         &syscall.PtraceRegs{},
		Process:      proc,
		ProcessState: ps,
	}

	return &debuggedProc, nil
}

// Obtains register values from the debugged process.
func (dbp *DebuggedProcess) Registers() (*syscall.PtraceRegs, error) {
	err := syscall.PtraceGetRegs(dbp.Pid, dbp.Regs)
	if err != nil {
		return nil, fmt.Errorf("Registers():", err)
	}

	return dbp.Regs, nil
}

// Steps through process.
func (dbp *DebuggedProcess) Step() error {
	return dbp.HandleResult(syscall.PtraceSingleStep(dbp.Pid))
}

// Continue process until next breakpoint.
func (dbp *DebuggedProcess) Continue() error {
	return dbp.HandleResult(syscall.PtraceCont(dbp.Pid, 0))
}

func (dbp *DebuggedProcess) HandleResult(err error) error {
	if err != nil {
		return err
	}

	ps, err := dbp.Process.Wait()
	if err != nil {
		return err
	}

	dbp.ProcessState = ps

	return nil
}
<commit_msg>Don't export handleResult()<commit_after>// Package proctl provides functions for attaching to and manipulating
// a process during the debug session.
package proctl

import (
	"fmt"
	"os"
	"syscall"
)

// Struct representing a debugged process. Holds onto pid, register values,
// process struct and process state.
type DebuggedProcess struct {
	Pid          int
	Regs         *syscall.PtraceRegs
	Process      *os.Process
	ProcessState *os.ProcessState
}

// Returns a new DebuggedProcess struct with sensible defaults.
func NewDebugProcess(pid int) (*DebuggedProcess, error) {
	err := syscall.PtraceAttach(pid)
	if err != nil {
		return nil, err
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		return nil, err
	}

	ps, err := proc.Wait()
	if err != nil {
		return nil, err
	}

	debuggedProc := DebuggedProcess{
		Pid:          pid,
		Regs:         &syscall.PtraceRegs{},
		Process:      proc,
		ProcessState: ps,
	}

	return &debuggedProc, nil
}

// Obtains register values from the debugged process.
func (dbp *DebuggedProcess) Registers() (*syscall.PtraceRegs, error) {
	err := syscall.PtraceGetRegs(dbp.Pid, dbp.Regs)
	if err != nil {
		return nil, fmt.Errorf("Registers():", err)
	}

	return dbp.Regs, nil
}

// Steps through process.
func (dbp *DebuggedProcess) Step() error {
	return dbp.handleResult(syscall.PtraceSingleStep(dbp.Pid))
}

// Continue process until next breakpoint.
func (dbp *DebuggedProcess) Continue() error {
	return dbp.handleResult(syscall.PtraceCont(dbp.Pid, 0))
}

func (dbp *DebuggedProcess) handleResult(err error) error {
	if err != nil {
		return err
	}

	ps, err := dbp.Process.Wait()
	if err != nil {
		return err
	}

	dbp.ProcessState = ps

	return nil
}
<|endoftext|>


<commit_before>package sorter

import "testing"

// TestBubbleSort generates the array with 8 elements randomly
// and checks the BubbleSort is running correctly or not
func TestBubbleSort(t *testing.T) {
	generatedArr, err := GenerateArray(8)
	if err != nil {
		t.Fatal(err.Error())
	}

	arr := BubbleSort(generatedArr)

	isSorted, err := IsSorted(arr)
	if err != nil {
		t.Fatal(err.Error())
	}

	if !isSorted {
		t.Fatal("Array is not sorted")
	}
}

func TestBubbleSortParallel(t *testing.T) {
	tests := []struct {
		arraySize int
		isSorted  bool
		err       error
	}{
		{0, false, ErrArrayNoLength},
		{1, true, nil},
		{2, true, nil},
		{3, true, nil},
		{4, true, nil},
		{10, true, nil},
	}

	for _, test := range tests {
		test := test // capture variable
		t.Run("", func(t *testing.T) {
			t.Parallel()
			arr, err := GenerateArray(test.arraySize)
			if err != test.err {
				t.Fatalf("error should be: %v, but got: %v", test.err, err)
			}

			if len(arr) != test.arraySize {
				t.Fatalf("array length should equal: %v, but got: %v", test.arraySize, len(arr))
			}

		})
	}

}
<commit_msg>tests/bubblesort: add Issorted checking for bubblesort<commit_after>package sorter

import "testing"

// TestBubbleSort generates the array with 8 elements randomly
// and checks the BubbleSort is running correctly or not
func TestBubbleSort(t *testing.T) {
	generatedArr, err := GenerateArray(8)
	if err != nil {
		t.Fatal(err.Error())
	}

	arr := BubbleSort(generatedArr)

	isSorted, err := IsSorted(arr)
	if err != nil {
		t.Fatal(err.Error())
	}

	if !isSorted {
		t.Fatal("Array is not sorted")
	}
}

func TestBubbleSortParallel(t *testing.T) {
	tests := []struct {
		arraySize int
		isSorted  bool
		err       error
	}{
		{0, false, ErrArrayNoLength},
		{1, true, nil},
		{2, true, nil},
		{3, true, nil},
		{4, true, nil},
		{10, true, nil},
	}

	for _, test := range tests {
		test := test // capture variable
		t.Run("", func(t *testing.T) {
			t.Parallel()
			arr, err := GenerateArray(test.arraySize)
			if err != test.err {
				t.Fatalf("error should be: %v, but got: %v", test.err, err)
			}

			if len(arr) != test.arraySize {
				t.Fatalf("array length should equal: %v, but got: %v", test.arraySize, len(arr))
			}

			sorted, err := IsSorted(BubbleSort(arr))
			if err != nil {
				t.Fatalf("error should be nil, but got:%v", err)
			}
			if !sorted {
				t.Fatalf("array should be sorted:%v", sorted)
			}
		})
	}

}
<|endoftext|>


<commit_before>// +build selinux,linux

package selinux

import (
	"bufio"
	"bytes"
	"os"
	"testing"
)

func TestSetFileLabel(t *testing.T) {
	if !GetEnabled() {
		t.Skip("SELinux not enabled, skipping.")
	}

	tmp := "selinux_test"
	con := "system_u:object_r:bin_t:s0"
	out, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE, 0)
	if err != nil {
		t.Fatalf("unable to open %s: %s", tmp, err)
	}
	out.Close()
	defer os.Remove(tmp)

	if err := SetFileLabel(tmp, con); err != nil {
		t.Fatalf("SetFileLabel failed: %s", err)
	}
	filelabel, err := FileLabel(tmp)
	if err != nil {
		t.Fatalf("FileLabel failed: %s", err)
	}
	if con != filelabel {
		t.Fatalf("FileLabel failed, returned %s expected %s", filelabel, con)
	}
}

func TestSELinux(t *testing.T) {
	if !GetEnabled() {
		t.Skip("SELinux not enabled, skipping.")
	}

	var (
		err            error
		plabel, flabel string
	)

	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ReleaseLabel(plabel)
	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ReleaseLabel(plabel)
	t.Log("Enforcing Mode", EnforceMode())
	mode := DefaultEnforceMode()
	t.Log("Default Enforce Mode ", mode)

	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ClearLabels()
	t.Log("ClearLabels")
	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ReleaseLabel(plabel)

	defer SetEnforceMode(mode)
	if err := SetEnforceMode(Enforcing); err != nil {
		t.Fatalf("enforcing selinux failed: %v", err)
	}
	if err := SetEnforceMode(Permissive); err != nil {
		t.Fatalf("setting selinux mode to permissive failed: %v", err)
	}
	SetEnforceMode(mode)

	pid := os.Getpid()
	t.Logf("PID:%d MCS:%s\n", pid, intToMcs(pid, 1023))
	err = SetFSCreateLabel("unconfined_u:unconfined_r:unconfined_t:s0")
	if err == nil {
		t.Log(FSCreateLabel())
	} else {
		t.Log("SetFSCreateLabel failed", err)
		t.Fatal(err)
	}
	err = SetFSCreateLabel("")
	if err == nil {
		t.Log(FSCreateLabel())
	} else {
		t.Log("SetFSCreateLabel failed", err)
		t.Fatal(err)
	}
	t.Log(PidLabel(1))
}

func TestCanonicalizeContext(t *testing.T) {
	if !GetEnabled() {
		t.Skip("SELinux not enabled, skipping.")
	}

	con := "system_u:object_r:bin_t:s0:c1,c2,c3"
	checkcon := "system_u:object_r:bin_t:s0:c1.c3"
	newcon, err := CanonicalizeContext(con)
	if err != nil {
		t.Fatal(err)
	}
	if newcon != checkcon {
		t.Fatalf("CanonicalizeContext(%s) returned %s expected %s", con, newcon, checkcon)
	}
	con = "system_u:object_r:bin_t:s0:c5,c2"
	checkcon = "system_u:object_r:bin_t:s0:c2,c5"
	newcon, err = CanonicalizeContext(con)
	if err != nil {
		t.Fatal(err)
	}
	if newcon != checkcon {
		t.Fatalf("CanonicalizeContext(%s) returned %s expected %s", con, newcon, checkcon)
	}
}

func TestFindSELinuxfsInMountinfo(t *testing.T) {
	const mountinfo = `18 62 0:17 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel
19 62 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
20 62 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=3995472k,nr_inodes=998868,mode=755
21 18 0:16 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:7 - securityfs securityfs rw
22 20 0:18 / /dev/shm rw,nosuid,nodev shared:3 - tmpfs tmpfs rw,seclabel
23 20 0:11 / /dev/pts rw,nosuid,noexec,relatime shared:4 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000
24 62 0:19 / /run rw,nosuid,nodev shared:23 - tmpfs tmpfs rw,seclabel,mode=755
25 18 0:20 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:8 - tmpfs tmpfs ro,seclabel,mode=755
26 25 0:21 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:9 - cgroup cgroup rw,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd
27 18 0:22 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:20 - pstore pstore rw
28 25 0:23 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,perf_event
29 25 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,devices
30 25 0:25 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,cpuacct,cpu
31 25 0:26 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,freezer
32 25 0:27 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,net_prio,net_cls
33 25 0:28 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,cpuset
34 25 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
35 25 0:30 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,pids
36 25 0:31 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,hugetlb
37 25 0:32 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,blkio
59 18 0:33 / /sys/kernel/config rw,relatime shared:21 - configfs configfs rw
62 1 253:1 / / rw,relatime shared:1 - ext4 /dev/vda1 rw,seclabel,data=ordered
38 18 0:15 / /sys/fs/selinux rw,relatime shared:22 - selinuxfs selinuxfs rw
39 19 0:35 / /proc/sys/fs/binfmt_misc rw,relatime shared:24 - autofs systemd-1 rw,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=11601
40 20 0:36 / /dev/hugepages rw,relatime shared:25 - hugetlbfs hugetlbfs rw,seclabel
41 20 0:14 / /dev/mqueue rw,relatime shared:26 - mqueue mqueue rw,seclabel
42 18 0:6 / /sys/kernel/debug rw,relatime shared:27 - debugfs debugfs rw
112 62 253:1 /var/lib/docker/plugins /var/lib/docker/plugins rw,relatime - ext4 /dev/vda1 rw,seclabel,data=ordered
115 62 253:1 /var/lib/docker/overlay2 /var/lib/docker/overlay2 rw,relatime - ext4 /dev/vda1 rw,seclabel,data=ordered
118 62 7:0 / /root/mnt rw,relatime shared:66 - ext4 /dev/loop0 rw,seclabel,data=ordered
121 115 0:38 / /var/lib/docker/overlay2/8cdbabf81bc89b14ea54eaf418c1922068f06917fff57e184aa26541ff291073/merged rw,relatime - overlay overlay rw,seclabel,lowerdir=/var/lib/docker/overlay2/l/CPD4XI7UD4GGTGSJVPQSHWZKTK:/var/lib/docker/overlay2/l/NQKORR3IS7KNQDER35AZECLH4Z,upperdir=/var/lib/docker/overlay2/8cdbabf81bc89b14ea54eaf418c1922068f06917fff57e184aa26541ff291073/diff,workdir=/var/lib/docker/overlay2/8cdbabf81bc89b14ea54eaf418c1922068f06917fff57e184aa26541ff291073/work
125 62 0:39 / /var/lib/docker/containers/5e3fce422957c291a5b502c2cf33d512fc1fcac424e4113136c808360e5b7215/shm rw,nosuid,nodev,noexec,relatime shared:68 - tmpfs shm rw,seclabel,size=65536k
186 24 0:3 / /run/docker/netns/0a08e7496c6d rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
130 62 0:15 / /root/chroot/selinux rw,relatime shared:22 - selinuxfs selinuxfs rw
109 24 0:37 / /run/user/0 rw,nosuid,nodev,relatime shared:62 - tmpfs tmpfs rw,seclabel,size=801032k,mode=700
`
	s := bufio.NewScanner(bytes.NewBuffer([]byte(mountinfo)))
	for _, expected := range []string{"/sys/fs/selinux", "/root/chroot/selinux", ""} {
		mnt := findSELinuxfsMount(s)
		t.Logf("found %q", mnt)
		if mnt != expected {
			t.Fatalf("expected %q, got %q", expected, mnt)
		}
	}
}
<commit_msg>TestSetEnforceMode: separate and fix for non-root<commit_after>// +build selinux,linux

package selinux

import (
	"bufio"
	"bytes"
	"os"
	"testing"
)

func TestSetFileLabel(t *testing.T) {
	if !GetEnabled() {
		t.Skip("SELinux not enabled, skipping.")
	}

	tmp := "selinux_test"
	con := "system_u:object_r:bin_t:s0"
	out, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE, 0)
	if err != nil {
		t.Fatalf("unable to open %s: %s", tmp, err)
	}
	out.Close()
	defer os.Remove(tmp)

	if err := SetFileLabel(tmp, con); err != nil {
		t.Fatalf("SetFileLabel failed: %s", err)
	}
	filelabel, err := FileLabel(tmp)
	if err != nil {
		t.Fatalf("FileLabel failed: %s", err)
	}
	if con != filelabel {
		t.Fatalf("FileLabel failed, returned %s expected %s", filelabel, con)
	}
}

func TestSELinux(t *testing.T) {
	if !GetEnabled() {
		t.Skip("SELinux not enabled, skipping.")
	}

	var (
		err            error
		plabel, flabel string
	)

	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ReleaseLabel(plabel)
	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ReleaseLabel(plabel)

	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ClearLabels()
	t.Log("ClearLabels")
	plabel, flabel = ContainerLabels()
	t.Log(plabel)
	t.Log(flabel)
	ReleaseLabel(plabel)

	pid := os.Getpid()
	t.Logf("PID:%d MCS:%s\n", pid, intToMcs(pid, 1023))
	err = SetFSCreateLabel("unconfined_u:unconfined_r:unconfined_t:s0")
	if err == nil {
		t.Log(FSCreateLabel())
	} else {
		t.Log("SetFSCreateLabel failed", err)
		t.Fatal(err)
	}
	err = SetFSCreateLabel("")
	if err == nil {
		t.Log(FSCreateLabel())
	} else {
		t.Log("SetFSCreateLabel failed", err)
		t.Fatal(err)
	}
	t.Log(PidLabel(1))
}

func TestSetEnforceMode(t *testing.T) {
	if !GetEnabled() {
		t.Skip("SELinux not enabled, skipping.")
	}
	if os.Geteuid() != 0 {
		t.Skip("root required, skipping")
	}

	t.Log("Enforcing Mode:", EnforceMode())
	mode := DefaultEnforceMode()
	t.Log("Default Enforce Mode:", mode)
	defer SetEnforceMode(mode)

	if err := SetEnforceMode(Enforcing); err != nil {
		t.Fatalf("setting selinux mode to enforcing failed: %v", err)
	}
	if err := SetEnforceMode(Permissive); err != nil {
		t.Fatalf("setting selinux mode to permissive failed: %v", err)
	}
}

func TestCanonicalizeContext(t *testing.T) {
	if !GetEnabled() {
		t.Skip("SELinux not enabled, skipping.")
	}

	con := "system_u:object_r:bin_t:s0:c1,c2,c3"
	checkcon := "system_u:object_r:bin_t:s0:c1.c3"
	newcon, err := CanonicalizeContext(con)
	if err != nil {
		t.Fatal(err)
	}
	if newcon != checkcon {
		t.Fatalf("CanonicalizeContext(%s) returned %s expected %s", con, newcon, checkcon)
	}
	con = "system_u:object_r:bin_t:s0:c5,c2"
	checkcon = "system_u:object_r:bin_t:s0:c2,c5"
	newcon, err = CanonicalizeContext(con)
	if err != nil {
		t.Fatal(err)
	}
	if newcon != checkcon {
		t.Fatalf("CanonicalizeContext(%s) returned %s expected %s", con, newcon, checkcon)
	}
}

func TestFindSELinuxfsInMountinfo(t *testing.T) {
	const mountinfo = `18 62 0:17 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel
19 62 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
20 62 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=3995472k,nr_inodes=998868,mode=755
21 18 0:16 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:7 - securityfs securityfs rw
22 20 0:18 / /dev/shm rw,nosuid,nodev shared:3 - tmpfs tmpfs rw,seclabel
23 20 0:11 / /dev/pts rw,nosuid,noexec,relatime shared:4 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000
24 62 0:19 / /run rw,nosuid,nodev shared:23 - tmpfs tmpfs rw,seclabel,mode=755
25 18 0:20 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:8 - tmpfs tmpfs ro,seclabel,mode=755
26 25 0:21 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:9 - cgroup cgroup rw,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd
27 18 0:22 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:20 - pstore pstore rw
28 25 0:23 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,perf_event
29 25 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,devices
30 25 0:25 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,cpuacct,cpu
31 25 0:26 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,freezer
32 25 0:27 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,net_prio,net_cls
33 25 0:28 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,cpuset
34 25 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
35 25 0:30 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,pids
36 25 0:31 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,hugetlb
37 25 0:32 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,blkio
59 18 0:33 / /sys/kernel/config rw,relatime shared:21 - configfs configfs rw
62 1 253:1 / / rw,relatime shared:1 - ext4 /dev/vda1 rw,seclabel,data=ordered
38 18 0:15 / /sys/fs/selinux rw,relatime shared:22 - selinuxfs selinuxfs rw
39 19 0:35 / /proc/sys/fs/binfmt_misc rw,relatime shared:24 - autofs systemd-1 rw,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=11601
40 20 0:36 / /dev/hugepages rw,relatime shared:25 - hugetlbfs hugetlbfs rw,seclabel
41 20 0:14 / /dev/mqueue rw,relatime shared:26 - mqueue mqueue rw,seclabel
42 18 0:6 / /sys/kernel/debug rw,relatime shared:27 - debugfs debugfs rw
112 62 253:1 /var/lib/docker/plugins /var/lib/docker/plugins rw,relatime - ext4 /dev/vda1 rw,seclabel,data=ordered
115 62 253:1 /var/lib/docker/overlay2 /var/lib/docker/overlay2 rw,relatime - ext4 /dev/vda1 rw,seclabel,data=ordered
118 62 7:0 / /root/mnt rw,relatime shared:66 - ext4 /dev/loop0 rw,seclabel,data=ordered
121 115 0:38 / /var/lib/docker/overlay2/8cdbabf81bc89b14ea54eaf418c1922068f06917fff57e184aa26541ff291073/merged rw,relatime - overlay overlay rw,seclabel,lowerdir=/var/lib/docker/overlay2/l/CPD4XI7UD4GGTGSJVPQSHWZKTK:/var/lib/docker/overlay2/l/NQKORR3IS7KNQDER35AZECLH4Z,upperdir=/var/lib/docker/overlay2/8cdbabf81bc89b14ea54eaf418c1922068f06917fff57e184aa26541ff291073/diff,workdir=/var/lib/docker/overlay2/8cdbabf81bc89b14ea54eaf418c1922068f06917fff57e184aa26541ff291073/work
125 62 0:39 / /var/lib/docker/containers/5e3fce422957c291a5b502c2cf33d512fc1fcac424e4113136c808360e5b7215/shm rw,nosuid,nodev,noexec,relatime shared:68 - tmpfs shm rw,seclabel,size=65536k
186 24 0:3 / /run/docker/netns/0a08e7496c6d rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
130 62 0:15 / /root/chroot/selinux rw,relatime shared:22 - selinuxfs selinuxfs rw
109 24 0:37 / /run/user/0 rw,nosuid,nodev,relatime shared:62 - tmpfs tmpfs rw,seclabel,size=801032k,mode=700
`
	s := bufio.NewScanner(bytes.NewBuffer([]byte(mountinfo)))
	for _, expected := range []string{"/sys/fs/selinux", "/root/chroot/selinux", ""} {
		mnt := findSELinuxfsMount(s)
		t.Logf("found %q", mnt)
		if mnt != expected {
			t.Fatalf("expected %q, got %q", expected, mnt)
		}
	}
}
<|endoftext|>


<commit_before>package workflow

import (
	"fmt"
	"strings"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/worker"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/interpolate"
	"github.com/ovh/cds/sdk/log"
)

// processNodeJobRunRequirements returns requirements list interpolated, and true or false if at least
// one requirement is of type "Service"
func processNodeJobRunRequirements(db gorp.SqlExecutor, j sdk.Job, run *sdk.WorkflowNodeRun, execsGroupIDs []int64) (sdk.RequirementList, bool, string, *sdk.MultiError) {
	var requirements sdk.RequirementList
	var errm sdk.MultiError
	var containsService bool
	var model string
	var tmp = sdk.ParametersToMap(run.BuildParameters)

	for _, v := range j.Action.Requirements {
		name, errName := interpolate.Do(v.Name, tmp)
		if errName != nil {
			errm.Append(errName)
			continue
		}
		value, errValue := interpolate.Do(v.Value, tmp)
		if errValue != nil {
			errm.Append(errValue)
			continue
		}

		if v.Type == sdk.ServiceRequirement {
			containsService = true
		}
		if v.Type == sdk.ModelRequirement {
			// It is forbidden to have more than one model requirement.
			if model != "" {
				errm.Append(sdk.ErrInvalidJobRequirementDuplicateModel)
				break
			}
			value = strings.Split(value, " ")[0]
			model = value
		}

		sdk.AddRequirement(&requirements, v.ID, name, v.Type, value)
	}

	var modelType string
	if model != "" {
		// Load the worker model
		wm, err := worker.LoadWorkerModelByName(db, model)
		if err != nil {
			log.Error("getNodeJobRunRequirements> error while getting worker model %s: %v", model, err)
			errm.Append(sdk.ErrNoWorkerModel)
		} else {
			// Check that the worker model is in an exec group
			if !sdk.IsInInt64Array(wm.GroupID, execsGroupIDs) {
				errm.Append(sdk.ErrInvalidJobRequirementWorkerModelPermission)
			}

			// Check that the worker model has the binaries capabilitites
			// only if the worker model doesn't need registration
			if !wm.NeedRegistration && !wm.CheckRegistration {
				for _, req := range requirements {
					if req.Type == sdk.BinaryRequirement {
						var hasCapa bool
						for _, cap := range wm.RegisteredCapabilities {
							if cap.Value == req.Value {
								hasCapa = true
								break
							}
						}
						if !hasCapa {
							errm.Append(sdk.ErrInvalidJobRequirementWorkerModelCapabilitites)
							break
						}
					}
				}
			}

			modelType = wm.Type
		}

	}

	if errm.IsEmpty() {
		return requirements, containsService, modelType, nil
	}
	return requirements, containsService, modelType, &errm
}

func prepareRequirementsToNodeJobRunParameters(reqs sdk.RequirementList) []sdk.Parameter {
	params := []sdk.Parameter{}
	for _, r := range reqs {
		if r.Type == sdk.ServiceRequirement {
			k := fmt.Sprintf("job.requirement.%s.%s", strings.ToLower(r.Type), strings.ToLower(r.Name))
			values := strings.Split(r.Value, " ")
			if len(values) > 1 {
				sdk.AddParameter(&params, k+".image", sdk.StringParameter, values[0])
				sdk.AddParameter(&params, k+".options", sdk.StringParameter, strings.Join(values[1:], " "))
			}
		}
		k := fmt.Sprintf("job.requirement.%s.%s", strings.ToLower(r.Type), strings.ToLower(r.Name))
		sdk.AddParameter(&params, k, sdk.StringParameter, r.Value)
	}
	return params
}
<commit_msg>fix(api): don't split requirements model value (#4122)<commit_after>package workflow

import (
	"fmt"
	"strings"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/worker"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/interpolate"
	"github.com/ovh/cds/sdk/log"
)

// processNodeJobRunRequirements returns requirements list interpolated, and true or false if at least
// one requirement is of type "Service"
func processNodeJobRunRequirements(db gorp.SqlExecutor, j sdk.Job, run *sdk.WorkflowNodeRun, execsGroupIDs []int64) (sdk.RequirementList, bool, string, *sdk.MultiError) {
	var requirements sdk.RequirementList
	var errm sdk.MultiError
	var containsService bool
	var model string
	var tmp = sdk.ParametersToMap(run.BuildParameters)

	for _, v := range j.Action.Requirements {
		name, errName := interpolate.Do(v.Name, tmp)
		if errName != nil {
			errm.Append(errName)
			continue
		}
		value, errValue := interpolate.Do(v.Value, tmp)
		if errValue != nil {
			errm.Append(errValue)
			continue
		}

		if v.Type == sdk.ServiceRequirement {
			containsService = true
		}
		if v.Type == sdk.ModelRequirement {
			// It is forbidden to have more than one model requirement.
			if model != "" {
				errm.Append(sdk.ErrInvalidJobRequirementDuplicateModel)
				break
			}
			model = value
		}

		sdk.AddRequirement(&requirements, v.ID, name, v.Type, value)
	}

	var modelType string
	if model != "" {
		// Load the worker model
		wm, err := worker.LoadWorkerModelByName(db, strings.Split(model, " ")[0])
		if err != nil {
			log.Error("getNodeJobRunRequirements> error while getting worker model %s: %v", model, err)
			errm.Append(sdk.ErrNoWorkerModel)
		} else {
			// Check that the worker model is in an exec group
			if !sdk.IsInInt64Array(wm.GroupID, execsGroupIDs) {
				errm.Append(sdk.ErrInvalidJobRequirementWorkerModelPermission)
			}

			// Check that the worker model has the binaries capabilitites
			// only if the worker model doesn't need registration
			if !wm.NeedRegistration && !wm.CheckRegistration {
				for _, req := range requirements {
					if req.Type == sdk.BinaryRequirement {
						var hasCapa bool
						for _, cap := range wm.RegisteredCapabilities {
							if cap.Value == req.Value {
								hasCapa = true
								break
							}
						}
						if !hasCapa {
							errm.Append(sdk.ErrInvalidJobRequirementWorkerModelCapabilitites)
							break
						}
					}
				}
			}

			modelType = wm.Type
		}

	}

	if errm.IsEmpty() {
		return requirements, containsService, modelType, nil
	}
	return requirements, containsService, modelType, &errm
}

func prepareRequirementsToNodeJobRunParameters(reqs sdk.RequirementList) []sdk.Parameter {
	params := []sdk.Parameter{}
	for _, r := range reqs {
		if r.Type == sdk.ServiceRequirement {
			k := fmt.Sprintf("job.requirement.%s.%s", strings.ToLower(r.Type), strings.ToLower(r.Name))
			values := strings.Split(r.Value, " ")
			if len(values) > 1 {
				sdk.AddParameter(&params, k+".image", sdk.StringParameter, values[0])
				sdk.AddParameter(&params, k+".options", sdk.StringParameter, strings.Join(values[1:], " "))
			}
		}
		k := fmt.Sprintf("job.requirement.%s.%s", strings.ToLower(r.Type), strings.ToLower(r.Name))
		sdk.AddParameter(&params, k, sdk.StringParameter, r.Value)
	}
	return params
}
<|endoftext|>


<commit_before>package io

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os"
)

// gadgetHeader is the formatting for meta-information used by Gadget 2.
type lGadget2Header struct {
	NPart                                     [6]uint32
	Mass                                      [6]float64
	Time, Redshift                            float64
	FlagSfr, FlagFeedback                     int32
	NPartTotal                                [6]uint32
	FlagCooling, NumFiles                     int32
	BoxSize, Omega0, OmegaLambda, HubbleParam float64
	FlagStellarAge, HashTabSize               int32

	Padding [88]byte
}

func (gh *lGadget2Header) postprocess(xs [][3]float32, out *Header) {
	// Assumes the catalog has already been checked for corruption.

	out.N = int64(gh.NPart[1]) + int64(gh.NPart[0])<<32
	out.TotalWidth = gh.BoxSize

	out.Cosmo.Z = gh.Redshift
	out.Cosmo.OmegaM = gh.Omega0
	out.Cosmo.OmegaL = gh.OmegaLambda
	out.Cosmo.H100 = gh.HubbleParam

	out.Origin, out.Width = boundingBox(xs, gh.BoxSize)
}

// readInt32 returns single 32-bit interger from the given file using the
// given endianness.
func readInt32(r io.Reader, order binary.ByteOrder) int32 {
	var n int32
	if err := binary.Read(r, order, &n); err != nil {
		panic(err)
	}
	return n
}

func readLGadget2Header(
	path string, order binary.ByteOrder, out *lGadget2Header,
) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	_ = readInt32(f, order)
	err = binary.Read(f, binary.LittleEndian, out)
	return err
}

func (buf *LGadget2Buffer) readLGadget2Particles(
	path string,
	order binary.ByteOrder,
	xsBuf, vsBuf [][3]float32,
	msBuf []float32,
	idsBuf []int64,
) (xs, vs [][3]float32, ms []float32, ids []int64, err error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, nil, nil, nil, err
	}
	defer f.Close()

	gh := &lGadget2Header{}

	_ = readInt32(f, order)
	binary.Read(f, binary.LittleEndian, gh)
	_ = readInt32(f, order)
	count := int(int64(gh.NPart[1]) + int64(gh.NPart[0])<<32)
	xsBuf = expandVectors(xsBuf[:0], count)
	vsBuf = expandVectors(vsBuf[:0], count)
	idsBuf = expandInts(idsBuf[:0], count)

	_ = readInt32(f, order)
	readVecAsByte(f, order, xsBuf)
	_ = readInt32(f, order)
	_ = readInt32(f, order)
	readVecAsByte(f, order, vsBuf)

	f.Seek(4*2+12*int64(len(xsBuf))+4*2, 1)
	readInt64AsByte(f, order, idsBuf)

	// Fix periodicity of particles and convert the units of our velocities.

	rootA := float32(math.Sqrt(float64(gh.Time)))

	tw := float32(gh.BoxSize)
	for i := range xsBuf {
		for j := 0; j < 3; j++ {
			if xsBuf[i][j] < 0 {
				xsBuf[i][j] += tw
			} else if xsBuf[i][j] >= tw {
				xsBuf[i][j] -= tw
			}

			if math.IsNaN(float64(xsBuf[i][j])) ||
				math.IsInf(float64(xsBuf[i][j]), 0) ||
				xsBuf[i][j] < -tw || xsBuf[i][j] > 2*tw {

				return nil, nil, nil, nil, fmt.Errorf(
					"Corruption detected in the file %s. I can't analyze it.",
					path,
				)
			}

			for j := 0; j < 3; j++ {
				vs[i][j] = vs[i][j] * rootA
			}
		}
	}

	panic("Velocities not yet implemented!!")

	msBuf = expandScalars(msBuf, count)
	for i := range msBuf {
		msBuf[i] = buf.mass
	}

	return xsBuf, vsBuf, msBuf, idsBuf, nil
}

func expandVectors(vecs [][3]float32, n int) [][3]float32 {
	switch {
	case cap(vecs) >= n:
		return vecs[:n]
	case int(float64(cap(vecs))*1.5) > n:
		return append(vecs[:cap(vecs)],
			make([][3]float32, n-cap(vecs))...)
	default:
		return make([][3]float32, n)
	}
}

func expandScalars(scalars []float32, n int) []float32 {
	switch {
	case cap(scalars) >= n:
		return scalars[:n]
	case int(float64(cap(scalars))*1.5) > n:
		return append(scalars[:cap(scalars)],
			make([]float32, n-cap(scalars))...)
	default:
		return make([]float32, n)
	}
}

func expandInts(ints []int64, n int) []int64 {
	switch {
	case cap(ints) >= n:
		return ints[:n]
	case int(float64(cap(ints))*1.5) > n:
		return append(ints[:cap(ints)], make([]int64, n-cap(ints))...)
	default:
		return make([]int64, n)
	}
}

type LGadget2Buffer struct {
	open   bool
	order  binary.ByteOrder
	hd     lGadget2Header
	mass   float32
	xs, vs [][3]float32
	ms     []float32
	ids    []int64
}

func NewLGadget2Buffer(path, orderFlag string) (VectorBuffer, error) {
	var order binary.ByteOrder = binary.LittleEndian
	switch orderFlag {
	case "LittleEndian":
	case "BigEndian":
		order = binary.BigEndian
	case "SystemOrder":
		if !IsSysOrder(order) {
			order = binary.BigEndian
		}
	}

	buf := &LGadget2Buffer{order: order}
	err := readLGadget2Header(path, order, &buf.hd)
	if err != nil {
		return nil, err
	}

	c := CosmologyHeader{
		Z: buf.hd.Redshift, OmegaM: buf.hd.Omega0,
		OmegaL: buf.hd.OmegaLambda, H100: buf.hd.HubbleParam,
	}
	totCount := int64(buf.hd.NPartTotal[1]) + int64(buf.hd.NPartTotal[0])<<32
	buf.mass = calcUniformMass(totCount, buf.hd.BoxSize, c)

	return buf, nil
}

func (buf *LGadget2Buffer) Read(fname string) (
	xs, vs [][3]float32, ms []float32, ids []int64, err error,
) {
	if buf.open {
		panic("Buffer already open.")
	}
	buf.open = true

	buf.xs, buf.vs, buf.ms, buf.ids, err = buf.readLGadget2Particles(
		fname, buf.order, buf.xs, buf.vs, buf.ms, buf.ids,
	)

	return buf.xs, nil, buf.ms, buf.ids, err
}

func (buf *LGadget2Buffer) Close() {
	if !buf.open {
		panic("Buffer not open.")
	}
	buf.open = false
}

func (buf *LGadget2Buffer) IsOpen() bool {
	return buf.open
}

func (buf *LGadget2Buffer) ReadHeader(fname string, out *Header) error {
	err := readLGadget2Header(fname, buf.order, &buf.hd)
	defer buf.Close()
	if err != nil {
		return err
	}
	xs, _, _, _, err := buf.Read(fname)
	if err != nil {
		return err
	}

	buf.hd.postprocess(xs, out)

	return nil
}

func (buf *LGadget2Buffer) MinMass() float32 { return buf.mass }
<commit_msg>Fixed naming bug in lgadget reader.<commit_after>package io

import (
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os"
)

// gadgetHeader is the formatting for meta-information used by Gadget 2.
type lGadget2Header struct {
	NPart                                     [6]uint32
	Mass                                      [6]float64
	Time, Redshift                            float64
	FlagSfr, FlagFeedback                     int32
	NPartTotal                                [6]uint32
	FlagCooling, NumFiles                     int32
	BoxSize, Omega0, OmegaLambda, HubbleParam float64
	FlagStellarAge, HashTabSize               int32

	Padding [88]byte
}

func (gh *lGadget2Header) postprocess(xs [][3]float32, out *Header) {
	// Assumes the catalog has already been checked for corruption.

	out.N = int64(gh.NPart[1]) + int64(gh.NPart[0])<<32
	out.TotalWidth = gh.BoxSize

	out.Cosmo.Z = gh.Redshift
	out.Cosmo.OmegaM = gh.Omega0
	out.Cosmo.OmegaL = gh.OmegaLambda
	out.Cosmo.H100 = gh.HubbleParam

	out.Origin, out.Width = boundingBox(xs, gh.BoxSize)
}

// readInt32 returns single 32-bit interger from the given file using the
// given endianness.
func readInt32(r io.Reader, order binary.ByteOrder) int32 {
	var n int32
	if err := binary.Read(r, order, &n); err != nil {
		panic(err)
	}
	return n
}

func readLGadget2Header(
	path string, order binary.ByteOrder, out *lGadget2Header,
) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	_ = readInt32(f, order)
	err = binary.Read(f, binary.LittleEndian, out)
	return err
}

func (buf *LGadget2Buffer) readLGadget2Particles(
	path string,
	order binary.ByteOrder,
	xsBuf, vsBuf [][3]float32,
	msBuf []float32,
	idsBuf []int64,
) (xs, vs [][3]float32, ms []float32, ids []int64, err error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, nil, nil, nil, err
	}
	defer f.Close()

	gh := &lGadget2Header{}

	_ = readInt32(f, order)
	binary.Read(f, binary.LittleEndian, gh)
	_ = readInt32(f, order)
	count := int(int64(gh.NPart[1]) + int64(gh.NPart[0])<<32)
	xsBuf = expandVectors(xsBuf[:0], count)
	vsBuf = expandVectors(vsBuf[:0], count)
	idsBuf = expandInts(idsBuf[:0], count)

	_ = readInt32(f, order)
	readVecAsByte(f, order, xsBuf)
	_ = readInt32(f, order)
	_ = readInt32(f, order)
	readVecAsByte(f, order, vsBuf)

	f.Seek(4*2+12*int64(len(xsBuf))+4*2, 1)
	readInt64AsByte(f, order, idsBuf)

	// Fix periodicity of particles and convert the units of our velocities.

	rootA := float32(math.Sqrt(float64(gh.Time)))

	tw := float32(gh.BoxSize)
	for i := range xsBuf {
		for j := 0; j < 3; j++ {
			vsBuf[i][j] = vsBuf[i][j] * rootA

			if xsBuf[i][j] < 0 {
				xsBuf[i][j] += tw
			} else if xsBuf[i][j] >= tw {
				xsBuf[i][j] -= tw
			}

			if math.IsNaN(float64(xsBuf[i][j])) ||
				math.IsInf(float64(xsBuf[i][j]), 0) ||
				xsBuf[i][j] < -tw || xsBuf[i][j] > 2*tw {

				return nil, nil, nil, nil, fmt.Errorf(
					"Corruption detected in the file %s. I can't analyze it.",
					path,
				)
			}
		}
	}

	msBuf = expandScalars(msBuf, count)
	for i := range msBuf {
		msBuf[i] = buf.mass
	}

	return xsBuf, vsBuf, msBuf, idsBuf, nil
}

func expandVectors(vecs [][3]float32, n int) [][3]float32 {
	switch {
	case cap(vecs) >= n:
		return vecs[:n]
	case int(float64(cap(vecs))*1.5) > n:
		return append(vecs[:cap(vecs)],
			make([][3]float32, n-cap(vecs))...)
	default:
		return make([][3]float32, n)
	}
}

func expandScalars(scalars []float32, n int) []float32 {
	switch {
	case cap(scalars) >= n:
		return scalars[:n]
	case int(float64(cap(scalars))*1.5) > n:
		return append(scalars[:cap(scalars)],
			make([]float32, n-cap(scalars))...)
	default:
		return make([]float32, n)
	}
}

func expandInts(ints []int64, n int) []int64 {
	switch {
	case cap(ints) >= n:
		return ints[:n]
	case int(float64(cap(ints))*1.5) > n:
		return append(ints[:cap(ints)], make([]int64, n-cap(ints))...)
	default:
		return make([]int64, n)
	}
}

type LGadget2Buffer struct {
	open   bool
	order  binary.ByteOrder
	hd     lGadget2Header
	mass   float32
	xs, vs [][3]float32
	ms     []float32
	ids    []int64
}

func NewLGadget2Buffer(path, orderFlag string) (VectorBuffer, error) {
	var order binary.ByteOrder = binary.LittleEndian
	switch orderFlag {
	case "LittleEndian":
	case "BigEndian":
		order = binary.BigEndian
	case "SystemOrder":
		if !IsSysOrder(order) {
			order = binary.BigEndian
		}
	}

	buf := &LGadget2Buffer{order: order}
	err := readLGadget2Header(path, order, &buf.hd)
	if err != nil {
		return nil, err
	}

	c := CosmologyHeader{
		Z: buf.hd.Redshift, OmegaM: buf.hd.Omega0,
		OmegaL: buf.hd.OmegaLambda, H100: buf.hd.HubbleParam,
	}
	totCount := int64(buf.hd.NPartTotal[1]) + int64(buf.hd.NPartTotal[0])<<32
	buf.mass = calcUniformMass(totCount, buf.hd.BoxSize, c)

	return buf, nil
}

func (buf *LGadget2Buffer) Read(fname string) (
	xs, vs [][3]float32, ms []float32, ids []int64, err error,
) {
	if buf.open {
		panic("Buffer already open.")
	}
	buf.open = true

	buf.xs, buf.vs, buf.ms, buf.ids, err = buf.readLGadget2Particles(
		fname, buf.order, buf.xs, buf.vs, buf.ms, buf.ids,
	)

	return buf.xs, nil, buf.ms, buf.ids, err
}

func (buf *LGadget2Buffer) Close() {
	if !buf.open {
		panic("Buffer not open.")
	}
	buf.open = false
}

func (buf *LGadget2Buffer) IsOpen() bool {
	return buf.open
}

func (buf *LGadget2Buffer) ReadHeader(fname string, out *Header) error {
	err := readLGadget2Header(fname, buf.order, &buf.hd)
	defer buf.Close()
	if err != nil {
		return err
	}
	xs, _, _, _, err := buf.Read(fname)
	if err != nil {
		return err
	}

	buf.hd.postprocess(xs, out)

	return nil
}

func (buf *LGadget2Buffer) MinMass() float32 { return buf.mass }
<|endoftext|>


<commit_before>// +build linux

package ioctl

import "unsafe"

// BlkPart send blkpart ioctl to fd
func BlkRRPart(fd uintptr) error {
	return IOCTL(fd, IO(0x12, 95), uintptr(0))
}

// BlkPg send blkpg ioctl to fd
func BlkPg(fd, data uintptr) error {
	return IOCTL(fd, IO(0x12, 105), data)
	return nil
}

type FsTrimRange struct {
	Start     uint64
	Length    uint64
	MinLength uint64
}

// Fitrim send fitrim ioctl to fd
func Fitrim(fd, data uintptr) error {
	r := FsTrimRange{}
	return IOCTL(fd, IOWR('X', 121, uintptr(unsafe.Pointer(&r))), data)
}

// Firfreeze send firfreeze ioctl to fd
func Firfreeze(fd, data uintptr) error {
	return IOCTL(fd, IOWR('X', 119, uintptr(0)), data)
}
<commit_msg>fix<commit_after>// +build linux

package ioctl

import "unsafe"

// BlkPart send blkpart ioctl to fd
func BlkRRPart(fd uintptr) error {
	return IOCTL(fd, IO(0x12, 95), uintptr(0))
}

// BlkPg send blkpg ioctl to fd
func BlkPg(fd, data uintptr) error {
	return IOCTL(fd, IO(0x12, 105), data)
	return nil
}

type FsTrimRange struct {
	Start     uint64
	Length    uint64
	MinLength uint64
}

// Fitrim send fitrim ioctl to fd
func Fitrim(fd, data uintptr) error {
	r := FsTrimRange{}
	return IOCTL(fd, IOWR('X', 121, uintptr(unsafe.Pointer(&r))), data)
}

// Fifreeze send fifreeze ioctl to fd
func Fifreeze(fd, data uintptr) error {
	return IOCTL(fd, IOWR('X', 119, uintptr(0)), data)
}

// Fithaw send fithaw ioctl to fd
func Fithaw(fd, data uintptr) error {
	return IOCTL(fd, IOWR('X', 120, uintptr(0)), data)
}
<|endoftext|>


<commit_before>package intra

import (
	"io"
	"math/rand"
	"net"
	"sync"
	"time"
)

type RetryStats struct {
	Bytes   int32 // Number of bytes uploaded before the retry.
	Chunks  int16 // Number of writes before the retry.
	Split   int16 // Number of bytes in the first retried segment.
	Timeout bool  // True if the retry was caused by a timeout.
}

// retrier implements the DuplexConn interface.
type retrier struct {
	// mutex is a lock that guards `conn`, `hello`, and `retryCompleteFlag`.
	// These fields must not be modified except under this lock.
	// After retryCompletedFlag is closed, these values will not be modified
	// again so locking is no longer required for reads.
	mutex   sync.Mutex
	network string
	addr    *net.TCPAddr
	// conn is the current underlying connection.  It is only modified by the reader
	// thread, so the reader functions may access it without acquiring a lock.
	conn *net.TCPConn
	// Time to wait between the first write and the first read before triggering a
	// retry.
	timeout time.Duration
	// hello is the contents written before the first read.  It is initially empty,
	// and is cleared when the first byte is received.
	hello []byte
	// Flag indicating when retry is finished or unnecessary.
	retryCompleteFlag chan struct{}
	// Flags indicating whether the caller has called CloseRead and CloseWrite.
	readCloseFlag  chan struct{}
	writeCloseFlag chan struct{}
	stats          RetryStats
	summary        *TCPSocketSummary
}

// Helper functions for reading flags.
// In this package, a "flag" is a thread-safe single-use status indicator that
// starts in the "open" state and transitions to "closed" when close() is called.
// It is implemented as a channel over which no data is ever sent.
// Some advantages of this implementation:
//  - The language enforces the one-way transition.
//  - Nonblocking and blocking access are both straightforward.
//  - Checking the status of a closed flag should be extremely fast (although currently
//    it's not optimized: https://github.com/golang/go/issues/32529)
func closed(c chan struct{}) bool {
	select {
	case <-c:
		// The channel has been closed.
		return true
	default:
		return false
	}
}

func (r *retrier) readClosed() bool {
	return closed(r.readCloseFlag)
}

func (r *retrier) writeClosed() bool {
	return closed(r.writeCloseFlag)
}

func (r *retrier) retryCompleted() bool {
	return closed(r.retryCompleteFlag)
}

// Given timestamps immediately before and after a successful socket connection
// (i.e. the time the SYN was sent and the time the SYNACK was received), this
// function returns a reasonable timeout for replies to a hello sent on this socket.
func timeout(before, after time.Time) time.Duration {
	// These values were chosen to have a <1% false positive rate based on test data.
	// False positives trigger an unnecessary retry, which can make connections slower, so they are
	// worth avoiding.  However, overly long timeouts make retry slower and less useful.
	rtt := after.Sub(before)
	return 1200*time.Millisecond + 2*rtt
}

// DialWithSplitRetry returns a TCP connection that transparently retries by
// splitting the initial upstream segment if the socket closes without receiving a
// reply.  Like net.Conn, it is intended for two-threaded use, with one thread calling
// Read and CloseRead, and another calling Write, ReadFrom, and CloseWrite.
func DialWithSplitRetry(network string, addr *net.TCPAddr, summary *TCPSocketSummary) (DuplexConn, error) {
	before := time.Now()
	conn, err := net.DialTCP(network, nil, addr)
	if err != nil {
		return nil, err
	}
	after := time.Now()

	r := &retrier{
		network:           network,
		addr:              addr,
		conn:              conn,
		timeout:           timeout(before, after),
		retryCompleteFlag: make(chan struct{}),
		readCloseFlag:     make(chan struct{}),
		writeCloseFlag:    make(chan struct{}),
		summary:           summary,
	}

	return r, nil
}

// Read-related functions.
func (r *retrier) Read(buf []byte) (n int, err error) {
	n, err = r.conn.Read(buf)
	if n == 0 && err == nil {
		// If no data was read, a nil error doesn't rule out the need for a retry.
		return
	}
	if !r.retryCompleted() {
		r.mutex.Lock()
		if err != nil {
			if neterr, ok := err.(net.Error); ok {
				r.stats.Timeout = neterr.Timeout()
			}
			// Read failed.  Retry.
			n, err = r.retry(buf)
		}
		close(r.retryCompleteFlag)
		r.hello = nil
		r.mutex.Unlock()
	}
	return
}

func (r *retrier) retry(buf []byte) (n int, err error) {
	r.conn.Close()
	var newConn *net.TCPConn
	if newConn, err = net.DialTCP(r.network, nil, r.addr); err != nil {
		return
	}
	r.conn = newConn
	first, second := splitHello(r.hello)
	r.stats.Split = int16(len(first))
	// Set Retry to a non-nil value, indicating that a retry occurred.
	r.summary.Retry = &r.stats
	if _, err = r.conn.Write(first); err != nil {
		return
	}
	if _, err = r.conn.Write(second); err != nil {
		return
	}
	// While we were creating the new socket, the caller might have called CloseRead
	// or CloseWrite on the old socket.  Copy that state to the new socket.
	// CloseRead and CloseWrite are idempotent, so this is safe even if the user's
	// action actually affected the new socket.
	if r.readClosed() {
		r.conn.CloseRead()
	}
	if r.writeClosed() {
		r.conn.CloseWrite()
	}
	return r.conn.Read(buf)
}

func (r *retrier) CloseRead() error {
	if !r.readClosed() {
		close(r.readCloseFlag)
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	return r.conn.CloseRead()
}

func splitHello(hello []byte) ([]byte, []byte) {
	if len(hello) == 0 {
		return hello, hello
	}
	const (
		MIN_SPLIT int = 32
		MAX_SPLIT int = 64
	)

	// Random number in the range [MIN_SPLIT, MAX_SPLIT]
	s := MIN_SPLIT + rand.Intn(MAX_SPLIT+1-MIN_SPLIT)
	limit := len(hello) / 2
	if s > limit {
		s = limit
	}
	return hello[:s], hello[s:]
}

// Write-related functions
func (r *retrier) Write(b []byte) (int, error) {
	// Double-checked locking pattern.  This avoids lock acquisition on
	// every packet after retry completes, while also ensuring that r.hello is
	// empty at steady-state.
	if !r.retryCompleted() {
		n := 0
		var err error
		attempted := false
		r.mutex.Lock()
		if !r.retryCompleted() {
			n, err = r.conn.Write(b)
			attempted = true
			r.hello = append(r.hello, b[:n]...)
			r.stats.Chunks++
			r.stats.Bytes = int32(len(r.hello))

			// We require a response or another write within the specified timeout.
			r.conn.SetReadDeadline(time.Now().Add(r.timeout))
		}
		r.mutex.Unlock()
		if attempted {
			if err == nil {
				return n, nil
			}
			// A write error occurred on the provisional socket.  This should be handled
			// by the retry procedure.  Block until we have a final socket (which will
			// already have replayed b[:n]), and retry.
			<-r.retryCompleteFlag
			r.mutex.Lock()
			r.mutex.Unlock()
			m, err := r.conn.Write(b[n:])
			return n + m, err
		}
	}

	// retryCompleted() is true, so r.conn is final and doesn't need locking.
	return r.conn.Write(b)
}

func (r *retrier) ReadFrom(reader io.Reader) (bytes int64, err error) {
	for !r.retryCompleted() {
		// This buffer is large enough to hold any ordinary first write
		// without introducing extra splitting.
		buf := make([]byte, 2048)
		var n int
		if n, err = reader.Read(buf); err != nil {
			return
		}
		n, err = r.Write(buf[:n])
		bytes += int64(n)
		if err != nil {
			return
		}
	}

	var b int64
	b, err = r.conn.ReadFrom(reader)
	bytes += b
	return
}

func (r *retrier) CloseWrite() error {
	if !r.writeClosed() {
		close(r.writeCloseFlag)
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	return r.conn.CloseWrite()
}
<commit_msg>Report SNI in retry stats<commit_after>package intra

import (
	"io"
	"math/rand"
	"net"
	"sync"
	"time"

	"github.com/Jigsaw-Code/getsni"
)

type RetryStats struct {
	SNI     string // TLS SNI observed, if present.
	Bytes   int32  // Number of bytes uploaded before the retry.
	Chunks  int16  // Number of writes before the retry.
	Split   int16  // Number of bytes in the first retried segment.
	Timeout bool   // True if the retry was caused by a timeout.
}

// retrier implements the DuplexConn interface.
type retrier struct {
	// mutex is a lock that guards `conn`, `hello`, and `retryCompleteFlag`.
	// These fields must not be modified except under this lock.
	// After retryCompletedFlag is closed, these values will not be modified
	// again so locking is no longer required for reads.
	mutex   sync.Mutex
	network string
	addr    *net.TCPAddr
	// conn is the current underlying connection.  It is only modified by the reader
	// thread, so the reader functions may access it without acquiring a lock.
	conn *net.TCPConn
	// Time to wait between the first write and the first read before triggering a
	// retry.
	timeout time.Duration
	// hello is the contents written before the first read.  It is initially empty,
	// and is cleared when the first byte is received.
	hello []byte
	// Flag indicating when retry is finished or unnecessary.
	retryCompleteFlag chan struct{}
	// Flags indicating whether the caller has called CloseRead and CloseWrite.
	readCloseFlag  chan struct{}
	writeCloseFlag chan struct{}
	stats          RetryStats
	summary        *TCPSocketSummary
}

// Helper functions for reading flags.
// In this package, a "flag" is a thread-safe single-use status indicator that
// starts in the "open" state and transitions to "closed" when close() is called.
// It is implemented as a channel over which no data is ever sent.
// Some advantages of this implementation:
//  - The language enforces the one-way transition.
//  - Nonblocking and blocking access are both straightforward.
//  - Checking the status of a closed flag should be extremely fast (although currently
//    it's not optimized: https://github.com/golang/go/issues/32529)
func closed(c chan struct{}) bool {
	select {
	case <-c:
		// The channel has been closed.
		return true
	default:
		return false
	}
}

func (r *retrier) readClosed() bool {
	return closed(r.readCloseFlag)
}

func (r *retrier) writeClosed() bool {
	return closed(r.writeCloseFlag)
}

func (r *retrier) retryCompleted() bool {
	return closed(r.retryCompleteFlag)
}

// Given timestamps immediately before and after a successful socket connection
// (i.e. the time the SYN was sent and the time the SYNACK was received), this
// function returns a reasonable timeout for replies to a hello sent on this socket.
func timeout(before, after time.Time) time.Duration {
	// These values were chosen to have a <1% false positive rate based on test data.
	// False positives trigger an unnecessary retry, which can make connections slower, so they are
	// worth avoiding.  However, overly long timeouts make retry slower and less useful.
	rtt := after.Sub(before)
	return 1200*time.Millisecond + 2*rtt
}

// DialWithSplitRetry returns a TCP connection that transparently retries by
// splitting the initial upstream segment if the socket closes without receiving a
// reply.  Like net.Conn, it is intended for two-threaded use, with one thread calling
// Read and CloseRead, and another calling Write, ReadFrom, and CloseWrite.
func DialWithSplitRetry(network string, addr *net.TCPAddr, summary *TCPSocketSummary) (DuplexConn, error) {
	before := time.Now()
	conn, err := net.DialTCP(network, nil, addr)
	if err != nil {
		return nil, err
	}
	after := time.Now()

	r := &retrier{
		network:           network,
		addr:              addr,
		conn:              conn,
		timeout:           timeout(before, after),
		retryCompleteFlag: make(chan struct{}),
		readCloseFlag:     make(chan struct{}),
		writeCloseFlag:    make(chan struct{}),
		summary:           summary,
	}

	return r, nil
}

// Read-related functions.
func (r *retrier) Read(buf []byte) (n int, err error) {
	n, err = r.conn.Read(buf)
	if n == 0 && err == nil {
		// If no data was read, a nil error doesn't rule out the need for a retry.
		return
	}
	if !r.retryCompleted() {
		r.mutex.Lock()
		if err != nil {
			if neterr, ok := err.(net.Error); ok {
				r.stats.Timeout = neterr.Timeout()
			}
			// Read failed.  Retry.
			n, err = r.retry(buf)
		}
		close(r.retryCompleteFlag)
		r.hello = nil
		r.mutex.Unlock()
	}
	return
}

func (r *retrier) retry(buf []byte) (n int, err error) {
	r.conn.Close()
	var newConn *net.TCPConn
	if newConn, err = net.DialTCP(r.network, nil, r.addr); err != nil {
		return
	}
	r.conn = newConn
	first, second := splitHello(r.hello)
	r.stats.Split = int16(len(first))
	// Set Retry to a non-nil value, indicating that a retry occurred.
	r.summary.Retry = &r.stats
	if _, err = r.conn.Write(first); err != nil {
		return
	}
	if _, err = r.conn.Write(second); err != nil {
		return
	}
	// While we were creating the new socket, the caller might have called CloseRead
	// or CloseWrite on the old socket.  Copy that state to the new socket.
	// CloseRead and CloseWrite are idempotent, so this is safe even if the user's
	// action actually affected the new socket.
	if r.readClosed() {
		r.conn.CloseRead()
	}
	if r.writeClosed() {
		r.conn.CloseWrite()
	}
	return r.conn.Read(buf)
}

func (r *retrier) CloseRead() error {
	if !r.readClosed() {
		close(r.readCloseFlag)
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	return r.conn.CloseRead()
}

func splitHello(hello []byte) ([]byte, []byte) {
	if len(hello) == 0 {
		return hello, hello
	}
	const (
		MIN_SPLIT int = 32
		MAX_SPLIT int = 64
	)

	// Random number in the range [MIN_SPLIT, MAX_SPLIT]
	s := MIN_SPLIT + rand.Intn(MAX_SPLIT+1-MIN_SPLIT)
	limit := len(hello) / 2
	if s > limit {
		s = limit
	}
	return hello[:s], hello[s:]
}

// Write-related functions
func (r *retrier) Write(b []byte) (int, error) {
	// Double-checked locking pattern.  This avoids lock acquisition on
	// every packet after retry completes, while also ensuring that r.hello is
	// empty at steady-state.
	if !r.retryCompleted() {
		n := 0
		var err error
		attempted := false
		r.mutex.Lock()
		if !r.retryCompleted() {
			n, err = r.conn.Write(b)
			attempted = true
			r.hello = append(r.hello, b[:n]...)

			r.stats.Chunks++
			r.stats.Bytes = int32(len(r.hello))
			if r.stats.SNI == "" {
				r.stats.SNI, _ = getsni.GetSNI(r.hello)
			}

			// We require a response or another write within the specified timeout.
			r.conn.SetReadDeadline(time.Now().Add(r.timeout))
		}
		r.mutex.Unlock()
		if attempted {
			if err == nil {
				return n, nil
			}
			// A write error occurred on the provisional socket.  This should be handled
			// by the retry procedure.  Block until we have a final socket (which will
			// already have replayed b[:n]), and retry.
			<-r.retryCompleteFlag
			r.mutex.Lock()
			r.mutex.Unlock()
			m, err := r.conn.Write(b[n:])
			return n + m, err
		}
	}

	// retryCompleted() is true, so r.conn is final and doesn't need locking.
	return r.conn.Write(b)
}

func (r *retrier) ReadFrom(reader io.Reader) (bytes int64, err error) {
	for !r.retryCompleted() {
		// This buffer is large enough to hold any ordinary first write
		// without introducing extra splitting.
		buf := make([]byte, 2048)
		var n int
		if n, err = reader.Read(buf); err != nil {
			return
		}
		n, err = r.Write(buf[:n])
		bytes += int64(n)
		if err != nil {
			return
		}
	}

	var b int64
	b, err = r.conn.ReadFrom(reader)
	bytes += b
	return
}

func (r *retrier) CloseWrite() error {
	if !r.writeClosed() {
		close(r.writeCloseFlag)
	}
	r.mutex.Lock()
	defer r.mutex.Unlock()
	return r.conn.CloseWrite()
}
<|endoftext|>


<commit_before>package graph

import (
	"strconv"
	"os"
	"time"
)

// A Facebook Page.
// http://developers.facebook.com/docs/reference/api/page
type Page struct {
	// ID
	ID string
	// Name
	Name string
	// Profile picture
	picture string // Documented as a connection
	// Category
	Category string
	// Number of fans the page has
	FanCount float64

	// Undocumented properties but streamed
	Link            string
	Website         string
	Username        string
	Founded         *time.Time
	CompanyOverview string
	Mission         string
	Products        string

	// Connections
	feed string
	// picture  string does not exist as a connection
	tagged   string
	links    string
	photos   string
	groups   string // Not yet implemented
	albums   string
	statuses string
	videos   string
	notes    string
	posts    string
	events   string
	checkins string
}

// Gets the page's wall. Available to everyone on Facebook.
// Returns an array of Post objects.
func (p *Page) GetWall() (ps []Post, err os.Error) {
	if len(p.feed) == 0 {
		err = os.NewError("Error: Page.GetWall with ID " + p.ID + " feed URL is empty.")
	}
	return fetchPosts(p.feed)
}

// Gets the page's profile picture. Publicly available.
// Returns an HTTP 302 URL string with the location set to the picture URL.
// NOTE: This is not a connection but documented as one.
func (p *Page) GetPicture() (pic *Picture, err os.Error) {
	if len(p.picture) == 0 {
		err = os.NewError("Error: Page.GetPicture with ID " + p.ID + " picture URL is empty.")
	}
	return NewPicture(p.picture), err
}

// Gets the photos, videos, and posts in which this page has been tagged. Publicly available.
// Returns an heterogeneous array of Photo, Video or Post objects.
func (p *Page) GetTagged() (t []interface{}, err os.Error) {
	if len(p.tagged) == 0 {
		err = os.NewError("Error: Page.GetTagged with ID " + p.ID + " tagged URL is empty.")
		return
	}
	data, err := getData(p.tagged)
	if err != nil {
		return
	}
	t = make([]interface{}, len(data))
	for i, v := range data {
		tag := v.(map[string]interface{})
		tp, ok := tag["type"].(string)
		if ok {
			switch tp {
			case "status":
				t[i], err = parsePost(tag)
				if err != nil {
					return
				}
			case "photo", "link":
				t[i], err = parsePhoto(tag)
				if err != nil {
					return
				}
			case "video":
				t[i], err = parseVideo(tag)
				if err != nil {
					return
				}
			default:
				err = os.NewError("Tag has unknown type.")
				return
			}
		} else {
			err = os.NewError("Tag is corrupted.")
			return
		}
	}
	return
}

// Gets the page's posted links. Publicly available.
// Returns an array of Link objects.
func (p *Page) GetLinks() (ls []Link, err os.Error) {
	if len(p.links) == 0 {
		err = os.NewError("Error: Page.GetLinks with ID " + p.ID + " links URL is empty.")
	}
	return getLinks(p.links)
}

// Gets the photos this page has uploaded. Publicly available.
// Returns an array of Photo objects.
func (p *Page) GetPhotos() (ps []Photo, err os.Error) {
	if len(p.photos) == 0 {
		err = os.NewError("Error: Page.GetPhotos with ID " + p.ID + " photos URL is empty.")
	}
	return getPhotos(p.photos)
}

// Gets the groups this page is a member of. Available to everyone on Facebook.
// Returns an array of objects containing group id, version, name and unread fields.
// TODO: function for GetGroups

// Gets the page albums this page has created. Publicly available.
// Returns an array of Album objects.
func (p *Page) GetAlbums() (as []Album, err os.Error) {
	if len(p.albums) == 0 {
		err = os.NewError("Error: Page.GetAlbums with ID " + p.ID + " albums URL is empty.")
	}
	return getAlbums(p.albums)
}

// Gets the page's status updates. Publicly available.
// Returns an array of StatusMessage objects.
func (p *Page) GetStatuses() (sms []StatusMessage, err os.Error) {
	if len(p.statuses) == 0 {
		err = os.NewError("Error: Page.GetStatuses with ID " + p.ID + " statuses URL is empty.")
	}
	return getStatusMessages(p.statuses)
}

// Gets the videos this page has created. Publicly available.
// Returns an array of Video objects.
func (p *Page) GetVideos() (vs []Video, err os.Error) {
	if len(p.videos) == 0 {
		err = os.NewError("Error: Page.GetVideos with ID " + p.ID + " videos URL is empty.")
	}
	return getVideos(p.videos)
}

// Gets the page's notes. Publicly available.
// Returns an array of Note objects.
func (p *Page) GetNotes() (ns []Note, err os.Error) {
	if len(p.notes) == 0 {
		err = os.NewError("Error: Page.GetNotes with ID " + p.ID + " notes URL is empty.")
	}
	return getNotes(p.notes)
}

// Gets the page's own posts. Publicly available.
// Returns an array of Post objects.
func (p *Page) GetPosts() (feed []Post, err os.Error) {
	if len(p.posts) == 0 {
		err = os.NewError("Error: Page.GetPosts with ID " + p.ID + " posts URL is empty.")
	}
	return fetchPosts(p.posts)
}

// Gets the events this page is managing. Publicly available.
// Returns an array of Event objects.
func (p *Page) GetEvents() (es []Event, err os.Error) {
	if len(p.events) == 0 {
		err = os.NewError("Error: Page.GetEvents with ID " + p.ID + " events URL is empty.")
	}
	return getEvents(p.events)
}

// Gets Checkins made by friends of the current session user. Requires friends_checkins permissions.
// Returns an array of Checkin objects.
func (p *Page) GetCheckins() (cs []Checkin, err os.Error) {
	if len(p.checkins) == 0 {
		err = os.NewError("Error: Page.GetCheckins with ID " + p.ID + " checkins URL is empty.")
	}
	return getCheckins(p.checkins)
}

func (p *Page) String() string {
	return "ID: " + p.ID + "\tName: " + p.Name + "\tPicture: " + p.picture +
		"\tLink: " + p.Link + "\tCategory: " + p.Category + "\tWebsite: " +
		p.Website + "\tUsername: " + p.Username + "\tFounded: " + p.Founded.String() +
		"\tCompany overview: " + p.CompanyOverview + "\tMission: " + p.Mission +
		"\tProducts: " + p.Products + "\tFan count:" +
		strconv.Ftoa64(p.FanCount, 'e', -1) + "\n"
}

func parsePage(data map[string]interface{}) (p Page, err os.Error) {
	for key, value := range data {
		switch key {
		case "website":
			p.Website = value.(string)
		case "picture":
			p.picture = value.(string)
		case "fan_count":
			p.FanCount = value.(float64)
		case "mission":
			p.Mission = value.(string)
		case "category":
			p.Category = value.(string)
		case "name":
			p.Name = value.(string)
		case "username":
			p.Username = value.(string)
		case "link":
			p.Link = value.(string)
		case "id":
			p.ID = value.(string)
		case "products":
			p.Products = value.(string)
		case "founded":
			p.Founded, err = parseTime(value.(string))
		case "company_overview":
			p.CompanyOverview = value.(string)
		case "metadata":
			metadata := value.(map[string]interface{})
			for k, va := range metadata["connections"].(map[string]interface{}) {
				switch k {
				case "feed":
					p.feed = va.(string)
				/*
					case "picture":
						p.picture = va.(string)
				*/
				case "tagged":
					p.tagged = va.(string)
				case "links":
					p.links = va.(string)
				case "photos":
					p.photos = va.(string)
				case "groups":
					p.groups = va.(string)
				case "albums":
					p.albums = va.(string)
				case "statuses":
					p.statuses = va.(string)
				case "videos":
					p.videos = va.(string)
				case "notes":
					p.notes = va.(string)
				case "posts":
					p.notes = va.(string)
				case "events":
					p.events = va.(string)
				case "checkins":
					p.checkins = va.(string)
				}
			}
		}
	}
	return
}
<commit_msg>GetStatusMessage requires access token. Again not documented on facebook.<commit_after>package graph

import (
	"strconv"
	"os"
	"time"
)

// A Facebook Page.
// http://developers.facebook.com/docs/reference/api/page
type Page struct {
	// ID
	ID string
	// Name
	Name string
	// Profile picture
	picture string // Documented as a connection
	// Category
	Category string
	// Number of fans the page has
	FanCount float64

	// Undocumented properties but streamed
	Link            string
	Website         string
	Username        string
	Founded         *time.Time
	CompanyOverview string
	Mission         string
	Products        string

	// Connections
	feed string
	// picture  string does not exist as a connection
	tagged   string
	links    string
	photos   string
	groups   string // Not yet implemented
	albums   string
	statuses string
	videos   string
	notes    string
	posts    string
	events   string
	checkins string
}

// Gets the page's wall. Available to everyone on Facebook.
// Returns an array of Post objects.
func (p *Page) GetWall() (ps []Post, err os.Error) {
	if len(p.feed) == 0 {
		err = os.NewError("Error: Page.GetWall with ID " + p.ID + " feed URL is empty.")
	}
	return fetchPosts(p.feed)
}

// Gets the page's profile picture. Publicly available.
// Returns an HTTP 302 URL string with the location set to the picture URL.
// NOTE: This is not a connection but documented as one.
func (p *Page) GetPicture() (pic *Picture, err os.Error) {
	if len(p.picture) == 0 {
		err = os.NewError("Error: Page.GetPicture with ID " + p.ID + " picture URL is empty.")
	}
	return NewPicture(p.picture), err
}

// Gets the photos, videos, and posts in which this page has been tagged. Publicly available.
// Returns an heterogeneous array of Photo, Video or Post objects.
func (p *Page) GetTagged() (t []interface{}, err os.Error) {
	if len(p.tagged) == 0 {
		err = os.NewError("Error: Page.GetTagged with ID " + p.ID + " tagged URL is empty.")
		return
	}
	data, err := getData(p.tagged)
	if err != nil {
		return
	}
	t = make([]interface{}, len(data))
	for i, v := range data {
		tag := v.(map[string]interface{})
		tp, ok := tag["type"].(string)
		if ok {
			switch tp {
			case "status":
				t[i], err = parsePost(tag)
				if err != nil {
					return
				}
			case "photo", "link":
				t[i], err = parsePhoto(tag)
				if err != nil {
					return
				}
			case "video":
				t[i], err = parseVideo(tag)
				if err != nil {
					return
				}
			default:
				err = os.NewError("Tag has unknown type.")
				return
			}
		} else {
			err = os.NewError("Tag is corrupted.")
			return
		}
	}
	return
}

// Gets the page's posted links. Publicly available.
// Returns an array of Link objects.
func (p *Page) GetLinks() (ls []Link, err os.Error) {
	if len(p.links) == 0 {
		err = os.NewError("Error: Page.GetLinks with ID " + p.ID + " links URL is empty.")
	}
	return getLinks(p.links)
}

// Gets the photos this page has uploaded. Publicly available.
// Returns an array of Photo objects.
func (p *Page) GetPhotos() (ps []Photo, err os.Error) {
	if len(p.photos) == 0 {
		err = os.NewError("Error: Page.GetPhotos with ID " + p.ID + " photos URL is empty.")
	}
	return getPhotos(p.photos)
}

// Gets the groups this page is a member of. Available to everyone on Facebook.
// Returns an array of objects containing group id, version, name and unread fields.
// TODO: function for GetGroups

// Gets the page albums this page has created. Publicly available.
// Returns an array of Album objects.
func (p *Page) GetAlbums() (as []Album, err os.Error) {
	if len(p.albums) == 0 {
		err = os.NewError("Error: Page.GetAlbums with ID " + p.ID + " albums URL is empty.")
	}
	return getAlbums(p.albums)
}

// Gets the page's status updates. Publicly available. Requires access token.
// Returns an array of StatusMessage objects.
func (p *Page) GetStatuses() (sms []StatusMessage, err os.Error) {
	if len(p.statuses) == 0 {
		err = os.NewError("Error: Page.GetStatuses with ID " + p.ID + " statuses URL is empty.")
	}
	return getStatusMessages(p.statuses)
}

// Gets the videos this page has created. Publicly available.
// Returns an array of Video objects.
func (p *Page) GetVideos() (vs []Video, err os.Error) {
	if len(p.videos) == 0 {
		err = os.NewError("Error: Page.GetVideos with ID " + p.ID + " videos URL is empty.")
	}
	return getVideos(p.videos)
}

// Gets the page's notes. Publicly available.
// Returns an array of Note objects.
func (p *Page) GetNotes() (ns []Note, err os.Error) {
	if len(p.notes) == 0 {
		err = os.NewError("Error: Page.GetNotes with ID " + p.ID + " notes URL is empty.")
	}
	return getNotes(p.notes)
}

// Gets the page's own posts. Publicly available.
// Returns an array of Post objects.
func (p *Page) GetPosts() (feed []Post, err os.Error) {
	if len(p.posts) == 0 {
		err = os.NewError("Error: Page.GetPosts with ID " + p.ID + " posts URL is empty.")
	}
	return fetchPosts(p.posts)
}

// Gets the events this page is managing. Publicly available.
// Returns an array of Event objects.
func (p *Page) GetEvents() (es []Event, err os.Error) {
	if len(p.events) == 0 {
		err = os.NewError("Error: Page.GetEvents with ID " + p.ID + " events URL is empty.")
	}
	return getEvents(p.events)
}

// Gets Checkins made by friends of the current session user. Requires friends_checkins permissions.
// Returns an array of Checkin objects.
func (p *Page) GetCheckins() (cs []Checkin, err os.Error) {
	if len(p.checkins) == 0 {
		err = os.NewError("Error: Page.GetCheckins with ID " + p.ID + " checkins URL is empty.")
	}
	return getCheckins(p.checkins)
}

func (p *Page) String() string {
	return "ID: " + p.ID + "\tName: " + p.Name + "\tPicture: " + p.picture +
		"\tLink: " + p.Link + "\tCategory: " + p.Category + "\tWebsite: " +
		p.Website + "\tUsername: " + p.Username + "\tFounded: " + p.Founded.String() +
		"\tCompany overview: " + p.CompanyOverview + "\tMission: " + p.Mission +
		"\tProducts: " + p.Products + "\tFan count:" +
		strconv.Ftoa64(p.FanCount, 'e', -1) + "\n"
}

func parsePage(data map[string]interface{}) (p Page, err os.Error) {
	for key, value := range data {
		switch key {
		case "website":
			p.Website = value.(string)
		case "picture":
			p.picture = value.(string)
		case "fan_count":
			p.FanCount = value.(float64)
		case "mission":
			p.Mission = value.(string)
		case "category":
			p.Category = value.(string)
		case "name":
			p.Name = value.(string)
		case "username":
			p.Username = value.(string)
		case "link":
			p.Link = value.(string)
		case "id":
			p.ID = value.(string)
		case "products":
			p.Products = value.(string)
		case "founded":
			p.Founded, err = parseTime(value.(string))
		case "company_overview":
			p.CompanyOverview = value.(string)
		case "metadata":
			metadata := value.(map[string]interface{})
			for k, va := range metadata["connections"].(map[string]interface{}) {
				switch k {
				case "feed":
					p.feed = va.(string)
				/*
					case "picture":
						p.picture = va.(string)
				*/
				case "tagged":
					p.tagged = va.(string)
				case "links":
					p.links = va.(string)
				case "photos":
					p.photos = va.(string)
				case "groups":
					p.groups = va.(string)
				case "albums":
					p.albums = va.(string)
				case "statuses":
					p.statuses = va.(string)
				case "videos":
					p.videos = va.(string)
				case "notes":
					p.notes = va.(string)
				case "posts":
					p.notes = va.(string)
				case "events":
					p.events = va.(string)
				case "checkins":
					p.checkins = va.(string)
				}
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"time"
	"fmt"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

// Note: Channels can be used as function parameters / arguments
func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // send sum to c
}

func channelExamples() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	// Split the work between two goroutines - slicing the array to achieve the portioning of the work
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // receive from c

	fmt.Println(x, y, x+y)
}

func bufferedChannelExamples() {
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2
	// ch <- 3 - would cause the code to block here (fatal error: all goroutines are asleep - deadlock!)
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

func main() {
	// GOROUTINE
	// A goroutine is a lightweight thread managed by the Go runtime.
	// go <func> starts a new goroutine running
	go say("world")
	say("hello")

	// Goroutines run in the same address space, so access to shared memory must be synchronized.
	// The sync package provides useful primitives, although you won't need them much in Go as there are other primitives...

	// CHANNELS
	// Channels are a typed conduit through which you can send and receive values with the channel operator, <-. (The data flows in the direction of the arrow.)
	// e.g.
	// ch <- v    // Send v to channel ch.
	// v := <-ch  // Receive from ch, and assign value to v.

	// Like maps and slices, channels must be created before use:
	// ch := make(chan int)
	// By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
	channelExamples()

	// BUFFERED CHANNELS
	// Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel:
	// ch := make(chan int, 100)
	// Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.
	bufferedChannelExamples()
}
<commit_msg>Further exploration into channels<commit_after>package main

import (
	"time"
	"fmt"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

// Note: Channels can be used as function parameters / arguments
func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // send sum to c
}

func channelExamples() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	// Split the work between two goroutines - slicing the array to achieve the portioning of the work
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // receive from c

	fmt.Println(x, y, x+y)
}

func bufferedChannelExamples() {
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2
	// ch <- 3 - would cause the code to block here (fatal error: all goroutines are asleep - deadlock!)
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

func fibonacciChannels(n int, c chan int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	// Note: Only the sender should close a channel, never the receiver. Sending on a closed channel will cause a panic.
	// Note: Channels aren't like files; you don't usually need to close them. Closing is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop.
	close(c)
}

func controllingChannelsExample() {
	c := make(chan int, 10)
	go fibonacciChannels(cap(c), c) // Also works if it is not run in a separate goroutine
	// The loop for i := range c receives values from the channel repeatedly until it is closed.
	for i := range c {
		fmt.Println(i)
	}
}

func main() {
	// GOROUTINE
	// A goroutine is a lightweight thread managed by the Go runtime.
	// go <func> starts a new goroutine running
	fmt.Println("calling the goroutine statements")
	go say("world")
	say("hello")

	// Goroutines run in the same address space, so access to shared memory must be synchronized.
	// The sync package provides useful primitives, although you won't need them much in Go as there are other primitives...

	// CHANNELS
	// Channels are a typed conduit through which you can send and receive values with the channel operator, <-. (The data flows in the direction of the arrow.)
	// e.g.
	// ch <- v    // Send v to channel ch.
	// v := <-ch  // Receive from ch, and assign value to v.

	// Like maps and slices, channels must be created before use:
	// ch := make(chan int)
	// NOTE: By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
	fmt.Println("calling the channel statements")
	channelExamples()

	// BUFFERED CHANNELS
	// Channels can be buffered. Provide the buffer length as the second argument to make to initialize a buffered channel:
	// ch := make(chan int, 100)
	// Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.
	fmt.Println("calling the buffered channel statements")
	bufferedChannelExamples()

	// CONTROLLING CHANNELS
	// A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression:
	// v, ok := <-ch
	// ok is false if there are no more values to receive and the channel is closed.
	fmt.Println("calling the controlling channels statements")
	controllingChannelsExample()

}
<|endoftext|>