<commit_before>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package planbuilder

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/sqlparser"
	"github.com/youtube/vitess/go/vt/vtgate/engine"
	"github.com/youtube/vitess/go/vt/vtgate/vindexes"
)

// buildInsertPlan builds the route for an INSERT statement.
func buildInsertPlan(ins *sqlparser.Insert, vschema VSchema) (*engine.Route, error) {
	table, err := vschema.FindTable(ins.Table)
	if err != nil {
		return nil, err
	}
	if !table.Keyspace.Sharded {
		return buildInsertUnshardedPlan(ins, table, vschema)
	}
	if ins.Action == sqlparser.ReplaceStr {
		return nil, errors.New("unsupported: REPLACE INTO with sharded schema")
	}
	return buildInsertShardedPlan(ins, table)
}

func buildInsertUnshardedPlan(ins *sqlparser.Insert, table *vindexes.Table, vschema VSchema) (*engine.Route, error) {
	eRoute := &engine.Route{
		Opcode:   engine.InsertUnsharded,
		Table:    table,
		Keyspace: table.Keyspace,
	}
	if !validateSubquerySamePlan(eRoute, vschema, ins) {
		return nil, errors.New("unsupported: sharded subquery in insert values")
	}
	var rows sqlparser.Values
	switch insertValues := ins.Rows.(type) {
	case *sqlparser.Select, *sqlparser.Union:
		if eRoute.Table.AutoIncrement != nil {
			return nil, errors.New("unsupported: auto-inc and select in insert")
		}
		eRoute.Query = generateQuery(ins)
		return eRoute, nil
	case sqlparser.Values:
		rows = insertValues
	default:
		panic(fmt.Sprintf("BUG: unexpected construct in insert: %T", insertValues))
	}
	if eRoute.Table.AutoIncrement == nil {
		eRoute.Query = generateQuery(ins)
		return eRoute, nil
	}

	// Table has auto-inc and has a VALUES clause.
	if len(ins.Columns) == 0 {
		return nil, errors.New("column list required for tables with auto-inc columns")
	}
	for _, row := range rows {
		if len(ins.Columns) != len(row) {
			return nil, errors.New("column list doesn't match values")
		}
	}
	if err := modifyForAutoinc(ins, eRoute); err != nil {
		return nil, err
	}
	eRoute.Query = generateQuery(ins)
	return eRoute, nil
}

func buildInsertShardedPlan(ins *sqlparser.Insert, table *vindexes.Table) (*engine.Route, error) {
	eRoute := &engine.Route{
		Opcode:   engine.InsertSharded,
		Table:    table,
		Keyspace: table.Keyspace,
	}
	if ins.Ignore != "" {
		eRoute.Opcode = engine.InsertShardedIgnore
	}
	if ins.OnDup != nil {
		if isVindexChanging(sqlparser.UpdateExprs(ins.OnDup), eRoute.Table.ColumnVindexes) {
			return nil, errors.New("unsupported: DML cannot change vindex column")
		}
		eRoute.Opcode = engine.InsertShardedIgnore
	}
	if len(ins.Columns) == 0 {
		return nil, errors.New("no column list")
	}
	var rows sqlparser.Values
	switch insertValues := ins.Rows.(type) {
	case *sqlparser.Select, *sqlparser.Union:
		return nil, errors.New("unsupported: insert into select")
	case sqlparser.Values:
		rows = insertValues
		if hasSubquery(rows) {
			return nil, errors.New("unsupported: subquery in insert values")
		}
	default:
		panic(fmt.Sprintf("BUG: unexpected construct in insert: %T", insertValues))
	}
	for _, value := range rows {
		if len(ins.Columns) != len(value) {
			return nil, errors.New("column list doesn't match values")
		}
	}

	if eRoute.Table.AutoIncrement != nil {
		if err := modifyForAutoinc(ins, eRoute); err != nil {
			return nil, err
		}
	}

	routeValues := make([]sqltypes.PlanValue, len(eRoute.Table.ColumnVindexes))
	// Initialize each table vindex with the number of rows per insert.
	// There will be a plan value for each row.
	for colIdx, _ := range routeValues {
		routeValues[colIdx].Values = make([]sqltypes.PlanValue, len(rows))
	}
	// What's going in here?
	// For each vindex, we need to compute the column value for each row being inserted:
	// routeValues will contain a PlanValue for each Vindex.
	// In turn, each  PlanValue will have Values ([]sqltypes.PlanValue) for each row.
	// In each row will have Values for the columns that are defined in the vindex.
	// For instance, given the following insert statement:
	// INSERT INTO table_a (column_a, column_b, column_c) VALUES (value_a1, value_b1, value_c1), (value_a2, value_b2, value_c2)
	// Primary vindex on column_a and secondary vindex on columns b and c,
	// routeValues will look like the following:
	// [
	//  [[value_a1], [value_a2]], <- Values for each row primary vindex
	//  [[value_b1, value_c1], [value_b2, value_c2]] <- Values for each row multicolumn secondary vindex
	// ]

	for vIdx, colVindex := range eRoute.Table.ColumnVindexes {
		for _, col := range colVindex.Columns {
			colNum := findOrAddColumn(ins, col)
			// swap bind variables
			baseName := ":_" + col.CompliantName()
			for rowNum, row := range rows {
				innerpv, err := sqlparser.NewPlanValue(row[colNum])
				if err != nil {
					return nil, fmt.Errorf("could not compute value for vindex or auto-inc column: %v", err)
				}
				routeValues[vIdx].Values[rowNum].Values = append(routeValues[vIdx].Values[rowNum].Values, innerpv)
				row[colNum] = sqlparser.NewValArg([]byte(baseName + strconv.Itoa(rowNum)))
			}
		}
	}
	eRoute.Values = routeValues
	eRoute.Query = generateQuery(ins)
	generateInsertShardedQuery(ins, eRoute, rows)
	return eRoute, nil
}

func generateInsertShardedQuery(node *sqlparser.Insert, eRoute *engine.Route, valueTuples sqlparser.Values) {
	prefixBuf := sqlparser.NewTrackedBuffer(dmlFormatter)
	midBuf := sqlparser.NewTrackedBuffer(dmlFormatter)
	suffixBuf := sqlparser.NewTrackedBuffer(dmlFormatter)
	eRoute.Mid = make([]string, len(valueTuples))
	prefixBuf.Myprintf("insert %v%sinto %v%v values ",
		node.Comments, node.Ignore,
		node.Table, node.Columns)
	eRoute.Prefix = prefixBuf.String()
	for rowNum, val := range valueTuples {
		midBuf.Myprintf("%v", val)
		eRoute.Mid[rowNum] = midBuf.String()
		midBuf.Truncate(0)
	}
	suffixBuf.Myprintf("%v", node.OnDup)
	eRoute.Suffix = suffixBuf.String()
}

// modifyForAutoinc modfies the AST and the plan to generate
// necessary autoinc values. It must be called only if eRoute.Table.AutoIncrement
// is set.
func modifyForAutoinc(ins *sqlparser.Insert, eRoute *engine.Route) error {
	pos := findOrAddColumn(ins, eRoute.Table.AutoIncrement.Column)
	autoIncValues, err := swapBindVariables(ins.Rows.(sqlparser.Values), pos, ":"+engine.SeqVarName)
	if err != nil {
		return err
	}
	eRoute.Generate = &engine.Generate{
		Keyspace: eRoute.Table.AutoIncrement.Sequence.Keyspace,
		Query:    fmt.Sprintf("select next :n values from %s", sqlparser.String(eRoute.Table.AutoIncrement.Sequence.Name)),
		Values:   autoIncValues,
	}
	return nil
}

// swapBindVariables swaps in bind variable names at the the specified
// column position in the AST values and returns the converted values back.
// Bind variable names are generated using baseName.
func swapBindVariables(rows sqlparser.Values, colNum int, baseName string) (sqltypes.PlanValue, error) {
	pv := sqltypes.PlanValue{}
	for rowNum, row := range rows {
		innerpv, err := sqlparser.NewPlanValue(row[colNum])
		if err != nil {
			return pv, fmt.Errorf("could not compute value for vindex or auto-inc column: %v", err)
		}
		pv.Values = append(pv.Values, innerpv)
		row[colNum] = sqlparser.NewValArg([]byte(baseName + strconv.Itoa(rowNum)))
	}
	return pv, nil
}

// findOrAddColumn finds the position of a column in the insert. If it's
// absent it appends it to the with NULL values and returns that position.
func findOrAddColumn(ins *sqlparser.Insert, col sqlparser.ColIdent) int {
	for i, column := range ins.Columns {
		if col.Equal(column) {
			return i
		}
	}
	ins.Columns = append(ins.Columns, col)
	rows := ins.Rows.(sqlparser.Values)
	for i := range rows {
		rows[i] = append(rows[i], &sqlparser.NullVal{})
	}
	return len(ins.Columns) - 1
}

// isVindexChanging returns true if any of the update
// expressions modify a vindex column.
func isVindexChanging(setClauses sqlparser.UpdateExprs, colVindexes []*vindexes.ColumnVindex) bool {
	for _, assignment := range setClauses {
		for _, vcol := range colVindexes {
			for _, col := range vcol.Columns {
				if col.Equal(assignment.Name.Name) {
					return true
				}
			}
		}
	}
	return false
}
<commit_msg>Rename value for clarity<commit_after>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package planbuilder

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/sqlparser"
	"github.com/youtube/vitess/go/vt/vtgate/engine"
	"github.com/youtube/vitess/go/vt/vtgate/vindexes"
)

// buildInsertPlan builds the route for an INSERT statement.
func buildInsertPlan(ins *sqlparser.Insert, vschema VSchema) (*engine.Route, error) {
	table, err := vschema.FindTable(ins.Table)
	if err != nil {
		return nil, err
	}
	if !table.Keyspace.Sharded {
		return buildInsertUnshardedPlan(ins, table, vschema)
	}
	if ins.Action == sqlparser.ReplaceStr {
		return nil, errors.New("unsupported: REPLACE INTO with sharded schema")
	}
	return buildInsertShardedPlan(ins, table)
}

func buildInsertUnshardedPlan(ins *sqlparser.Insert, table *vindexes.Table, vschema VSchema) (*engine.Route, error) {
	eRoute := &engine.Route{
		Opcode:   engine.InsertUnsharded,
		Table:    table,
		Keyspace: table.Keyspace,
	}
	if !validateSubquerySamePlan(eRoute, vschema, ins) {
		return nil, errors.New("unsupported: sharded subquery in insert values")
	}
	var rows sqlparser.Values
	switch insertValues := ins.Rows.(type) {
	case *sqlparser.Select, *sqlparser.Union:
		if eRoute.Table.AutoIncrement != nil {
			return nil, errors.New("unsupported: auto-inc and select in insert")
		}
		eRoute.Query = generateQuery(ins)
		return eRoute, nil
	case sqlparser.Values:
		rows = insertValues
	default:
		panic(fmt.Sprintf("BUG: unexpected construct in insert: %T", insertValues))
	}
	if eRoute.Table.AutoIncrement == nil {
		eRoute.Query = generateQuery(ins)
		return eRoute, nil
	}

	// Table has auto-inc and has a VALUES clause.
	if len(ins.Columns) == 0 {
		return nil, errors.New("column list required for tables with auto-inc columns")
	}
	for _, row := range rows {
		if len(ins.Columns) != len(row) {
			return nil, errors.New("column list doesn't match values")
		}
	}
	if err := modifyForAutoinc(ins, eRoute); err != nil {
		return nil, err
	}
	eRoute.Query = generateQuery(ins)
	return eRoute, nil
}

