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

package fun

import (
	"math"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/plt"
	"github.com/cpmech/gosl/utl"
)

// Interpolation grid kinds
var (

	// UniformGridKind defines the uniform 1D grid kind
	UniformGridKind = io.NewEnum("Uniform", "fun.uniform", "U", "Uniform 1D grid")

	// ChebyGaussGridKind defines the Chebyshev-Gauss 1D grid kind
	ChebyGaussGridKind = io.NewEnum("ChebyGauss", "fun.chebygauss", "CG", "Chebyshev-Gauss 1D grid")
)

// LagrangeInterp implements Lagrange interpolators associated with a grid X
//
//   An interpolant I^X_N{f} (associated with a grid X; of degree N; with N+1 points)
//   is expressed in the Lagrange form as follows:
//
//                     N
//         X          ————             X
//        I {f}(x) =  \     f(x[i]) ⋅ ℓ (x)
//         N          /                i
//                    ————
//                    i = 0
//
//   where ℓ^X_i(x) is the i-th Lagrange cardinal polynomial associated with grid X and given by:
//
//                 N
//         N      ━━━━    x  -  X[j]
//        ℓ (x) = ┃  ┃  —————————————           0 ≤ i ≤ N
//         i      ┃  ┃   X[i] - X[j]
//               j = 0
//               j ≠ i
//
type LagrangeInterp struct {
	N int       // degree: N = len(X)-1
	X []float64 // grid points: len(X) = P+1; generated in [-1, 1]
}

// NewLagrangeInterp allocates a new LagrangeInterp
//   N        -- degree
//   gridType -- type of grid; e.g. uniform
//   NOTE: the grid will be generated in [-1, 1]
func NewLagrangeInterp(N int, gridType io.Enum) (o *LagrangeInterp, err error) {
	if N < 0 {
		return nil, chk.Err("N must be at least equal to 0. N=%d is invalid\n", N)
	}
	o = new(LagrangeInterp)
	o.N = N
	switch gridType {
	case UniformGridKind:
		o.X = utl.LinSpace(-1, 1, N+1)
	case ChebyGaussGridKind:
		o.X = make([]float64, N+1)
		h := math.Pi / float64(2*(N+1))
		for i := 0; i < N+1; i++ {
			o.X[i] = -math.Cos(h * float64(2*i+1))
		}
	default:
		return nil, chk.Err("cannot create grid type %q\n", gridType)
	}
	return
}

// W computes the generating (nodal) polynomial associated with grid X. The nodal polynomial is the
// unique polynomial of degree N+1 and leading coefficient whose zeros are the N+1 nodes of X.
//
//                 N
//         X      ━━━━
//        W (x) = ┃  ┃ (x - X[i])
//        N+1     ┃  ┃
//               i = 0
//
func (o *LagrangeInterp) W(x float64) (w float64) {
	w = 1
	for i := 0; i < o.N+1; i++ {
		w *= x - o.X[i]
	}
	return
}

// L computes the i-th Lagrange cardinal polynomial ℓ^X_i(x) associated with grid X
//
//                 N
//         X      ━━━━    x  -  X[j]
//        ℓ (x) = ┃  ┃  —————————————           0 ≤ i ≤ N
//         i      ┃  ┃   X[i] - X[j]
//               j = 0
//               j ≠ i
//
//   Input:
//      i -- index of X[i] point
//      x -- where to evaluate the polynomial
//   Output:
//      lix -- ℓ^X_i(x)
func (o *LagrangeInterp) L(i int, x float64) (lix float64) {
	lix = 1
	for j := 0; j < o.N+1; j++ {
		if i != j {
			lix *= (x - o.X[j]) / (o.X[i] - o.X[j])
		}
	}
	return
}

// I computes the interpolation I^X_N{f}(x) @ x
//
//                     N
//         X          ————             X
//        I {f}(x) =  \     f(x[i]) ⋅ ℓ (x)
//         N          /                i
//                    ————
//                    i = 0
//
func (o *LagrangeInterp) I(x float64, f Ss) (ix float64, err error) {
	for i := 0; i < o.N+1; i++ {
		fxi, e := f(o.X[i])
		if e != nil {
			return 0, e
		}
		ix += fxi * o.L(i, x)
	}
	return
}

// EstimateLebesgue estimates the Lebesgue constant by using 10000 stations along [-1,1]
func (o *LagrangeInterp) EstimateLebesgue() (ΛN float64) {
	nsta := 10000 // generate several points along [-1,1]
	for j := 0; j < nsta; j++ {
		x := -1.0 + 2.0*float64(j)/float64(nsta-1)
		sum := math.Abs(o.L(0, x))
		for i := 1; i < o.N+1; i++ {
			sum += math.Abs(o.L(i, x))
		}
		if sum > ΛN {
			ΛN = sum
		}
	}
	return
}

// EstimateMaxErr estimates the maximum error using 10000 stations along [-1,1]
// This function also returns the location (xloc) of the estimated max error
//   Computes:
//             maxerr = max(|f(x) - I{f}(x)|)
func (o *LagrangeInterp) EstimateMaxErr(f Ss) (maxerr, xloc float64) {
	nsta := 10000 // generate several points along [-1,1]
	xloc = -1
	for i := 0; i < nsta; i++ {
		x := -1.0 + 2.0*float64(i)/float64(nsta-1)
		fx, err := f(x)
		if err != nil {
			chk.Panic("f(x) failed:%v\n", err)
		}
		ix, err := o.I(x, f)
		if err != nil {
			chk.Panic("I(x) failed:%v\n", err)
		}
		e := math.Abs(fx - ix)
		if e > maxerr {
			maxerr = e
			xloc = x
		}
	}
	return
}

// PlotLagInterpL plots cardinal polynomials ℓ
func PlotLagInterpL(N int, gridType io.Enum) {
	xx := utl.LinSpace(-1, 1, 201)
	yy := make([]float64, len(xx))
	o, _ := NewLagrangeInterp(N, gridType)
	for n := 0; n < N+1; n++ {
		for k, x := range xx {
			yy[k] = o.L(n, x)
		}
		plt.Plot(xx, yy, &plt.A{NoClip: true})
	}
	Y := make([]float64, N+1)
	plt.Plot(o.X, Y, &plt.A{C: "k", Ls: "none", M: "o", Void: true, NoClip: true})
	plt.Gll("$x$", "$\\ell(x)$", nil)
	plt.Cross(0, 0, &plt.A{C: "grey"})
	plt.HideAllBorders()
}

// PlotLagInterpW plots nodal polynomial
func PlotLagInterpW(N int, gridType io.Enum) {
	npts := 201
	xx := utl.LinSpace(-1, 1, npts)
	yy := make([]float64, len(xx))
	o, _ := NewLagrangeInterp(N, gridType)
	for k, x := range xx {
		yy[k] = o.W(x)
	}
	Y := make([]float64, len(o.X))
	plt.Plot(o.X, Y, &plt.A{C: "k", Ls: "none", M: "o", Void: true, NoClip: true})
	plt.Plot(xx, yy, &plt.A{C: "b", Lw: 1, NoClip: true})
	plt.Gll("$x$", "$w(x)$", nil)
	plt.Cross(0, 0, &plt.A{C: "grey"})
	plt.HideAllBorders()
}

// PlotLagInterpI plots Lagrange interpolation I(x) function for many degrees Nvalues
func PlotLagInterpI(Nvalues []int, gridType io.Enum, f Ss) {
	npts := 201
	xx := utl.LinSpace(-1, 1, npts)
	yy := make([]float64, len(xx))
	for k, x := range xx {
		yy[k], _ = f(x)
	}
	iy := make([]float64, len(xx))
	plt.Plot(xx, yy, &plt.A{C: "k", Lw: 4, NoClip: true})
	for _, N := range Nvalues {
		p, _ := NewLagrangeInterp(N, gridType)
		for k, x := range xx {
			iy[k], _ = p.I(x, f)
		}
		E, xloc := p.EstimateMaxErr(f)
		plt.AxVline(xloc, &plt.A{C: "k", Ls: ":"})
		plt.Plot(xx, iy, &plt.A{L: io.Sf("$N=%d\\;E=%.3e$", N, E), NoClip: true})
	}
	plt.Cross(0, 0, &plt.A{C: "grey"})
	plt.Gll("$x$", "$f(x)\\quad I{f}(x)$", nil)
	plt.HideAllBorders()
}
<commit_msg>Rename function to compute generating polynomial<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fun

import (
	"math"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/plt"
	"github.com/cpmech/gosl/utl"
)

// Interpolation grid kinds
var (

	// UniformGridKind defines the uniform 1D grid kind
	UniformGridKind = io.NewEnum("Uniform", "fun.uniform", "U", "Uniform 1D grid")

	// ChebyGaussGridKind defines the Chebyshev-Gauss 1D grid kind
	ChebyGaussGridKind = io.NewEnum("ChebyGauss", "fun.chebygauss", "CG", "Chebyshev-Gauss 1D grid")
)

// LagrangeInterp implements Lagrange interpolators associated with a grid X
//
//   An interpolant I^X_N{f} (associated with a grid X; of degree N; with N+1 points)
//   is expressed in the Lagrange form as follows:
//
//                     N
//         X          ————             X
//        I {f}(x) =  \     f(x[i]) ⋅ ℓ (x)
//         N          /                i
//                    ————
//                    i = 0
//
//   where ℓ^X_i(x) is the i-th Lagrange cardinal polynomial associated with grid X and given by:
//
//                 N
//         N      ━━━━    x  -  X[j]
//        ℓ (x) = ┃  ┃  —————————————           0 ≤ i ≤ N
//         i      ┃  ┃   X[i] - X[j]
//               j = 0
//               j ≠ i
//
type LagrangeInterp struct {
	N int       // degree: N = len(X)-1
	X []float64 // grid points: len(X) = P+1; generated in [-1, 1]
}

// NewLagrangeInterp allocates a new LagrangeInterp
//   N        -- degree
//   gridType -- type of grid; e.g. uniform
//   NOTE: the grid will be generated in [-1, 1]
func NewLagrangeInterp(N int, gridType io.Enum) (o *LagrangeInterp, err error) {
	if N < 0 {
		return nil, chk.Err("N must be at least equal to 0. N=%d is invalid\n", N)
	}
	o = new(LagrangeInterp)
	o.N = N
	switch gridType {
	case UniformGridKind:
		o.X = utl.LinSpace(-1, 1, N+1)
	case ChebyGaussGridKind:
		o.X = make([]float64, N+1)
		h := math.Pi / float64(2*(N+1))
		for i := 0; i < N+1; i++ {
			o.X[i] = -math.Cos(h * float64(2*i+1))
		}
	default:
		return nil, chk.Err("cannot create grid type %q\n", gridType)
	}
	return
}

// Phi computes the generating (nodal) polynomial associated with grid X. The nodal polynomial is
// the unique polynomial of degree N+1 and leading coefficient whose zeros are the N+1 nodes of X.
//
//                 N
//         X      ━━━━
//        Φ (x) = ┃  ┃ (x - X[i])
//        N+1     ┃  ┃
//               i = 0
//
func (o *LagrangeInterp) Phi(x float64) (Φ float64) {
	Φ = 1
	for i := 0; i < o.N+1; i++ {
		Φ *= x - o.X[i]
	}
	return
}

// L computes the i-th Lagrange cardinal polynomial ℓ^X_i(x) associated with grid X
//
//                 N
//         X      ━━━━    x  -  X[j]
//        ℓ (x) = ┃  ┃  —————————————           0 ≤ i ≤ N
//         i      ┃  ┃   X[i] - X[j]
//               j = 0
//               j ≠ i
//
//   Input:
//      i -- index of X[i] point
//      x -- where to evaluate the polynomial
//   Output:
//      lix -- ℓ^X_i(x)
func (o *LagrangeInterp) L(i int, x float64) (lix float64) {
	lix = 1
	for j := 0; j < o.N+1; j++ {
		if i != j {
			lix *= (x - o.X[j]) / (o.X[i] - o.X[j])
		}
	}
	return
}

