<commit_before>package hepmc

// Event represents a record for MC generators (for use at any stage of generation)
//
// This type is intended as both a "container class" ( to store a MC
//  event for interface between MC generators and detector simulation )
//  and also as a "work in progress class" ( that could be used inside
//  a generator and modified as the event is built ).
type Event struct {
	SignalProcessId int     // id of the signal process
	EventNumber     int     // event number
	Mpi             int     // number of multi particle interactions
	Scale           float64 // energy scale,
	AlphaQCD        float64 // QCD coupling, see hep-ph/0109068
	AlphaQED        float64 // QED coupling, see hep-ph/0109068

	SignalVertex *Vertex      // signal vertex
	Beams        [2]*Particle // incoming beams
	Weights      Weights      // weights for this event. first weight is used by default for hit and miss
	RandomStates []int64      // container of random number generator states

	Vertices  map[int]*Vertex
	Particles map[int]*Particle

	CrossSection *CrossSection
	HeavyIon     *HeavyIon
	PdfInfo      *PdfInfo
	MomentumUnit MomentumUnit
	LengthUnit   LengthUnit
}

// Particle represents a generator particle within an event coming in/out of a vertex
//
// Particle is the basic building block of the event record
type Particle struct {
	Momentum      FourVector   // momentum vector
	PdgId         int          // id according to PDG convention
	Status        int          // status code as defined for HEPEVT
	Flow          Flow         // flow of this particle
	Polarization  Polarization // polarization of this particle
	ProdVertex    *Vertex      // pointer to production vertex (nil if vacuum or beam)
	EndVertex     *Vertex      // pointer to decay vertex (nil if not-decayed)
	Barcode       int          // unique identifier in the event
	GeneratedMass float64      // mass of this particle when it was generated
}

// Vertex represents a generator vertex within an event
// A vertex is indirectly (via particle "edges") linked to other
//   vertices ("nodes") to form a composite "graph"
type Vertex struct {
	Position     FourVector  // 4-vector of vertex [mm]
	ParticlesIn  []*Particle // all incoming particles
	ParticlesOut []*Particle // all outgoing particles
	Id           int         // vertex id
	Weights      Weights     // weights for this vertex
	Event        *Event      // pointer to event owning this vertex
	Barcode      int         // unique identifier in the event
}

type HeavyIon struct {
	Ncoll_hard                   int     // number of hard scatterings
	Npart_proj                   int     // number of projectile participants
	Npart_targ                   int     // number of target participants
	Ncoll                        int     // number of NN (nucleon-nucleon) collisions
	N_Nwounded_collisions        int     // Number of N-Nwounded collisions
	Nwounded_N_collisions        int     // Number of Nwounded-N collisons
	Nwounded_Nwounded_collisions int     // Number of Nwounded-Nwounded collisions
	Spectator_neutrons           int     // Number of spectators neutrons
	Spectator_protons            int     // Number of spectators protons
	Impact_parameter             float32 // Impact Parameter(fm) of collision
	Event_plane_angle            float32 // Azimuthal angle of event plane
	Eccentricity                 float32 // eccentricity of participating nucleons in the transverse plane (as in phobos nucl-ex/0510031)
	Sigma_inel_NN                float32 // nucleon-nucleon inelastic (including diffractive) cross-section
}

// CrossSection is used to store the generated cross section.
// This type is meant to be used to pass, on an event by event basis,
// the current best guess of the total cross section.
// It is expected that the final cross section will be stored elsewhere.
type CrossSection struct {
	Value float64 // value of the cross-section (in pb)
	Error float64 // error on the value of the cross-section (in pb)
	//IsSet bool
}

type PdfInfo struct {
	Id1      int     // flavour code of first parton
	Id2      int     // flavour code of second parton
	LHAPdf1  int     // LHA PDF id of first parton
	LHAPdf2  int     // LHA PDF id of second parton
	X1       float64 // fraction of beam momentum carried by first parton ("beam side")
	X2       float64 // fraction of beam momentum carried by second parton ("target side")
	ScalePDF float64 //  Q-scale used in evaluation of PDF's   (in GeV)
	Pdf1     float64 // PDF (id1, x1, Q)
	Pdf2     float64 // PDF (id2, x2, Q)
}

// Flow represents a particle's flow and keeps track of an arbitrary number of flow patterns within a graph (i.e. color flow, charge flow, lepton number flow,...)
//
// Flow patterns are coded with an integer, in the same manner as in Herwig.
// Note: 0 is NOT allowed as code index nor as flow code since it
//       is used to indicate null.
//
// This class can be used to keep track of flow patterns within
//  a graph. An example is color flow. If we have two quarks going through
//  an s-channel gluon to form two more quarks:
//
//  \q1       /q3   then we can keep track of the color flow with the
//   \_______/      HepMC::Flow class as follows:
//   /   g   \.
//  /q2       \q4
//
//  lets say the color flows from q2-->g-->q3  and q1-->g-->q4
//  the individual colors are unimportant, but the flow pattern is.
//  We can capture this flow by assigning the first pattern (q2-->g-->q3)
//  a unique (arbitrary) flow code 678 and the second pattern (q1-->g-->q4)
//  flow code 269  ( you can ask HepMC::Flow to choose
//  a unique code for you using Flow::set_unique_icode() ).
//  these codes with the particles as follows:
//    q2->flow().set_icode(1,678);
//    g->flow().set_icode(1,678);
//    q3->flow().set_icode(1,678);
//    q1->flow().set_icode(1,269);
//    g->flow().set_icode(2,269);
//    q4->flow().set_icode(1,269);
//  later on if we wish to know the color partner of q1 we can ask for a list
//  of all particles connected via this code to q1 which do have less than
//  2 color partners using:
//    vector<GenParticle*> result=q1->dangling_connected_partners(q1->icode(1),1,2);
//  this will return a list containing q1 and q4.
//    vector<GenParticle*> result=q1->connected_partners(q1->icode(1),1,2);
//  would return a list containing q1, g, and q4.
type Flow struct {
	Particle *Particle   // the particle this flow describes
	Icode    map[int]int // flow patterns as (code_index, icode)
}

type Polarization struct {
	Theta float64 // polar angle of polarization in radians [0, math.Pi)
	Phi   float64 // azimuthal angle of polarization in radians [0, 2*math.Pi)
}

type Weights struct {
	Slice []float64      // the slice of weight values
	Map   map[string]int // the map of name->index-in-the-slice
}

func (w Weights) At(n string) float64 {
	idx, ok := w.Map[n]
	if ok {
		return w.Slice[idx]
	}
	panic("hepmc.Weights.At: invalid name [" + n + "]")
}

func NewWeights() Weights {
	return Weights{
		Slice: make([]float64, 0, 1),
		Map:   make(map[string]int),
	}
}

// EOF
<commit_msg>hepmc: add (beginning of) API to create hepmc.Event by hand<commit_after>package hepmc

import (
	"errors"
	"fmt"
	"io"
)

var errNilVtx = errors.New("hepmc: nil Vertex")
var errNilParticle = errors.New("hepmc: nil Particle")

// Event represents a record for MC generators (for use at any stage of generation)
//
// This type is intended as both a "container class" ( to store a MC
//  event for interface between MC generators and detector simulation )
//  and also as a "work in progress class" ( that could be used inside
//  a generator and modified as the event is built ).
type Event struct {
	SignalProcessId int     // id of the signal process
	EventNumber     int     // event number
	Mpi             int     // number of multi particle interactions
	Scale           float64 // energy scale,
	AlphaQCD        float64 // QCD coupling, see hep-ph/0109068
	AlphaQED        float64 // QED coupling, see hep-ph/0109068

	SignalVertex *Vertex      // signal vertex
	Beams        [2]*Particle // incoming beams
	Weights      Weights      // weights for this event. first weight is used by default for hit and miss
	RandomStates []int64      // container of random number generator states

	Vertices  map[int]*Vertex
	Particles map[int]*Particle

	CrossSection *CrossSection
	HeavyIon     *HeavyIon
	PdfInfo      *PdfInfo
	MomentumUnit MomentumUnit
	LengthUnit   LengthUnit
}

// AddVertex adds a vertex to this event
func (evt *Event) AddVertex(vtx *Vertex) error {
	if vtx == nil {
		return errNilVtx
	}
	if vtx.Event != nil && vtx.Event != evt {
		//TODO: warn and remove from previous event
	}
	return vtx.set_parent_event(evt)
}

func (evt *Event) Print(w io.Writer) error {
	var err error
	_, err = fmt.Fprintf(
		w,
		"________________________________________________________________________________\n")
	if err != nil {
		return err
	}

	sig_vtx := 0
	if evt.SignalVertex != nil {
		sig_vtx = evt.SignalVertex.Barcode
	}
	_, err = fmt.Fprintf(
		w,
		"GenEvent: #%04d ID=%5d SignalProcessGenVertex Barcode: %d\n",
		evt.EventNumber,
		evt.SignalProcessId,
		sig_vtx,
	)
	if err != nil {
		return err
	}
	return err
}

// Particle represents a generator particle within an event coming in/out of a vertex
//
// Particle is the basic building block of the event record
type Particle struct {
	Momentum      FourVector   // momentum vector
	PdgId         int          // id according to PDG convention
	Status        int          // status code as defined for HEPEVT
	Flow          Flow         // flow of this particle
	Polarization  Polarization // polarization of this particle
	ProdVertex    *Vertex      // pointer to production vertex (nil if vacuum or beam)
	EndVertex     *Vertex      // pointer to decay vertex (nil if not-decayed)
	Barcode       int          // unique identifier in the event
	GeneratedMass float64      // mass of this particle when it was generated
}

// Vertex represents a generator vertex within an event
// A vertex is indirectly (via particle "edges") linked to other
//   vertices ("nodes") to form a composite "graph"
type Vertex struct {
	Position     FourVector  // 4-vector of vertex [mm]
	ParticlesIn  []*Particle // all incoming particles
	ParticlesOut []*Particle // all outgoing particles
	Id           int         // vertex id
	Weights      Weights     // weights for this vertex
	Event        *Event      // pointer to event owning this vertex
	Barcode      int         // unique identifier in the event
}

func (vtx *Vertex) set_parent_event(evt *Event) error {
	var err error
	orig_evt := vtx.Event
	vtx.Event = evt
	if orig_evt == evt {
		return err
	}
	if evt != nil {
		evt.Vertices[vtx.Barcode] = vtx
	}
	if orig_evt != nil {
		delete(orig_evt.Vertices, vtx.Barcode)
	}
	// we also need to loop over all the particles which are owned by
	// this vertex and remove their barcodes from the old event.
	for _, p := range vtx.ParticlesIn {
		if p.ProdVertex == nil {
			if evt != nil {
				evt.Particles[p.Barcode] = p
			}
			if orig_evt != nil {
				delete(orig_evt.Particles, p.Barcode)
			}
		}
	}

	for _, p := range vtx.ParticlesOut {
		if evt != nil {
			evt.Particles[p.Barcode] = p
		}
		if orig_evt != nil {
			delete(orig_evt.Particles, p.Barcode)
		}
	}
	return err
}

// AddParticleIn adds a particle to the list of in-coming particles to this vertex
func (vtx *Vertex) AddParticleIn(p *Particle) error {
	var err error
	if p == nil {
		return errNilParticle
	}
	// if p had a decay vertex, remove it from that vertex's list
	if p.EndVertex != nil {
		err = p.EndVertex.remove_particle_in(p)
		if err != nil {
			return err
		}
	}
	// make sure we don't add it twice...
	err = vtx.remove_particle_in(p)
	if err != nil {
		return err
	}
	p.EndVertex = vtx
	vtx.ParticlesIn = append(vtx.ParticlesIn, p)
	return err
}

// AddParticleOut adds a particle to the list of out-going particles to this vertex
func (vtx *Vertex) AddParticleOut(p *Particle) error {
	var err error
	if p == nil {
		return errNilParticle
	}
	// if p had a production vertex, remove it from that vertex's list
	if p.ProdVertex != nil {
		err = p.ProdVertex.remove_particle_out(p)
		if err != nil {
			return err
		}
	}
	// make sure we don't add it twice...
	err = vtx.remove_particle_out(p)
	if err != nil {
		return err
	}
	p.ProdVertex = vtx
	vtx.ParticlesOut = append(vtx.ParticlesOut, p)
	return err
}

func (vtx *Vertex) remove_particle_in(p *Particle) error {
	var err error
	nparts := len(vtx.ParticlesIn)
	switch nparts {
	case 0:
		//FIXME: logical error ?
		return err
	case 1:
		vtx.ParticlesIn = make([]*Particle, 0)
		return err
	}
	parts := make([]*Particle, 0, nparts-1)
	for _, pp := range vtx.ParticlesIn {
		if pp == p {
			continue
		}
		parts = append(parts, pp)
	}
	vtx.ParticlesIn = parts
	return err
}