func buildInsertShardedPlan(ins *sqlparser.Insert, table *vindexes.Table) (*engine.Route, error) {
	eRoute := &engine.Route{
		Opcode:   engine.InsertSharded,
		Table:    table,
		Keyspace: table.Keyspace,
	}
	if ins.Ignore != "" {
		eRoute.Opcode = engine.InsertShardedIgnore
	}
	if ins.OnDup != nil {
		if isVindexChanging(sqlparser.UpdateExprs(ins.OnDup), eRoute.Table.ColumnVindexes) {
			return nil, errors.New("unsupported: DML cannot change vindex column")
		}
		eRoute.Opcode = engine.InsertShardedIgnore
	}
	if len(ins.Columns) == 0 {
		return nil, errors.New("no column list")
	}
	var rows sqlparser.Values
	switch insertValues := ins.Rows.(type) {
	case *sqlparser.Select, *sqlparser.Union:
		return nil, errors.New("unsupported: insert into select")
	case sqlparser.Values:
		rows = insertValues
		if hasSubquery(rows) {
			return nil, errors.New("unsupported: subquery in insert values")
		}
	default:
		panic(fmt.Sprintf("BUG: unexpected construct in insert: %T", insertValues))
	}
	for _, value := range rows {
		if len(ins.Columns) != len(value) {
			return nil, errors.New("column list doesn't match values")
		}
	}

	if eRoute.Table.AutoIncrement != nil {
		if err := modifyForAutoinc(ins, eRoute); err != nil {
			return nil, err
		}
	}

	routeValues := make([]sqltypes.PlanValue, len(eRoute.Table.ColumnVindexes))
	// Initialize each table vindex with the number of rows per insert.
	// There will be a plan value for each row.
	for vIdx, _ := range routeValues {
		routeValues[vIdx].Values = make([]sqltypes.PlanValue, len(rows))
	}
	// What's going in here?
	// For each vindex, we need to compute the column value for each row being inserted:
	// routeValues will contain a PlanValue for each Vindex.
	// In turn, each  PlanValue will have Values ([]sqltypes.PlanValue) for each row.
	// In each row will have Values for the columns that are defined in the vindex.
	// For instance, given the following insert statement:
	// INSERT INTO table_a (column_a, column_b, column_c) VALUES (value_a1, value_b1, value_c1), (value_a2, value_b2, value_c2)
	// Primary vindex on column_a and secondary vindex on columns b and c,
	// routeValues will look like the following:
	// [
	//  [[value_a1], [value_a2]], <- Values for each row primary vindex
	//  [[value_b1, value_c1], [value_b2, value_c2]] <- Values for each row multicolumn secondary vindex
	// ]

	for vIdx, colVindex := range eRoute.Table.ColumnVindexes {
		for _, col := range colVindex.Columns {
			colNum := findOrAddColumn(ins, col)
			// swap bind variables
			baseName := ":_" + col.CompliantName()
			for rowNum, row := range rows {
				innerpv, err := sqlparser.NewPlanValue(row[colNum])
				if err != nil {
					return nil, fmt.Errorf("could not compute value for vindex or auto-inc column: %v", err)
				}
				routeValues[vIdx].Values[rowNum].Values = append(routeValues[vIdx].Values[rowNum].Values, innerpv)
				row[colNum] = sqlparser.NewValArg([]byte(baseName + strconv.Itoa(rowNum)))
			}
		}
	}
	eRoute.Values = routeValues
	eRoute.Query = generateQuery(ins)
	generateInsertShardedQuery(ins, eRoute, rows)
	return eRoute, nil
}

func generateInsertShardedQuery(node *sqlparser.Insert, eRoute *engine.Route, valueTuples sqlparser.Values) {
	prefixBuf := sqlparser.NewTrackedBuffer(dmlFormatter)
	midBuf := sqlparser.NewTrackedBuffer(dmlFormatter)
	suffixBuf := sqlparser.NewTrackedBuffer(dmlFormatter)
	eRoute.Mid = make([]string, len(valueTuples))
	prefixBuf.Myprintf("insert %v%sinto %v%v values ",
		node.Comments, node.Ignore,
		node.Table, node.Columns)
	eRoute.Prefix = prefixBuf.String()
	for rowNum, val := range valueTuples {
		midBuf.Myprintf("%v", val)
		eRoute.Mid[rowNum] = midBuf.String()
		midBuf.Truncate(0)
	}
	suffixBuf.Myprintf("%v", node.OnDup)
	eRoute.Suffix = suffixBuf.String()
}

// modifyForAutoinc modfies the AST and the plan to generate
// necessary autoinc values. It must be called only if eRoute.Table.AutoIncrement
// is set.
func modifyForAutoinc(ins *sqlparser.Insert, eRoute *engine.Route) error {
	pos := findOrAddColumn(ins, eRoute.Table.AutoIncrement.Column)
	autoIncValues, err := swapBindVariables(ins.Rows.(sqlparser.Values), pos, ":"+engine.SeqVarName)
	if err != nil {
		return err
	}
	eRoute.Generate = &engine.Generate{
		Keyspace: eRoute.Table.AutoIncrement.Sequence.Keyspace,
		Query:    fmt.Sprintf("select next :n values from %s", sqlparser.String(eRoute.Table.AutoIncrement.Sequence.Name)),
		Values:   autoIncValues,
	}
	return nil
}

// swapBindVariables swaps in bind variable names at the the specified
// column position in the AST values and returns the converted values back.
// Bind variable names are generated using baseName.
func swapBindVariables(rows sqlparser.Values, colNum int, baseName string) (sqltypes.PlanValue, error) {
	pv := sqltypes.PlanValue{}
	for rowNum, row := range rows {
		innerpv, err := sqlparser.NewPlanValue(row[colNum])
		if err != nil {
			return pv, fmt.Errorf("could not compute value for vindex or auto-inc column: %v", err)
		}
		pv.Values = append(pv.Values, innerpv)
		row[colNum] = sqlparser.NewValArg([]byte(baseName + strconv.Itoa(rowNum)))
	}
	return pv, nil
}

// findOrAddColumn finds the position of a column in the insert. If it's
// absent it appends it to the with NULL values and returns that position.
func findOrAddColumn(ins *sqlparser.Insert, col sqlparser.ColIdent) int {
	for i, column := range ins.Columns {
		if col.Equal(column) {
			return i
		}
	}
	ins.Columns = append(ins.Columns, col)
	rows := ins.Rows.(sqlparser.Values)
	for i := range rows {
		rows[i] = append(rows[i], &sqlparser.NullVal{})
	}
	return len(ins.Columns) - 1
}

// isVindexChanging returns true if any of the update
// expressions modify a vindex column.
func isVindexChanging(setClauses sqlparser.UpdateExprs, colVindexes []*vindexes.ColumnVindex) bool {
	for _, assignment := range setClauses {
		for _, vcol := range colVindexes {
			for _, col := range vcol.Columns {
				if col.Equal(assignment.Name.Name) {
					return true
				}
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"net"
)

func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
	ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
	if err != nil {
		return nil, err
	}
	return parseIP(ip)
}

// returns an IP for the ID given, allocating a fresh one if necessary
func (client *Client) AllocateIP(ID string) (*net.IPNet, error) {
	return client.ipamOp(ID, "POST")
}

func (client *Client) AllocateIPInSubnet(ID string, subnet *net.IPNet) (*net.IPNet, error) {
	ip, err := client.httpVerb("POST", fmt.Sprintf("/ip/%s/%s", ID, subnet), nil)
	if err != nil {
		return nil, err
	}
	return parseIP(ip)
}

// returns an IP for the ID given, or nil if one has not been
// allocated
func (client *Client) LookupIP(ID string) (*net.IPNet, error) {
	return client.ipamOp(ID, "GET")
}

// Claim a specific IP on behalf of the ID
func (client *Client) ClaimIP(ID string, cidr *net.IPNet) error {
	_, err := client.httpVerb("PUT", fmt.Sprintf("/ip/%s/%s", ID, cidr), nil)
	return err
}

// release all IPs owned by an ID
func (client *Client) ReleaseIPsFor(ID string) error {
	_, err := client.ipamOp(ID, "DELETE")
	return err
}

func (client *Client) DefaultSubnet() (*net.IPNet, error) {
	cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), nil)
	if err != nil {
		return nil, err
	}
	_, ipnet, err := net.ParseCIDR(cidr)
	return ipnet, err
}

func parseIP(body string) (*net.IPNet, error) {
	ip, ipnet, err := net.ParseCIDR(string(body))
	if err != nil {
		return nil, err
	}
	ipnet.IP = ip
	return ipnet, nil
}
<commit_msg>Don't try to parse return from DELETE address<commit_after>package api

import (
	"fmt"
	"net"
)

func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) {
	ip, err := client.httpVerb(op, fmt.Sprintf("/ip/%s", ID), nil)
	if err != nil {
		return nil, err
	}
	return parseIP(ip)
}

// returns an IP for the ID given, allocating a fresh one if necessary
func (client *Client) AllocateIP(ID string) (*net.IPNet, error) {
	return client.ipamOp(ID, "POST")
}

func (client *Client) AllocateIPInSubnet(ID string, subnet *net.IPNet) (*net.IPNet, error) {
	ip, err := client.httpVerb("POST", fmt.Sprintf("/ip/%s/%s", ID, subnet), nil)
	if err != nil {
		return nil, err
	}
	return parseIP(ip)
}

// returns an IP for the ID given, or nil if one has not been
// allocated
func (client *Client) LookupIP(ID string) (*net.IPNet, error) {
	return client.ipamOp(ID, "GET")
}

// Claim a specific IP on behalf of the ID
func (client *Client) ClaimIP(ID string, cidr *net.IPNet) error {
	_, err := client.httpVerb("PUT", fmt.Sprintf("/ip/%s/%s", ID, cidr), nil)
	return err
}

// release all IPs owned by an ID
func (client *Client) ReleaseIPsFor(ID string) error {
	_, err := client.httpVerb("DELETE", fmt.Sprintf("/ip/%s", ID), nil)
	return err
}

func (client *Client) DefaultSubnet() (*net.IPNet, error) {
	cidr, err := client.httpVerb("GET", fmt.Sprintf("/ipinfo/defaultsubnet"), nil)
	if err != nil {
		return nil, err
	}
	_, ipnet, err := net.ParseCIDR(cidr)
	return ipnet, err
}

func parseIP(body string) (*net.IPNet, error) {
	ip, ipnet, err := net.ParseCIDR(string(body))
	if err != nil {
		return nil, err
	}
	ipnet.IP = ip
	return ipnet, nil
}
<|endoftext|>