// I computes the interpolation I^X_N{f}(x) @ x
//
//                     N
//         X          ————             X
//        I {f}(x) =  \     f(x[i]) ⋅ ℓ (x)
//         N          /                i
//                    ————
//                    i = 0
//
func (o *LagrangeInterp) I(x float64, f Ss) (ix float64, err error) {
	for i := 0; i < o.N+1; i++ {
		fxi, e := f(o.X[i])
		if e != nil {
			return 0, e
		}
		ix += fxi * o.L(i, x)
	}
	return
}

// EstimateLebesgue estimates the Lebesgue constant by using 10000 stations along [-1,1]
func (o *LagrangeInterp) EstimateLebesgue() (ΛN float64) {
	nsta := 10000 // generate several points along [-1,1]
	for j := 0; j < nsta; j++ {
		x := -1.0 + 2.0*float64(j)/float64(nsta-1)
		sum := math.Abs(o.L(0, x))
		for i := 1; i < o.N+1; i++ {
			sum += math.Abs(o.L(i, x))
		}
		if sum > ΛN {
			ΛN = sum
		}
	}
	return
}

// EstimateMaxErr estimates the maximum error using 10000 stations along [-1,1]
// This function also returns the location (xloc) of the estimated max error
//   Computes:
//             maxerr = max(|f(x) - I{f}(x)|)
func (o *LagrangeInterp) EstimateMaxErr(f Ss) (maxerr, xloc float64) {
	nsta := 10000 // generate several points along [-1,1]
	xloc = -1
	for i := 0; i < nsta; i++ {
		x := -1.0 + 2.0*float64(i)/float64(nsta-1)
		fx, err := f(x)
		if err != nil {
			chk.Panic("f(x) failed:%v\n", err)
		}
		ix, err := o.I(x, f)
		if err != nil {
			chk.Panic("I(x) failed:%v\n", err)
		}
		e := math.Abs(fx - ix)
		if e > maxerr {
			maxerr = e
			xloc = x
		}
	}
	return
}

// PlotLagInterpL plots cardinal polynomials ℓ
func PlotLagInterpL(N int, gridType io.Enum) {
	xx := utl.LinSpace(-1, 1, 201)
	yy := make([]float64, len(xx))
	o, _ := NewLagrangeInterp(N, gridType)
	for n := 0; n < N+1; n++ {
		for k, x := range xx {
			yy[k] = o.L(n, x)
		}
		plt.Plot(xx, yy, &plt.A{NoClip: true})
	}
	Y := make([]float64, N+1)
	plt.Plot(o.X, Y, &plt.A{C: "k", Ls: "none", M: "o", Void: true, NoClip: true})
	plt.Gll("$x$", "$\\ell(x)$", nil)
	plt.Cross(0, 0, &plt.A{C: "grey"})
	plt.HideAllBorders()
}

// PlotLagInterpW plots nodal polynomial
func PlotLagInterpW(N int, gridType io.Enum) {
	npts := 201
	xx := utl.LinSpace(-1, 1, npts)
	yy := make([]float64, len(xx))
	o, _ := NewLagrangeInterp(N, gridType)
	for k, x := range xx {
		yy[k] = o.Phi(x)
	}
	Y := make([]float64, len(o.X))
	plt.Plot(o.X, Y, &plt.A{C: "k", Ls: "none", M: "o", Void: true, NoClip: true})
	plt.Plot(xx, yy, &plt.A{C: "b", Lw: 1, NoClip: true})
	plt.Gll("$x$", "$w(x)$", nil)
	plt.Cross(0, 0, &plt.A{C: "grey"})
	plt.HideAllBorders()
}

// PlotLagInterpI plots Lagrange interpolation I(x) function for many degrees Nvalues
func PlotLagInterpI(Nvalues []int, gridType io.Enum, f Ss) {
	npts := 201
	xx := utl.LinSpace(-1, 1, npts)
	yy := make([]float64, len(xx))
	for k, x := range xx {
		yy[k], _ = f(x)
	}
	iy := make([]float64, len(xx))
	plt.Plot(xx, yy, &plt.A{C: "k", Lw: 4, NoClip: true})
	for _, N := range Nvalues {
		p, _ := NewLagrangeInterp(N, gridType)
		for k, x := range xx {
			iy[k], _ = p.I(x, f)
		}
		E, xloc := p.EstimateMaxErr(f)
		plt.AxVline(xloc, &plt.A{C: "k", Ls: ":"})
		plt.Plot(xx, iy, &plt.A{L: io.Sf("$N=%d\\;E=%.3e$", N, E), NoClip: true})
	}
	plt.Cross(0, 0, &plt.A{C: "grey"})
	plt.Gll("$x$", "$f(x)\\quad I{f}(x)$", nil)
	plt.HideAllBorders()
}
<|endoftext|>


<commit_before>package dsig

import (
	"testing"

	"github.com/beevik/etree"
)

const (
	assertion       = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" Version="2.0" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AssertionConsumerServiceIndex="0" AttributeConsumingServiceIndex="0" IssueInstant="2016-04-28T15:37:17" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO"><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""/><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>`
	actual          = `<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>`
	assertionC14ned = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>`
)

func TestC14N10(t *testing.T) {
	raw := etree.NewDocument()

	raw.ReadFromString(assertion)

	hacked := excCanonicalPrep(raw.Root())

	hd := etree.NewDocument()

	hd.SetRoot(hacked)

	hd.WriteSettings = etree.WriteSettings{
		CanonicalAttrVal: true,
		CanonicalEndTags: true,
	}

	hs, err := hd.WriteToString()
	if err != nil {
		t.Fatalf("could not write c14n doc: %v", err)
	}

	if hs != assertionC14ned {
		t.Log(hs)
		t.Log(assertionC14ned)
		t.Errorf("Wrong canonical representation.")
	}
}
<commit_msg>Add test for original c14n11 algorithm<commit_after>package dsig

import (
	"testing"

	"github.com/beevik/etree"
)

const (
	assertion       = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" Version="2.0" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AssertionConsumerServiceIndex="0" AttributeConsumingServiceIndex="0" IssueInstant="2016-04-28T15:37:17" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO"><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""/><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>`
	c14n11          = `<samlp:AuthnRequest xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer>https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>`
	assertionC14ned = `<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceIndex="0" AssertionConsumerServiceURL="https://saml2.test.astuart.co/sso/saml2" AttributeConsumingServiceIndex="0" Destination="http://idp.astuart.co/idp/profile/SAML2/Redirect/SSO" ID="_88a93ebe-abdf-48cd-9ed0-b0dd1b252909" IssueInstant="2016-04-28T15:37:17" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://saml2.test.astuart.co/sso/saml2</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format=""></samlp:NameIDPolicy><samlp:RequestedAuthnContext Comparison="exact"><saml:AuthnContextClassRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext></samlp:AuthnRequest>`
)

func TestExcC14N10(t *testing.T) {
	raw := etree.NewDocument()

	raw.ReadFromString(assertion)

	hacked := excCanonicalPrep(raw.Root())

	hd := etree.NewDocument()

	hd.SetRoot(hacked)

	hd.WriteSettings = etree.WriteSettings{
		CanonicalAttrVal: true,
		CanonicalEndTags: true,
	}

	hs, err := hd.WriteToString()
	if err != nil {
		t.Fatalf("could not write c14n doc: %v", err)
	}

	if hs != assertionC14ned {
		t.Log(hs)
		t.Log(assertionC14ned)
		t.Errorf("Wrong canonical representation.")
	}
}

func TestC14N11(t *testing.T) {
	raw := etree.NewDocument()

	raw.ReadFromString(assertion)

	hacked := canonicalHack(raw.Root())

	hd := etree.NewDocument()

	hd.SetRoot(hacked)

	hd.WriteSettings = etree.WriteSettings{
		CanonicalAttrVal: true,
		CanonicalEndTags: true,
	}

	hs, err := hd.WriteToString()
	if err != nil {
		t.Fatalf("could not write c14n doc: %v", err)
	}

	if hs != c14n11 {
		t.Log(hs)
		t.Log(c14n11)
		t.Errorf("Wrong canonical representation.")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	start := time.Now()
	ch := make(chan string)
	for _, url := range os.Args[1:] {
		if !strings.HasPrefix(url, "http://") {
			url = "http://" + url
		}
		go fetch(url, ch)
	}
	for range os.Args[1:] {
		fmt.Println(<-ch)
	}
	fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}

func fetch(url string, ch chan<- string) {
	start := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		ch <- fmt.Sprint(err)
		return
	}
	nbytes, err := io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	if err != nil {
		ch <- fmt.Sprintf("while reading %s: %v", url, err)
		return
	}
	secs := time.Since(start).Seconds()
	ch <- fmt.Sprintf("%.2fs  %7d  %s", secs, nbytes, url)
}
<commit_msg>Send start message to channel.<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	start := time.Now()
	ch := make(chan string)
	for _, url := range os.Args[1:] {
		if !strings.HasPrefix(url, "http://") {
			url = "http://" + url
		}
		go fetch(url, ch)
	}
	for range os.Args[1:] {
		fmt.Println(<-ch)
	}
	fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}

func fetch(url string, ch chan<- string) {
	ch <- fmt.Sprintf("Fetching %s...", url)
	start := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		ch <- fmt.Sprint(err)
		return
	}
	nbytes, err := io.Copy(ioutil.Discard, resp.Body)
	resp.Body.Close()
	if err != nil {
		ch <- fmt.Sprintf("while reading %s: %v", url, err)
		return
	}
	secs := time.Since(start).Seconds()
	ch <- fmt.Sprintf("%.2fs  %7d  %s", secs, nbytes, url)
}
<|endoftext|>