func (vtx *Vertex) remove_particle_out(p *Particle) error {
	var err error
	nparts := len(vtx.ParticlesOut)
	switch nparts {
	case 0:
		//FIXME: logical error ?
		return err
	case 1:
		vtx.ParticlesOut = make([]*Particle, 0)
		return err
	}
	parts := make([]*Particle, 0, nparts-1)
	for _, pp := range vtx.ParticlesOut {
		if pp == p {
			continue
		}
		parts = append(parts, pp)
	}
	vtx.ParticlesOut = parts
	return err
}

type HeavyIon struct {
	Ncoll_hard                   int     // number of hard scatterings
	Npart_proj                   int     // number of projectile participants
	Npart_targ                   int     // number of target participants
	Ncoll                        int     // number of NN (nucleon-nucleon) collisions
	N_Nwounded_collisions        int     // Number of N-Nwounded collisions
	Nwounded_N_collisions        int     // Number of Nwounded-N collisons
	Nwounded_Nwounded_collisions int     // Number of Nwounded-Nwounded collisions
	Spectator_neutrons           int     // Number of spectators neutrons
	Spectator_protons            int     // Number of spectators protons
	Impact_parameter             float32 // Impact Parameter(fm) of collision
	Event_plane_angle            float32 // Azimuthal angle of event plane
	Eccentricity                 float32 // eccentricity of participating nucleons in the transverse plane (as in phobos nucl-ex/0510031)
	Sigma_inel_NN                float32 // nucleon-nucleon inelastic (including diffractive) cross-section
}

// CrossSection is used to store the generated cross section.
// This type is meant to be used to pass, on an event by event basis,
// the current best guess of the total cross section.
// It is expected that the final cross section will be stored elsewhere.
type CrossSection struct {
	Value float64 // value of the cross-section (in pb)
	Error float64 // error on the value of the cross-section (in pb)
	//IsSet bool
}

type PdfInfo struct {
	Id1      int     // flavour code of first parton
	Id2      int     // flavour code of second parton
	LHAPdf1  int     // LHA PDF id of first parton
	LHAPdf2  int     // LHA PDF id of second parton
	X1       float64 // fraction of beam momentum carried by first parton ("beam side")
	X2       float64 // fraction of beam momentum carried by second parton ("target side")
	ScalePDF float64 //  Q-scale used in evaluation of PDF's   (in GeV)
	Pdf1     float64 // PDF (id1, x1, Q)
	Pdf2     float64 // PDF (id2, x2, Q)
}

// Flow represents a particle's flow and keeps track of an arbitrary number of flow patterns within a graph (i.e. color flow, charge flow, lepton number flow,...)
//
// Flow patterns are coded with an integer, in the same manner as in Herwig.
// Note: 0 is NOT allowed as code index nor as flow code since it
//       is used to indicate null.
//
// This class can be used to keep track of flow patterns within
//  a graph. An example is color flow. If we have two quarks going through
//  an s-channel gluon to form two more quarks:
//
//  \q1       /q3   then we can keep track of the color flow with the
//   \_______/      HepMC::Flow class as follows:
//   /   g   \.
//  /q2       \q4
//
//  lets say the color flows from q2-->g-->q3  and q1-->g-->q4
//  the individual colors are unimportant, but the flow pattern is.
//  We can capture this flow by assigning the first pattern (q2-->g-->q3)
//  a unique (arbitrary) flow code 678 and the second pattern (q1-->g-->q4)
//  flow code 269  ( you can ask HepMC::Flow to choose
//  a unique code for you using Flow::set_unique_icode() ).
//  these codes with the particles as follows:
//    q2->flow().set_icode(1,678);
//    g->flow().set_icode(1,678);
//    q3->flow().set_icode(1,678);
//    q1->flow().set_icode(1,269);
//    g->flow().set_icode(2,269);
//    q4->flow().set_icode(1,269);
//  later on if we wish to know the color partner of q1 we can ask for a list
//  of all particles connected via this code to q1 which do have less than
//  2 color partners using:
//    vector<GenParticle*> result=q1->dangling_connected_partners(q1->icode(1),1,2);
//  this will return a list containing q1 and q4.
//    vector<GenParticle*> result=q1->connected_partners(q1->icode(1),1,2);
//  would return a list containing q1, g, and q4.
type Flow struct {
	Particle *Particle   // the particle this flow describes
	Icode    map[int]int // flow patterns as (code_index, icode)
}

type Polarization struct {
	Theta float64 // polar angle of polarization in radians [0, math.Pi)
	Phi   float64 // azimuthal angle of polarization in radians [0, 2*math.Pi)
}

type Weights struct {
	Slice []float64      // the slice of weight values
	Map   map[string]int // the map of name->index-in-the-slice
}

func (w Weights) At(n string) float64 {
	idx, ok := w.Map[n]
	if ok {
		return w.Slice[idx]
	}
	panic("hepmc.Weights.At: invalid name [" + n + "]")
}

func NewWeights() Weights {
	return Weights{
		Slice: make([]float64, 0, 1),
		Map:   make(map[string]int),
	}
}

// EOF
<|endoftext|>


<commit_before>//
// Laser Range Finder
// image.go
//
// Cole Smith - css@nyu.edu
// Eric Lin   - eric.lin@nyu.edu
// LICENSE: Apache 2.0
//

package rangefinder

import (
	"fmt"
	"image"
	"image/color"
	"math"
)

// Defines an image as a two dimensional array of hues
// from the HSV colorspace
type ImageMatrix struct {
	width  int
	height int
	//image  [][]float64
	image [][]*Pixel
}

// Generates a new ImageMatrix struct given an input
// image of type image.RGBA
func NewImageMatrix(inputImage *image.RGBA) *ImageMatrix {
	// Get Image width and height
	bounds := inputImage.Bounds()
	width := bounds.Max.X
	height := bounds.Max.Y

	// Fill the image 2D slice with hues
	image := make([][]*Pixel, height)
	for i := range image {
		image[i] = make([]*Pixel, width)
		for j := range image[i] {
			pixel := getHSVFromRGBA(inputImage.At(i, j))
			image[i][j] = pixel
		}
	}
	return &ImageMatrix{width, height, image}
}

// Defines a new image in binary greyscale using bool values
type MonoImageMatrix struct {
	Width         int
	Height        int
	ValueTreshold float64
	Image         [][]bool
}

// Generates a new MonoImageMatrix struct given an image of type image.RGBA,
// and the treshold at which the Value (Lume) of an image is considered a 1
// or a 0 such that:  1 <- pixel >= valueThreshold, 0 <- pixel < valueThreshold
func NewMonoImageMatrix(inputImage *image.RGBA, valueThreshold float64) *MonoImageMatrix {
	// Get Image width and height
	bounds := inputImage.Bounds()
	width := bounds.Max.X
	height := bounds.Max.Y

	image := make([][]bool, height)
	for i := range image {
		image[i] = make([]bool, width)
		for j := range image[i] {
			val := getHSVFromRGBA(inputImage.At(j, i)).val
			image[i][j] = val >= valueThreshold
		}
	}
	return &MonoImageMatrix{width, height, valueThreshold, image}
}

// Returns an empty greyscale image of width and height
// Defaults to all pixels false and a valueThreshold of 0
func NewEmptyMonoImageMatrix(width, height int) *MonoImageMatrix {
	image := make([][]bool, height)
	for i := range image {
		image[i] = make([]bool, width)
		for j := range image[i] {
			image[i][j] = false
		}
	}
	return &MonoImageMatrix{width, height, 0, image}
}

// Converts an ImageMatrix to a MonoImageMatrix using value thresholding
func (image ImageMatrix) ConvertToMonoImageMatrix(valueThreshold float64) *MonoImageMatrix {
	mono := make([][]bool, image.height)
	for i, _ := range mono {
		mono[i] = make([]bool, image.width)
		for j, _ := range mono[i] {
			val := image.image[i][j].val
			mono[i][j] = val >= valueThreshold
		}
	}
	return &MonoImageMatrix{image.width, image.height, valueThreshold, mono}
}

// Binds the pixel offset of the laser dot from the center plane
// of the image to a specified inital distance of units.
// Example: (image, 0.64, 1, "meters")
func Calibrate(image ImageMatrix, laserHue float64, initialDistance int, unitSuffix string) {
}

// Runs the image through a filter pass, to isolate the laser dot in the
// image by decreasing luminosity and apply edge detection
func (image ImageMatrix) filterImage() ImageMatrix {
	return image
}

// Iterates through image array to detect the laser dot. The pixels that
// match the hue, plus or minus the threshold value, will be marked true
// on a binary image.
func detectDotInImage(image ImageMatrix, laserHue int) MonoImageMatrix {
	dotImage := NewEmptyMonoImageMatrix(image.width, image.height)
	return *dotImage
}

// Returns the centroid of the marked pixel cluster of a binary image
func getCentroid(monoImage MonoImageMatrix) Pixel {
	var centroid Pixel
	return centroid
}

// A pixel for an image defined in the
// HSV colorspace
type Pixel struct {
	hue float64
	sat float64
	val float64
}

// Returns a Hue angle as a float64 from an RGBA Color
func getHSVFromRGBA(rgba color.Color) *Pixel {
	//Get RGB values
	red, green, blue, _ := rgba.RGBA()
	r := float64(red)
	g := float64(green)
	b := float64(blue)

	//Set up computed variables
	var hue float64 = 0.0
	var sat float64 = 0.0
	var val float64 = 0.0
	var d float64 = 0.0
	var h float64 = 0.0

	fmt.Println(r, g, b)

	//Standardize rgb values
	r = r / 255
	g = g / 255
	b = b / 255

	//Get min and max for RGB
	min := math.Min(math.Min(r, g), b)
	max := math.Max(math.Max(r, g), b)

	//If min is equal to max, we can assume it is black and white
	if min == max {
		return &Pixel{0, 0, min}
	}

	//Get delta for max and min
	if r == min {
		d = g - b
		h = 3
	} else {
		if b == min {
			d = r - g
			h = 1
		} else {
			d = b - r
			h = 5
		}
	}

	hue = 60 * (h - d/(max-min))
	sat = (max - min) / max
	val = max

	fmt.Println(val)

	return &Pixel{hue, sat, val}
}

//// Returns the Value (Lume) as a float64 from an RGBA Color
//func getValueFromRGBA(rgba color.Color) float64 {
//red, green, blue, _ := rgba.RGBA()
//r := float64(red)
//g := float64(green)
//b := float64(blue)

//return math.Max(math.Max(r, g), b)
//}
<commit_msg>removed print statements<commit_after>//
// Laser Range Finder
// image.go
//
// Cole Smith - css@nyu.edu
// Eric Lin   - eric.lin@nyu.edu
// LICENSE: Apache 2.0
//

package rangefinder

import (
	"image"
	"image/color"
	"math"
)

// Defines an image as a two dimensional array of hues
// from the HSV colorspace
type ImageMatrix struct {
	width  int
	height int
	//image  [][]float64
	image [][]*Pixel
}

// Generates a new ImageMatrix struct given an input
// image of type image.RGBA
func NewImageMatrix(inputImage *image.RGBA) *ImageMatrix {
	// Get Image width and height
	bounds := inputImage.Bounds()
	width := bounds.Max.X
	height := bounds.Max.Y

	// Fill the image 2D slice with hues
	image := make([][]*Pixel, height)
	for i := range image {
		image[i] = make([]*Pixel, width)
		for j := range image[i] {
			pixel := getHSVFromRGBA(inputImage.At(i, j))
			image[i][j] = pixel
		}
	}
	return &ImageMatrix{width, height, image}
}

// Defines a new image in binary greyscale using bool values
type MonoImageMatrix struct {
	Width         int
	Height        int
	ValueTreshold float64
	Image         [][]bool
}

// Generates a new MonoImageMatrix struct given an image of type image.RGBA,
// and the treshold at which the Value (Lume) of an image is considered a 1
// or a 0 such that:  1 <- pixel >= valueThreshold, 0 <- pixel < valueThreshold
func NewMonoImageMatrix(inputImage *image.RGBA, valueThreshold float64) *MonoImageMatrix {
	// Get Image width and height
	bounds := inputImage.Bounds()
	width := bounds.Max.X
	height := bounds.Max.Y

	image := make([][]bool, height)
	for i := range image {
		image[i] = make([]bool, width)
		for j := range image[i] {
			val := getHSVFromRGBA(inputImage.At(j, i)).val
			image[i][j] = val >= valueThreshold
		}
	}
	return &MonoImageMatrix{width, height, valueThreshold, image}
}

// Returns an empty greyscale image of width and height
// Defaults to all pixels false and a valueThreshold of 0
func NewEmptyMonoImageMatrix(width, height int) *MonoImageMatrix {
	image := make([][]bool, height)
	for i := range image {
		image[i] = make([]bool, width)
		for j := range image[i] {
			image[i][j] = false
		}
	}
	return &MonoImageMatrix{width, height, 0, image}
}

// Converts an ImageMatrix to a MonoImageMatrix using value thresholding
func (image ImageMatrix) ConvertToMonoImageMatrix(valueThreshold float64) *MonoImageMatrix {
	mono := make([][]bool, image.height)
	for i, _ := range mono {
		mono[i] = make([]bool, image.width)
		for j, _ := range mono[i] {
			val := image.image[i][j].val
			mono[i][j] = val >= valueThreshold
		}
	}
	return &MonoImageMatrix{image.width, image.height, valueThreshold, mono}
}