<commit_before>// Package bitstring implmements a fixed length bit string type and common
// operations on bit strings.
package bitstring

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math/big"
	"math/rand"
)

const wordLength = 32

// BitString implements a fixed-length bit-string.
//
// Internally, bits are packed into an array of ints. This implementation makes
// more efficient use of space than the alternative approach of using an array
// of booleans.
type BitString struct {
	// length in bits of the bit string
	length int

	// bits are packed in an array of 32-bit ints.
	data []uint32
}

// New creates a bit string of the specified length (in bits) with all bits
// initially set to zero (off).
func New(length int) (*BitString, error) {
	if length < 0 {
		return nil, errors.New("BitString length must be non-negative")
	}
	return &BitString{
		length: length,
		data:   make([]uint32, (length+wordLength-1)/wordLength),
	}, nil
}

// NewRandom creates a BitString of the specified length with each bit set
// randomly (the distribution of bits is uniform so long as the output
// from the provided pseudo-random number generator is also uniform).
//
// Using NewRandom is more efficient than creating a bit string and then
// randomly setting each bit individually.
func NewRandom(length int, rng *rand.Rand) (*BitString, error) {
	bt, err := New(length)
	if err != nil {
		return nil, err
	}

	// Instead of setting each bit with a random boolean, we create a random
	// byte buffer and fill the data field with it.
	buf := make([]byte, len(bt.data)*4)
	rng.Read(buf) // no need for error checking on Rand.Read
	for i := 0; i < len(bt.data); i++ {
		randomInt, _ := binary.Varint(buf[i : i+4])
		bt.data[i] = uint32(randomInt)
	}

	// If the last word is not fully utilised, zero any out-of-bounds bits.
	// This is necessary because the CountSetBits() methods will count
	// out-of-bounds bits.
	bitsUsed := uint32(length % wordLength)
	if bitsUsed < wordLength {
		unusedBits := wordLength - bitsUsed
		mask := uint32(0xffffffff >> unusedBits)
		bt.data[len(bt.data)-1] &= mask
	}
	return bt, nil
}

// NewFromString creates a new BitString from a character string of 1s and 0s
// in big-endian order.
func NewFromString(value string) (*BitString, error) {
	bt, err := New(len(value))
	if err != nil {
		return nil, err
	}

	for i, c := range value {
		switch c {
		case '0':
			continue
		case '1':
			bt.SetBit(len(value)-i-1, true)
		default:
			return nil, fmt.Errorf("illegal character at position %v: %#U", i, c)
		}
	}
	return bt, nil
}

// Len returns the length of the BitString in bits.
func (bt *BitString) Len() int {
	return bt.length
}

// Bit returns the bit at the specified index.
//
// index is the index of the bit to look-up (0 is the least-significant bit).
// Returns a boolean indicating whether the bit is set or not.
//
// Will panic if the specified index is not a bit position in this bit string.
func (bt *BitString) Bit(index int) bool {
	bt.assertValidIndex(index)
	word := uint32(index / wordLength)
	offset := uint32(index % wordLength)
	return (bt.data[word] & (1 << offset)) != 0
}

// SetBit sets the bit at the specified index.
//
// index is the index of the bit to set (0 is the least-significant bit).
// set is a boolean indicating whether the bit should be set or not.
//
// Will panic if the specified index is not a bit position in this bit string.
func (bt *BitString) SetBit(index int, set bool) {
	bt.assertValidIndex(index)
	word := uint32(index / wordLength)
	offset := uint32(index % wordLength)
	if set {
		bt.data[word] |= (1 << offset)
	} else {
		// Unset the bit.
		bt.data[word] &= ^(1 << offset)
	}
}

// FlipBit inverts the value of the bit at the specified index.
//
// param index is the bit to flip (0 is the least-significant bit).
//
// Will panic if the specified index is not a bit position in this bit string.
func (bt *BitString) FlipBit(index int) {
	bt.assertValidIndex(index)
	word := uint32(index / wordLength)
	offset := uint32(index % wordLength)
	bt.data[word] ^= (1 << offset)
}

// Helper method to check whether a bit index is valid or not.
// Will panic if the index is not valid.
func (bt *BitString) assertValidIndex(index int) {
	if index >= bt.length || index < 0 {
		panic(fmt.Sprintf("invalid index: %v (length: %v)", index, bt.length))
	}
}

// CountSetBits returns the number of bits that are 1s rather than 0s.
func (bt *BitString) CountSetBits() int {
	var count int
	for _, x := range bt.data {
		for x != 0 {
			x &= (x - 1) // Unsets the least significant on bit.
			count++      // Count how many times we have to unset a bit before x equals zero.
		}
	}
	return count
}

// CountUnsetBits returns the number of bits that are 0s rather than 1s.
func (bt *BitString) CountUnsetBits() int {
	return bt.length - bt.CountSetBits()
}

// ToBigInt interprets this bit string as being a binary numeric value and returns
// the integer that it represents.
//
// Returns a big.Int that contains the numeric value represented by this bit
// string.
func (bt *BitString) ToBigInt() *big.Int {
	bi := new(big.Int)
	if _, ok := bi.SetString(bt.String(), 2); !ok {
		panic(fmt.Sprintf("couldn't convert bit string \"%s\" to big.Int", bt.String()))
	}
	return bi
}

// SwapSubstring is an efficient method for exchanging data between two bit
// strings. Both bit strings must be long enough that they contain the full
// length of the specified substring.
//
// other is the bitstring with which this bitstring should swap bits.
// start is the start position for the substrings to be exchanged. All bit
// indices are big-endian, which means position 0 is the rightmost bit.
// length is the number of contiguous bits to swap.
func (bt *BitString) SwapSubstring(other *BitString, start, length int) {
	bt.assertValidIndex(start)
	other.assertValidIndex(start)

	word := start / wordLength
	partialWordSize := (wordLength - start) % wordLength
	if partialWordSize > 0 {
		bt.swapBits(other, word, 0xffffffff<<uint32(wordLength-partialWordSize))
		word++
	}

	remainingBits := length - partialWordSize
	stop := remainingBits / wordLength
	for i := word; i < stop; i++ {
		bt.data[i], other.data[i] = other.data[i], bt.data[i]
	}

	remainingBits %= wordLength
	if remainingBits > 0 {
		bt.swapBits(other, len(bt.data)-1, 0xffffffff>>uint32(wordLength-remainingBits))
	}
}

// other is the BitString to exchange bits with.
// word is the word index of the word that will be swapped between the two
// bit strings.
// swapMask is a mask that specifies which bits in the word will be swapped.
func (bt *BitString) swapBits(other *BitString, word int, swapMask uint32) {
	preserveMask := ^swapMask
	preservedThis := bt.data[word] & preserveMask
	preservedThat := other.data[word] & preserveMask
	swapThis := bt.data[word] & swapMask
	swapThat := other.data[word] & swapMask
	bt.data[word] = preservedThis | swapThat
	other.data[word] = preservedThat | swapThis
}

// String creates a textual representation of this bit string in big-endian
// order (index 0 is the right-most bit).
//
// Returns this bit string rendered as a string of 1s and 0s.
func (bt *BitString) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, bt.length))
	for i := bt.length - 1; i >= 0; i-- {
		if bt.Bit(i) {
			buf.WriteString("1")
		} else {
			buf.WriteString("0")
		}
	}
	return buf.String()
}

// Clone returns an identical copy of this bit string.
func (bt *BitString) Clone() *BitString {
	clone, _ := New(bt.length)
	copy(clone.data, bt.data)
	return clone
}

// Equals returns true if other is a BitString instance and both bit strings are
// the same length with identical bits set/unset.
func (bt *BitString) Equals(other *BitString) bool {
	switch {
	case bt == other:
		return true
	case bt != nil && other == nil:
		break
	case bt == nil && other != nil:
		break
	case bt.length == other.length:
		for i, v := range bt.data {
			if v != other.data[i] {
				return false
			}
		}
		return true
	}
	return false
}

// Data returns the bitstring underlying slice
func (bt *BitString) Data() []uint32 {
	return bt.data
}
<commit_msg>Fix random bitstring generation<commit_after>// Package bitstring implmements a fixed length bit string type and common
// operations on bit strings.
package bitstring

import (
	"bytes"
	"errors"
	"fmt"
	"math/big"
	"math/rand"
)

const wordLength = 32

// BitString implements a fixed-length bit-string.
//
// Internally, bits are packed into an array of ints. This implementation makes
// more efficient use of space than the alternative approach of using an array
// of booleans.
type BitString struct {
	// length in bits of the bit string
	length int

	// bits are packed in an array of 32-bit ints.
	data []uint32
}

// New creates a bit string of the specified length (in bits) with all bits
// initially set to zero (off).
func New(length int) (*BitString, error) {
	if length < 0 {
		return nil, errors.New("BitString length must be non-negative")
	}
	return &BitString{
		length: length,
		data:   make([]uint32, (length+wordLength-1)/wordLength),
	}, nil
}

// NewRandom creates a BitString of the specified length with each bit set
// randomly (the distribution of bits is uniform so long as the output
// from the provided pseudo-random number generator is also uniform).
//
// Using NewRandom is more efficient than creating a bit string and then
// randomly setting each bit individually.
func NewRandom(length int, rng *rand.Rand) (*BitString, error) {
	bt, err := New(length)
	if err != nil {
		return nil, err
	}

	// fill the slice with random values
	for i := 0; i < len(bt.data); i++ {
		bt.data[i] = rng.Uint32()
	}

	// If the last word is not fully utilised, zero any out-of-bounds bits.
	// This is necessary because the CountSetBits() methods will count
	// out-of-bounds bits.
	bitsUsed := uint32(length % wordLength)
	if bitsUsed < wordLength {
		unusedBits := wordLength - bitsUsed
		mask := uint32(0xffffffff >> unusedBits)
		bt.data[len(bt.data)-1] &= mask
	}
	return bt, nil
}

// NewFromString creates a new BitString from a character string of 1s and 0s
// in big-endian order.
func NewFromString(value string) (*BitString, error) {
	bt, err := New(len(value))
	if err != nil {
		return nil, err
	}

	for i, c := range value {
		switch c {
		case '0':
			continue
		case '1':
			bt.SetBit(len(value)-i-1, true)
		default:
			return nil, fmt.Errorf("illegal character at position %v: %#U", i, c)
		}
	}
	return bt, nil
}

// Len returns the length of the BitString in bits.
func (bt *BitString) Len() int {
	return bt.length
}

// Bit returns the bit at the specified index.
//
// index is the index of the bit to look-up (0 is the least-significant bit).
// Returns a boolean indicating whether the bit is set or not.
//
// Will panic if the specified index is not a bit position in this bit string.
func (bt *BitString) Bit(index int) bool {
	bt.assertValidIndex(index)
	word := uint32(index / wordLength)
	offset := uint32(index % wordLength)
	return (bt.data[word] & (1 << offset)) != 0
}