<commit_before>/*
   Copyright 2017 Shlomi Noach, GitHub 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 kv

import (
	consulapi "github.com/armon/consul-api"
	"github.com/github/orchestrator/go/config"
	"github.com/openark/golib/log"
)

// A Consul store based on config's `ConsulAddress` and `ConsulKVPrefix`
type consulStore struct {
	client *consulapi.Client
}

// NewConsulStore creates a new consul store. It is possible that the client for this store is nil,
// which is the case if no consul config is provided.
func NewConsulStore() KVStore {
	store := &consulStore{}

	if config.Config.ConsulAddress != "" {
		consulConfig := consulapi.DefaultConfig()
		consulConfig.Address = config.Config.ConsulAddress
		// ConsulAclToken defaults to ""
		consulConfig.Token = config.Config.ConsulAclToken
		if client, err := consulapi.NewClient(consulConfig); err != nil {
			log.Errore(err)
		} else {
			store.client = client
		}
	}
	return store
}

func (this *consulStore) PutKeyValue(key string, value string) (err error) {
	if this.client == nil {
		return nil
	}
	pair := &consulapi.KVPair{Key: key, Value: []byte(value)}
	_, err = this.client.KV().Put(pair, nil)
	return err
}

func (this *consulStore) GetKeyValue(key string) (value string, found bool, err error) {
	if this.client == nil {
		return value, found, nil
	}
	pair, _, err := this.client.KV().Get(key, nil)
	if err != nil {
		return value, found, err
	}
	return string(pair.Value), (pair != nil), nil
}

func (this *consulStore) DistributePairs(kvPairs [](*KVPair)) (failedDistributions []string, err error) {
	log.Errorf("consulStore.DistributePairs(): %d", len(kvPairs))
	if !config.Config.ConsulCrossDataCenterDistribution {
		log.Errorf("consulStore.DistributePairs(): !config.Config.ConsulCrossDataCenterDistribution")
		return failedDistributions, nil
	}
	datacenters, err := this.client.Catalog().Datacenters()
	if err != nil {
		return failedDistributions, err
	}
	log.Errorf("consulStore.DistributePairs(): %d datacenters", len(datacenters))
	consulPairs := [](*consulapi.KVPair){}
	for _, kvPair := range kvPairs {
		consulPairs = append(consulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
	}
	for _, datacenter := range datacenters {
		writeOptions := &consulapi.WriteOptions{Datacenter: datacenter}
		for _, consulPair := range consulPairs {
			if _, e := this.client.KV().Put(consulPair, writeOptions); e != nil {
				log.Errorf("consulStore.DistributePairs(): failed writing %s", datacenter)
				failedDistributions = append(failedDistributions, datacenter)
				err = e
			}
		}
	}
	return failedDistributions, err
}
<commit_msg>even mroe auditing<commit_after>/*
   Copyright 2017 Shlomi Noach, GitHub 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 kv

import (
	consulapi "github.com/armon/consul-api"
	"github.com/github/orchestrator/go/config"
	"github.com/openark/golib/log"
)

// A Consul store based on config's `ConsulAddress` and `ConsulKVPrefix`
type consulStore struct {
	client *consulapi.Client
}

// NewConsulStore creates a new consul store. It is possible that the client for this store is nil,
// which is the case if no consul config is provided.
func NewConsulStore() KVStore {
	store := &consulStore{}

	if config.Config.ConsulAddress != "" {
		consulConfig := consulapi.DefaultConfig()
		consulConfig.Address = config.Config.ConsulAddress
		// ConsulAclToken defaults to ""
		consulConfig.Token = config.Config.ConsulAclToken
		if client, err := consulapi.NewClient(consulConfig); err != nil {
			log.Errore(err)
		} else {
			store.client = client
		}
	}
	return store
}

func (this *consulStore) PutKeyValue(key string, value string) (err error) {
	if this.client == nil {
		return nil
	}
	pair := &consulapi.KVPair{Key: key, Value: []byte(value)}
	_, err = this.client.KV().Put(pair, nil)
	return err
}

func (this *consulStore) GetKeyValue(key string) (value string, found bool, err error) {
	if this.client == nil {
		return value, found, nil
	}
	pair, _, err := this.client.KV().Get(key, nil)
	if err != nil {
		return value, found, err
	}
	return string(pair.Value), (pair != nil), nil
}

func (this *consulStore) DistributePairs(kvPairs [](*KVPair)) (failedDistributions []string, err error) {
	log.Errorf("consulStore.DistributePairs(): %d pairs", len(kvPairs))
	if !config.Config.ConsulCrossDataCenterDistribution {
		log.Errorf("consulStore.DistributePairs(): !config.Config.ConsulCrossDataCenterDistribution")
		return failedDistributions, nil
	}
	datacenters, err := this.client.Catalog().Datacenters()
	if err != nil {
		return failedDistributions, err
	}
	log.Errorf("consulStore.DistributePairs(): %d datacenters", len(datacenters))
	consulPairs := [](*consulapi.KVPair){}
	for _, kvPair := range kvPairs {
		log.Errorf("consulStore.DistributePairs():kvpair: %+v", kvPair)
		consulPairs = append(consulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
	}
	for _, datacenter := range datacenters {
		writeOptions := &consulapi.WriteOptions{Datacenter: datacenter}
		for _, consulPair := range consulPairs {
			if _, e := this.client.KV().Put(consulPair, writeOptions); e != nil {
				log.Errorf("consulStore.DistributePairs(): failed writing %s", datacenter)
				failedDistributions = append(failedDistributions, datacenter)
				err = e
			}
		}
	}
	return failedDistributions, err
}
<|endoftext|>


<commit_before>/*
   Copyright 2017 Shlomi Noach, GitHub 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 kv

import (
	"fmt"
	"sync"
	"sync/atomic"

	"github.com/github/orchestrator/go/config"

	consulapi "github.com/armon/consul-api"
	"github.com/patrickmn/go-cache"

	"github.com/openark/golib/log"
)

// A Consul store based on config's `ConsulAddress` and `ConsulKVPrefix`
type consulStore struct {
	client                        *consulapi.Client
	kvCache                       *cache.Cache
	pairsDistributionSuccessMutex sync.Mutex
	distributionReentry           int64
}

// NewConsulStore creates a new consul store. It is possible that the client for this store is nil,
// which is the case if no consul config is provided.
func NewConsulStore() KVStore {
	store := &consulStore{
		kvCache: cache.New(cache.NoExpiration, cache.DefaultExpiration),
	}

	if config.Config.ConsulAddress != "" {
		consulConfig := consulapi.DefaultConfig()
		consulConfig.Address = config.Config.ConsulAddress
		// ConsulAclToken defaults to ""
		consulConfig.Token = config.Config.ConsulAclToken
		if client, err := consulapi.NewClient(consulConfig); err != nil {
			log.Errore(err)
		} else {
			store.client = client
		}
	}
	return store
}

func (this *consulStore) PutKeyValue(key string, value string) (err error) {
	if this.client == nil {
		return nil
	}
	pair := &consulapi.KVPair{Key: key, Value: []byte(value)}
	_, err = this.client.KV().Put(pair, nil)
	return err
}

func (this *consulStore) GetKeyValue(key string) (value string, found bool, err error) {
	if this.client == nil {
		return value, found, nil
	}
	pair, _, err := this.client.KV().Get(key, nil)
	if err != nil {
		return value, found, err
	}
	return string(pair.Value), (pair != nil), nil
}

func (this *consulStore) DistributePairs(kvPairs [](*KVPair)) (err error) {
	// This function is non re-entrant (it can only be running once at any point in time)
	if atomic.CompareAndSwapInt64(&this.distributionReentry, 0, 1) {
		defer atomic.StoreInt64(&this.distributionReentry, 0)
	} else {
		return
	}

	if !config.Config.ConsulCrossDataCenterDistribution {
		return nil
	}

	datacenters, err := this.client.Catalog().Datacenters()
	if err != nil {
		return err
	}
	log.Debugf("consulStore.DistributePairs(): distributing %d pairs to %d datacenters", len(kvPairs), len(datacenters))
	consulPairs := [](*consulapi.KVPair){}
	for _, kvPair := range kvPairs {
		consulPairs = append(consulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
	}
	var wg sync.WaitGroup
	for _, datacenter := range datacenters {
		datacenter := datacenter
		wg.Add(1)
		go func() {
			defer wg.Done()

			writeOptions := &consulapi.WriteOptions{Datacenter: datacenter}
			queryOptions := &consulapi.QueryOptions{Datacenter: datacenter}
			skipped := 0
			existing := 0
			written := 0
			failed := 0

			for _, consulPair := range consulPairs {
				val := string(consulPair.Value)
				kcCacheKey := fmt.Sprintf("%s;%s", datacenter, consulPair.Key)

				if value, found := this.kvCache.Get(kcCacheKey); found && val == value {
					skipped++
					continue
				}
				if pair, _, err := this.client.KV().Get(consulPair.Key, queryOptions); err == nil && pair != nil {
					if val == string(pair.Value) {
						existing++
						this.kvCache.SetDefault(kcCacheKey, val)
						continue
					}
				}

				if _, e := this.client.KV().Put(consulPair, writeOptions); e != nil {
					log.Errorf("consulStore.DistributePairs(): failed %s", kcCacheKey)
					failed++
					err = e
				} else {
					log.Errorf("consulStore.DistributePairs(): written %s=%s", kcCacheKey, val)
					written++
					this.kvCache.SetDefault(kcCacheKey, val)
				}
			}
			log.Debugf("consulStore.DistributePairs(): datacenter: %s; skipped: %d, existing: %d, written: %d, failed: %d", datacenter, skipped, existing, written, failed)
		}()
	}
	wg.Wait()
	return err
}

//
// func (this *consulStore) DistributePairs(canonicalPairs [](*KVPair), fullPairs [](*KVPair)) (err error) {
// 	log.Debugf("consulStore.DistributePairs(): %d pairs canonical, %d pairs full", len(canonicalPairs), len(fullPairs))
// 	if !config.Config.ConsulCrossDataCenterDistribution {
// 		log.Debugf("consulStore.DistributePairs(): !config.Config.ConsulCrossDataCenterDistribution")
// 		return nil
// 	}
//
// 	previousPairsDistributionStart := this.lastPairsDistributionStart
// 	this.lastPairsDistributionStart = time.Now()
//
// 	datacenters, err := this.client.Catalog().Datacenters()
// 	if err != nil {
// 		return err
// 	}
// 	log.Debugf("consulStore.DistributePairs(): %d datacenters", len(datacenters))
// 	canonicalConsulPairs := [](*consulapi.KVPair){}
// 	for _, kvPair := range canonicalPairs {
// 		log.Debugf("consulStore.DistributePairs():kvpair: %+v", kvPair)
// 		canonicalConsulPairs = append(canonicalConsulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
// 	}
// 	fullConsulPairs := [](*consulapi.KVPair){}
// 	for _, kvPair := range fullPairs {
// 		log.Debugf("consulStore.DistributePairs():kvpair: %+v", kvPair)
// 		fullConsulPairs = append(fullConsulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
// 	}
// 	for _, datacenter := range datacenters {
// 		consulPairs := canonicalConsulPairs
// 		if lastSuccess := this.pairsDistributionSuccess[datacenter]; lastSuccess.Before(previousPairsDistributionStart) {
// 			// default value is time.Zero
// 			// No sign of success
// 			consulPairs = fullConsulPairs
// 			log.Debugf("consulStore.DistributePairs(): full pairs for datacenter: %+v", datacenter)
// 		} else {
// 			log.Debugf("consulStore.DistributePairs(): canonical pairs for datacenter: %+v", datacenter)
// 		}
//
// 		dataCenterErrorFound := false
// 		log.Debugf("consulStore.DistributePairs():datacenter: %+v", datacenter)
// 		writeOptions := &consulapi.WriteOptions{Datacenter: datacenter}
// 		for _, consulPair := range consulPairs {
// 			if _, e := this.client.KV().Put(consulPair, writeOptions); e != nil {
// 				dataCenterErrorFound = true
// 				log.Errorf("consulStore.DistributePairs(): failed writing %s", datacenter)
// 				err = e
// 			}
// 		}
// 		if !dataCenterErrorFound {
// 			log.Errorf("consulStore.DistributePairs(): %d keys written successfully on %s", len(consulPairs), datacenter)
// 			this.pairsDistributionSuccess[datacenter] = time.Now()
// 		}
// 	}
// 	return err
// }
<commit_msg>debug, not error<commit_after>/*
   Copyright 2017 Shlomi Noach, GitHub 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 kv

import (
	"fmt"
	"sync"
	"sync/atomic"

	"github.com/github/orchestrator/go/config"

	consulapi "github.com/armon/consul-api"
	"github.com/patrickmn/go-cache"

	"github.com/openark/golib/log"
)

// A Consul store based on config's `ConsulAddress` and `ConsulKVPrefix`
type consulStore struct {
	client                        *consulapi.Client
	kvCache                       *cache.Cache
	pairsDistributionSuccessMutex sync.Mutex
	distributionReentry           int64
}

// NewConsulStore creates a new consul store. It is possible that the client for this store is nil,
// which is the case if no consul config is provided.
func NewConsulStore() KVStore {
	store := &consulStore{
		kvCache: cache.New(cache.NoExpiration, cache.DefaultExpiration),
	}

	if config.Config.ConsulAddress != "" {
		consulConfig := consulapi.DefaultConfig()
		consulConfig.Address = config.Config.ConsulAddress
		// ConsulAclToken defaults to ""
		consulConfig.Token = config.Config.ConsulAclToken
		if client, err := consulapi.NewClient(consulConfig); err != nil {
			log.Errore(err)
		} else {
			store.client = client
		}
	}
	return store
}

func (this *consulStore) PutKeyValue(key string, value string) (err error) {
	if this.client == nil {
		return nil
	}
	pair := &consulapi.KVPair{Key: key, Value: []byte(value)}
	_, err = this.client.KV().Put(pair, nil)
	return err
}

func (this *consulStore) GetKeyValue(key string) (value string, found bool, err error) {
	if this.client == nil {
		return value, found, nil
	}
	pair, _, err := this.client.KV().Get(key, nil)
	if err != nil {
		return value, found, err
	}
	return string(pair.Value), (pair != nil), nil
}

func (this *consulStore) DistributePairs(kvPairs [](*KVPair)) (err error) {
	// This function is non re-entrant (it can only be running once at any point in time)
	if atomic.CompareAndSwapInt64(&this.distributionReentry, 0, 1) {
		defer atomic.StoreInt64(&this.distributionReentry, 0)
	} else {
		return
	}

	if !config.Config.ConsulCrossDataCenterDistribution {
		return nil
	}

	datacenters, err := this.client.Catalog().Datacenters()
	if err != nil {
		return err
	}
	log.Debugf("consulStore.DistributePairs(): distributing %d pairs to %d datacenters", len(kvPairs), len(datacenters))
	consulPairs := [](*consulapi.KVPair){}
	for _, kvPair := range kvPairs {
		consulPairs = append(consulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
	}
	var wg sync.WaitGroup
	for _, datacenter := range datacenters {
		datacenter := datacenter
		wg.Add(1)
		go func() {
			defer wg.Done()

			writeOptions := &consulapi.WriteOptions{Datacenter: datacenter}
			queryOptions := &consulapi.QueryOptions{Datacenter: datacenter}
			skipped := 0
			existing := 0
			written := 0
			failed := 0

			for _, consulPair := range consulPairs {
				val := string(consulPair.Value)
				kcCacheKey := fmt.Sprintf("%s;%s", datacenter, consulPair.Key)

				if value, found := this.kvCache.Get(kcCacheKey); found && val == value {
					skipped++
					continue
				}
				if pair, _, err := this.client.KV().Get(consulPair.Key, queryOptions); err == nil && pair != nil {
					if val == string(pair.Value) {
						existing++
						this.kvCache.SetDefault(kcCacheKey, val)
						continue
					}
				}

				if _, e := this.client.KV().Put(consulPair, writeOptions); e != nil {
					log.Errorf("consulStore.DistributePairs(): failed %s", kcCacheKey)
					failed++
					err = e
				} else {
					log.Debugf("consulStore.DistributePairs(): written %s=%s", kcCacheKey, val)
					written++
					this.kvCache.SetDefault(kcCacheKey, val)
				}
			}
			log.Debugf("consulStore.DistributePairs(): datacenter: %s; skipped: %d, existing: %d, written: %d, failed: %d", datacenter, skipped, existing, written, failed)
		}()
	}
	wg.Wait()
	return err
}

//
// func (this *consulStore) DistributePairs(canonicalPairs [](*KVPair), fullPairs [](*KVPair)) (err error) {
// 	log.Debugf("consulStore.DistributePairs(): %d pairs canonical, %d pairs full", len(canonicalPairs), len(fullPairs))
// 	if !config.Config.ConsulCrossDataCenterDistribution {
// 		log.Debugf("consulStore.DistributePairs(): !config.Config.ConsulCrossDataCenterDistribution")
// 		return nil
// 	}
//
// 	previousPairsDistributionStart := this.lastPairsDistributionStart
// 	this.lastPairsDistributionStart = time.Now()
//
// 	datacenters, err := this.client.Catalog().Datacenters()
// 	if err != nil {
// 		return err
// 	}
// 	log.Debugf("consulStore.DistributePairs(): %d datacenters", len(datacenters))
// 	canonicalConsulPairs := [](*consulapi.KVPair){}
// 	for _, kvPair := range canonicalPairs {
// 		log.Debugf("consulStore.DistributePairs():kvpair: %+v", kvPair)
// 		canonicalConsulPairs = append(canonicalConsulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
// 	}
// 	fullConsulPairs := [](*consulapi.KVPair){}
// 	for _, kvPair := range fullPairs {
// 		log.Debugf("consulStore.DistributePairs():kvpair: %+v", kvPair)
// 		fullConsulPairs = append(fullConsulPairs, &consulapi.KVPair{Key: kvPair.Key, Value: []byte(kvPair.Value)})
// 	}
// 	for _, datacenter := range datacenters {
// 		consulPairs := canonicalConsulPairs
// 		if lastSuccess := this.pairsDistributionSuccess[datacenter]; lastSuccess.Before(previousPairsDistributionStart) {
// 			// default value is time.Zero
// 			// No sign of success
// 			consulPairs = fullConsulPairs
// 			log.Debugf("consulStore.DistributePairs(): full pairs for datacenter: %+v", datacenter)
// 		} else {
// 			log.Debugf("consulStore.DistributePairs(): canonical pairs for datacenter: %+v", datacenter)
// 		}
//
// 		dataCenterErrorFound := false
// 		log.Debugf("consulStore.DistributePairs():datacenter: %+v", datacenter)
// 		writeOptions := &consulapi.WriteOptions{Datacenter: datacenter}
// 		for _, consulPair := range consulPairs {
// 			if _, e := this.client.KV().Put(consulPair, writeOptions); e != nil {
// 				dataCenterErrorFound = true
// 				log.Errorf("consulStore.DistributePairs(): failed writing %s", datacenter)
// 				err = e
// 			}
// 		}
// 		if !dataCenterErrorFound {
// 			log.Errorf("consulStore.DistributePairs(): %d keys written successfully on %s", len(consulPairs), datacenter)
// 			this.pairsDistributionSuccess[datacenter] = time.Now()
// 		}
// 	}
// 	return err
// }
<|endoftext|>


<commit_before>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package pvl

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/keybase/client/go/protocol/keybase1"
)

func parse(in string) (pvlT, error) {
	b := []byte(in)
	p := pvlT{}
	p.PvlVersion = -1
	p.Revision = -1
	err := json.Unmarshal(b, &p)
	if p.PvlVersion == -1 {
		return p, fmt.Errorf("pvl_version required")
	}
	if p.Revision == -1 {
		return p, fmt.Errorf("revision required")
	}
	return p, err
}

type pvlT struct {
	PvlVersion int `json:"pvl_version"`
	Revision   int `json:"revision"`
	// services is a map from service to a list of scripts.
	// each script is a list of instructions.
	Services servicesT `json:"services"`
}

type servicesT struct {
	Map map[keybase1.ProofType][]scriptT
}

func (x *servicesT) UnmarshalJSON(b []byte) error {
	// read as string map
	m := make(map[string][]scriptT)
	err := json.Unmarshal(b, &m)
	if err != nil {
		return err
	}
	// copy to ProofType map
	x.Map = make(map[keybase1.ProofType][]scriptT)
	for k, v := range m {
		t, ok := keybase1.ProofTypeMap[strings.ToUpper(k)]
		if ok {
			x.Map[t] = v
		}
		// Unrecognized proof types are discarded silently
		// So that old clients don't break if a new service is added
	}
	return nil
}

type scriptT struct {
	Instructions []instructionT
}

func (x *scriptT) UnmarshalJSON(b []byte) error {
	err := json.Unmarshal(b, &x.Instructions)
	for _, ins := range x.Instructions {
		if ins.variantsFilled() != 1 {
			return fmt.Errorf("exactly 1 variant must appear in instruction")
		}
	}
	return err
}

type instructionT struct {
	// Exactly one of these shall be non-nil
	// This list is duplicated to:
	// - instructionT.variantsFilled
	// - instructionT.String
	// - stepInstruction
	// This invariant is enforced by scriptT.UnmarshalJSON
	AssertRegexMatch    *assertRegexMatchT    `json:"assert_regex_match,omitempty"`
	AssertFindBase64    *assertFindBase64T    `json:"assert_find_base64,omitempty"`
	WhitespaceNormalize *whitespaceNormalizeT `json:"whitespace_normalize,omitempty"`
	RegexCapture        *regexCaptureT        `json:"regex_capture,omitempty"`
	Fetch               *fetchT               `json:"fetch,omitempty"`
	SelectorJSON        *selectorJSONT        `json:"selector_json,omitempty"`
	SelectorCSS         *selectorCSST         `json:"selector_css,omitempty"`
	TransformURL        *transformURLT        `json:"transform_url,omitempty"`
}

func (ins *instructionT) variantsFilled() int {
	n := 0
	if ins.AssertRegexMatch != nil {
		n += 1
	}
	if ins.AssertFindBase64 != nil {
		n += 1
	}
	if ins.WhitespaceNormalize != nil {
		n += 1
	}
	if ins.RegexCapture != nil {
		n += 1
	}
	if ins.Fetch != nil {
		n += 1
	}
	if ins.SelectorJSON != nil {
		n += 1
	}
	if ins.SelectorCSS != nil {
		n += 1
	}
	if ins.TransformURL != nil {
		n += 1
	}
	return n
}

func (x instructionT) Name() string {
	switch {
	case x.AssertRegexMatch != nil:
		return string(cmdAssertRegexMatch)
	case x.AssertFindBase64 != nil:
		return string(cmdAssertFindBase64)
	case x.WhitespaceNormalize != nil:
		return string(cmdWhitespaceNormalize)
	case x.RegexCapture != nil:
		return string(cmdRegexCapture)
	case x.Fetch != nil:
		return string(cmdFetch)
	case x.SelectorJSON != nil:
		return string(cmdSelectorJSON)
	case x.SelectorCSS != nil:
		return string(cmdSelectorCSS)
	case x.TransformURL != nil:
		return string(cmdTransformURL)
	}
	return "<invalid instruction>"
}

func (x instructionT) String() string {
	var s = ""
	switch {
	case x.AssertRegexMatch != nil:
		s = fmt.Sprintf("%v", x.AssertRegexMatch)
	case x.AssertFindBase64 != nil:
		s = fmt.Sprintf("%v", x.AssertFindBase64)
	case x.WhitespaceNormalize != nil:
		s = fmt.Sprintf("%v", x.WhitespaceNormalize)
	case x.RegexCapture != nil:
		s = fmt.Sprintf("%v", x.RegexCapture)
	case x.Fetch != nil:
		s = fmt.Sprintf("%v", x.Fetch)
	case x.SelectorJSON != nil:
		s = fmt.Sprintf("%v", x.SelectorJSON)
	case x.SelectorCSS != nil:
		s = fmt.Sprintf("%v", x.SelectorCSS)
	case x.TransformURL != nil:
		s = fmt.Sprintf("%v", x.TransformURL)
	}
	if s != "" {
		return fmt.Sprintf("[ins %v]", s)
	} else {
		return "[nil instruction]"
	}
}

type assertRegexMatchT struct {
	Pattern         string  `json:"pattern"`
	CaseInsensitive bool    `json:"case_insensitive"`
	MultiLine       bool    `json:"multiline"`
	Error           *errorT `json:"error"`
}

type assertFindBase64T struct {
	Var   string  `json:"var"`
	Error *errorT `json:"error"`
}

type whitespaceNormalizeT struct {
	Error *errorT `json:"error"`
}

type regexCaptureT struct {
	Pattern         string  `json:"pattern"`
	MultiLine       bool    `json:"multiline"`
	CaseInsensitive bool    `json:"case_insensitive"`
	Error           *errorT `json:"error"`
}

type fetchT struct {
	Kind  string  `json:"kind"`
	Error *errorT `json:"error"`
}

type selectorJSONT struct {
	Selectors []interface{} `json:"selectors"`
	Error     *errorT       `json:"error"`
}

type selectorCSST struct {
	Selectors []interface{} `json:"selectors"`
	Attr      string        `json:"attr"`
	// Whether the final selection can contain multiple elements.
	Multi bool    `json:"multi"`
	Error *errorT `json:"error"`
}

type transformURLT struct {
	Pattern         string  `json:"pattern"`
	MultiLine       bool    `json:"multiline"`
	CaseInsensitive bool    `json:"case_insensitive"`
	ToPattern       string  `json:"to_pattern"`
	Error           *errorT `json:"error"`
}

type errorT struct {
	Status      keybase1.ProofStatus
	Description string
}

func (x *errorT) UnmarshalJSON(b []byte) error {
	ss := []string{}
	err := json.Unmarshal(b, &ss)
	if err != nil {
		return err
	}
	if len(ss) != 2 {
		return fmt.Errorf("error desc must be of length 2")
	}
	status, ok := keybase1.ProofStatusMap[ss[0]]
	if !ok {
		return fmt.Errorf("unrecognized proof status '%v'", ss[0])
	}
	x.Status = keybase1.ProofStatus(status)
	x.Description = ss[1]
	return nil
}
<commit_msg>short circuit earlier, duplicate a bit less<commit_after>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package pvl

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/keybase/client/go/protocol/keybase1"
)

func parse(in string) (pvlT, error) {
	b := []byte(in)
	p := pvlT{}
	p.PvlVersion = -1
	p.Revision = -1

	err := json.Unmarshal(b, &p)
	if err != nil {
		return p, err
	}

	if p.PvlVersion == -1 {
		return p, fmt.Errorf("pvl_version required")
	}
	if p.Revision == -1 {
		return p, fmt.Errorf("revision required")
	}
	return p, nil
}

type pvlT struct {
	PvlVersion int `json:"pvl_version"`
	Revision   int `json:"revision"`
	// services is a map from service to a list of scripts.
	// each script is a list of instructions.
	Services servicesT `json:"services"`
}

type servicesT struct {
	Map map[keybase1.ProofType][]scriptT
}

func (x *servicesT) UnmarshalJSON(b []byte) error {
	// read as string map
	m := make(map[string][]scriptT)
	err := json.Unmarshal(b, &m)
	if err != nil {
		return err
	}
	// copy to ProofType map
	x.Map = make(map[keybase1.ProofType][]scriptT)
	for k, v := range m {
		t, ok := keybase1.ProofTypeMap[strings.ToUpper(k)]
		if ok {
			x.Map[t] = v
		}
		// Unrecognized proof types are discarded silently
		// So that old clients don't break if a new service is added
	}
	return nil
}

type scriptT struct {
	Instructions []instructionT
}

func (x *scriptT) UnmarshalJSON(b []byte) error {
	err := json.Unmarshal(b, &x.Instructions)
	for _, ins := range x.Instructions {
		if ins.variantsFilled() != 1 {
			return fmt.Errorf("exactly 1 variant must appear in instruction")
		}
	}
	return err
}

type instructionT struct {
	// Exactly one of these shall be non-nil
	// This list is duplicated to:
	// - instructionT.variantsFilled
	// - instructionT.Name
	// - stepInstruction
	// This invariant is enforced by scriptT.UnmarshalJSON
	AssertRegexMatch    *assertRegexMatchT    `json:"assert_regex_match,omitempty"`
	AssertFindBase64    *assertFindBase64T    `json:"assert_find_base64,omitempty"`
	WhitespaceNormalize *whitespaceNormalizeT `json:"whitespace_normalize,omitempty"`
	RegexCapture        *regexCaptureT        `json:"regex_capture,omitempty"`
	Fetch               *fetchT               `json:"fetch,omitempty"`
	SelectorJSON        *selectorJSONT        `json:"selector_json,omitempty"`
	SelectorCSS         *selectorCSST         `json:"selector_css,omitempty"`
	TransformURL        *transformURLT        `json:"transform_url,omitempty"`
}

func (ins *instructionT) variantsFilled() int {
	n := 0
	if ins.AssertRegexMatch != nil {
		n++
	}
	if ins.AssertFindBase64 != nil {
		n++
	}
	if ins.WhitespaceNormalize != nil {
		n++
	}
	if ins.RegexCapture != nil {
		n++
	}
	if ins.Fetch != nil {
		n++
	}
	if ins.SelectorJSON != nil {
		n++
	}
	if ins.SelectorCSS != nil {
		n++
	}
	if ins.TransformURL != nil {
		n++
	}
	return n
}

func (ins instructionT) Name() string {
	switch {
	case ins.AssertRegexMatch != nil:
		return string(cmdAssertRegexMatch)
	case ins.AssertFindBase64 != nil:
		return string(cmdAssertFindBase64)
	case ins.WhitespaceNormalize != nil:
		return string(cmdWhitespaceNormalize)
	case ins.RegexCapture != nil:
		return string(cmdRegexCapture)
	case ins.Fetch != nil:
		return string(cmdFetch)
	case ins.SelectorJSON != nil:
		return string(cmdSelectorJSON)
	case ins.SelectorCSS != nil:
		return string(cmdSelectorCSS)
	case ins.TransformURL != nil:
		return string(cmdTransformURL)
	}
	return "<invalid instruction>"
}

func (ins instructionT) String() string {
	return fmt.Sprintf("[ins %v]", ins.Name())
}

type assertRegexMatchT struct {
	Pattern         string  `json:"pattern"`
	CaseInsensitive bool    `json:"case_insensitive"`
	MultiLine       bool    `json:"multiline"`
	Error           *errorT `json:"error"`
}

type assertFindBase64T struct {
	Var   string  `json:"var"`
	Error *errorT `json:"error"`
}

type whitespaceNormalizeT struct {
	Error *errorT `json:"error"`
}

type regexCaptureT struct {
	Pattern         string  `json:"pattern"`
	MultiLine       bool    `json:"multiline"`
	CaseInsensitive bool    `json:"case_insensitive"`
	Error           *errorT `json:"error"`
}

type fetchT struct {
	Kind  string  `json:"kind"`
	Error *errorT `json:"error"`
}

type selectorJSONT struct {
	Selectors []interface{} `json:"selectors"`
	Error     *errorT       `json:"error"`
}

type selectorCSST struct {
	Selectors []interface{} `json:"selectors"`
	Attr      string        `json:"attr"`
	// Whether the final selection can contain multiple elements.
	Multi bool    `json:"multi"`
	Error *errorT `json:"error"`
}

type transformURLT struct {
	Pattern         string  `json:"pattern"`
	MultiLine       bool    `json:"multiline"`
	CaseInsensitive bool    `json:"case_insensitive"`
	ToPattern       string  `json:"to_pattern"`
	Error           *errorT `json:"error"`
}

type errorT struct {
	Status      keybase1.ProofStatus
	Description string
}

func (x *errorT) UnmarshalJSON(b []byte) error {
	ss := []string{}
	err := json.Unmarshal(b, &ss)
	if err != nil {
		return err
	}
	if len(ss) != 2 {
		return fmt.Errorf("error desc must be of length 2")
	}
	status, ok := keybase1.ProofStatusMap[ss[0]]
	if !ok {
		return fmt.Errorf("unrecognized proof status '%v'", ss[0])
	}
	x.Status = keybase1.ProofStatus(status)
	x.Description = ss[1]
	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright 2016 GitHub Inc.
	 See https://github.com/github/gh-ost/blob/master/LICENSE
*/