// Binds the pixel offset of the laser dot from the center plane
// of the image to a specified inital distance of units.
// Example: (image, 0.64, 1, "meters")
func Calibrate(image ImageMatrix, laserHue float64, initialDistance int, unitSuffix string) {
}

// Runs the image through a filter pass, to isolate the laser dot in the
// image by decreasing luminosity and apply edge detection
func (image ImageMatrix) filterImage() ImageMatrix {
	return image
}

// Iterates through image array to detect the laser dot. The pixels that
// match the hue, plus or minus the threshold value, will be marked true
// on a binary image.
func detectDotInImage(image ImageMatrix, laserHue int) MonoImageMatrix {
	dotImage := NewEmptyMonoImageMatrix(image.width, image.height)
	return *dotImage
}

// Returns the centroid of the marked pixel cluster of a binary image
func getCentroid(monoImage MonoImageMatrix) Pixel {
	var centroid Pixel
	return centroid
}

// A pixel for an image defined in the
// HSV colorspace
type Pixel struct {
	hue float64
	sat float64
	val float64
}

// Returns a Hue angle as a float64 from an RGBA Color
func getHSVFromRGBA(rgba color.Color) *Pixel {
	//Get RGB values
	red, green, blue, _ := rgba.RGBA()
	r := float64(red)
	g := float64(green)
	b := float64(blue)

	//Set up computed variables
	var hue float64 = 0.0
	var sat float64 = 0.0
	var val float64 = 0.0
	var d float64 = 0.0
	var h float64 = 0.0

	//Standardize rgb values
	r = r / 65535.0
	g = g / 65535.0
	b = b / 65535.0

	//Get min and max for RGB
	min := math.Min(math.Min(r, g), b)
	max := math.Max(math.Max(r, g), b)

	//If min is equal to max, we can assume it is black and white
	if min == max {
		return &Pixel{0, 0, min}
	}

	//Get delta for max and min
	if r == min {
		d = g - b
		h = 3
	} else {
		if b == min {
			d = r - g
			h = 1
		} else {
			d = b - r
			h = 5
		}
	}

	hue = 60 * (h - d/(max-min))
	sat = (max - min) / max
	val = max

	return &Pixel{hue, sat, val}
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten

import (
	"github.com/hajimehoshi/ebiten/internal"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
	"image"
	"image/color"
)

type innerImage struct {
	framebuffer *opengl.Framebuffer
	texture     *opengl.Texture
}

func newInnerImage(texture *opengl.Texture) (*innerImage, error) {
	framebuffer, err := opengl.NewFramebufferFromTexture(texture)
	if err != nil {
		return nil, err
	}
	return &innerImage{framebuffer, texture}, nil
}

func (i *innerImage) size() (width, height int) {
	return i.framebuffer.Size()
}

func (i *innerImage) Clear() error {
	return i.Fill(color.Transparent)
}

func (i *innerImage) Fill(clr color.Color) error {
	if err := i.framebuffer.SetAsViewport(); err != nil {
		return err
	}
	r, g, b, a := internal.RGBA(clr)
	opengl.Clear(r, g, b, a)
	return nil
}

func (i *innerImage) drawImage(img *innerImage, options *DrawImageOptions) error {
	if options == nil {
		options = &DrawImageOptions{}
	}
	dsts := options.DstParts
	srcs := options.SrcParts
	if srcs == nil || dsts == nil {
		w, h := img.size()
		dsts = []image.Rectangle{
			image.Rect(0, 0, w, h),
		}
		srcs = []image.Rectangle{
			image.Rect(0, 0, w, h),
		}
	}
	geo := options.GeometryMatrix
	if geo == nil {
		i := GeometryMatrixI()
		geo = &i
	}
	clr := options.ColorMatrix
	if clr == nil {
		i := ColorMatrixI()
		clr = &i
	}

	if err := i.framebuffer.SetAsViewport(); err != nil {
		return err
	}
	w, h := img.texture.Size()
	quads := textureQuads(dsts, srcs, w, h)
	projectionMatrix := i.framebuffer.ProjectionMatrix()
	shader.DrawTexture(img.texture.Native(), projectionMatrix, quads, geo, clr)
	return nil
}

func u(x float64, width int) float32 {
	return float32(x) / float32(internal.NextPowerOf2Int(width))
}

func v(y float64, height int) float32 {
	return float32(y) / float32(internal.NextPowerOf2Int(height))
}

func textureQuads(dsts, srcs []image.Rectangle, width, height int) []shader.TextureQuad {
	l := len(dsts)
	if len(srcs) < l {
		l = len(srcs)
	}
	quads := make([]shader.TextureQuad, 0, l)
	for i := 0; i < l; i++ {
		dst, src := dsts[i], srcs[i]
		x1 := float32(dst.Min.X)
		x2 := float32(dst.Max.X)
		y1 := float32(dst.Min.Y)
		y2 := float32(dst.Max.Y)
		u1 := u(float64(src.Min.X), width)
		u2 := u(float64(src.Max.X), width)
		v1 := v(float64(src.Min.Y), height)
		v2 := v(float64(src.Max.Y), height)
		quad := shader.TextureQuad{x1, x2, y1, y2, u1, u2, v1, v2}
		quads = append(quads, quad)
	}
	return quads
}

type syncer interface {
	Sync(func())
}

// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
type Image struct {
	syncer syncer
	inner  *innerImage
	pixels []uint8
}

// Size returns the size of the image.
func (i *Image) Size() (width, height int) {
	return i.inner.size()
}

// Clear resets the pixels of the image into 0.
func (i *Image) Clear() (err error) {
	i.pixels = nil
	i.syncer.Sync(func() {
		err = i.inner.Clear()
	})
	return
}

// Fill fills the image with a solid color.
func (i *Image) Fill(clr color.Color) (err error) {
	i.pixels = nil
	i.syncer.Sync(func() {
		err = i.inner.Fill(clr)
	})
	return
}

// DrawImage draws the given image on the receiver image.
// This method accepts the parts of the given image at the parts of the destination as the options.
// After determining parts to draw, this applies the geometry matrix and the color matrix as the options.
//
// If you want to draw a whole image simply, use DrawWholeImage.
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) {
	return i.drawImage(image.inner, options)
}

// DrawImageAt draws the given image on the receiver image at the position (x, y).
//
// If a geometry matrix is specified, the geometry matrix is applied ahead of the image is translated by (x, y).
func (i *Image) DrawImageAt(image *Image, x, y int, options *DrawImageOptions) (err error) {
	if options == nil {
		options = &DrawImageOptions{}
	}
	if options.GeometryMatrix == nil {
		geo := TranslateGeometry(float64(x), float64(y))
		options.GeometryMatrix = &geo
	} else {
		options.GeometryMatrix.Concat(TranslateGeometry(float64(x), float64(y)))
	}
	return i.drawImage(image.inner, options)
}

func (i *Image) drawImage(image *innerImage, option *DrawImageOptions) (err error) {
	i.pixels = nil
	i.syncer.Sync(func() {
		err = i.inner.drawImage(image, option)
	})
	return
}

// Bounds returns the bounds of the image.
func (i *Image) Bounds() image.Rectangle {
	w, h := i.inner.size()
	return image.Rect(0, 0, w, h)
}

// ColorModel returns the color model of the image.
func (i *Image) ColorModel() color.Model {
	return color.RGBAModel
}

// At returns the color of the image at (x, y).
//
// This method loads pixels from GPU to VRAM if necessary.
func (i *Image) At(x, y int) color.Color {
	if i.pixels == nil {
		i.syncer.Sync(func() {
			var err error
			i.pixels, err = i.inner.texture.Pixels()
			if err != nil {
				panic(err)
			}
		})
	}
	w, _ := i.inner.size()
	w = internal.NextPowerOf2Int(w)
	idx := 4*x + 4*y*w
	r, g, b, a := i.pixels[idx], i.pixels[idx+1], i.pixels[idx+2], i.pixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// A DrawImageOptions presents options to render an image on an image.
type DrawImageOptions struct {
	DstParts       []image.Rectangle
	SrcParts       []image.Rectangle
	GeometryMatrix *GeometryMatrix
	ColorMatrix    *ColorMatrix
}
<commit_msg>Fix a comment<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 ebiten

import (
	"github.com/hajimehoshi/ebiten/internal"
	"github.com/hajimehoshi/ebiten/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
	"image"
	"image/color"
)

type innerImage struct {
	framebuffer *opengl.Framebuffer
	texture     *opengl.Texture
}

func newInnerImage(texture *opengl.Texture) (*innerImage, error) {
	framebuffer, err := opengl.NewFramebufferFromTexture(texture)
	if err != nil {
		return nil, err
	}
	return &innerImage{framebuffer, texture}, nil
}

func (i *innerImage) size() (width, height int) {
	return i.framebuffer.Size()
}

func (i *innerImage) Clear() error {
	return i.Fill(color.Transparent)
}

func (i *innerImage) Fill(clr color.Color) error {
	if err := i.framebuffer.SetAsViewport(); err != nil {
		return err
	}
	r, g, b, a := internal.RGBA(clr)
	opengl.Clear(r, g, b, a)
	return nil
}

func (i *innerImage) drawImage(img *innerImage, options *DrawImageOptions) error {
	if options == nil {
		options = &DrawImageOptions{}
	}
	dsts := options.DstParts
	srcs := options.SrcParts
	if srcs == nil || dsts == nil {
		w, h := img.size()
		dsts = []image.Rectangle{
			image.Rect(0, 0, w, h),
		}
		srcs = []image.Rectangle{
			image.Rect(0, 0, w, h),
		}
	}
	geo := options.GeometryMatrix
	if geo == nil {
		i := GeometryMatrixI()
		geo = &i
	}
	clr := options.ColorMatrix
	if clr == nil {
		i := ColorMatrixI()
		clr = &i
	}

	if err := i.framebuffer.SetAsViewport(); err != nil {
		return err
	}
	w, h := img.texture.Size()
	quads := textureQuads(dsts, srcs, w, h)
	projectionMatrix := i.framebuffer.ProjectionMatrix()
	shader.DrawTexture(img.texture.Native(), projectionMatrix, quads, geo, clr)
	return nil
}

func u(x float64, width int) float32 {
	return float32(x) / float32(internal.NextPowerOf2Int(width))
}

func v(y float64, height int) float32 {
	return float32(y) / float32(internal.NextPowerOf2Int(height))
}

func textureQuads(dsts, srcs []image.Rectangle, width, height int) []shader.TextureQuad {
	l := len(dsts)
	if len(srcs) < l {
		l = len(srcs)
	}
	quads := make([]shader.TextureQuad, 0, l)
	for i := 0; i < l; i++ {
		dst, src := dsts[i], srcs[i]
		x1 := float32(dst.Min.X)
		x2 := float32(dst.Max.X)
		y1 := float32(dst.Min.Y)
		y2 := float32(dst.Max.Y)
		u1 := u(float64(src.Min.X), width)
		u2 := u(float64(src.Max.X), width)
		v1 := v(float64(src.Min.Y), height)
		v2 := v(float64(src.Max.Y), height)
		quad := shader.TextureQuad{x1, x2, y1, y2, u1, u2, v1, v2}
		quads = append(quads, quad)
	}
	return quads
}

type syncer interface {
	Sync(func())
}

// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
type Image struct {
	syncer syncer
	inner  *innerImage
	pixels []uint8
}

// Size returns the size of the image.
func (i *Image) Size() (width, height int) {
	return i.inner.size()
}

// Clear resets the pixels of the image into 0.
func (i *Image) Clear() (err error) {
	i.pixels = nil
	i.syncer.Sync(func() {
		err = i.inner.Clear()
	})
	return
}

// Fill fills the image with a solid color.
func (i *Image) Fill(clr color.Color) (err error) {
	i.pixels = nil
	i.syncer.Sync(func() {
		err = i.inner.Fill(clr)
	})
	return
}

// DrawImage draws the given image on the receiver image.
// This method accepts the parts of the given image at the parts of the destination as the options.
// After determining parts to draw, this applies the geometry matrix and the color matrix as the options.
//
// If you want to draw a whole image simply, use DrawWholeImage.
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) {
	return i.drawImage(image.inner, options)
}

// DrawImageAt draws the given image on the receiver image at the position (x, y).
//
// If a geometry matrix is specified, the geometry matrix is applied ahead of translating the image by (x, y).
func (i *Image) DrawImageAt(image *Image, x, y int, options *DrawImageOptions) (err error) {
	if options == nil {
		options = &DrawImageOptions{}
	}
	if options.GeometryMatrix == nil {
		geo := TranslateGeometry(float64(x), float64(y))
		options.GeometryMatrix = &geo
	} else {
		options.GeometryMatrix.Concat(TranslateGeometry(float64(x), float64(y)))
	}
	return i.drawImage(image.inner, options)
}

func (i *Image) drawImage(image *innerImage, option *DrawImageOptions) (err error) {
	i.pixels = nil
	i.syncer.Sync(func() {
		err = i.inner.drawImage(image, option)
	})
	return
}