// SetBit sets the bit at the specified index.
//
// index is the index of the bit to set (0 is the least-significant bit).
// set is a boolean indicating whether the bit should be set or not.
//
// Will panic if the specified index is not a bit position in this bit string.
func (bt *BitString) SetBit(index int, set bool) {
	bt.assertValidIndex(index)
	word := uint32(index / wordLength)
	offset := uint32(index % wordLength)
	if set {
		bt.data[word] |= (1 << offset)
	} else {
		// Unset the bit.
		bt.data[word] &= ^(1 << offset)
	}
}

// FlipBit inverts the value of the bit at the specified index.
//
// param index is the bit to flip (0 is the least-significant bit).
//
// Will panic if the specified index is not a bit position in this bit string.
func (bt *BitString) FlipBit(index int) {
	bt.assertValidIndex(index)
	word := uint32(index / wordLength)
	offset := uint32(index % wordLength)
	bt.data[word] ^= (1 << offset)
}

// Helper method to check whether a bit index is valid or not.
// Will panic if the index is not valid.
func (bt *BitString) assertValidIndex(index int) {
	if index >= bt.length || index < 0 {
		panic(fmt.Sprintf("invalid index: %v (length: %v)", index, bt.length))
	}
}

// CountSetBits returns the number of bits that are 1s rather than 0s.
func (bt *BitString) CountSetBits() int {
	var count int
	for _, x := range bt.data {
		for x != 0 {
			x &= (x - 1) // Unsets the least significant on bit.
			count++      // Count how many times we have to unset a bit before x equals zero.
		}
	}
	return count
}

// CountUnsetBits returns the number of bits that are 0s rather than 1s.
func (bt *BitString) CountUnsetBits() int {
	return bt.length - bt.CountSetBits()
}

// ToBigInt interprets this bit string as being a binary numeric value and returns
// the integer that it represents.
//
// Returns a big.Int that contains the numeric value represented by this bit
// string.
func (bt *BitString) ToBigInt() *big.Int {
	bi := new(big.Int)
	if _, ok := bi.SetString(bt.String(), 2); !ok {
		panic(fmt.Sprintf("couldn't convert bit string \"%s\" to big.Int", bt.String()))
	}
	return bi
}

// SwapSubstring is an efficient method for exchanging data between two bit
// strings. Both bit strings must be long enough that they contain the full
// length of the specified substring.
//
// other is the bitstring with which this bitstring should swap bits.
// start is the start position for the substrings to be exchanged. All bit
// indices are big-endian, which means position 0 is the rightmost bit.
// length is the number of contiguous bits to swap.
func (bt *BitString) SwapSubstring(other *BitString, start, length int) {
	bt.assertValidIndex(start)
	other.assertValidIndex(start)

	word := start / wordLength
	partialWordSize := (wordLength - start) % wordLength
	if partialWordSize > 0 {
		bt.swapBits(other, word, 0xffffffff<<uint32(wordLength-partialWordSize))
		word++
	}

	remainingBits := length - partialWordSize
	stop := remainingBits / wordLength
	for i := word; i < stop; i++ {
		bt.data[i], other.data[i] = other.data[i], bt.data[i]
	}

	remainingBits %= wordLength
	if remainingBits > 0 {
		bt.swapBits(other, len(bt.data)-1, 0xffffffff>>uint32(wordLength-remainingBits))
	}
}

// other is the BitString to exchange bits with.
// word is the word index of the word that will be swapped between the two
// bit strings.
// swapMask is a mask that specifies which bits in the word will be swapped.
func (bt *BitString) swapBits(other *BitString, word int, swapMask uint32) {
	preserveMask := ^swapMask
	preservedThis := bt.data[word] & preserveMask
	preservedThat := other.data[word] & preserveMask
	swapThis := bt.data[word] & swapMask
	swapThat := other.data[word] & swapMask
	bt.data[word] = preservedThis | swapThat
	other.data[word] = preservedThat | swapThis
}

// String creates a textual representation of this bit string in big-endian
// order (index 0 is the right-most bit).
//
// Returns this bit string rendered as a string of 1s and 0s.
func (bt *BitString) String() string {
	buf := bytes.NewBuffer(make([]byte, 0, bt.length))
	for i := bt.length - 1; i >= 0; i-- {
		if bt.Bit(i) {
			buf.WriteString("1")
		} else {
			buf.WriteString("0")
		}
	}
	return buf.String()
}

// Clone returns an identical copy of this bit string.
func (bt *BitString) Clone() *BitString {
	clone, _ := New(bt.length)
	copy(clone.data, bt.data)
	return clone
}

// Equals returns true if other is a BitString instance and both bit strings are
// the same length with identical bits set/unset.
func (bt *BitString) Equals(other *BitString) bool {
	switch {
	case bt == other:
		return true
	case bt != nil && other == nil:
		break
	case bt == nil && other != nil:
		break
	case bt.length == other.length:
		for i, v := range bt.data {
			if v != other.data[i] {
				return false
			}
		}
		return true
	}
	return false
}

// Data returns the bitstring underlying slice
func (bt *BitString) Data() []uint32 {
	return bt.data
}
<|endoftext|>


<commit_before>//
package google

import (
	"fmt"
	"reflect"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	computeBeta "google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

func instanceSchedulingNodeAffinitiesElemSchema() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"operator": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringInSlice([]string{"IN", "NOT_IN"}, false),
			},
			"values": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func expandAliasIpRanges(ranges []interface{}) []*computeBeta.AliasIpRange {
	ipRanges := make([]*computeBeta.AliasIpRange, 0, len(ranges))
	for _, raw := range ranges {
		data := raw.(map[string]interface{})
		ipRanges = append(ipRanges, &computeBeta.AliasIpRange{
			IpCidrRange:         data["ip_cidr_range"].(string),
			SubnetworkRangeName: data["subnetwork_range_name"].(string),
		})
	}
	return ipRanges
}

func flattenAliasIpRange(ranges []*computeBeta.AliasIpRange) []map[string]interface{} {
	rangesSchema := make([]map[string]interface{}, 0, len(ranges))
	for _, ipRange := range ranges {
		rangesSchema = append(rangesSchema, map[string]interface{}{
			"ip_cidr_range":         ipRange.IpCidrRange,
			"subnetwork_range_name": ipRange.SubnetworkRangeName,
		})
	}
	return rangesSchema
}

func expandScheduling(v interface{}) (*computeBeta.Scheduling, error) {
	if v == nil {
		// We can't set default values for lists.
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	ls := v.([]interface{})
	if len(ls) == 0 {
		// We can't set default values for lists
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	if len(ls) > 1 || ls[0] == nil {
		return nil, fmt.Errorf("expected exactly one scheduling block")
	}

	original := ls[0].(map[string]interface{})
	scheduling := &computeBeta.Scheduling{
		ForceSendFields: make([]string, 0, 4),
	}

	if v, ok := original["automatic_restart"]; ok {
		scheduling.AutomaticRestart = googleapi.Bool(v.(bool))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "AutomaticRestart")
	}

	if v, ok := original["preemptible"]; ok {
		scheduling.Preemptible = v.(bool)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "Preemptible")
	}

	if v, ok := original["on_host_maintenance"]; ok {
		scheduling.OnHostMaintenance = v.(string)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "OnHostMaintenance")
	}

	if v, ok := original["node_affinities"]; ok && v != nil {
		naSet := v.(*schema.Set).List()
		scheduling.NodeAffinities = make([]*computeBeta.SchedulingNodeAffinity, len(ls))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "NodeAffinities")
		for _, nodeAffRaw := range naSet {
			if nodeAffRaw == nil {
				continue
			}
			nodeAff := nodeAffRaw.(map[string]interface{})
			transformed := &computeBeta.SchedulingNodeAffinity{
				Key:      nodeAff["key"].(string),
				Operator: nodeAff["operator"].(string),
				Values:   convertStringArr(nodeAff["values"].(*schema.Set).List()),
			}
			scheduling.NodeAffinities = append(scheduling.NodeAffinities, transformed)
		}
	}

	return scheduling, nil
}

func flattenScheduling(resp *computeBeta.Scheduling) []map[string]interface{} {
	schedulingMap := map[string]interface{}{
		"on_host_maintenance": resp.OnHostMaintenance,
		"preemptible":         resp.Preemptible,
	}

	if resp.AutomaticRestart != nil {
		schedulingMap["automatic_restart"] = *resp.AutomaticRestart
	}

	nodeAffinities := schema.NewSet(schema.HashResource(instanceSchedulingNodeAffinitiesElemSchema()), nil)
	for _, na := range resp.NodeAffinities {
		nodeAffinities.Add(map[string]interface{}{
			"key":      na.Key,
			"operator": na.Operator,
			"values":   schema.NewSet(schema.HashString, convertStringArrToInterface(na.Values)),
		})
	}
	schedulingMap["node_affinities"] = nodeAffinities

	return []map[string]interface{}{schedulingMap}
}

func flattenAccessConfigs(accessConfigs []*computeBeta.AccessConfig) ([]map[string]interface{}, string) {
	flattened := make([]map[string]interface{}, len(accessConfigs))
	natIP := ""
	for i, ac := range accessConfigs {
		flattened[i] = map[string]interface{}{
			"nat_ip":       ac.NatIP,
			"network_tier": ac.NetworkTier,
		}
		if ac.SetPublicPtr {
			flattened[i]["public_ptr_domain_name"] = ac.PublicPtrDomainName
		}
		if natIP == "" {
			natIP = ac.NatIP
		}
	}
	return flattened, natIP
}

func flattenNetworkInterfaces(d *schema.ResourceData, config *Config, networkInterfaces []*computeBeta.NetworkInterface) ([]map[string]interface{}, string, string, string, error) {
	flattened := make([]map[string]interface{}, len(networkInterfaces))
	var region, internalIP, externalIP string

	for i, iface := range networkInterfaces {
		var ac []map[string]interface{}
		ac, externalIP = flattenAccessConfigs(iface.AccessConfigs)

		subnet, err := ParseSubnetworkFieldValue(iface.Subnetwork, d, config)
		if err != nil {
			return nil, "", "", "", err
		}
		region = subnet.Region

		flattened[i] = map[string]interface{}{
			"network_ip":         iface.NetworkIP,
			"network":            ConvertSelfLinkToV1(iface.Network),
			"subnetwork":         ConvertSelfLinkToV1(iface.Subnetwork),
			"subnetwork_project": subnet.Project,
			"access_config":      ac,
			"alias_ip_range":     flattenAliasIpRange(iface.AliasIpRanges),
			"nic_type":           iface.NicType,
		}
		// Instance template interfaces never have names, so they're absent
		// in the instance template network_interface schema. We want to use the
		// same flattening code for both resource types, so we avoid trying to
		// set the name field when it's not set at the GCE end.
		if iface.Name != "" {
			flattened[i]["name"] = iface.Name
		}
		if internalIP == "" {
			internalIP = iface.NetworkIP
		}
	}
	return flattened, region, internalIP, externalIP, nil
}