package sql

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type ColumnType int

const (
	UnknownColumnType   ColumnType = iota
	TimestampColumnType            = iota
	DateTimeColumnType             = iota
	EnumColumnType                 = iota
	MediumIntColumnType            = iota
	JSONColumnType                 = iota
	FloatColumnType                = iota
)

const maxMediumintUnsigned int32 = 16777215

type TimezoneConvertion struct {
	ToTimezone string
}

type Column struct {
	Name               string
	IsUnsigned         bool
	Charset            string
	Type               ColumnType
	timezoneConversion *TimezoneConvertion
}

func (this *Column) convertArg(arg interface{}) interface{} {
	if s, ok := arg.(string); ok {
		// string, charset conversion
		if encoding, ok := charsetEncodingMap[this.Charset]; ok {
			arg, _ = encoding.NewDecoder().String(s)
		}
		return arg
	}

	if this.IsUnsigned {
		if i, ok := arg.(int8); ok {
			return uint8(i)
		}
		if i, ok := arg.(int16); ok {
			return uint16(i)
		}
		if i, ok := arg.(int32); ok {
			if this.Type == MediumIntColumnType {
				// problem with mediumint is that it's a 3-byte type. There is no compatible golang type to match that.
				// So to convert from negative to positive we'd need to convert the value manually
				if i >= 0 {
					return i
				}
				return uint32(maxMediumintUnsigned + i + 1)
			}
			return uint32(i)
		}
		if i, ok := arg.(int64); ok {
			return strconv.FormatUint(uint64(i), 10)
		}
		if i, ok := arg.(int); ok {
			return uint(i)
		}
	}
	return arg
}