// Bounds returns the bounds of the image.
func (i *Image) Bounds() image.Rectangle {
	w, h := i.inner.size()
	return image.Rect(0, 0, w, h)
}

// ColorModel returns the color model of the image.
func (i *Image) ColorModel() color.Model {
	return color.RGBAModel
}

// At returns the color of the image at (x, y).
//
// This method loads pixels from GPU to VRAM if necessary.
func (i *Image) At(x, y int) color.Color {
	if i.pixels == nil {
		i.syncer.Sync(func() {
			var err error
			i.pixels, err = i.inner.texture.Pixels()
			if err != nil {
				panic(err)
			}
		})
	}
	w, _ := i.inner.size()
	w = internal.NextPowerOf2Int(w)
	idx := 4*x + 4*y*w
	r, g, b, a := i.pixels[idx], i.pixels[idx+1], i.pixels[idx+2], i.pixels[idx+3]
	return color.RGBA{r, g, b, a}
}

// A DrawImageOptions presents options to render an image on an image.
type DrawImageOptions struct {
	DstParts       []image.Rectangle
	SrcParts       []image.Rectangle
	GeometryMatrix *GeometryMatrix
	ColorMatrix    *ColorMatrix
}
<|endoftext|>


<commit_before>package chevalier

import (
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"github.com/mattbaird/elastigo/api"
	es "github.com/mattbaird/elastigo/core"
	"strings"
)

// ElasticsearchSource is the type used to serialize sources for
// indexing.
type ElasticsearchSource struct {
	Origin string
	Source map[string]string `json:"source"`
}

// ElasticsearchOrigin stores metadata for each origin.
type ElasticsearchOrigin struct {
	Origin string `json:"origin"`
	Count string `json:"count"`
}

// GetID returns a (probably) unique ID for an ElasticsearchSource, in
// the form of a sha1 hash of underscore-separated field-value pairs
// separated by newlines.
func (s *ElasticsearchSource) GetID() string {
	tagKeys := make([]string, len(s.Source)+1)
	idx := 0
	for field, value := range s.Source {
		tagKeys[idx] = fmt.Sprintf("%s_%s", field, value)
		idx++
	}
	tagKeys[idx] = fmt.Sprintf("Origin", s.Origin)
	key := []byte(strings.Join(tagKeys, "\n"))
	hash := sha1.Sum(key)
	id := base64.StdEncoding.EncodeToString(hash[:sha1.Size])
	return id
}

func NewElasticsearchSource(origin string, source *DataSource) *ElasticsearchSource {
	esSource := new(ElasticsearchSource)
	esSource.Origin = origin
	esSource.Source = make(map[string]string, 0)
	for _, tagPtr := range source.Source {
		esSource.Source[*tagPtr.Field] = *tagPtr.Value
	}
	return esSource
}

// Unmarshal turns an ElasticsearchSource (presumably itself unmarshaled
// from a JSON object stored in Elasticsearch) into the equivalent
// DataSource.
func (s *ElasticsearchSource) Unmarshal() *DataSource {
	tags := make([]*DataSource_Tag, len(s.Source))
	idx := 0
	for field, value := range s.Source {
		tags[idx] = NewDataSourceTag(field, value)
		idx++
	}
	pb := NewDataSource(tags)
	return pb
}

func MarshalElasticsearchSources(origin string, b *DataSourceBurst) []*ElasticsearchSource {
	sources := make([]*ElasticsearchSource, len(b.Sources))
	for i, s := range b.Sources {
		esSource := NewElasticsearchSource(origin, s)
		sources[i] = esSource
	}
	return sources
}

// ElasticsearchWriter maintains context for writes to the index.
type ElasticsearchWriter struct {
	indexer   *es.BulkIndexer
	indexName string
	dataType  string
	done      chan bool
}

// NewElasticsearchWriter builds a new Writer. retrySeconds is for the
// bulk indexer. index and dataType can be anything as long as they're
// consistent.
func NewElasticsearchWriter(host string, maxConns int, retrySeconds int, index, dataType string) *ElasticsearchWriter {
	writer := new(ElasticsearchWriter)
	api.Domain = host
	writer.indexer = es.NewBulkIndexerErrors(maxConns, retrySeconds)
	writer.indexName = index
	writer.dataType = dataType
	writer.done = make(chan bool)
	writer.indexer.Run(writer.done)
	return writer
}

// Write queues a DataSource for writing by the bulk indexer.
// Non-blocking.
func (w *ElasticsearchWriter) Write(origin string, source *DataSource) error {
	esSource := NewElasticsearchSource(origin, source)
	update := map[string]interface{}{
		"doc":           esSource,
		"doc_as_upsert": true,
	}
	err := w.indexer.Update(w.indexName, w.dataType, esSource.GetID(), "", nil, update)
	return err
}

// Shutdown signals the bulk indexer to flush all pending writes.
func (w *ElasticsearchWriter) Shutdown() {
	w.done <- true
}

// GetErrorChan returns the channel the bulk indexer writes errors to.
func (w *ElasticsearchWriter) GetErrorChan() chan *es.ErrorBuffer {
	return w.indexer.ErrorChannel
}
<commit_msg>ElasticsearchOrigin needs a LastUpdated member<commit_after>package chevalier

import (
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"github.com/mattbaird/elastigo/api"
	es "github.com/mattbaird/elastigo/core"
	"strings"
)

// ElasticsearchSource is the type used to serialize sources for
// indexing.
type ElasticsearchSource struct {
	Origin string
	Source map[string]string `json:"source"`
}

// ElasticsearchOrigin stores metadata for each origin.
type ElasticsearchOrigin struct {
	Origin string `json:"origin"`
	Count string `json:"count"`
	LastUpdated uint64 `json:"last_updated"`
}

// GetID returns a (probably) unique ID for an ElasticsearchSource, in
// the form of a sha1 hash of underscore-separated field-value pairs
// separated by newlines.
func (s *ElasticsearchSource) GetID() string {
	tagKeys := make([]string, len(s.Source)+1)
	idx := 0
	for field, value := range s.Source {
		tagKeys[idx] = fmt.Sprintf("%s_%s", field, value)
		idx++
	}
	tagKeys[idx] = fmt.Sprintf("Origin", s.Origin)
	key := []byte(strings.Join(tagKeys, "\n"))
	hash := sha1.Sum(key)
	id := base64.StdEncoding.EncodeToString(hash[:sha1.Size])
	return id
}

// NewElasticsearchSource converts a (datasource + origin) to an
// ElasticsearchSource.
func NewElasticsearchSource(origin string, source *DataSource) *ElasticsearchSource {
	esSource := new(ElasticsearchSource)
	esSource.Origin = origin
	esSource.Source = make(map[string]string, 0)
	for _, tagPtr := range source.Source {
		esSource.Source[*tagPtr.Field] = *tagPtr.Value
	}
	return esSource
}

// Unmarshal turns an ElasticsearchSource (presumably itself unmarshaled
// from a JSON object stored in Elasticsearch) into the equivalent
// DataSource.
func (s *ElasticsearchSource) Unmarshal() *DataSource {
	tags := make([]*DataSource_Tag, len(s.Source))
	idx := 0
	for field, value := range s.Source {
		tags[idx] = NewDataSourceTag(field, value)
		idx++
	}
	pb := NewDataSource(tags)
	return pb
}

// MarshalElasticsearchSources converts source bursts (plus an origin)
// into ElasticsearchSource objects ready for indexing.
func MarshalElasticsearchSources(origin string, b *DataSourceBurst) []*ElasticsearchSource {
	sources := make([]*ElasticsearchSource, len(b.Sources))
	for i, s := range b.Sources {
		esSource := NewElasticsearchSource(origin, s)
		sources[i] = esSource
	}
	return sources
}

// ElasticsearchWriter maintains context for writes to the index.
type ElasticsearchWriter struct {
	indexer   *es.BulkIndexer
	indexName string
	dataType  string
	done      chan bool
}

// NewElasticsearchWriter builds a new Writer. retrySeconds is for the
// bulk indexer. index and dataType can be anything as long as they're
// consistent.
func NewElasticsearchWriter(host string, maxConns int, retrySeconds int, index, dataType string) *ElasticsearchWriter {
	writer := new(ElasticsearchWriter)
	api.Domain = host
	writer.indexer = es.NewBulkIndexerErrors(maxConns, retrySeconds)
	writer.indexName = index
	writer.dataType = dataType
	writer.done = make(chan bool)
	writer.indexer.Run(writer.done)
	return writer
}

// Write queues a DataSource for writing by the bulk indexer.
// Non-blocking.
func (w *ElasticsearchWriter) Write(origin string, source *DataSource) error {
	esSource := NewElasticsearchSource(origin, source)
	update := map[string]interface{}{
		"doc":           esSource,
		"doc_as_upsert": true,
	}
	err := w.indexer.Update(w.indexName, w.dataType, esSource.GetID(), "", nil, update)
	return err
}

// Shutdown signals the bulk indexer to flush all pending writes.
func (w *ElasticsearchWriter) Shutdown() {
	w.done <- true
}

// GetErrorChan returns the channel the bulk indexer writes errors to.
func (w *ElasticsearchWriter) GetErrorChan() chan *es.ErrorBuffer {
	return w.indexer.ErrorChannel
}
<|endoftext|>


<commit_before>package commandevaluators