func expandAccessConfigs(configs []interface{}) []*computeBeta.AccessConfig {
	acs := make([]*computeBeta.AccessConfig, len(configs))
	for i, raw := range configs {
		acs[i] = &computeBeta.AccessConfig{}
		acs[i].Type = "ONE_TO_ONE_NAT"
		if raw != nil {
			data := raw.(map[string]interface{})
			acs[i].NatIP = data["nat_ip"].(string)
			acs[i].NetworkTier = data["network_tier"].(string)
			if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" {
				acs[i].SetPublicPtr = true
				acs[i].PublicPtrDomainName = ptr.(string)
			}
		}
	}
	return acs
}

func expandNetworkInterfaces(d TerraformResourceData, config *Config) ([]*computeBeta.NetworkInterface, error) {
	configs := d.Get("network_interface").([]interface{})
	ifaces := make([]*computeBeta.NetworkInterface, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		network := data["network"].(string)
		subnetwork := data["subnetwork"].(string)
		if network == "" && subnetwork == "" {
			return nil, fmt.Errorf("exactly one of network or subnetwork must be provided")
		}

		nf, err := ParseNetworkFieldValue(network, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for network %q: %s", network, err)
		}

		subnetProjectField := fmt.Sprintf("network_interface.%d.subnetwork_project", i)
		sf, err := ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err)
		}

		ifaces[i] = &computeBeta.NetworkInterface{
			NetworkIP:     data["network_ip"].(string),
			Network:       nf.RelativeLink(),
			Subnetwork:    sf.RelativeLink(),
			AccessConfigs: expandAccessConfigs(data["access_config"].([]interface{})),
			AliasIpRanges: expandAliasIpRanges(data["alias_ip_range"].([]interface{})),
			NicType:       expandNicType(data["nic_type"].(interface{})),
		}

	}
	return ifaces, nil
}

func expandNicType(d interface{}) string {
	if d == nil {
		return ""
	}
	return d.(string)
}

func flattenServiceAccounts(serviceAccounts []*computeBeta.ServiceAccount) []map[string]interface{} {
	result := make([]map[string]interface{}, len(serviceAccounts))
	for i, serviceAccount := range serviceAccounts {
		result[i] = map[string]interface{}{
			"email":  serviceAccount.Email,
			"scopes": schema.NewSet(stringScopeHashcode, convertStringArrToInterface(serviceAccount.Scopes)),
		}
	}
	return result
}

func expandServiceAccounts(configs []interface{}) []*computeBeta.ServiceAccount {
	accounts := make([]*computeBeta.ServiceAccount, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		accounts[i] = &computeBeta.ServiceAccount{
			Email:  data["email"].(string),
			Scopes: canonicalizeServiceScopes(convertStringSet(data["scopes"].(*schema.Set))),
		}

		if accounts[i].Email == "" {
			accounts[i].Email = "default"
		}
	}
	return accounts
}

func flattenGuestAccelerators(accelerators []*computeBeta.AcceleratorConfig) []map[string]interface{} {
	acceleratorsSchema := make([]map[string]interface{}, len(accelerators))
	for i, accelerator := range accelerators {
		acceleratorsSchema[i] = map[string]interface{}{
			"count": accelerator.AcceleratorCount,
			"type":  accelerator.AcceleratorType,
		}
	}
	return acceleratorsSchema
}

func resourceInstanceTags(d TerraformResourceData) *computeBeta.Tags {
	// Calculate the tags
	var tags *computeBeta.Tags
	if v := d.Get("tags"); v != nil {
		vs := v.(*schema.Set)
		tags = new(computeBeta.Tags)
		tags.Items = make([]string, vs.Len())
		for i, v := range vs.List() {
			tags.Items[i] = v.(string)
		}

		tags.Fingerprint = d.Get("tags_fingerprint").(string)
	}

	return tags
}

func expandShieldedVmConfigs(d TerraformResourceData) *computeBeta.ShieldedInstanceConfig {
	if _, ok := d.GetOk("shielded_instance_config"); !ok {
		return nil
	}

	prefix := "shielded_instance_config.0"
	return &computeBeta.ShieldedInstanceConfig{
		EnableSecureBoot:          d.Get(prefix + ".enable_secure_boot").(bool),
		EnableVtpm:                d.Get(prefix + ".enable_vtpm").(bool),
		EnableIntegrityMonitoring: d.Get(prefix + ".enable_integrity_monitoring").(bool),
		ForceSendFields:           []string{"EnableSecureBoot", "EnableVtpm", "EnableIntegrityMonitoring"},
	}
}

func expandConfidentialInstanceConfig(d TerraformResourceData) *computeBeta.ConfidentialInstanceConfig {
	if _, ok := d.GetOk("confidential_instance_config"); !ok {
		return nil
	}

	prefix := "confidential_instance_config.0"
	return &computeBeta.ConfidentialInstanceConfig{
		EnableConfidentialCompute: d.Get(prefix + ".enable_confidential_compute").(bool),
		ForceSendFields:           []string{"EnableSecureBoot"},
	}
}

func flattenConfidentialInstanceConfig(ConfidentialInstanceConfig *computeBeta.ConfidentialInstanceConfig) []map[string]bool {
	if ConfidentialInstanceConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_confidential_compute": ConfidentialInstanceConfig.EnableConfidentialCompute,
	}}
}

func flattenShieldedVmConfig(shieldedVmConfig *computeBeta.ShieldedInstanceConfig) []map[string]bool {
	if shieldedVmConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_secure_boot":          shieldedVmConfig.EnableSecureBoot,
		"enable_vtpm":                 shieldedVmConfig.EnableVtpm,
		"enable_integrity_monitoring": shieldedVmConfig.EnableIntegrityMonitoring,
	}}
}

func expandDisplayDevice(d TerraformResourceData) *computeBeta.DisplayDevice {
	if _, ok := d.GetOk("enable_display"); !ok {
		return nil
	}
	return &computeBeta.DisplayDevice{
		EnableDisplay:   d.Get("enable_display").(bool),
		ForceSendFields: []string{"EnableDisplay"},
	}
}

func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{} {
	if displayDevice == nil {
		return nil
	}

	return displayDevice.EnableDisplay
}

// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
// https://github.com/hashicorp/terraform-plugin-sdk/issues/98
func schedulingHasChange(d *schema.ResourceData) bool {
	if !d.HasChange("scheduling") {
		// This doesn't work correctly, which is why this method exists
		// But it is here for posterity
		return false
	}
	o, n := d.GetChange("scheduling")
	oScheduling := o.([]interface{})[0].(map[string]interface{})
	newScheduling := n.([]interface{})[0].(map[string]interface{})
	originalNa := oScheduling["node_affinities"].(*schema.Set)
	newNa := newScheduling["node_affinities"].(*schema.Set)
	if oScheduling["automatic_restart"] != newScheduling["automatic_restart"] {
		return true
	}

	if oScheduling["preemptible"] != newScheduling["preemptible"] {
		return true
	}

	if oScheduling["on_host_maintenance"] != newScheduling["on_host_maintenance"] {
		return true
	}

	return reflect.DeepEqual(newNa, originalNa)
}
<commit_msg>Revert "GA for gVNIC support (#4511)" (#4519) (#637)<commit_after>//
package google

import (
	"fmt"
	"reflect"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	computeBeta "google.golang.org/api/compute/v0.beta"
	"google.golang.org/api/googleapi"
)

func instanceSchedulingNodeAffinitiesElemSchema() *schema.Resource {
	return &schema.Resource{
		Schema: map[string]*schema.Schema{
			"key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"operator": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringInSlice([]string{"IN", "NOT_IN"}, false),
			},
			"values": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
				Set:      schema.HashString,
			},
		},
	}
}

func expandAliasIpRanges(ranges []interface{}) []*computeBeta.AliasIpRange {
	ipRanges := make([]*computeBeta.AliasIpRange, 0, len(ranges))
	for _, raw := range ranges {
		data := raw.(map[string]interface{})
		ipRanges = append(ipRanges, &computeBeta.AliasIpRange{
			IpCidrRange:         data["ip_cidr_range"].(string),
			SubnetworkRangeName: data["subnetwork_range_name"].(string),
		})
	}
	return ipRanges
}

func flattenAliasIpRange(ranges []*computeBeta.AliasIpRange) []map[string]interface{} {
	rangesSchema := make([]map[string]interface{}, 0, len(ranges))
	for _, ipRange := range ranges {
		rangesSchema = append(rangesSchema, map[string]interface{}{
			"ip_cidr_range":         ipRange.IpCidrRange,
			"subnetwork_range_name": ipRange.SubnetworkRangeName,
		})
	}
	return rangesSchema
}

func expandScheduling(v interface{}) (*computeBeta.Scheduling, error) {
	if v == nil {
		// We can't set default values for lists.
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	ls := v.([]interface{})
	if len(ls) == 0 {
		// We can't set default values for lists
		return &computeBeta.Scheduling{
			AutomaticRestart: googleapi.Bool(true),
		}, nil
	}

	if len(ls) > 1 || ls[0] == nil {
		return nil, fmt.Errorf("expected exactly one scheduling block")
	}

	original := ls[0].(map[string]interface{})
	scheduling := &computeBeta.Scheduling{
		ForceSendFields: make([]string, 0, 4),
	}

	if v, ok := original["automatic_restart"]; ok {
		scheduling.AutomaticRestart = googleapi.Bool(v.(bool))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "AutomaticRestart")
	}

	if v, ok := original["preemptible"]; ok {
		scheduling.Preemptible = v.(bool)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "Preemptible")
	}

	if v, ok := original["on_host_maintenance"]; ok {
		scheduling.OnHostMaintenance = v.(string)
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "OnHostMaintenance")
	}

	if v, ok := original["node_affinities"]; ok && v != nil {
		naSet := v.(*schema.Set).List()
		scheduling.NodeAffinities = make([]*computeBeta.SchedulingNodeAffinity, len(ls))
		scheduling.ForceSendFields = append(scheduling.ForceSendFields, "NodeAffinities")
		for _, nodeAffRaw := range naSet {
			if nodeAffRaw == nil {
				continue
			}
			nodeAff := nodeAffRaw.(map[string]interface{})
			transformed := &computeBeta.SchedulingNodeAffinity{
				Key:      nodeAff["key"].(string),
				Operator: nodeAff["operator"].(string),
				Values:   convertStringArr(nodeAff["values"].(*schema.Set).List()),
			}
			scheduling.NodeAffinities = append(scheduling.NodeAffinities, transformed)
		}
	}

	return scheduling, nil
}

func flattenScheduling(resp *computeBeta.Scheduling) []map[string]interface{} {
	schedulingMap := map[string]interface{}{
		"on_host_maintenance": resp.OnHostMaintenance,
		"preemptible":         resp.Preemptible,
	}

	if resp.AutomaticRestart != nil {
		schedulingMap["automatic_restart"] = *resp.AutomaticRestart
	}

	nodeAffinities := schema.NewSet(schema.HashResource(instanceSchedulingNodeAffinitiesElemSchema()), nil)
	for _, na := range resp.NodeAffinities {
		nodeAffinities.Add(map[string]interface{}{
			"key":      na.Key,
			"operator": na.Operator,
			"values":   schema.NewSet(schema.HashString, convertStringArrToInterface(na.Values)),
		})
	}
	schedulingMap["node_affinities"] = nodeAffinities

	return []map[string]interface{}{schedulingMap}
}