func NewColumns(names []string) []Column {
	result := make([]Column, len(names))
	for i := range names {
		result[i].Name = names[i]
	}
	return result
}

func ParseColumns(names string) []Column {
	namesArray := strings.Split(names, ",")
	return NewColumns(namesArray)
}

// ColumnsMap maps a column name onto its ordinal position
type ColumnsMap map[string]int

func NewEmptyColumnsMap() ColumnsMap {
	columnsMap := make(map[string]int)
	return ColumnsMap(columnsMap)
}

func NewColumnsMap(orderedColumns []Column) ColumnsMap {
	columnsMap := NewEmptyColumnsMap()
	for i, column := range orderedColumns {
		columnsMap[column.Name] = i
	}
	return columnsMap
}

// ColumnList makes for a named list of columns
type ColumnList struct {
	columns  []Column
	Ordinals ColumnsMap
}

// NewColumnList creates an object given ordered list of column names
func NewColumnList(names []string) *ColumnList {
	result := &ColumnList{
		columns: NewColumns(names),
	}
	result.Ordinals = NewColumnsMap(result.columns)
	return result
}

// ParseColumnList parses a comma delimited list of column names
func ParseColumnList(names string) *ColumnList {
	result := &ColumnList{
		columns: ParseColumns(names),
	}
	result.Ordinals = NewColumnsMap(result.columns)
	return result
}

func (this *ColumnList) Columns() []Column {
	return this.columns
}

func (this *ColumnList) Names() []string {
	names := make([]string, len(this.columns))
	for i := range this.columns {
		names[i] = this.columns[i].Name
	}
	return names
}

func (this *ColumnList) GetColumn(columnName string) *Column {
	if ordinal, ok := this.Ordinals[columnName]; ok {
		return &this.columns[ordinal]
	}
	return nil
}

func (this *ColumnList) SetUnsigned(columnName string) {
	this.GetColumn(columnName).IsUnsigned = true
}

func (this *ColumnList) IsUnsigned(columnName string) bool {
	return this.GetColumn(columnName).IsUnsigned
}

func (this *ColumnList) SetCharset(columnName string, charset string) {
	this.GetColumn(columnName).Charset = charset
}

func (this *ColumnList) GetCharset(columnName string) string {
	return this.GetColumn(columnName).Charset
}

func (this *ColumnList) SetColumnType(columnName string, columnType ColumnType) {
	this.GetColumn(columnName).Type = columnType
}

func (this *ColumnList) GetColumnType(columnName string) ColumnType {
	return this.GetColumn(columnName).Type
}

func (this *ColumnList) SetConvertDatetimeToTimestamp(columnName string, toTimezone string) {
	this.GetColumn(columnName).timezoneConversion = &TimezoneConvertion{ToTimezone: toTimezone}
}