import (
	"errors"
	"strings"

	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

//ChangeInput is struct that implements the CommandEvaluation struct
type ChangeInputDefault struct {
}

//Evaluate fulfills the CommmandEvaluation evaluate requirement.
func (p *ChangeInputDefault) Evaluate(room base.PublicRoom) (actions []base.ActionStructure, err error) {
	//RoomWideSetVideoInput
	if len(room.CurrentVideoInput) > 0 { // Check if the user sent a PUT body changing the current video input
		var tempActions []base.ActionStructure

		tempActions, err = generateChangeInputByRole(
			"VideoOut",
			room.CurrentVideoInput,
			room.Room,
			room.Building,
		)

		if err != nil {
			return
		}

		actions = append(actions, tempActions...)
	}

	//RoomWideSetAudioInput
	if len(room.CurrentAudioInput) > 0 { // Check if the user sent a PUT body changing the current audio input
		var tempActions []base.ActionStructure

		//generate action
		tempActions, err = generateChangeInputByRole(
			"AudioOut",
			room.CurrentVideoInput,
			room.Room,
			room.Building,
		)
		if err != nil {
			return
		}
		actions = append(actions, tempActions...)
	}

	//Displays
	for _, d := range room.Displays { // Loop through the devices array (potentially) passed in the user's PUT body
		var action base.ActionStructure
		action, err = generateChangeInputByDevice(d.Device, room.Room, room.Building)
		if err != nil {
			return
		}
		actions = append(actions, action)
	}

	//AudioDevice
	for _, d := range room.AudioDevices { // Loop through the audio devices array (potentially) passed in the user's PUT body
		var action base.ActionStructure
		action, err = generateChangeInputByDevice(d.Device, room.Room, room.Building)
		if err != nil {
			return
		}
		actions = append(actions, action)
	}

	return
}

//Validate fulfills the Fulfill requirement on the command interface
func (p *ChangeInputDefault) Validate(action base.ActionStructure) (err error) {
	return nil
}

//GetIncompatableCommands keeps track of actions that are incompatable (on the same device)
func (p *ChangeInputDefault) GetIncompatableCommands() (incompatableActions []string) {
	return
}

func generateChangeInputByDevice(dev base.Device, room string, building string) (action base.ActionStructure, err error) {
	var curDevice accessors.Device

	curDevice, err = dbo.GetDeviceByName(building, room, dev.Name)
	if err != nil {
		return
	}

	paramMap := make(map[string]string)

	for _, port := range curDevice.Ports {
		if strings.EqualFold(port.Source, dev.Input) {
			paramMap["port"] = port.Name
			break
		}
	}

	if len(paramMap) == 0 {
		err = errors.New("No port found for input.")
		return
	}

	action = base.ActionStructure{
		Action:              "change-input",
		GeneratingEvaluator: "changeInput",
		Device:              curDevice,
		Parameters:          paramMap,
		DeviceSpecific:      true,
		Overridden:          false,
	}

	return
}

func generateChangeInputByRole(role string, input string, room string, building string) (actions []base.ActionStructure, err error) {
	videoOutDevices, err := dbo.GetDevicesByBuildingAndRoomAndRole(building, room, role)
	if err != nil {
		return
	}

	for _, d := range devicesToChange { // Loop through the devices in the room
		paramMap := make(map[string]string) // Start building parameter map

		//Get the port mapping for the device
		for _, curPort := range d.Ports { // Loop through the found ports
			if strings.EqualFold(curPort.Source, input) {
				paramMap["port"] = curPort.Name
				break
			}
		}

		if len(paramMap) == 0 {
			err = errors.New("No port found for input.")
			return
		}

		action := base.ActionStructure{
			Action:              "ChangeInput",
			GeneratingEvaluator: "ChangeInputDefault",
			Device:              d,
			Parameters:          paramMap,
			DeviceSpecific:      false,
			Overridden:          false,
		}

		actions = append(actions, action)

		return
	}

	return
}
<commit_msg>One last fix<commit_after>package commandevaluators

import (
	"errors"
	"strings"

	"github.com/byuoitav/av-api/base"
	"github.com/byuoitav/av-api/dbo"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

//ChangeInput is struct that implements the CommandEvaluation struct
type ChangeInputDefault struct {
}

//Evaluate fulfills the CommmandEvaluation evaluate requirement.
func (p *ChangeInputDefault) Evaluate(room base.PublicRoom) (actions []base.ActionStructure, err error) {
	//RoomWideSetVideoInput
	if len(room.CurrentVideoInput) > 0 { // Check if the user sent a PUT body changing the current video input
		var tempActions []base.ActionStructure

		tempActions, err = generateChangeInputByRole(
			"VideoOut",
			room.CurrentVideoInput,
			room.Room,
			room.Building,
		)

		if err != nil {
			return
		}

		actions = append(actions, tempActions...)
	}

	//RoomWideSetAudioInput
	if len(room.CurrentAudioInput) > 0 { // Check if the user sent a PUT body changing the current audio input
		var tempActions []base.ActionStructure

		//generate action
		tempActions, err = generateChangeInputByRole(
			"AudioOut",
			room.CurrentVideoInput,
			room.Room,
			room.Building,
		)
		if err != nil {
			return
		}
		actions = append(actions, tempActions...)
	}

	//Displays
	for _, d := range room.Displays { // Loop through the devices array (potentially) passed in the user's PUT body
		var action base.ActionStructure
		action, err = generateChangeInputByDevice(d.Device, room.Room, room.Building)
		if err != nil {
			return
		}
		actions = append(actions, action)
	}

	//AudioDevice
	for _, d := range room.AudioDevices { // Loop through the audio devices array (potentially) passed in the user's PUT body
		var action base.ActionStructure
		action, err = generateChangeInputByDevice(d.Device, room.Room, room.Building)
		if err != nil {
			return
		}
		actions = append(actions, action)
	}

	return
}

//Validate fulfills the Fulfill requirement on the command interface
func (p *ChangeInputDefault) Validate(action base.ActionStructure) (err error) {
	return nil
}

//GetIncompatableCommands keeps track of actions that are incompatable (on the same device)
func (p *ChangeInputDefault) GetIncompatableCommands() (incompatableActions []string) {
	return
}

func generateChangeInputByDevice(dev base.Device, room string, building string) (action base.ActionStructure, err error) {
	var curDevice accessors.Device

	curDevice, err = dbo.GetDeviceByName(building, room, dev.Name)
	if err != nil {
		return
	}

	paramMap := make(map[string]string)

	for _, port := range curDevice.Ports {
		if strings.EqualFold(port.Source, dev.Input) {
			paramMap["port"] = port.Name
			break
		}
	}

	if len(paramMap) == 0 {
		err = errors.New("No port found for input.")
		return
	}

	action = base.ActionStructure{
		Action:              "change-input",
		GeneratingEvaluator: "changeInput",
		Device:              curDevice,
		Parameters:          paramMap,
		DeviceSpecific:      true,
		Overridden:          false,
	}

	return
}

func generateChangeInputByRole(role string, input string, room string, building string) (actions []base.ActionStructure, err error) {
	devicesToChange, err := dbo.GetDevicesByBuildingAndRoomAndRole(building, room, role)
	if err != nil {
		return
	}

	for _, d := range devicesToChange { // Loop through the devices in the room
		paramMap := make(map[string]string) // Start building parameter map

		//Get the port mapping for the device
		for _, curPort := range d.Ports { // Loop through the found ports
			if strings.EqualFold(curPort.Source, input) {
				paramMap["port"] = curPort.Name
				break
			}
		}

		if len(paramMap) == 0 {
			err = errors.New("No port found for input.")
			return
		}

		action := base.ActionStructure{
			Action:              "ChangeInput",
			GeneratingEvaluator: "ChangeInputDefault",
			Device:              d,
			Parameters:          paramMap,
			DeviceSpecific:      false,
			Overridden:          false,
		}

		actions = append(actions, action)

		return
	}

	return
}
<|endoftext|>


<commit_before>// Code generated by "stringer -type KeepAliveModeType"; DO NOT EDIT

package main

import "fmt"

const _KeepAliveModeType_name = "KEEPALIVE_TRANSPARENTKEEPALIVE_NO_BACKEND"

var _KeepAliveModeType_index = [...]uint8{0, 21, 41}

func (i KeepAliveModeType) String() string {
	if i < 0 || i >= KeepAliveModeType(len(_KeepAliveModeType_index)-1) {
		return fmt.Sprintf("KeepAliveModeType(%d)", i)
	}
	return _KeepAliveModeType_name[_KeepAliveModeType_index[i]:_KeepAliveModeType_index[i+1]]
}
<commit_msg>update stringer<commit_after>// Code generated by "stringer -type KeepAliveModeType"; DO NOT EDIT.

package main

import "fmt"

const _KeepAliveModeType_name = "KEEPALIVE_TRANSPARENTKEEPALIVE_NO_BACKEND"

var _KeepAliveModeType_index = [...]uint8{0, 21, 41}

func (i KeepAliveModeType) String() string {
	if i < 0 || i >= KeepAliveModeType(len(_KeepAliveModeType_index)-1) {
		return fmt.Sprintf("KeepAliveModeType(%d)", i)
	}
	return _KeepAliveModeType_name[_KeepAliveModeType_index[i]:_KeepAliveModeType_index[i+1]]
}
<|endoftext|>


<commit_before>package bitswap

import (
	"context"
	"fmt"
	"time"

	notifications "github.com/ipfs/go-ipfs/exchange/bitswap/notifications"

	cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid"
	blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format"
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
	loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables"
	lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru"
	peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
)

const activeWantsLimit = 16

// Session holds state for an individual bitswap transfer operation.
// This allows bitswap to make smarter decisions about who to send wantlist
// info to, and who to request blocks from
type Session struct {
	ctx            context.Context
	tofetch        *cidQueue
	activePeers    map[peer.ID]struct{}
	activePeersArr []peer.ID

	bs           *Bitswap
	incoming     chan blkRecv
	newReqs      chan []*cid.Cid
	cancelKeys   chan []*cid.Cid
	interestReqs chan interestReq

	interest  *lru.Cache
	liveWants map[string]time.Time

	tick          *time.Timer
	baseTickDelay time.Duration

	latTotal time.Duration
	fetchcnt int

	notif notifications.PubSub

	uuid logging.Loggable

	id  uint64
	tag string
}

// NewSession creates a new bitswap session whose lifetime is bounded by the
// given context
func (bs *Bitswap) NewSession(ctx context.Context) *Session {
	s := &Session{
		activePeers:   make(map[peer.ID]struct{}),
		liveWants:     make(map[string]time.Time),
		newReqs:       make(chan []*cid.Cid),
		cancelKeys:    make(chan []*cid.Cid),
		tofetch:       newCidQueue(),
		interestReqs:  make(chan interestReq),
		ctx:           ctx,
		bs:            bs,
		incoming:      make(chan blkRecv),
		notif:         notifications.New(),
		uuid:          loggables.Uuid("GetBlockRequest"),
		baseTickDelay: time.Millisecond * 500,
		id:            bs.getNextSessionID(),
	}

	s.tag = fmt.Sprint("bs-ses-", s.id)

	cache, _ := lru.New(2048)
	s.interest = cache

	bs.sessLk.Lock()
	bs.sessions = append(bs.sessions, s)
	bs.sessLk.Unlock()

	go s.run(ctx)

	return s
}

func (bs *Bitswap) removeSession(s *Session) {
	bs.sessLk.Lock()
	defer bs.sessLk.Unlock()
	for i := 0; i < len(bs.sessions); i++ {
		if bs.sessions[i] == s {
			bs.sessions[i] = bs.sessions[len(bs.sessions)-1]
			bs.sessions = bs.sessions[:len(bs.sessions)-1]
			return
		}
	}
}

type blkRecv struct {
	from peer.ID
	blk  blocks.Block
}

func (s *Session) receiveBlockFrom(from peer.ID, blk blocks.Block) {
	select {
	case s.incoming <- blkRecv{from: from, blk: blk}:
	case <-s.ctx.Done():
	}
}

type interestReq struct {
	c    *cid.Cid
	resp chan bool
}

// TODO: PERF: this is using a channel to guard a map access against race
// conditions. This is definitely much slower than a mutex, though its unclear
// if it will actually induce any noticeable slowness. This is implemented this
// way to avoid adding a more complex set of mutexes around the liveWants map.
// note that in the average case (where this session *is* interested in the
// block we received) this function will not be called, as the cid will likely
// still be in the interest cache.
func (s *Session) isLiveWant(c *cid.Cid) bool {
	resp := make(chan bool, 1)
	s.interestReqs <- interestReq{
		c:    c,
		resp: resp,
	}

	select {
	case want := <-resp:
		return want
	case <-s.ctx.Done():
		return false
	}
}

func (s *Session) interestedIn(c *cid.Cid) bool {
	return s.interest.Contains(c.KeyString()) || s.isLiveWant(c)
}

const provSearchDelay = time.Second * 10

func (s *Session) addActivePeer(p peer.ID) {
	if _, ok := s.activePeers[p]; !ok {
		s.activePeers[p] = struct{}{}
		s.activePeersArr = append(s.activePeersArr, p)

		cmgr := s.bs.network.ConnectionManager()
		cmgr.TagPeer(p, s.tag, 10)
	}
}

func (s *Session) resetTick() {
	if s.latTotal == 0 {
		s.tick.Reset(provSearchDelay)
	} else {
		avLat := s.latTotal / time.Duration(s.fetchcnt)
		s.tick.Reset(s.baseTickDelay + (3 * avLat))
	}
}

func (s *Session) run(ctx context.Context) {
	s.tick = time.NewTimer(provSearchDelay)
	newpeers := make(chan peer.ID, 16)
	for {
		select {
		case blk := <-s.incoming:
			s.tick.Stop()

			if blk.from != "" {
				s.addActivePeer(blk.from)
			}

			s.receiveBlock(ctx, blk.blk)

			s.resetTick()
		case keys := <-s.newReqs:
			for _, k := range keys {
				s.interest.Add(k.KeyString(), nil)
			}
			if len(s.liveWants) < activeWantsLimit {
				toadd := activeWantsLimit - len(s.liveWants)
				if toadd > len(keys) {
					toadd = len(keys)
				}

				now := keys[:toadd]
				keys = keys[toadd:]

				s.wantBlocks(ctx, now)
			}
			for _, k := range keys {
				s.tofetch.Push(k)
			}
		case keys := <-s.cancelKeys:
			s.cancel(keys)

		case <-s.tick.C:
			var live []*cid.Cid
			for c := range s.liveWants {
				cs, _ := cid.Cast([]byte(c))
				live = append(live, cs)
				s.liveWants[c] = time.Now()
			}

			// Broadcast these keys to everyone we're connected to
			s.bs.wm.WantBlocks(ctx, live, nil, s.id)

			if len(live) > 0 {
				go func(k *cid.Cid) {
					// TODO: have a task queue setup for this to:
					// - rate limit
					// - manage timeouts
					// - ensure two 'findprovs' calls for the same block don't run concurrently
					// - share peers between sessions based on interest set
					for p := range s.bs.network.FindProvidersAsync(ctx, k, 10) {
						newpeers <- p
					}
				}(live[0])
			}
			s.resetTick()
		case p := <-newpeers:
			s.addActivePeer(p)
		case lwchk := <-s.interestReqs:
			lwchk.resp <- s.cidIsWanted(lwchk.c)
		case <-ctx.Done():
			s.tick.Stop()
			s.bs.removeSession(s)

			cmgr := s.bs.network.ConnectionManager()
			for _, p := range s.activePeersArr {
				cmgr.UntagPeer(p, s.tag)
			}
			return
		}
	}
}

func (s *Session) cidIsWanted(c *cid.Cid) bool {
	_, ok := s.liveWants[c.KeyString()]
	if !ok {
		ok = s.tofetch.Has(c)
	}

	return ok
}

func (s *Session) receiveBlock(ctx context.Context, blk blocks.Block) {
	c := blk.Cid()
	if s.cidIsWanted(c) {
		ks := c.KeyString()
		tval, ok := s.liveWants[ks]
		if ok {
			s.latTotal += time.Since(tval)
			delete(s.liveWants, ks)
		} else {
			s.tofetch.Remove(c)
		}
		s.fetchcnt++
		s.notif.Publish(blk)

		if next := s.tofetch.Pop(); next != nil {
			s.wantBlocks(ctx, []*cid.Cid{next})
		}
	}
}

func (s *Session) wantBlocks(ctx context.Context, ks []*cid.Cid) {
	for _, c := range ks {
		s.liveWants[c.KeyString()] = time.Now()
	}
	s.bs.wm.WantBlocks(ctx, ks, s.activePeersArr, s.id)
}

func (s *Session) cancel(keys []*cid.Cid) {
	for _, c := range keys {
		s.tofetch.Remove(c)
	}
}

func (s *Session) cancelWants(keys []*cid.Cid) {
	s.cancelKeys <- keys
}

func (s *Session) fetch(ctx context.Context, keys []*cid.Cid) {
	select {
	case s.newReqs <- keys:
	case <-ctx.Done():
	}
}

// GetBlocks fetches a set of blocks within the context of this session and
// returns a channel that found blocks will be returned on. No order is
// guaranteed on the returned blocks.
func (s *Session) GetBlocks(ctx context.Context, keys []*cid.Cid) (<-chan blocks.Block, error) {
	ctx = logging.ContextWithLoggable(ctx, s.uuid)
	return getBlocksImpl(ctx, keys, s.notif, s.fetch, s.cancelWants)
}

// GetBlock fetches a single block
func (s *Session) GetBlock(parent context.Context, k *cid.Cid) (blocks.Block, error) {
	return getBlock(parent, k, s.GetBlocks)
}

type cidQueue struct {
	elems []*cid.Cid
	eset  *cid.Set
}

func newCidQueue() *cidQueue {
	return &cidQueue{eset: cid.NewSet()}
}

func (cq *cidQueue) Pop() *cid.Cid {
	for {
		if len(cq.elems) == 0 {
			return nil
		}

		out := cq.elems[0]
		cq.elems = cq.elems[1:]

		if cq.eset.Has(out) {
			cq.eset.Remove(out)
			return out
		}
	}
}

func (cq *cidQueue) Push(c *cid.Cid) {
	if cq.eset.Visit(c) {
		cq.elems = append(cq.elems, c)
	}
}

func (cq *cidQueue) Remove(c *cid.Cid) {
	cq.eset.Remove(c)
}

func (cq *cidQueue) Has(c *cid.Cid) bool {
	return cq.eset.Has(c)
}

func (cq *cidQueue) Len() int {
	return cq.eset.Len()
}
<commit_msg>fix deadlock in bitswap sessions<commit_after>package bitswap

import (
	"context"
	"fmt"
	"time"

	notifications "github.com/ipfs/go-ipfs/exchange/bitswap/notifications"

	cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid"
	blocks "gx/ipfs/QmSn9Td7xgxm9EV7iEjTckpUWmWApggzPxu7eFGWkkpwin/go-block-format"
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
	loggables "gx/ipfs/QmT4PgCNdv73hnFAqzHqwW44q7M9PWpykSswHDxndquZbc/go-libp2p-loggables"
	lru "gx/ipfs/QmVYxfoJQiZijTgPNHCHgHELvQpbsJNTg6Crmc3dQkj3yy/golang-lru"
	peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
)

const activeWantsLimit = 16

// Session holds state for an individual bitswap transfer operation.
// This allows bitswap to make smarter decisions about who to send wantlist
// info to, and who to request blocks from
type Session struct {
	ctx            context.Context
	tofetch        *cidQueue
	activePeers    map[peer.ID]struct{}
	activePeersArr []peer.ID

	bs           *Bitswap
	incoming     chan blkRecv
	newReqs      chan []*cid.Cid
	cancelKeys   chan []*cid.Cid
	interestReqs chan interestReq

	interest  *lru.Cache
	liveWants map[string]time.Time

	tick          *time.Timer
	baseTickDelay time.Duration

	latTotal time.Duration
	fetchcnt int

	notif notifications.PubSub

	uuid logging.Loggable

	id  uint64
	tag string
}

// NewSession creates a new bitswap session whose lifetime is bounded by the
// given context
func (bs *Bitswap) NewSession(ctx context.Context) *Session {
	s := &Session{
		activePeers:   make(map[peer.ID]struct{}),
		liveWants:     make(map[string]time.Time),
		newReqs:       make(chan []*cid.Cid),
		cancelKeys:    make(chan []*cid.Cid),
		tofetch:       newCidQueue(),
		interestReqs:  make(chan interestReq),
		ctx:           ctx,
		bs:            bs,
		incoming:      make(chan blkRecv),
		notif:         notifications.New(),
		uuid:          loggables.Uuid("GetBlockRequest"),
		baseTickDelay: time.Millisecond * 500,
		id:            bs.getNextSessionID(),
	}

	s.tag = fmt.Sprint("bs-ses-", s.id)

	cache, _ := lru.New(2048)
	s.interest = cache

	bs.sessLk.Lock()
	bs.sessions = append(bs.sessions, s)
	bs.sessLk.Unlock()

	go s.run(ctx)

	return s
}

func (bs *Bitswap) removeSession(s *Session) {
	bs.sessLk.Lock()
	defer bs.sessLk.Unlock()
	for i := 0; i < len(bs.sessions); i++ {
		if bs.sessions[i] == s {
			bs.sessions[i] = bs.sessions[len(bs.sessions)-1]
			bs.sessions = bs.sessions[:len(bs.sessions)-1]
			return
		}
	}
}

type blkRecv struct {
	from peer.ID
	blk  blocks.Block
}

func (s *Session) receiveBlockFrom(from peer.ID, blk blocks.Block) {
	select {
	case s.incoming <- blkRecv{from: from, blk: blk}:
	case <-s.ctx.Done():
	}
}

type interestReq struct {
	c    *cid.Cid
	resp chan bool
}

// TODO: PERF: this is using a channel to guard a map access against race
// conditions. This is definitely much slower than a mutex, though its unclear
// if it will actually induce any noticeable slowness. This is implemented this
// way to avoid adding a more complex set of mutexes around the liveWants map.
// note that in the average case (where this session *is* interested in the
// block we received) this function will not be called, as the cid will likely
// still be in the interest cache.
func (s *Session) isLiveWant(c *cid.Cid) bool {
	resp := make(chan bool, 1)
	select {
	case s.interestReqs <- interestReq{
		c:    c,
		resp: resp,
	}:
	case <-s.ctx.Done():
		return false
	}

	select {
	case want := <-resp:
		return want
	case <-s.ctx.Done():
		return false
	}
}

func (s *Session) interestedIn(c *cid.Cid) bool {
	return s.interest.Contains(c.KeyString()) || s.isLiveWant(c)
}

const provSearchDelay = time.Second * 10

func (s *Session) addActivePeer(p peer.ID) {
	if _, ok := s.activePeers[p]; !ok {
		s.activePeers[p] = struct{}{}
		s.activePeersArr = append(s.activePeersArr, p)

		cmgr := s.bs.network.ConnectionManager()
		cmgr.TagPeer(p, s.tag, 10)
	}
}

func (s *Session) resetTick() {
	if s.latTotal == 0 {
		s.tick.Reset(provSearchDelay)
	} else {
		avLat := s.latTotal / time.Duration(s.fetchcnt)
		s.tick.Reset(s.baseTickDelay + (3 * avLat))
	}
}

func (s *Session) run(ctx context.Context) {
	s.tick = time.NewTimer(provSearchDelay)
	newpeers := make(chan peer.ID, 16)
	for {
		select {
		case blk := <-s.incoming:
			s.tick.Stop()

			if blk.from != "" {
				s.addActivePeer(blk.from)
			}

			s.receiveBlock(ctx, blk.blk)

			s.resetTick()
		case keys := <-s.newReqs:
			for _, k := range keys {
				s.interest.Add(k.KeyString(), nil)
			}
			if len(s.liveWants) < activeWantsLimit {
				toadd := activeWantsLimit - len(s.liveWants)
				if toadd > len(keys) {
					toadd = len(keys)
				}

				now := keys[:toadd]
				keys = keys[toadd:]

				s.wantBlocks(ctx, now)
			}
			for _, k := range keys {
				s.tofetch.Push(k)
			}
		case keys := <-s.cancelKeys:
			s.cancel(keys)

		case <-s.tick.C:
			var live []*cid.Cid
			for c := range s.liveWants {
				cs, _ := cid.Cast([]byte(c))
				live = append(live, cs)
				s.liveWants[c] = time.Now()
			}

			// Broadcast these keys to everyone we're connected to
			s.bs.wm.WantBlocks(ctx, live, nil, s.id)

			if len(live) > 0 {
				go func(k *cid.Cid) {
					// TODO: have a task queue setup for this to:
					// - rate limit
					// - manage timeouts
					// - ensure two 'findprovs' calls for the same block don't run concurrently
					// - share peers between sessions based on interest set
					for p := range s.bs.network.FindProvidersAsync(ctx, k, 10) {
						newpeers <- p
					}
				}(live[0])
			}
			s.resetTick()
		case p := <-newpeers:
			s.addActivePeer(p)
		case lwchk := <-s.interestReqs:
			lwchk.resp <- s.cidIsWanted(lwchk.c)
		case <-ctx.Done():
			s.tick.Stop()
			s.bs.removeSession(s)

			cmgr := s.bs.network.ConnectionManager()
			for _, p := range s.activePeersArr {
				cmgr.UntagPeer(p, s.tag)
			}
			return
		}
	}
}

func (s *Session) cidIsWanted(c *cid.Cid) bool {
	_, ok := s.liveWants[c.KeyString()]
	if !ok {
		ok = s.tofetch.Has(c)
	}

	return ok
}

func (s *Session) receiveBlock(ctx context.Context, blk blocks.Block) {
	c := blk.Cid()
	if s.cidIsWanted(c) {
		ks := c.KeyString()
		tval, ok := s.liveWants[ks]
		if ok {
			s.latTotal += time.Since(tval)
			delete(s.liveWants, ks)
		} else {
			s.tofetch.Remove(c)
		}
		s.fetchcnt++
		s.notif.Publish(blk)

		if next := s.tofetch.Pop(); next != nil {
			s.wantBlocks(ctx, []*cid.Cid{next})
		}
	}
}

func (s *Session) wantBlocks(ctx context.Context, ks []*cid.Cid) {
	for _, c := range ks {
		s.liveWants[c.KeyString()] = time.Now()
	}
	s.bs.wm.WantBlocks(ctx, ks, s.activePeersArr, s.id)
}

func (s *Session) cancel(keys []*cid.Cid) {
	for _, c := range keys {
		s.tofetch.Remove(c)
	}
}

func (s *Session) cancelWants(keys []*cid.Cid) {
	select {
	case s.cancelKeys <- keys:
	case <-s.ctx.Done():
	}
}

func (s *Session) fetch(ctx context.Context, keys []*cid.Cid) {
	select {
	case s.newReqs <- keys:
	case <-ctx.Done():
	case <-s.ctx.Done():
	}
}

// GetBlocks fetches a set of blocks within the context of this session and
// returns a channel that found blocks will be returned on. No order is
// guaranteed on the returned blocks.
func (s *Session) GetBlocks(ctx context.Context, keys []*cid.Cid) (<-chan blocks.Block, error) {
	ctx = logging.ContextWithLoggable(ctx, s.uuid)
	return getBlocksImpl(ctx, keys, s.notif, s.fetch, s.cancelWants)
}

// GetBlock fetches a single block
func (s *Session) GetBlock(parent context.Context, k *cid.Cid) (blocks.Block, error) {
	return getBlock(parent, k, s.GetBlocks)
}

type cidQueue struct {
	elems []*cid.Cid
	eset  *cid.Set
}

func newCidQueue() *cidQueue {
	return &cidQueue{eset: cid.NewSet()}
}

func (cq *cidQueue) Pop() *cid.Cid {
	for {
		if len(cq.elems) == 0 {
			return nil
		}

		out := cq.elems[0]
		cq.elems = cq.elems[1:]

		if cq.eset.Has(out) {
			cq.eset.Remove(out)
			return out
		}
	}
}

func (cq *cidQueue) Push(c *cid.Cid) {
	if cq.eset.Visit(c) {
		cq.elems = append(cq.elems, c)
	}
}

func (cq *cidQueue) Remove(c *cid.Cid) {
	cq.eset.Remove(c)
}

func (cq *cidQueue) Has(c *cid.Cid) bool {
	return cq.eset.Has(c)
}

func (cq *cidQueue) Len() int {
	return cq.eset.Len()
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * 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.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 */

package client

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

	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	migrate "github.com/rubenv/sql-migrate"
	"github.com/sirupsen/logrus"
	"gopkg.in/square/go-jose.v2"

	"github.com/ory/fosite"
	"github.com/ory/x/dbal"
	"github.com/ory/x/sqlcon"
	"github.com/ory/x/stringsx"
)

var Migrations = map[string]*dbal.PackrMigrationSource{
	dbal.DriverMySQL:       dbal.NewMustPackerMigrationSource(logrus.New(), AssetNames(), Asset, []string{"migrations/sql/shared", "migrations/sql/mysql"}, true),
	dbal.DriverPostgreSQL:  dbal.NewMustPackerMigrationSource(logrus.New(), AssetNames(), Asset, []string{"migrations/sql/shared", "migrations/sql/postgres"}, true),
	dbal.DriverCockroachDB: dbal.NewMustPackerMigrationSource(logrus.New(), AssetNames(), Asset, []string{"migrations/sql/cockroach"}, true),
}

func NewSQLManager(db *sqlx.DB, r InternalRegistry) *SQLManager {
	return &SQLManager{
		r:  r,
		DB: db,
	}
}

type SQLManager struct {
	r  InternalRegistry
	DB *sqlx.DB
}

type sqlData struct {
	PK                                int       `db:"pk"`
	ID                                string    `db:"id"`
	Name                              string    `db:"client_name"`
	Secret                            string    `db:"client_secret"`
	RedirectURIs                      string    `db:"redirect_uris"`
	GrantTypes                        string    `db:"grant_types"`
	ResponseTypes                     string    `db:"response_types"`
	Scope                             string    `db:"scope"`
	Owner                             string    `db:"owner"`
	PolicyURI                         string    `db:"policy_uri"`
	TermsOfServiceURI                 string    `db:"tos_uri"`
	ClientURI                         string    `db:"client_uri"`
	LogoURI                           string    `db:"logo_uri"`
	Contacts                          string    `db:"contacts"`
	SecretExpiresAt                   int       `db:"client_secret_expires_at"`
	SectorIdentifierURI               string    `db:"sector_identifier_uri"`
	JSONWebKeysURI                    string    `db:"jwks_uri"`
	JSONWebKeys                       string    `db:"jwks"`
	TokenEndpointAuthMethod           string    `db:"token_endpoint_auth_method"`
	RequestURIs                       string    `db:"request_uris"`
	SubjectType                       string    `db:"subject_type"`
	RequestObjectSigningAlgorithm     string    `db:"request_object_signing_alg"`
	UserinfoSignedResponseAlg         string    `db:"userinfo_signed_response_alg"`
	AllowedCORSOrigins                string    `db:"allowed_cors_origins"`
	Audience                          string    `db:"audience"`
	UpdatedAt                         time.Time `db:"updated_at"`
	CreatedAt                         time.Time `db:"created_at"`
	FrontChannelLogoutURI             string    `db:"frontchannel_logout_uri"`
	FrontChannelLogoutSessionRequired bool      `db:"frontchannel_logout_session_required"`
	PostLogoutRedirectURIs            string    `db:"post_logout_redirect_uris"`
	BackChannelLogoutURI              string    `db:"backchannel_logout_uri"`
	BackChannelLogoutSessionRequired  bool      `db:"backchannel_logout_session_required"`
}

var sqlParams = []string{
	"id",
	"client_name",
	"client_secret",
	"redirect_uris",
	"grant_types",
	"response_types",
	"scope",
	"owner",
	"policy_uri",
	"tos_uri",
	"client_uri",
	"subject_type",
	"logo_uri",
	"contacts",
	"client_secret_expires_at",
	"sector_identifier_uri",
	"jwks",
	"jwks_uri",
	"token_endpoint_auth_method",
	"request_uris",
	"request_object_signing_alg",
	"userinfo_signed_response_alg",
	"allowed_cors_origins",
	"audience",
	"updated_at",
	"created_at",
	"frontchannel_logout_uri",
	"frontchannel_logout_session_required",
	"post_logout_redirect_uris",
	"backchannel_logout_uri",
	"backchannel_logout_session_required",
}

func sqlDataFromClient(d *Client) (*sqlData, error) {
	jwks := ""

	if d.JSONWebKeys != nil {
		out, err := json.Marshal(d.JSONWebKeys)
		if err != nil {
			return nil, errors.WithStack(err)
		}
		jwks = string(out)
	}

	var createdAt, updatedAt = d.CreatedAt, d.UpdatedAt

	if d.CreatedAt.IsZero() {
		createdAt = time.Now()
	}

	if d.UpdatedAt.IsZero() {
		updatedAt = time.Now()
	}

	return &sqlData{
		ID:                                d.GetID(),
		Name:                              d.Name,
		Secret:                            d.Secret,
		RedirectURIs:                      strings.Join(d.RedirectURIs, "|"),
		Audience:                          strings.Join(d.Audience, "|"),
		GrantTypes:                        strings.Join(d.GrantTypes, "|"),
		ResponseTypes:                     strings.Join(d.ResponseTypes, "|"),
		Scope:                             d.Scope,
		Owner:                             d.Owner,
		PolicyURI:                         d.PolicyURI,
		TermsOfServiceURI:                 d.TermsOfServiceURI,
		ClientURI:                         d.ClientURI,
		LogoURI:                           d.LogoURI,
		Contacts:                          strings.Join(d.Contacts, "|"),
		SecretExpiresAt:                   d.SecretExpiresAt,
		SectorIdentifierURI:               d.SectorIdentifierURI,
		JSONWebKeysURI:                    d.JSONWebKeysURI,
		JSONWebKeys:                       jwks,
		TokenEndpointAuthMethod:           d.TokenEndpointAuthMethod,
		RequestObjectSigningAlgorithm:     d.RequestObjectSigningAlgorithm,
		RequestURIs:                       strings.Join(d.RequestURIs, "|"),
		UserinfoSignedResponseAlg:         d.UserinfoSignedResponseAlg,
		SubjectType:                       d.SubjectType,
		AllowedCORSOrigins:                strings.Join(d.AllowedCORSOrigins, "|"),
		CreatedAt:                         createdAt.Round(time.Second),
		UpdatedAt:                         updatedAt.Round(time.Second),
		FrontChannelLogoutURI:             d.FrontChannelLogoutURI,
		FrontChannelLogoutSessionRequired: d.FrontChannelLogoutSessionRequired,
		PostLogoutRedirectURIs:            strings.Join(d.PostLogoutRedirectURIs, "|"),
		BackChannelLogoutURI:              d.BackChannelLogoutURI,
		BackChannelLogoutSessionRequired:  d.BackChannelLogoutSessionRequired,
	}, nil
}

func (d *sqlData) ToClient() (*Client, error) {
	c := &Client{
		ClientID:                          d.ID,
		Name:                              d.Name,
		Secret:                            d.Secret,
		Audience:                          stringsx.Splitx(d.Audience, "|"),
		RedirectURIs:                      stringsx.Splitx(d.RedirectURIs, "|"),
		GrantTypes:                        stringsx.Splitx(d.GrantTypes, "|"),
		ResponseTypes:                     stringsx.Splitx(d.ResponseTypes, "|"),
		Scope:                             d.Scope,
		Owner:                             d.Owner,
		PolicyURI:                         d.PolicyURI,
		TermsOfServiceURI:                 d.TermsOfServiceURI,
		ClientURI:                         d.ClientURI,
		LogoURI:                           d.LogoURI,
		Contacts:                          stringsx.Splitx(d.Contacts, "|"),
		SecretExpiresAt:                   d.SecretExpiresAt,
		SectorIdentifierURI:               d.SectorIdentifierURI,
		JSONWebKeysURI:                    d.JSONWebKeysURI,
		TokenEndpointAuthMethod:           d.TokenEndpointAuthMethod,
		RequestObjectSigningAlgorithm:     d.RequestObjectSigningAlgorithm,
		RequestURIs:                       stringsx.Splitx(d.RequestURIs, "|"),
		UserinfoSignedResponseAlg:         d.UserinfoSignedResponseAlg,
		SubjectType:                       d.SubjectType,
		AllowedCORSOrigins:                stringsx.Splitx(d.AllowedCORSOrigins, "|"),
		CreatedAt:                         d.CreatedAt,
		UpdatedAt:                         d.UpdatedAt,
		FrontChannelLogoutURI:             d.FrontChannelLogoutURI,
		FrontChannelLogoutSessionRequired: d.FrontChannelLogoutSessionRequired,
		PostLogoutRedirectURIs:            stringsx.Splitx(d.PostLogoutRedirectURIs, "|"),
		BackChannelLogoutURI:              d.BackChannelLogoutURI,
		BackChannelLogoutSessionRequired:  d.BackChannelLogoutSessionRequired,
	}

	if d.JSONWebKeys != "" {
		c.JSONWebKeys = new(jose.JSONWebKeySet)
		if err := json.Unmarshal([]byte(d.JSONWebKeys), &c.JSONWebKeys); err != nil {
			return nil, errors.WithStack(err)
		}
	}

	return c, nil
}

func (m *SQLManager) PlanMigration(dbName string) ([]*migrate.PlannedMigration, error) {
	migrate.SetTable("hydra_client_migration")
	plan, _, err := migrate.PlanMigration(m.DB.DB, dbal.Canonicalize(m.DB.DriverName()), Migrations[dbName], migrate.Up, 0)
	return plan, errors.WithStack(err)
}

func (m *SQLManager) CreateSchemas(dbName string) (int, error) {
	migrate.SetTable("hydra_client_migration")
	n, err := migrate.Exec(m.DB.DB, dbal.Canonicalize(m.DB.DriverName()), Migrations[dbName], migrate.Up)
	if err != nil {
		return 0, errors.Wrapf(err, "Could not migrate sql schema, applied %d Migrations", n)
	}
	return n, nil
}

func (m *SQLManager) GetConcreteClient(ctx context.Context, id string) (*Client, error) {
	var d sqlData
	if err := m.DB.GetContext(ctx, &d, m.DB.Rebind("SELECT * FROM hydra_client WHERE id=?"), id); err != nil {
		return nil, sqlcon.HandleError(err)
	}

	return d.ToClient()
}

func (m *SQLManager) GetClient(ctx context.Context, id string) (fosite.Client, error) {
	return m.GetConcreteClient(ctx, id)
}

func (m *SQLManager) UpdateClient(ctx context.Context, c *Client) error {
	o, err := m.GetClient(ctx, c.GetID())
	if err != nil {
		return errors.WithStack(err)
	}

	if c.Secret == "" {
		c.Secret = string(o.GetHashedSecret())
	} else {
		h, err := m.r.ClientHasher().Hash(ctx, []byte(c.Secret))
		if err != nil {
			return errors.WithStack(err)
		}
		c.Secret = string(h)
	}

	s, err := sqlDataFromClient(c)
	if err != nil {
		return errors.WithStack(err)
	}

	var query []string
	for _, param := range sqlParams {
		query = append(query, fmt.Sprintf("%s=:%s", param, param))
	}

	if _, err := m.DB.NamedExecContext(ctx, fmt.Sprintf(`UPDATE hydra_client SET %s WHERE id=:id`, strings.Join(query, ", ")), s); err != nil {
		return sqlcon.HandleError(err)
	}
	return nil
}

func (m *SQLManager) Authenticate(ctx context.Context, id string, secret []byte) (*Client, error) {
	c, err := m.GetConcreteClient(ctx, id)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	if err := m.r.ClientHasher().Compare(ctx, c.GetHashedSecret(), secret); err != nil {
		return nil, errors.WithStack(err)
	}

	return c, nil
}

func (m *SQLManager) CreateClient(ctx context.Context, c *Client) error {
	h, err := m.r.ClientHasher().Hash(ctx, []byte(c.Secret))
	if err != nil {
		return errors.WithStack(err)
	}
	c.Secret = string(h)

	data, err := sqlDataFromClient(c)
	if err != nil {
		return errors.WithStack(err)
	}

	if _, err := m.DB.NamedExecContext(ctx, fmt.Sprintf(
		"INSERT INTO hydra_client (%s) VALUES (%s)",
		strings.Join(sqlParams, ", "),
		":"+strings.Join(sqlParams, ", :"),
	), data); err != nil {
		return sqlcon.HandleError(err)
	}

	return nil
}

func (m *SQLManager) DeleteClient(ctx context.Context, id string) error {
	if _, err := m.DB.ExecContext(ctx, m.DB.Rebind(`DELETE FROM hydra_client WHERE id=?`), id); err != nil {
		return sqlcon.HandleError(err)
	}
	return nil
}

func (m *SQLManager) GetClients(ctx context.Context, limit, offset int) (clients []Client, err error) {
	d := make([]sqlData, 0)

	if err := m.DB.SelectContext(ctx, &d, m.DB.Rebind("SELECT * FROM hydra_client ORDER BY id LIMIT ? OFFSET ?"), limit, offset); err != nil {
		return nil, sqlcon.HandleError(err)
	}

	clients = make([]Client, len(d))
	for i, k := range d {
		c, err := k.ToClient()
		if err != nil {
			return nil, errors.WithStack(err)
		}

		clients[i] = *c
	}

	return clients, nil
}

func (m *SQLManager) CountClients(ctx context.Context) (int, error) {
	var n int
	if err := m.DB.QueryRow("SELECT count(*) FROM hydra_client").Scan(&n); err != nil {
		fmt.Println(err.Error())
		return 0, sqlcon.HandleError(err)
	}

	return n, nil
}
<commit_msg>client: Change pk field to int64 (#1597)<commit_after>/*
 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 * 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.
 *
 * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
 * @license 	Apache-2.0
 */

package client

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

	"github.com/jmoiron/sqlx"
	"github.com/pkg/errors"
	migrate "github.com/rubenv/sql-migrate"
	"github.com/sirupsen/logrus"
	"gopkg.in/square/go-jose.v2"

	"github.com/ory/fosite"
	"github.com/ory/x/dbal"
	"github.com/ory/x/sqlcon"
	"github.com/ory/x/stringsx"
)

var Migrations = map[string]*dbal.PackrMigrationSource{
	dbal.DriverMySQL:       dbal.NewMustPackerMigrationSource(logrus.New(), AssetNames(), Asset, []string{"migrations/sql/shared", "migrations/sql/mysql"}, true),
	dbal.DriverPostgreSQL:  dbal.NewMustPackerMigrationSource(logrus.New(), AssetNames(), Asset, []string{"migrations/sql/shared", "migrations/sql/postgres"}, true),
	dbal.DriverCockroachDB: dbal.NewMustPackerMigrationSource(logrus.New(), AssetNames(), Asset, []string{"migrations/sql/cockroach"}, true),
}

func NewSQLManager(db *sqlx.DB, r InternalRegistry) *SQLManager {
	return &SQLManager{
		r:  r,
		DB: db,
	}
}

type SQLManager struct {
	r  InternalRegistry
	DB *sqlx.DB
}

type sqlData struct {
	PK                                int64     `db:"pk"`
	ID                                string    `db:"id"`
	Name                              string    `db:"client_name"`
	Secret                            string    `db:"client_secret"`
	RedirectURIs                      string    `db:"redirect_uris"`
	GrantTypes                        string    `db:"grant_types"`
	ResponseTypes                     string    `db:"response_types"`
	Scope                             string    `db:"scope"`
	Owner                             string    `db:"owner"`
	PolicyURI                         string    `db:"policy_uri"`
	TermsOfServiceURI                 string    `db:"tos_uri"`
	ClientURI                         string    `db:"client_uri"`
	LogoURI                           string    `db:"logo_uri"`
	Contacts                          string    `db:"contacts"`
	SecretExpiresAt                   int       `db:"client_secret_expires_at"`
	SectorIdentifierURI               string    `db:"sector_identifier_uri"`
	JSONWebKeysURI                    string    `db:"jwks_uri"`
	JSONWebKeys                       string    `db:"jwks"`
	TokenEndpointAuthMethod           string    `db:"token_endpoint_auth_method"`
	RequestURIs                       string    `db:"request_uris"`
	SubjectType                       string    `db:"subject_type"`
	RequestObjectSigningAlgorithm     string    `db:"request_object_signing_alg"`
	UserinfoSignedResponseAlg         string    `db:"userinfo_signed_response_alg"`
	AllowedCORSOrigins                string    `db:"allowed_cors_origins"`
	Audience                          string    `db:"audience"`
	UpdatedAt                         time.Time `db:"updated_at"`
	CreatedAt                         time.Time `db:"created_at"`
	FrontChannelLogoutURI             string    `db:"frontchannel_logout_uri"`
	FrontChannelLogoutSessionRequired bool      `db:"frontchannel_logout_session_required"`
	PostLogoutRedirectURIs            string    `db:"post_logout_redirect_uris"`
	BackChannelLogoutURI              string    `db:"backchannel_logout_uri"`
	BackChannelLogoutSessionRequired  bool      `db:"backchannel_logout_session_required"`
}

var sqlParams = []string{
	"id",
	"client_name",
	"client_secret",
	"redirect_uris",
	"grant_types",
	"response_types",
	"scope",
	"owner",
	"policy_uri",
	"tos_uri",
	"client_uri",
	"subject_type",
	"logo_uri",
	"contacts",
	"client_secret_expires_at",
	"sector_identifier_uri",
	"jwks",
	"jwks_uri",
	"token_endpoint_auth_method",
	"request_uris",
	"request_object_signing_alg",
	"userinfo_signed_response_alg",
	"allowed_cors_origins",
	"audience",
	"updated_at",
	"created_at",
	"frontchannel_logout_uri",
	"frontchannel_logout_session_required",
	"post_logout_redirect_uris",
	"backchannel_logout_uri",
	"backchannel_logout_session_required",
}

func sqlDataFromClient(d *Client) (*sqlData, error) {
	jwks := ""

	if d.JSONWebKeys != nil {
		out, err := json.Marshal(d.JSONWebKeys)
		if err != nil {
			return nil, errors.WithStack(err)
		}
		jwks = string(out)
	}

	var createdAt, updatedAt = d.CreatedAt, d.UpdatedAt

	if d.CreatedAt.IsZero() {
		createdAt = time.Now()
	}

	if d.UpdatedAt.IsZero() {
		updatedAt = time.Now()
	}

	return &sqlData{
		ID:                                d.GetID(),
		Name:                              d.Name,
		Secret:                            d.Secret,
		RedirectURIs:                      strings.Join(d.RedirectURIs, "|"),
		Audience:                          strings.Join(d.Audience, "|"),
		GrantTypes:                        strings.Join(d.GrantTypes, "|"),
		ResponseTypes:                     strings.Join(d.ResponseTypes, "|"),
		Scope:                             d.Scope,
		Owner:                             d.Owner,
		PolicyURI:                         d.PolicyURI,
		TermsOfServiceURI:                 d.TermsOfServiceURI,
		ClientURI:                         d.ClientURI,
		LogoURI:                           d.LogoURI,
		Contacts:                          strings.Join(d.Contacts, "|"),
		SecretExpiresAt:                   d.SecretExpiresAt,
		SectorIdentifierURI:               d.SectorIdentifierURI,
		JSONWebKeysURI:                    d.JSONWebKeysURI,
		JSONWebKeys:                       jwks,
		TokenEndpointAuthMethod:           d.TokenEndpointAuthMethod,
		RequestObjectSigningAlgorithm:     d.RequestObjectSigningAlgorithm,
		RequestURIs:                       strings.Join(d.RequestURIs, "|"),
		UserinfoSignedResponseAlg:         d.UserinfoSignedResponseAlg,
		SubjectType:                       d.SubjectType,
		AllowedCORSOrigins:                strings.Join(d.AllowedCORSOrigins, "|"),
		CreatedAt:                         createdAt.Round(time.Second),
		UpdatedAt:                         updatedAt.Round(time.Second),
		FrontChannelLogoutURI:             d.FrontChannelLogoutURI,
		FrontChannelLogoutSessionRequired: d.FrontChannelLogoutSessionRequired,
		PostLogoutRedirectURIs:            strings.Join(d.PostLogoutRedirectURIs, "|"),
		BackChannelLogoutURI:              d.BackChannelLogoutURI,
		BackChannelLogoutSessionRequired:  d.BackChannelLogoutSessionRequired,
	}, nil
}

func (d *sqlData) ToClient() (*Client, error) {
	c := &Client{
		ClientID:                          d.ID,
		Name:                              d.Name,
		Secret:                            d.Secret,
		Audience:                          stringsx.Splitx(d.Audience, "|"),
		RedirectURIs:                      stringsx.Splitx(d.RedirectURIs, "|"),
		GrantTypes:                        stringsx.Splitx(d.GrantTypes, "|"),
		ResponseTypes:                     stringsx.Splitx(d.ResponseTypes, "|"),
		Scope:                             d.Scope,
		Owner:                             d.Owner,
		PolicyURI:                         d.PolicyURI,
		TermsOfServiceURI:                 d.TermsOfServiceURI,
		ClientURI:                         d.ClientURI,
		LogoURI:                           d.LogoURI,
		Contacts:                          stringsx.Splitx(d.Contacts, "|"),
		SecretExpiresAt:                   d.SecretExpiresAt,
		SectorIdentifierURI:               d.SectorIdentifierURI,
		JSONWebKeysURI:                    d.JSONWebKeysURI,
		TokenEndpointAuthMethod:           d.TokenEndpointAuthMethod,
		RequestObjectSigningAlgorithm:     d.RequestObjectSigningAlgorithm,
		RequestURIs:                       stringsx.Splitx(d.RequestURIs, "|"),
		UserinfoSignedResponseAlg:         d.UserinfoSignedResponseAlg,
		SubjectType:                       d.SubjectType,
		AllowedCORSOrigins:                stringsx.Splitx(d.AllowedCORSOrigins, "|"),
		CreatedAt:                         d.CreatedAt,
		UpdatedAt:                         d.UpdatedAt,
		FrontChannelLogoutURI:             d.FrontChannelLogoutURI,
		FrontChannelLogoutSessionRequired: d.FrontChannelLogoutSessionRequired,
		PostLogoutRedirectURIs:            stringsx.Splitx(d.PostLogoutRedirectURIs, "|"),
		BackChannelLogoutURI:              d.BackChannelLogoutURI,
		BackChannelLogoutSessionRequired:  d.BackChannelLogoutSessionRequired,
	}

	if d.JSONWebKeys != "" {
		c.JSONWebKeys = new(jose.JSONWebKeySet)
		if err := json.Unmarshal([]byte(d.JSONWebKeys), &c.JSONWebKeys); err != nil {
			return nil, errors.WithStack(err)
		}
	}

	return c, nil
}

func (m *SQLManager) PlanMigration(dbName string) ([]*migrate.PlannedMigration, error) {
	migrate.SetTable("hydra_client_migration")
	plan, _, err := migrate.PlanMigration(m.DB.DB, dbal.Canonicalize(m.DB.DriverName()), Migrations[dbName], migrate.Up, 0)
	return plan, errors.WithStack(err)
}

func (m *SQLManager) CreateSchemas(dbName string) (int, error) {
	migrate.SetTable("hydra_client_migration")
	n, err := migrate.Exec(m.DB.DB, dbal.Canonicalize(m.DB.DriverName()), Migrations[dbName], migrate.Up)
	if err != nil {
		return 0, errors.Wrapf(err, "Could not migrate sql schema, applied %d Migrations", n)
	}
	return n, nil
}

func (m *SQLManager) GetConcreteClient(ctx context.Context, id string) (*Client, error) {
	var d sqlData
	if err := m.DB.GetContext(ctx, &d, m.DB.Rebind("SELECT * FROM hydra_client WHERE id=?"), id); err != nil {
		return nil, sqlcon.HandleError(err)
	}

	return d.ToClient()
}

func (m *SQLManager) GetClient(ctx context.Context, id string) (fosite.Client, error) {
	return m.GetConcreteClient(ctx, id)
}

func (m *SQLManager) UpdateClient(ctx context.Context, c *Client) error {
	o, err := m.GetClient(ctx, c.GetID())
	if err != nil {
		return errors.WithStack(err)
	}

	if c.Secret == "" {
		c.Secret = string(o.GetHashedSecret())
	} else {
		h, err := m.r.ClientHasher().Hash(ctx, []byte(c.Secret))
		if err != nil {
			return errors.WithStack(err)
		}
		c.Secret = string(h)
	}

	s, err := sqlDataFromClient(c)
	if err != nil {
		return errors.WithStack(err)
	}

	var query []string
	for _, param := range sqlParams {
		query = append(query, fmt.Sprintf("%s=:%s", param, param))
	}

	if _, err := m.DB.NamedExecContext(ctx, fmt.Sprintf(`UPDATE hydra_client SET %s WHERE id=:id`, strings.Join(query, ", ")), s); err != nil {
		return sqlcon.HandleError(err)
	}
	return nil
}

func (m *SQLManager) Authenticate(ctx context.Context, id string, secret []byte) (*Client, error) {
	c, err := m.GetConcreteClient(ctx, id)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	if err := m.r.ClientHasher().Compare(ctx, c.GetHashedSecret(), secret); err != nil {
		return nil, errors.WithStack(err)
	}

	return c, nil
}

func (m *SQLManager) CreateClient(ctx context.Context, c *Client) error {
	h, err := m.r.ClientHasher().Hash(ctx, []byte(c.Secret))
	if err != nil {
		return errors.WithStack(err)
	}
	c.Secret = string(h)

	data, err := sqlDataFromClient(c)
	if err != nil {
		return errors.WithStack(err)
	}

	if _, err := m.DB.NamedExecContext(ctx, fmt.Sprintf(
		"INSERT INTO hydra_client (%s) VALUES (%s)",
		strings.Join(sqlParams, ", "),
		":"+strings.Join(sqlParams, ", :"),
	), data); err != nil {
		return sqlcon.HandleError(err)
	}

	return nil
}

func (m *SQLManager) DeleteClient(ctx context.Context, id string) error {
	if _, err := m.DB.ExecContext(ctx, m.DB.Rebind(`DELETE FROM hydra_client WHERE id=?`), id); err != nil {
		return sqlcon.HandleError(err)
	}
	return nil
}

func (m *SQLManager) GetClients(ctx context.Context, limit, offset int) (clients []Client, err error) {
	d := make([]sqlData, 0)

	if err := m.DB.SelectContext(ctx, &d, m.DB.Rebind("SELECT * FROM hydra_client ORDER BY id LIMIT ? OFFSET ?"), limit, offset); err != nil {
		return nil, sqlcon.HandleError(err)
	}

	clients = make([]Client, len(d))
	for i, k := range d {
		c, err := k.ToClient()
		if err != nil {
			return nil, errors.WithStack(err)
		}

		clients[i] = *c
	}

	return clients, nil
}

func (m *SQLManager) CountClients(ctx context.Context) (int, error) {
	var n int
	if err := m.DB.QueryRow("SELECT count(*) FROM hydra_client").Scan(&n); err != nil {
		fmt.Println(err.Error())
		return 0, sqlcon.HandleError(err)
	}

	return n, nil
}
<|endoftext|>