func flattenAccessConfigs(accessConfigs []*computeBeta.AccessConfig) ([]map[string]interface{}, string) {
	flattened := make([]map[string]interface{}, len(accessConfigs))
	natIP := ""
	for i, ac := range accessConfigs {
		flattened[i] = map[string]interface{}{
			"nat_ip":       ac.NatIP,
			"network_tier": ac.NetworkTier,
		}
		if ac.SetPublicPtr {
			flattened[i]["public_ptr_domain_name"] = ac.PublicPtrDomainName
		}
		if natIP == "" {
			natIP = ac.NatIP
		}
	}
	return flattened, natIP
}

func flattenNetworkInterfaces(d *schema.ResourceData, config *Config, networkInterfaces []*computeBeta.NetworkInterface) ([]map[string]interface{}, string, string, string, error) {
	flattened := make([]map[string]interface{}, len(networkInterfaces))
	var region, internalIP, externalIP string

	for i, iface := range networkInterfaces {
		var ac []map[string]interface{}
		ac, externalIP = flattenAccessConfigs(iface.AccessConfigs)

		subnet, err := ParseSubnetworkFieldValue(iface.Subnetwork, d, config)
		if err != nil {
			return nil, "", "", "", err
		}
		region = subnet.Region

		flattened[i] = map[string]interface{}{
			"network_ip":         iface.NetworkIP,
			"network":            ConvertSelfLinkToV1(iface.Network),
			"subnetwork":         ConvertSelfLinkToV1(iface.Subnetwork),
			"subnetwork_project": subnet.Project,
			"access_config":      ac,
			"alias_ip_range":     flattenAliasIpRange(iface.AliasIpRanges),
		}
		// Instance template interfaces never have names, so they're absent
		// in the instance template network_interface schema. We want to use the
		// same flattening code for both resource types, so we avoid trying to
		// set the name field when it's not set at the GCE end.
		if iface.Name != "" {
			flattened[i]["name"] = iface.Name
		}
		if internalIP == "" {
			internalIP = iface.NetworkIP
		}
	}
	return flattened, region, internalIP, externalIP, nil
}

func expandAccessConfigs(configs []interface{}) []*computeBeta.AccessConfig {
	acs := make([]*computeBeta.AccessConfig, len(configs))
	for i, raw := range configs {
		acs[i] = &computeBeta.AccessConfig{}
		acs[i].Type = "ONE_TO_ONE_NAT"
		if raw != nil {
			data := raw.(map[string]interface{})
			acs[i].NatIP = data["nat_ip"].(string)
			acs[i].NetworkTier = data["network_tier"].(string)
			if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" {
				acs[i].SetPublicPtr = true
				acs[i].PublicPtrDomainName = ptr.(string)
			}
		}
	}
	return acs
}

func expandNetworkInterfaces(d TerraformResourceData, config *Config) ([]*computeBeta.NetworkInterface, error) {
	configs := d.Get("network_interface").([]interface{})
	ifaces := make([]*computeBeta.NetworkInterface, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		network := data["network"].(string)
		subnetwork := data["subnetwork"].(string)
		if network == "" && subnetwork == "" {
			return nil, fmt.Errorf("exactly one of network or subnetwork must be provided")
		}

		nf, err := ParseNetworkFieldValue(network, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for network %q: %s", network, err)
		}

		subnetProjectField := fmt.Sprintf("network_interface.%d.subnetwork_project", i)
		sf, err := ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config)
		if err != nil {
			return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err)
		}

		ifaces[i] = &computeBeta.NetworkInterface{
			NetworkIP:     data["network_ip"].(string),
			Network:       nf.RelativeLink(),
			Subnetwork:    sf.RelativeLink(),
			AccessConfigs: expandAccessConfigs(data["access_config"].([]interface{})),
			AliasIpRanges: expandAliasIpRanges(data["alias_ip_range"].([]interface{})),
		}

	}
	return ifaces, nil
}

func flattenServiceAccounts(serviceAccounts []*computeBeta.ServiceAccount) []map[string]interface{} {
	result := make([]map[string]interface{}, len(serviceAccounts))
	for i, serviceAccount := range serviceAccounts {
		result[i] = map[string]interface{}{
			"email":  serviceAccount.Email,
			"scopes": schema.NewSet(stringScopeHashcode, convertStringArrToInterface(serviceAccount.Scopes)),
		}
	}
	return result
}

func expandServiceAccounts(configs []interface{}) []*computeBeta.ServiceAccount {
	accounts := make([]*computeBeta.ServiceAccount, len(configs))
	for i, raw := range configs {
		data := raw.(map[string]interface{})

		accounts[i] = &computeBeta.ServiceAccount{
			Email:  data["email"].(string),
			Scopes: canonicalizeServiceScopes(convertStringSet(data["scopes"].(*schema.Set))),
		}

		if accounts[i].Email == "" {
			accounts[i].Email = "default"
		}
	}
	return accounts
}

func flattenGuestAccelerators(accelerators []*computeBeta.AcceleratorConfig) []map[string]interface{} {
	acceleratorsSchema := make([]map[string]interface{}, len(accelerators))
	for i, accelerator := range accelerators {
		acceleratorsSchema[i] = map[string]interface{}{
			"count": accelerator.AcceleratorCount,
			"type":  accelerator.AcceleratorType,
		}
	}
	return acceleratorsSchema
}

func resourceInstanceTags(d TerraformResourceData) *computeBeta.Tags {
	// Calculate the tags
	var tags *computeBeta.Tags
	if v := d.Get("tags"); v != nil {
		vs := v.(*schema.Set)
		tags = new(computeBeta.Tags)
		tags.Items = make([]string, vs.Len())
		for i, v := range vs.List() {
			tags.Items[i] = v.(string)
		}

		tags.Fingerprint = d.Get("tags_fingerprint").(string)
	}

	return tags
}

func expandShieldedVmConfigs(d TerraformResourceData) *computeBeta.ShieldedInstanceConfig {
	if _, ok := d.GetOk("shielded_instance_config"); !ok {
		return nil
	}

	prefix := "shielded_instance_config.0"
	return &computeBeta.ShieldedInstanceConfig{
		EnableSecureBoot:          d.Get(prefix + ".enable_secure_boot").(bool),
		EnableVtpm:                d.Get(prefix + ".enable_vtpm").(bool),
		EnableIntegrityMonitoring: d.Get(prefix + ".enable_integrity_monitoring").(bool),
		ForceSendFields:           []string{"EnableSecureBoot", "EnableVtpm", "EnableIntegrityMonitoring"},
	}
}

func expandConfidentialInstanceConfig(d TerraformResourceData) *computeBeta.ConfidentialInstanceConfig {
	if _, ok := d.GetOk("confidential_instance_config"); !ok {
		return nil
	}

	prefix := "confidential_instance_config.0"
	return &computeBeta.ConfidentialInstanceConfig{
		EnableConfidentialCompute: d.Get(prefix + ".enable_confidential_compute").(bool),
		ForceSendFields:           []string{"EnableSecureBoot"},
	}
}

func flattenConfidentialInstanceConfig(ConfidentialInstanceConfig *computeBeta.ConfidentialInstanceConfig) []map[string]bool {
	if ConfidentialInstanceConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_confidential_compute": ConfidentialInstanceConfig.EnableConfidentialCompute,
	}}
}

func flattenShieldedVmConfig(shieldedVmConfig *computeBeta.ShieldedInstanceConfig) []map[string]bool {
	if shieldedVmConfig == nil {
		return nil
	}

	return []map[string]bool{{
		"enable_secure_boot":          shieldedVmConfig.EnableSecureBoot,
		"enable_vtpm":                 shieldedVmConfig.EnableVtpm,
		"enable_integrity_monitoring": shieldedVmConfig.EnableIntegrityMonitoring,
	}}
}

func expandDisplayDevice(d TerraformResourceData) *computeBeta.DisplayDevice {
	if _, ok := d.GetOk("enable_display"); !ok {
		return nil
	}
	return &computeBeta.DisplayDevice{
		EnableDisplay:   d.Get("enable_display").(bool),
		ForceSendFields: []string{"EnableDisplay"},
	}
}

func flattenEnableDisplay(displayDevice *computeBeta.DisplayDevice) interface{} {
	if displayDevice == nil {
		return nil
	}

	return displayDevice.EnableDisplay
}

// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
// https://github.com/hashicorp/terraform-plugin-sdk/issues/98
func schedulingHasChange(d *schema.ResourceData) bool {
	if !d.HasChange("scheduling") {
		// This doesn't work correctly, which is why this method exists
		// But it is here for posterity
		return false
	}
	o, n := d.GetChange("scheduling")
	oScheduling := o.([]interface{})[0].(map[string]interface{})
	newScheduling := n.([]interface{})[0].(map[string]interface{})
	originalNa := oScheduling["node_affinities"].(*schema.Set)
	newNa := newScheduling["node_affinities"].(*schema.Set)
	if oScheduling["automatic_restart"] != newScheduling["automatic_restart"] {
		return true
	}

	if oScheduling["preemptible"] != newScheduling["preemptible"] {
		return true
	}

	if oScheduling["on_host_maintenance"] != newScheduling["on_host_maintenance"] {
		return true
	}

	return reflect.DeepEqual(newNa, originalNa)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"fmt"
	"flag"
	"log"
	"log/syslog"
	mrand "math/rand"
	"net"
	"net/http"
	"crypto/rand"
	"strings"
	"sync"
	"time"
)

type RtpMsg struct {
	sn uint16
	delay uint32
}

type RtpStats struct {
	unixtime int64
	recv uint16
	ooo uint16 // Out Of Order packets
	delay uint32
	last_sn uint16
}

var rtpproxyAddr string
var rtpproxyPort uint
var listenPort uint
var payloadSize uint
var payloadType uint
var histSize uint
var histTime uint
var reverseTags bool
var syslogLog bool

var window *MovingWindow
var currRtpStats RtpStats

func init() {
	flag.UintVar(&listenPort, "hport", 8080, "Port to run HTTP server at")
	flag.UintVar(&histSize, "hsize", 10, "History backlog size (in steps)")
	flag.UintVar(&histTime, "htime", 60, "History interval (in seconds)")
	flag.StringVar(&rtpproxyAddr, "rhost", "127.0.0.1", "RTPproxy address")
	flag.UintVar(&rtpproxyPort, "rport", 22222, "RTPproxy port")
	flag.UintVar(&payloadSize, "psize", 160, "RTP payload size (in bytes)")
	flag.UintVar(&payloadType, "ptype", 8, "RTP payload type")
	flag.BoolVar(&reverseTags, "revtags", false, "Reverse tags in Lookup command")
	flag.BoolVar(&syslogLog, "syslog", false, "Log to syslog")
}