func (this *ColumnList) HasTimezoneConversion(columnName string) bool {
	return this.GetColumn(columnName).timezoneConversion != nil
}

func (this *ColumnList) String() string {
	return strings.Join(this.Names(), ",")
}

func (this *ColumnList) Equals(other *ColumnList) bool {
	return reflect.DeepEqual(this.Columns, other.Columns)
}

func (this *ColumnList) EqualsByNames(other *ColumnList) bool {
	return reflect.DeepEqual(this.Names(), other.Names())
}

// IsSubsetOf returns 'true' when column names of this list are a subset of
// another list, in arbitrary order (order agnostic)
func (this *ColumnList) IsSubsetOf(other *ColumnList) bool {
	for _, column := range this.columns {
		if _, exists := other.Ordinals[column.Name]; !exists {
			return false
		}
	}
	return true
}

func (this *ColumnList) Len() int {
	return len(this.columns)
}

// UniqueKey is the combination of a key's name and columns
type UniqueKey struct {
	Name            string
	Columns         ColumnList
	HasNullable     bool
	IsAutoIncrement bool
}

// IsPrimary checks if this unique key is primary
func (this *UniqueKey) IsPrimary() bool {
	return this.Name == "PRIMARY"
}

func (this *UniqueKey) Len() int {
	return this.Columns.Len()
}

func (this *UniqueKey) String() string {
	description := this.Name
	if this.IsAutoIncrement {
		description = fmt.Sprintf("%s (auto_increment)", description)
	}
	return fmt.Sprintf("%s: %s; has nullable: %+v", description, this.Columns.Names(), this.HasNullable)
}

type ColumnValues struct {
	abstractValues []interface{}
	ValuesPointers []interface{}
}

func NewColumnValues(length int) *ColumnValues {
	result := &ColumnValues{
		abstractValues: make([]interface{}, length),
		ValuesPointers: make([]interface{}, length),
	}
	for i := 0; i < length; i++ {
		result.ValuesPointers[i] = &result.abstractValues[i]
	}

	return result
}

func ToColumnValues(abstractValues []interface{}) *ColumnValues {
	result := &ColumnValues{
		abstractValues: abstractValues,
		ValuesPointers: make([]interface{}, len(abstractValues)),
	}
	for i := 0; i < len(abstractValues); i++ {
		result.ValuesPointers[i] = &result.abstractValues[i]
	}

	return result
}

func (this *ColumnValues) AbstractValues() []interface{} {
	return this.abstractValues
}

func (this *ColumnValues) StringColumn(index int) string {
	val := this.AbstractValues()[index]
	if ints, ok := val.([]uint8); ok {
		return string(ints)
	}
	return fmt.Sprintf("%+v", val)
}

func (this *ColumnValues) String() string {
	stringValues := []string{}
	for i := range this.AbstractValues() {
		stringValues = append(stringValues, this.StringColumn(i))
	}
	return strings.Join(stringValues, ",")
}
<commit_msg>spelling: conversion<commit_after>/*
   Copyright 2016 GitHub Inc.
	 See https://github.com/github/gh-ost/blob/master/LICENSE
*/

package sql

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type ColumnType int

const (
	UnknownColumnType   ColumnType = iota
	TimestampColumnType            = iota
	DateTimeColumnType             = iota
	EnumColumnType                 = iota
	MediumIntColumnType            = iota
	JSONColumnType                 = iota
	FloatColumnType                = iota
)

const maxMediumintUnsigned int32 = 16777215

type TimezoneConversion struct {
	ToTimezone string
}

type Column struct {
	Name               string
	IsUnsigned         bool
	Charset            string
	Type               ColumnType
	timezoneConversion *TimezoneConversion
}

func (this *Column) convertArg(arg interface{}) interface{} {
	if s, ok := arg.(string); ok {
		// string, charset conversion
		if encoding, ok := charsetEncodingMap[this.Charset]; ok {
			arg, _ = encoding.NewDecoder().String(s)
		}
		return arg
	}

	if this.IsUnsigned {
		if i, ok := arg.(int8); ok {
			return uint8(i)
		}
		if i, ok := arg.(int16); ok {
			return uint16(i)
		}
		if i, ok := arg.(int32); ok {
			if this.Type == MediumIntColumnType {
				// problem with mediumint is that it's a 3-byte type. There is no compatible golang type to match that.
				// So to convert from negative to positive we'd need to convert the value manually
				if i >= 0 {
					return i
				}
				return uint32(maxMediumintUnsigned + i + 1)
			}
			return uint32(i)
		}
		if i, ok := arg.(int64); ok {
			return strconv.FormatUint(uint64(i), 10)
		}
		if i, ok := arg.(int); ok {
			return uint(i)
		}
	}
	return arg
}

func NewColumns(names []string) []Column {
	result := make([]Column, len(names))
	for i := range names {
		result[i].Name = names[i]
	}
	return result
}

func ParseColumns(names string) []Column {
	namesArray := strings.Split(names, ",")
	return NewColumns(namesArray)
}

// ColumnsMap maps a column name onto its ordinal position
type ColumnsMap map[string]int

func NewEmptyColumnsMap() ColumnsMap {
	columnsMap := make(map[string]int)
	return ColumnsMap(columnsMap)
}

func NewColumnsMap(orderedColumns []Column) ColumnsMap {
	columnsMap := NewEmptyColumnsMap()
	for i, column := range orderedColumns {
		columnsMap[column.Name] = i
	}
	return columnsMap
}

// ColumnList makes for a named list of columns
type ColumnList struct {
	columns  []Column
	Ordinals ColumnsMap
}

// NewColumnList creates an object given ordered list of column names
func NewColumnList(names []string) *ColumnList {
	result := &ColumnList{
		columns: NewColumns(names),
	}
	result.Ordinals = NewColumnsMap(result.columns)
	return result
}

// ParseColumnList parses a comma delimited list of column names
func ParseColumnList(names string) *ColumnList {
	result := &ColumnList{
		columns: ParseColumns(names),
	}
	result.Ordinals = NewColumnsMap(result.columns)
	return result
}

func (this *ColumnList) Columns() []Column {
	return this.columns
}

func (this *ColumnList) Names() []string {
	names := make([]string, len(this.columns))
	for i := range this.columns {
		names[i] = this.columns[i].Name
	}
	return names
}

func (this *ColumnList) GetColumn(columnName string) *Column {
	if ordinal, ok := this.Ordinals[columnName]; ok {
		return &this.columns[ordinal]
	}
	return nil
}

func (this *ColumnList) SetUnsigned(columnName string) {
	this.GetColumn(columnName).IsUnsigned = true
}

func (this *ColumnList) IsUnsigned(columnName string) bool {
	return this.GetColumn(columnName).IsUnsigned
}

func (this *ColumnList) SetCharset(columnName string, charset string) {
	this.GetColumn(columnName).Charset = charset
}

func (this *ColumnList) GetCharset(columnName string) string {
	return this.GetColumn(columnName).Charset
}

func (this *ColumnList) SetColumnType(columnName string, columnType ColumnType) {
	this.GetColumn(columnName).Type = columnType
}

func (this *ColumnList) GetColumnType(columnName string) ColumnType {
	return this.GetColumn(columnName).Type
}

func (this *ColumnList) SetConvertDatetimeToTimestamp(columnName string, toTimezone string) {
	this.GetColumn(columnName).timezoneConversion = &TimezoneConversion{ToTimezone: toTimezone}
}

func (this *ColumnList) HasTimezoneConversion(columnName string) bool {
	return this.GetColumn(columnName).timezoneConversion != nil
}

func (this *ColumnList) String() string {
	return strings.Join(this.Names(), ",")
}

func (this *ColumnList) Equals(other *ColumnList) bool {
	return reflect.DeepEqual(this.Columns, other.Columns)
}

func (this *ColumnList) EqualsByNames(other *ColumnList) bool {
	return reflect.DeepEqual(this.Names(), other.Names())
}

// IsSubsetOf returns 'true' when column names of this list are a subset of
// another list, in arbitrary order (order agnostic)
func (this *ColumnList) IsSubsetOf(other *ColumnList) bool {
	for _, column := range this.columns {
		if _, exists := other.Ordinals[column.Name]; !exists {
			return false
		}
	}
	return true
}

func (this *ColumnList) Len() int {
	return len(this.columns)
}

// UniqueKey is the combination of a key's name and columns
type UniqueKey struct {
	Name            string
	Columns         ColumnList
	HasNullable     bool
	IsAutoIncrement bool
}

// IsPrimary checks if this unique key is primary
func (this *UniqueKey) IsPrimary() bool {
	return this.Name == "PRIMARY"
}

func (this *UniqueKey) Len() int {
	return this.Columns.Len()
}

func (this *UniqueKey) String() string {
	description := this.Name
	if this.IsAutoIncrement {
		description = fmt.Sprintf("%s (auto_increment)", description)
	}
	return fmt.Sprintf("%s: %s; has nullable: %+v", description, this.Columns.Names(), this.HasNullable)
}

type ColumnValues struct {
	abstractValues []interface{}
	ValuesPointers []interface{}
}

func NewColumnValues(length int) *ColumnValues {
	result := &ColumnValues{
		abstractValues: make([]interface{}, length),
		ValuesPointers: make([]interface{}, length),
	}
	for i := 0; i < length; i++ {
		result.ValuesPointers[i] = &result.abstractValues[i]
	}

	return result
}

func ToColumnValues(abstractValues []interface{}) *ColumnValues {
	result := &ColumnValues{
		abstractValues: abstractValues,
		ValuesPointers: make([]interface{}, len(abstractValues)),
	}
	for i := 0; i < len(abstractValues); i++ {
		result.ValuesPointers[i] = &result.abstractValues[i]
	}

	return result
}

func (this *ColumnValues) AbstractValues() []interface{} {
	return this.abstractValues
}

func (this *ColumnValues) StringColumn(index int) string {
	val := this.AbstractValues()[index]
	if ints, ok := val.([]uint8); ok {
		return string(ints)
	}
	return fmt.Sprintf("%+v", val)
}

func (this *ColumnValues) String() string {
	stringValues := []string{}
	for i := range this.AbstractValues() {
		stringValues = append(stringValues, this.StringColumn(i))
	}
	return strings.Join(stringValues, ",")
}
<|endoftext|>


<commit_before>package trie

import (
	"fmt"
	"sort"

	"go.skia.org/infra/go/util"
)

// Trie is a struct used for efficient searching on sets of strings.
type Trie struct {
	root *trieNode
}

// New returns a Trie instance.
func New() *Trie {
	return &Trie{
		root: newTrieNode(),
	}
}

func sorted(s []string) []string {
	cpy := make([]string, len(s))
	copy(cpy, s)
	sort.Strings(cpy)
	return cpy
}

// Insert inserts the data into the trie with the given string keys.
func (t *Trie) Insert(strs []string, data interface{}) {
	t.root.Insert(sorted(strs), data)
}

// Delete removes the data from the trie.
func (t *Trie) Delete(strs []string, data interface{}) {
	t.root.Delete(sorted(strs), data)
}

// Search returns all inserted data which exactly matches the given string keys.
func (t *Trie) Search(strs []string) []interface{} {
	return t.root.Search(sorted(strs))
}

type searchContext struct {
	count int
	data  [][]interface{}
}

// SearchSubset returns all inserted data which matches a subset of the given
// string keys.
func (t *Trie) SearchSubset(strs []string) []interface{} {
	keys := make(map[string]bool, len(strs))
	for _, k := range strs {
		keys[k] = true
	}
	ctx := searchContext{
		count: 0,
		data:  [][]interface{}{},
	}
	t.root.SearchSubset(keys, &ctx)
	rv := make([]interface{}, ctx.count)
	idx := 0
	for _, d := range ctx.data {
		copy(rv[idx:idx+len(d)], d)
		idx += len(d)
	}
	return rv
}