func viewHandlerRobo(w http.ResponseWriter, r *http.Request) {
	log.Printf("ROBO FROM: %s REQ: %s\n", r.RemoteAddr, r.URL.Path)
	var retStr string
	retStr += "["

	for i := range window.arr {
		retStr += fmt.Sprintf("{\"unixtime\":%d,\"received\":%d,\"ooo\":%d,\"delay\":%d},",
		window.arr[i].unixtime,
		window.arr[i].recv,
		window.arr[i].ooo,
		window.arr[i].delay)
	}

	retStr += "{}]"
	fmt.Fprintf(w, retStr)
}
func viewHandlerHuman(w http.ResponseWriter, r *http.Request) {
	log.Printf("FROM: %s REQ: %s\n", r.RemoteAddr, r.URL.Path)

	var retStr string

	retStr += "
"

	for i := range window.arr {
		retStr += fmt.Sprintf("
%d	%d	%d	%d
",
		window.arr[i].unixtime,
		window.arr[i].recv,
		window.arr[i].ooo,
		window.arr[i].delay)
	}

	fmt.Fprintf(w, "
%s
", retStr)
}

func sender(conn *net.UDPConn, payload []byte, delay time.Duration, w sync.WaitGroup) {
	var sn uint16
	var ts uint32
	sn = 0
	for {
		ts = getNtpStamp()
		binary.BigEndian.PutUint16(payload[2:], sn)
		binary.BigEndian.PutUint32(payload[4:], ts)
		_, _ = conn.Write(payload)
		time.Sleep(delay)
		if sn == 65535 {
			sn = 0
		} else {
			sn++
		}
	}
	w.Done()
}

func receiver(conn *net.UDPConn, channel chan RtpMsg, size uint, w sync.WaitGroup) {
	var recvbuf []byte = make([]byte, size)
	msg := RtpMsg{}
	for {
		_, _ = conn.Read(recvbuf[0:])
		msg.sn = binary.BigEndian.Uint16(recvbuf[2:])
		msg.delay = getNtpStamp() - binary.BigEndian.Uint32(recvbuf[4:])
		channel <- msg
	}
	w.Done()
}

// Number of seconds ellapsed from 1900 to 1970, see RFC 5905
const ntpEpochOffset = 2208988800

func getNtpStamp() uint32 {
	tm := time.Now().UnixNano()
	seconds := uint32(tm/1e9 + ntpEpochOffset) // Go uses ns, thus divide by 1e9 to get seconds
	msecs := (seconds % 1e6) * 8000 // 8 KHz
	return msecs + uint32((tm % 1e9) / (1e6 / 8)) // 8 KHz
}

func getRandStr(n uint) string {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	rand.Read(bytes)
	for i, b := range bytes {
		bytes[i] = alphanum[b % byte(len(alphanum))]
	}
	return string(bytes)
}

func makeCon(name string, haddr string, hport string) *net.UDPConn {
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%s", haddr, hport))
	if err != nil {
		log.Printf("ERR1: udp:%s:%s: %v\n", haddr, hport, err)
		return nil
	}

	con, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		log.Printf("ERR2: udp:%s:%s: %v\n", haddr, hport, err)
		return nil
	}
	log.Printf("Connected %s: [udp:%s:%s]\n", name, haddr, hport)
	return con
}

func main() {
	flag.Parse()

	if syslogLog {
		logger, _ := syslog.New(syslog.LOG_INFO, "rtpproxy_monitoring")
		log.SetOutput(logger)
	}

	const delay = 20 * time.Millisecond

	var n int

	window = New(int(histSize), 1)

	ca := make(chan RtpMsg)
	cb := make(chan RtpMsg)

	// Buffer for the control connection (~1 MTU)
	var buffer [1500]byte

	const rtpHeader uint = 0x80
	const rtpHeaderSize uint = 12

	var apayload []byte = make([]byte, rtpHeaderSize + payloadSize)

	aSSRC := mrand.Int31()

	apayload[0] = byte(rtpHeader)
	apayload[1] = byte(payloadType)
	binary.BigEndian.PutUint32(apayload[8:], uint32(aSSRC))
	for i := range apayload[rtpHeaderSize:] {
		apayload[rtpHeaderSize + uint(i)] = byte(i % 255)
	}

	var bpayload []byte = make([]byte, rtpHeaderSize + payloadSize)

	bSSRC := mrand.Int31()

	bpayload[0] = byte(rtpHeader)
	bpayload[1] = byte(payloadType)
	binary.BigEndian.PutUint32(bpayload[8:], uint32(bSSRC))
	for i := range bpayload[rtpHeaderSize:]  {
		bpayload[rtpHeaderSize + uint(i)] = byte(255 - i % 255)
	}

	// Synchronizaion object
	var w sync.WaitGroup

	// Wait for the following objects
	// - HTTP server
	// - Alice's sender
	// - Alice's receiver
	// - Bob's sender
	// - Bob's receiver
	// - Stats recalculator
	w.Add(6)

	go func(in1 <-chan RtpMsg, in2 <-chan RtpMsg) {
		var msg RtpMsg
		var tm int64
		currRtpStats.unixtime = time.Now().Unix()
		for {
			select {
			case msg = <-in1:
				// Alice's receiver
				tm = time.Now().Unix()
				if (tm >= currRtpStats.unixtime + int64(histTime)){
					log.Printf("CURRENT: sn:%d, recv:%d, ooo:%d delay:%d\n", currRtpStats.last_sn, currRtpStats.recv, currRtpStats.ooo, currRtpStats.delay) // 8 KHz
					// Push it to the Window
					window.PushBack(currRtpStats)
					// ...and clean up
					currRtpStats.unixtime += int64(histTime)
					currRtpStats.recv = 0
					currRtpStats.ooo = 0
					currRtpStats.delay = 0
				}

				// Append stats
				if((msg.sn > currRtpStats.last_sn) || ((msg.sn < 10000) && (currRtpStats.last_sn > 60000))) {
					currRtpStats.recv++
					currRtpStats.delay += msg.delay
					if ((msg.sn < 10000) && (currRtpStats.last_sn > 60000)) {
						currRtpStats.ooo += (msg.sn + (65535 - currRtpStats.last_sn))
					} else {
						currRtpStats.ooo += (msg.sn - (currRtpStats.last_sn + 1))
					}
					currRtpStats.last_sn = msg.sn
				}

			case msg = <-in2:
				// Discarding Bob's receiver data for now - it actually has almost the same parameters
			}
		}
		w.Done()
	} (ca, cb)

	// Open control connection
	rtpproxyCon := makeCon("RTPproxy", rtpproxyAddr, fmt.Sprintf("%d", rtpproxyPort))
	// Don't forget to close control connection before exit
	defer rtpproxyCon.Close()

	// Create random Cookie, CallID, FromTag, ToTag
	cookieA := getRandStr(8)
	cookieO := getRandStr(8)
	cookieD := getRandStr(8)
	callid := getRandStr(32)
	tagf := getRandStr(16)
	tagt := getRandStr(16)

	// Generate Offer
	offerStr := strings.Join([]string{
		cookieO,
		"Uc0,8,18,101",
		callid,
		"192.168.1.100",
		"10560",
		strings.Join([]string{tagf, ";1"},"")}, " ")

	log.Printf("Offer: %s\n", offerStr)
	// Send Offer to RTPproxy
	n, _ = rtpproxyCon.Write([]byte(offerStr))

	// Read Offer reply
	n, _ = rtpproxyCon.Read(buffer[0:])

	// Parse Offer reply
	offerReply := strings.Split(strings.TrimRight(string(buffer[:n]), "\n"), " ")
	log.Printf("Offer: %s [udp:%s:%s]\n", offerReply[0], offerReply[2], offerReply[1])

	// Open Bob's connection
	bobCon := makeCon("Bob", offerReply[2], offerReply[1])
	// Don't forget to close Bob's connection before exit
	defer bobCon.Close()

	// Generate Answer
	var answerStr string
	if reverseTags {
		answerStr = strings.Join([]string{
			cookieA,
			"Lc0,8,18,101",
			callid,
			"192.168.2.200",
			"20560",
			strings.Join([]string{tagt, ";1"}, ""),
			strings.Join([]string{tagf, ";1"}, "")}, " ")
	} else {
		answerStr = strings.Join([]string{
			cookieA,
			"Lc0,8,18,101",
			callid,
			"192.168.2.200",
			"20560",
			strings.Join([]string{tagf, ";1"}, ""),
			strings.Join([]string{tagt, ";1"}, "")}, " ")
	}
	log.Printf("Answer: %s\n", answerStr)

	// Send Answer to RTPproxy
	n, _ = rtpproxyCon.Write([]byte(answerStr))

	// Read Answer reply
	n, _ = rtpproxyCon.Read(buffer[0:])

	// Parse Answer reply
	answerReply := strings.Split(strings.TrimRight(string(buffer[:n]), "\n"), " ")
	log.Printf("Answer: %s [udp:%s:%s]\n", answerReply[0], answerReply[2], answerReply[1])

	// Open Alice's connection
	aliceCon := makeCon("Alice", answerReply[2], answerReply[1])
	// Don't forget to close Alice's connection before exit
	defer aliceCon.Close()

	// Run Bob's sender
	go sender(bobCon, bpayload, delay, w)

	// Run Bob's receiver
	go receiver(bobCon, cb, rtpHeaderSize + payloadSize, w)

	// Run Alice's sender
	go sender(aliceCon, apayload, delay, w)

	// Run Alice's  receiver
	go receiver(aliceCon, ca, rtpHeaderSize + payloadSize, w)

	go func() {
		// Run HTTP stats listener
		http.HandleFunc("/json", viewHandlerRobo)
		http.HandleFunc("/", viewHandlerHuman)
		log.Printf("HTTP started.\n")
		http.ListenAndServe(fmt.Sprintf(":%d", listenPort), nil)
		log.Printf("HTTP stopped.\n")

		w.Done()
	} ()

	w.Wait()

	// Generate Delete
	deleteStr := strings.Join([]string{
		cookieD,
		"D",
		callid,
		tagf,
		tagt}, " ")
	log.Printf("Delete: %s\n", deleteStr)

	// Send Delete to RTPproxy
	n, _ = rtpproxyCon.Write([]byte(deleteStr))

	// Read Delete reply
	_, _ = rtpproxyCon.Read(buffer[0:])
}
<commit_msg>Cosmetic<commit_after>package main

import (
	"encoding/binary"
	"fmt"
	"flag"
	"log"
	"log/syslog"
	mrand "math/rand"
	"net"
	"net/http"
	"crypto/rand"
	"strings"
	"sync"
	"time"
)

type RtpMsg struct {
	sn uint16
	delay uint32
}

type RtpStats struct {
	unixtime int64
	recv uint16
	ooo uint16 // Out Of Order packets
	delay uint32
	last_sn uint16
}

var rtpproxyAddr string
var rtpproxyPort uint
var listenPort uint
var payloadSize uint
var payloadType uint
var histSize uint
var histTime uint
var reverseTags bool
var syslogLog bool

var window *MovingWindow
var currRtpStats RtpStats

func init() {
	flag.UintVar(&listenPort, "hport", 8080, "Port to run HTTP server at")
	flag.UintVar(&histSize, "hsize", 10, "History backlog size (in steps)")
	flag.UintVar(&histTime, "htime", 60, "History interval (in seconds)")
	flag.StringVar(&rtpproxyAddr, "rhost", "127.0.0.1", "RTPproxy address")
	flag.UintVar(&rtpproxyPort, "rport", 22222, "RTPproxy port")
	flag.UintVar(&payloadSize, "psize", 160, "RTP payload size (in bytes)")
	flag.UintVar(&payloadType, "ptype", 8, "RTP payload type")
	flag.BoolVar(&reverseTags, "revtags", false, "Reverse tags in Lookup command")
	flag.BoolVar(&syslogLog, "syslog", false, "Log to syslog")
}

func viewHandlerRobo(w http.ResponseWriter, r *http.Request) {
	log.Printf("ROBO FROM: %s REQ: %s\n", r.RemoteAddr, r.URL.Path)
	var retStr string
	retStr += "["

	for i := range window.arr {
		retStr += fmt.Sprintf("{\"unixtime\":%d,\"received\":%d,\"ooo\":%d,\"delay\":%d},",
		window.arr[i].unixtime,
		window.arr[i].recv,
		window.arr[i].ooo,
		window.arr[i].delay)
	}

	retStr += "{}]"
	fmt.Fprintf(w, retStr)
}
func viewHandlerHuman(w http.ResponseWriter, r *http.Request) {
	log.Printf("FROM: %s REQ: %s\n", r.RemoteAddr, r.URL.Path)

	var retStr string

	retStr += "
"

	for i := range window.arr {
		retStr += fmt.Sprintf("
%d	%d	%d	%d
",
		window.arr[i].unixtime,
		window.arr[i].recv,
		window.arr[i].ooo,
		window.arr[i].delay)
	}

	fmt.Fprintf(w, "
%s
", retStr)
}

func sender(conn *net.UDPConn, payload []byte, delay time.Duration, w sync.WaitGroup) {
	var sn uint16
	var ts uint32
	sn = 0
	for {
		ts = getNtpStamp()
		binary.BigEndian.PutUint16(payload[2:], sn)
		binary.BigEndian.PutUint32(payload[4:], ts)
		_, _ = conn.Write(payload)
		time.Sleep(delay)
		if sn == 65535 {
			sn = 0
		} else {
			sn++
		}
	}
	w.Done()
}

func receiver(conn *net.UDPConn, channel chan RtpMsg, size uint, w sync.WaitGroup) {
	var recvbuf []byte = make([]byte, size)
	msg := RtpMsg{}
	for {
		_, _ = conn.Read(recvbuf[0:])
		msg.sn = binary.BigEndian.Uint16(recvbuf[2:])
		msg.delay = getNtpStamp() - binary.BigEndian.Uint32(recvbuf[4:])
		channel <- msg
	}
	w.Done()
}

// Number of seconds ellapsed from 1900 to 1970, see RFC 5905
const ntpEpochOffset = 2208988800

func getNtpStamp() uint32 {
	tm := time.Now().UnixNano()
	seconds := uint32(tm/1e9 + ntpEpochOffset) // Go uses ns, thus divide by 1e9 to get seconds
	msecs := (seconds % 1e6) * 8000 // 8 KHz
	return msecs + uint32((tm % 1e9) / (1e6 / 8)) // 8 KHz
}

func getRandStr(n uint) string {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	rand.Read(bytes)
	for i, b := range bytes {
		bytes[i] = alphanum[b % byte(len(alphanum))]
	}
	return string(bytes)
}

func makeCon(name string, haddr string, hport string) *net.UDPConn {
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%s", haddr, hport))
	if err != nil {
		log.Printf("ERR1: udp:%s:%s: %v\n", haddr, hport, err)
		return nil
	}

	con, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		log.Printf("ERR2: udp:%s:%s: %v\n", haddr, hport, err)
		return nil
	}
	log.Printf("Connected %s: [udp:%s:%s]\n", name, haddr, hport)
	return con
}

func main() {
	flag.Parse()

	if syslogLog {
		logger, _ := syslog.New(syslog.LOG_INFO, "rtpproxy_monitoring")
		log.SetOutput(logger)
	}

	const delay = 20 * time.Millisecond

	var n int

	window = New(int(histSize), 1)

	ca := make(chan RtpMsg)
	cb := make(chan RtpMsg)

	// Buffer for the control connection (~1 MTU)
	var buffer [1500]byte

	const rtpHeader uint = 0x80
	const rtpHeaderSize uint = 12

	var apayload []byte = make([]byte, rtpHeaderSize + payloadSize)

	aSSRC := mrand.Int31()

	apayload[0] = byte(rtpHeader)
	apayload[1] = byte(payloadType)
	binary.BigEndian.PutUint32(apayload[8:], uint32(aSSRC))
	for i := range apayload[rtpHeaderSize:] {
		apayload[rtpHeaderSize + uint(i)] = byte(i % 255)
	}

	var bpayload []byte = make([]byte, rtpHeaderSize + payloadSize)

	bSSRC := mrand.Int31()

	bpayload[0] = byte(rtpHeader)
	bpayload[1] = byte(payloadType)
	binary.BigEndian.PutUint32(bpayload[8:], uint32(bSSRC))
	for i := range bpayload[rtpHeaderSize:]  {
		bpayload[rtpHeaderSize + uint(i)] = byte(255 - i % 255)
	}

	// Synchronizaion object
	var w sync.WaitGroup

	// Wait for the following objects
	// - HTTP server
	// - Alice's sender
	// - Alice's receiver
	// - Bob's sender
	// - Bob's receiver
	// - Stats recalculator
	w.Add(6)

	go func(in1 <-chan RtpMsg, in2 <-chan RtpMsg) {
		var msg RtpMsg
		var tm int64
		currRtpStats.unixtime = time.Now().Unix()
		for {
			select {
			case msg = <-in1:
				// Alice's receiver
				tm = time.Now().Unix()
				if (tm >= currRtpStats.unixtime + int64(histTime)){
					log.Printf("stats: sn:%d, recv:%d, ooo:%d delay:%d\n", currRtpStats.last_sn, currRtpStats.recv, currRtpStats.ooo, currRtpStats.delay) // 8 KHz
					// Push it to the Window
					window.PushBack(currRtpStats)
					// ...and clean up
					currRtpStats.unixtime += int64(histTime)
					currRtpStats.recv = 0
					currRtpStats.ooo = 0
					currRtpStats.delay = 0
				}

				// Append stats
				if((msg.sn > currRtpStats.last_sn) || ((msg.sn < 10000) && (currRtpStats.last_sn > 60000))) {
					currRtpStats.recv++
					currRtpStats.delay += msg.delay
					if ((msg.sn < 10000) && (currRtpStats.last_sn > 60000)) {
						currRtpStats.ooo += (msg.sn + (65535 - currRtpStats.last_sn))
					} else {
						currRtpStats.ooo += (msg.sn - (currRtpStats.last_sn + 1))
					}
					currRtpStats.last_sn = msg.sn
				}

			case msg = <-in2:
				// Discarding Bob's receiver data for now - it actually has almost the same parameters
			}
		}
		w.Done()
	} (ca, cb)

	// Open control connection
	rtpproxyCon := makeCon("RTPproxy", rtpproxyAddr, fmt.Sprintf("%d", rtpproxyPort))
	// Don't forget to close control connection before exit
	defer rtpproxyCon.Close()

	// Create random Cookie, CallID, FromTag, ToTag
	cookieA := getRandStr(8)
	cookieO := getRandStr(8)
	cookieD := getRandStr(8)
	callid := getRandStr(32)
	tagf := getRandStr(16)
	tagt := getRandStr(16)

	// Generate Offer
	offerStr := strings.Join([]string{
		cookieO,
		"Uc0,8,18,101",
		callid,
		"192.168.1.100",
		"10560",
		strings.Join([]string{tagf, ";1"},"")}, " ")

	log.Printf("Offer: %s\n", offerStr)
	// Send Offer to RTPproxy
	n, _ = rtpproxyCon.Write([]byte(offerStr))

	// Read Offer reply
	n, _ = rtpproxyCon.Read(buffer[0:])

	// Parse Offer reply
	offerReply := strings.Split(strings.TrimRight(string(buffer[:n]), "\n"), " ")
	log.Printf("Offer: %s [udp:%s:%s]\n", offerReply[0], offerReply[2], offerReply[1])

	// Open Bob's connection
	bobCon := makeCon("Bob", offerReply[2], offerReply[1])
	// Don't forget to close Bob's connection before exit
	defer bobCon.Close()

	// Generate Answer
	var answerStr string
	if reverseTags {
		answerStr = strings.Join([]string{
			cookieA,
			"Lc0,8,18,101",
			callid,
			"192.168.2.200",
			"20560",
			strings.Join([]string{tagt, ";1"}, ""),
			strings.Join([]string{tagf, ";1"}, "")}, " ")
	} else {
		answerStr = strings.Join([]string{
			cookieA,
			"Lc0,8,18,101",
			callid,
			"192.168.2.200",
			"20560",
			strings.Join([]string{tagf, ";1"}, ""),
			strings.Join([]string{tagt, ";1"}, "")}, " ")
	}
	log.Printf("Answer: %s\n", answerStr)

	// Send Answer to RTPproxy
	n, _ = rtpproxyCon.Write([]byte(answerStr))

	// Read Answer reply
	n, _ = rtpproxyCon.Read(buffer[0:])

	// Parse Answer reply
	answerReply := strings.Split(strings.TrimRight(string(buffer[:n]), "\n"), " ")
	log.Printf("Answer: %s [udp:%s:%s]\n", answerReply[0], answerReply[2], answerReply[1])

	// Open Alice's connection
	aliceCon := makeCon("Alice", answerReply[2], answerReply[1])
	// Don't forget to close Alice's connection before exit
	defer aliceCon.Close()

	// Run Bob's sender
	go sender(bobCon, bpayload, delay, w)

	// Run Bob's receiver
	go receiver(bobCon, cb, rtpHeaderSize + payloadSize, w)

	// Run Alice's sender
	go sender(aliceCon, apayload, delay, w)

	// Run Alice's  receiver
	go receiver(aliceCon, ca, rtpHeaderSize + payloadSize, w)

	go func() {
		// Run HTTP stats listener
		http.HandleFunc("/json", viewHandlerRobo)
		http.HandleFunc("/", viewHandlerHuman)
		log.Printf("HTTP started at 0.0.0.0:%d\n", listenPort)
		http.ListenAndServe(fmt.Sprintf(":%d", listenPort), nil)
		log.Printf("HTTP stopped.\n")

		w.Done()
	} ()

	w.Wait()

	// Generate Delete
	deleteStr := strings.Join([]string{
		cookieD,
		"D",
		callid,
		tagf,
		tagt}, " ")
	log.Printf("Delete: %s\n", deleteStr)

	// Send Delete to RTPproxy
	n, _ = rtpproxyCon.Write([]byte(deleteStr))

	// Read Delete reply
	_, _ = rtpproxyCon.Read(buffer[0:])
}
<|endoftext|>