// String returns a string representation of the Trie.
func (t *Trie) String() string {
	return fmt.Sprintf("Trie(%s)", t.root.String(0))
}

type trieNode struct {
	children map[string]*trieNode
	data     []interface{}
}

func newTrieNode() *trieNode {
	return &trieNode{
		children: map[string]*trieNode{},
		data:     []interface{}{},
	}
}

func (n *trieNode) Insert(strs []string, data interface{}) {
	if len(strs) == 0 {
		n.data = append(n.data, data)
	} else {
		child, ok := n.children[strs[0]]
		if !ok {
			child = newTrieNode()
			n.children[strs[0]] = child
		}
		child.Insert(strs[1:], data)
	}
}

func (n *trieNode) Delete(strs []string, data interface{}) {
	if len(strs) == 0 {
		idx := -1
		for i, v := range n.data {
			if v == data {
				idx = i
			}
		}
		if idx == -1 {
			return
		}
		n.data = append(n.data[:idx], n.data[idx+1:]...)
	} else if child, ok := n.children[strs[0]]; ok {
		child.Delete(strs[1:], data)
	}
}

func (n *trieNode) Search(strs []string) []interface{} {
	if len(strs) == 0 {
		return n.data
	} else {
		if child, ok := n.children[strs[0]]; ok {
			return child.Search(strs[1:])
		} else {
			return []interface{}{}
		}
	}
}

func (n *trieNode) SearchSubset(strs map[string]bool, ctx *searchContext) {
	ctx.count += len(n.data)
	ctx.data = append(ctx.data, n.data)
	for k, c := range n.children {
		if strs[k] {
			c.SearchSubset(strs, ctx)
		}
	}
}

func (n *trieNode) String(indent int) string {
	rv := fmt.Sprintf("Node(%v, {", n.data)
	if len(n.children) == 0 {
		return rv + "})"
	}
	rv += "\n"
	for k, c := range n.children {
		rv += fmt.Sprintf("%s\"%s\": %s,\n", util.RepeatJoin("  ", "", indent+1), k, c.String(indent+1))
	}
	rv += fmt.Sprintf("%s})", util.RepeatJoin("  ", "", indent))
	return rv
}
<commit_msg>Fix nondeterministic Trie.String()<commit_after>package trie

import (
	"fmt"
	"sort"

	"go.skia.org/infra/go/util"
)

// Trie is a struct used for efficient searching on sets of strings.
type Trie struct {
	root *trieNode
}

// New returns a Trie instance.
func New() *Trie {
	return &Trie{
		root: newTrieNode(),
	}
}

func sorted(s []string) []string {
	cpy := make([]string, len(s))
	copy(cpy, s)
	sort.Strings(cpy)
	return cpy
}

// Insert inserts the data into the trie with the given string keys.
func (t *Trie) Insert(strs []string, data interface{}) {
	t.root.Insert(sorted(strs), data)
}

// Delete removes the data from the trie.
func (t *Trie) Delete(strs []string, data interface{}) {
	t.root.Delete(sorted(strs), data)
}

// Search returns all inserted data which exactly matches the given string keys.
func (t *Trie) Search(strs []string) []interface{} {
	return t.root.Search(sorted(strs))
}

type searchContext struct {
	count int
	data  [][]interface{}
}

// SearchSubset returns all inserted data which matches a subset of the given
// string keys.
func (t *Trie) SearchSubset(strs []string) []interface{} {
	keys := make(map[string]bool, len(strs))
	for _, k := range strs {
		keys[k] = true
	}
	ctx := searchContext{
		count: 0,
		data:  [][]interface{}{},
	}
	t.root.SearchSubset(keys, &ctx)
	rv := make([]interface{}, ctx.count)
	idx := 0
	for _, d := range ctx.data {
		copy(rv[idx:idx+len(d)], d)
		idx += len(d)
	}
	return rv
}

// String returns a string representation of the Trie.
func (t *Trie) String() string {
	return fmt.Sprintf("Trie(%s)", t.root.String(0))
}

type trieNode struct {
	children map[string]*trieNode
	data     []interface{}
}

func newTrieNode() *trieNode {
	return &trieNode{
		children: map[string]*trieNode{},
		data:     []interface{}{},
	}
}

func (n *trieNode) Insert(strs []string, data interface{}) {
	if len(strs) == 0 {
		n.data = append(n.data, data)
	} else {
		child, ok := n.children[strs[0]]
		if !ok {
			child = newTrieNode()
			n.children[strs[0]] = child
		}
		child.Insert(strs[1:], data)
	}
}

func (n *trieNode) Delete(strs []string, data interface{}) {
	if len(strs) == 0 {
		idx := -1
		for i, v := range n.data {
			if v == data {
				idx = i
			}
		}
		if idx == -1 {
			return
		}
		n.data = append(n.data[:idx], n.data[idx+1:]...)
	} else if child, ok := n.children[strs[0]]; ok {
		child.Delete(strs[1:], data)
	}
}

func (n *trieNode) Search(strs []string) []interface{} {
	if len(strs) == 0 {
		return n.data
	} else {
		if child, ok := n.children[strs[0]]; ok {
			return child.Search(strs[1:])
		} else {
			return []interface{}{}
		}
	}
}

func (n *trieNode) SearchSubset(strs map[string]bool, ctx *searchContext) {
	ctx.count += len(n.data)
	ctx.data = append(ctx.data, n.data)
	for k, c := range n.children {
		if strs[k] {
			c.SearchSubset(strs, ctx)
		}
	}
}

func (n *trieNode) String(indent int) string {
	rv := fmt.Sprintf("Node(%v, {", n.data)
	if len(n.children) == 0 {
		return rv + "})"
	}
	rv += "\n"
	childKeys := make([]string, 0, len(n.children))
	for k, _ := range n.children {
		childKeys = append(childKeys, k)
	}
	sort.Strings(childKeys)
	for _, k := range childKeys {
		rv += fmt.Sprintf("%s\"%s\": %s,\n", util.RepeatJoin("  ", "", indent+1), k, n.children[k].String(indent+1))
	}
	rv += fmt.Sprintf("%s})", util.RepeatJoin("  ", "", indent))
	return rv
}
<|endoftext|>


<commit_before>package gosql

import "database/sql"

func SelectStruct(db Dbi, i interface{}, q string, params ...interface{}) error {
	rows, e := db.Query(q, params...)
	if e != nil {
		return e
	}
	defer rows.Close()
	if ok := rows.Next(); !ok {
		return sql.ErrNoRows
	}
	return UnmarshalRow(rows, i)
}

func SelectStructs(db Dbi, q string, i interface{}) error {
	rows, e := db.Query(q)
	if e != nil {
		return e
	}
	defer rows.Close()
	return UnmarshalRows(rows, i)
}

func SelectInt(db Dbi, q string, vars ...interface{}) (int, error) {
	var i int
	return i, db.QueryRow(q, vars...).Scan(&i)
}

func SelectString(db Dbi, q string) (string, error) {
	var v string
	return v, db.QueryRow(q).Scan(&v)
}

func SelectInts(db Dbi, q string) ([]int, error) {
	out := []int{}

	e := selectRaw(db, q, func(rows *sql.Rows) error {
		var i int
		e := rows.Scan(&i)
		if e != nil {
			return e
		}
		out = append(out, i)
		return nil
	})
	return out, e
}

func SelectStrings(db Dbi, q string) ([]string, error) {
	out := []string{}

	e := selectRaw(db, q, func(rows *sql.Rows) error {
		var v string
		e := rows.Scan(&v)
		if e != nil {
			return e
		}
		out = append(out, v)
		return nil
	})
	return out, e
}

func selectRaw(db Dbi, q string, c func(*sql.Rows) error) error {
	rows, e := db.Query(q)
	if e != nil {
		return e
	}
	defer rows.Close()
	for rows.Next() {
		e = c(rows)
		if e != nil {
			return e
		}
	}
	return rows.Err()
}
<commit_msg>add params support for SelectStructs<commit_after>package gosql

import "database/sql"

func SelectStruct(db Dbi, i interface{}, q string, params ...interface{}) error {
	rows, e := db.Query(q, params...)
	if e != nil {
		return e
	}
	defer rows.Close()
	if ok := rows.Next(); !ok {
		return sql.ErrNoRows
	}
	return UnmarshalRow(rows, i)
}

func SelectStructs(db Dbi, q string, i interface{}, params ...interface{}) error {
	rows, e := db.Query(q, params...)
	if e != nil {
		return e
	}
	defer rows.Close()
	return UnmarshalRows(rows, i)
}

func SelectInt(db Dbi, q string, vars ...interface{}) (int, error) {
	var i int
	return i, db.QueryRow(q, vars...).Scan(&i)
}

func SelectString(db Dbi, q string) (string, error) {
	var v string
	return v, db.QueryRow(q).Scan(&v)
}

func SelectInts(db Dbi, q string) ([]int, error) {
	out := []int{}

	e := selectRaw(db, q, func(rows *sql.Rows) error {
		var i int
		e := rows.Scan(&i)
		if e != nil {
			return e
		}
		out = append(out, i)
		return nil
	})
	return out, e
}

func SelectStrings(db Dbi, q string) ([]string, error) {
	out := []string{}

	e := selectRaw(db, q, func(rows *sql.Rows) error {
		var v string
		e := rows.Scan(&v)
		if e != nil {
			return e
		}
		out = append(out, v)
		return nil
	})
	return out, e
}

func selectRaw(db Dbi, q string, c func(*sql.Rows) error) error {
	rows, e := db.Query(q)
	if e != nil {
		return e
	}
	defer rows.Close()
	for rows.Next() {
		e = c(rows)
		if e != nil {
			return e
		}
	}
	return rows.Err()
}
<|endoftext|>


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

package gossip

import (
	"math"
	"reflect"
	"sort"
	"time"

	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
)

// GroupType indicates the bounds of the values encountered within the group.
type GroupType int

const (
	// MinGroup maintains minimum values for keys matching group prefix.
	MinGroup GroupType = iota
	// MaxGroup maintains maximum values for keys matching group prefix.
	MaxGroup
)

// group organizes a collection of Info objects sharing a common key
// prefix (prefix is defined up to the last period character '.').
// Groups maintain a limited-size set of Info objects with set
// inclusion determined by group type. Two types are implemented here:
//
// MinGroup, MaxGroup: maintain only minimum/maximum values added
// to group respectively.
type group struct {
	Prefix      string    // Key prefix for Info items in group
	Limit       int       // Maximum number of keys in group
	TypeOf      GroupType // Minimums or maximums of all values encountered
	Infos       infoMap   // Map of infos in group
	minTTLStamp int64     // Minimum of all infos' TTLs (Unix nanos)
	gatekeeper  *info     // Minimum or maximum value in infos map, depending on type
}

// groupMap is a map of group prefixes => *group.
type groupMap map[string]*group

// newGroup allocates and returns a new group with prefix, limit and type.
func newGroup(prefix string, limit int, typeOf GroupType) *group {
	if limit <= 0 {
		log.Fatalf("group size limit must be a positive number (%d <= 0)", limit)
	}
	return &group{
		Prefix:      prefix,
		Limit:       limit,
		TypeOf:      typeOf,
		minTTLStamp: math.MaxInt64,
		Infos:       make(infoMap),
	}
}

// shouldInclude returns true if the specified info should belong
// in the group according to the group type and the value.
func (g *group) shouldInclude(i *info) bool {
	if g.gatekeeper == nil {
		return true
	}
	switch g.TypeOf {
	case MinGroup:
		return i.less(g.gatekeeper)
	case MaxGroup:
		return !i.less(g.gatekeeper)
	default:
		log.Fatalf("unknown group type %d", g.TypeOf)
		return false
	}
}

// updateIncremental incrementally updates group stats based on a
// single info. Stats include minimum time-to-live (used to decide
// when compaction is possible), and updates gatekeeper (used to
// decide when to add to group).
func (g *group) updateIncremental(i *info) {
	if i.TTLStamp < g.minTTLStamp {
		g.minTTLStamp = i.TTLStamp
	}
	// Update gatekeeper if it's currently nil --or-- if info shouldn't
	// be included in group (i.e. it's the most extreme min/max).
	if g.gatekeeper == nil || !g.shouldInclude(i) {
		g.gatekeeper = i
	}
}

// update iterates through group infos and updates stats.
func (g *group) update() {
	g.minTTLStamp = math.MaxInt64
	g.gatekeeper = nil

	for _, i := range g.Infos {
		g.updateIncremental(i)
	}
}

// compact compacts the group infos slice by removing expired info objects.
// Returns true if compaction occurred and space is free.
func (g *group) compact() bool {
	now := time.Now().UnixNano()
	if g.minTTLStamp > now {
		return false
	}

	// Delete expired entries && update group stats.
	g.minTTLStamp = math.MaxInt64
	g.gatekeeper = nil
	for _, i := range g.Infos {
		if i.expired(now) {
			g.removeInternal(i)
		} else {
			g.updateIncremental(i)
		}
	}

	return len(g.Infos) < g.Limit
}

// addInternal adds info to group, incrementally updating group stats.
func (g *group) addInternal(i *info) {
	g.Infos[i.Key] = i
	g.updateIncremental(i)
}

// removeInternal removes info from group, updating group stats wholesale if necessary.
func (g *group) removeInternal(i *info) {
	delete(g.Infos, i.Key)
	if g.gatekeeper == i || g.minTTLStamp == i.TTLStamp {
		g.update()
	}
}

// getInfo returns an info by key.
func (g *group) getInfo(key string) *info {
	if i, ok := g.Infos[key]; ok {
		// Check TTL and discard if too old.
		now := time.Now().UnixNano()
		if i.expired(now) {
			g.removeInternal(i)
			return nil
		}
		return i
	}
	return nil
}

// infosAsSlice returns an slice of infos from group, sorted by value;
// sort order is dependent on group type (MinGroup: ascending,
// MaxGroup: descending).
func (g *group) infosAsSlice() infoSlice {
	now := time.Now().UnixNano()
	infos := make(infoSlice, 0, len(g.Infos))
	for _, i := range g.Infos {
		// Check TTL and discard if too old.
		if i.expired(now) {
			g.removeInternal(i)
		} else {
			infos = append(infos, i)
		}
	}
	switch g.TypeOf {
	case MinGroup:
		sort.Sort(infos)
	case MaxGroup:
		sort.Sort(sort.Reverse(infos))
	}
	return infos
}

// addInfo adds the info to the group if there is sufficient space
// or if the info has a value which guarantees it a spot within the
// group according to the group type.
//
// Returns contentsChanged bool and an error. contentsChanged is true
// if this info is new or updates the contents of a preexisting info
// with the same key; if just the timestamp or number of hops changes,
// contentsChanged is false. An error is returned if the info types
// don't match an existing group or the info was older than what we
// currently have.
func (g *group) addInfo(i *info) (contentsChanged bool, err error) {
	// First, see if info is already in the group. If so, and this
	// info timestamp is newer, remove existing info. If the
	// timestamps are equal (i.e. this is the same info), but hops
	// value of prospective info is lower, take the minimum of the two
	// Hops values.
	if existingInfo, ok := g.Infos[i.Key]; ok {
		if existingInfo.Timestamp < i.Timestamp ||
			(existingInfo.Timestamp == i.Timestamp && existingInfo.Hops > i.Hops) {
			g.removeInternal(existingInfo)
		} else {
			err = util.Errorf("current group info %+v newer than proposed info %+v", existingInfo, i)
			return
		}
		contentsChanged = !reflect.DeepEqual(existingInfo.Val, i.Val)
	} else {
		// No preexisting info means contentsChanged is true.
		contentsChanged = true
	}

	// If the group is not empty, verify types match by comparing to
	// gatekeeper.
	if g.gatekeeper != nil {
		t1 := reflect.TypeOf(i.Val).Kind()
		t2 := reflect.TypeOf(g.gatekeeper.Val).Kind()
		if t1 != t2 {
			err = util.Errorf("info %+v has type %s whereas group has type %s", i, t1, t2)
			return
		}
	}

	// If there's free space or we successfully compacted, add info.
	if len(g.Infos) < g.Limit || g.compact() {
		g.addInternal(i)
		return
	}

	// Group limit is reached. Check gatekeeper; if we should include,
	// it means we toss current gatekeeper and add info.
	if g.shouldInclude(i) {
		g.removeInternal(g.gatekeeper)
		g.addInternal(i)
		return
	}

	err = util.Errorf("info %+v not added to group", i)
	return
}
<commit_msg>Eliminate trivial differences between `addInfo`s<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package gossip

import (
	"math"
	"reflect"
	"sort"
	"time"

	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
)

// GroupType indicates the bounds of the values encountered within the group.
type GroupType int

const (
	// MinGroup maintains minimum values for keys matching group prefix.
	MinGroup GroupType = iota
	// MaxGroup maintains maximum values for keys matching group prefix.
	MaxGroup
)

// group organizes a collection of Info objects sharing a common key
// prefix (prefix is defined up to the last period character '.').
// Groups maintain a limited-size set of Info objects with set
// inclusion determined by group type. Two types are implemented here:
//
// MinGroup, MaxGroup: maintain only minimum/maximum values added
// to group respectively.
type group struct {
	Prefix      string    // Key prefix for Info items in group
	Limit       int       // Maximum number of keys in group
	TypeOf      GroupType // Minimums or maximums of all values encountered
	Infos       infoMap   // Map of infos in group
	minTTLStamp int64     // Minimum of all infos' TTLs (Unix nanos)
	gatekeeper  *info     // Minimum or maximum value in infos map, depending on type
}

// groupMap is a map of group prefixes => *group.
type groupMap map[string]*group

// newGroup allocates and returns a new group with prefix, limit and type.
func newGroup(prefix string, limit int, typeOf GroupType) *group {
	if limit <= 0 {
		log.Fatalf("group size limit must be a positive number (%d <= 0)", limit)
	}
	return &group{
		Prefix:      prefix,
		Limit:       limit,
		TypeOf:      typeOf,
		minTTLStamp: math.MaxInt64,
		Infos:       make(infoMap),
	}
}

// shouldInclude returns true if the specified info should belong
// in the group according to the group type and the value.
func (g *group) shouldInclude(i *info) bool {
	if g.gatekeeper == nil {
		return true
	}
	switch g.TypeOf {
	case MinGroup:
		return i.less(g.gatekeeper)
	case MaxGroup:
		return !i.less(g.gatekeeper)
	default:
		log.Fatalf("unknown group type %d", g.TypeOf)
		return false
	}
}

// updateIncremental incrementally updates group stats based on a
// single info. Stats include minimum time-to-live (used to decide
// when compaction is possible), and updates gatekeeper (used to
// decide when to add to group).
func (g *group) updateIncremental(i *info) {
	if i.TTLStamp < g.minTTLStamp {
		g.minTTLStamp = i.TTLStamp
	}
	// Update gatekeeper if it's currently nil --or-- if info shouldn't
	// be included in group (i.e. it's the most extreme min/max).
	if g.gatekeeper == nil || !g.shouldInclude(i) {
		g.gatekeeper = i
	}
}

// update iterates through group infos and updates stats.
func (g *group) update() {
	g.minTTLStamp = math.MaxInt64
	g.gatekeeper = nil

	for _, i := range g.Infos {
		g.updateIncremental(i)
	}
}

// compact compacts the group infos slice by removing expired info objects.
// Returns true if compaction occurred and space is free.
func (g *group) compact() bool {
	now := time.Now().UnixNano()
	if g.minTTLStamp > now {
		return false
	}

	// Delete expired entries && update group stats.
	g.minTTLStamp = math.MaxInt64
	g.gatekeeper = nil
	for _, i := range g.Infos {
		if i.expired(now) {
			g.removeInternal(i)
		} else {
			g.updateIncremental(i)
		}
	}

	return len(g.Infos) < g.Limit
}

// addInternal adds info to group, incrementally updating group stats.
func (g *group) addInternal(i *info) {
	g.Infos[i.Key] = i
	g.updateIncremental(i)
}

// removeInternal removes info from group, updating group stats wholesale if necessary.
func (g *group) removeInternal(i *info) {
	delete(g.Infos, i.Key)
	if g.gatekeeper == i || g.minTTLStamp == i.TTLStamp {
		g.update()
	}
}

// getInfo returns an info by key.
func (g *group) getInfo(key string) *info {
	if i, ok := g.Infos[key]; ok {
		// Check TTL and discard if too old.
		now := time.Now().UnixNano()
		if i.expired(now) {
			g.removeInternal(i)
			return nil
		}
		return i
	}
	return nil
}

// infosAsSlice returns an slice of infos from group, sorted by value;
// sort order is dependent on group type (MinGroup: ascending,
// MaxGroup: descending).
func (g *group) infosAsSlice() infoSlice {
	now := time.Now().UnixNano()
	infos := make(infoSlice, 0, len(g.Infos))
	for _, i := range g.Infos {
		// Check TTL and discard if too old.
		if i.expired(now) {
			g.removeInternal(i)
		} else {
			infos = append(infos, i)
		}
	}
	switch g.TypeOf {
	case MinGroup:
		sort.Sort(infos)
	case MaxGroup:
		sort.Sort(sort.Reverse(infos))
	}
	return infos
}

// addInfo adds the info to the group if there is sufficient space
// or if the info has a value which guarantees it a spot within the
// group according to the group type.
//
// Returns contentsChanged bool and an error. contentsChanged is true
// if this info is new or updates the contents of a preexisting info
// with the same key; if just the timestamp or number of hops changes,
// contentsChanged is false. An error is returned if the info types
// don't match an existing group or the info was older than what we
// currently have.
func (g *group) addInfo(i *info) (contentsChanged bool, err error) {
	// First, see if info is already in the group. If so, and this
	// info timestamp is newer, remove existing info. If the
	// timestamps are equal (i.e. this is the same info), but hops
	// value of prospective info is lower, take the minimum of the two
	// Hops values.
	if existingInfo, ok := g.Infos[i.Key]; ok {
		if i.Timestamp < existingInfo.Timestamp ||
			(i.Timestamp == existingInfo.Timestamp && i.Hops >= existingInfo.Hops) {
			err = util.Errorf("info %+v older than current info %+v", i, existingInfo)
			return
		}
		g.removeInternal(existingInfo)
		contentsChanged = !reflect.DeepEqual(existingInfo.Val, i.Val)
	} else {
		// No preexisting info means contentsChanged is true.
		contentsChanged = true
	}

	// If the group is not empty, verify types match by comparing to
	// gatekeeper.
	if g.gatekeeper != nil {
		t1 := reflect.TypeOf(i.Val).Kind()
		t2 := reflect.TypeOf(g.gatekeeper.Val).Kind()
		if t1 != t2 {
			err = util.Errorf("info %+v has type %s whereas group has type %s", i, t1, t2)
			return
		}
	}

	// If there's free space or we successfully compacted, add info.
	if len(g.Infos) < g.Limit || g.compact() {
		g.addInternal(i)
		return
	}

	// Group limit is reached. Check gatekeeper; if we should include,
	// it means we toss current gatekeeper and add info.
	if g.shouldInclude(i) {
		g.removeInternal(g.gatekeeper)
		g.addInternal(i)
		return
	}

	err = util.Errorf("info %+v not added to group", i)
	return
}
<|endoftext|>