<commit_before>// Package quantile computes approximate quantiles over an unbounded data
// stream within low memory and CPU bounds.
//
// A small amount of accuracy is traded to achieve the above properties.
//
// Multiple streams can be merged before calling Query to generate a single set
// of results. This is meaningful when the streams represent the same type of
// data. See Merge and Samples.
//
// For more detailed information about the algorithm used, see:
//
// Effective Computation of Biased Quantiles over Data Streams
//
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
package quantile

import (
	"container/list"
	"math"
	"sort"
)

// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
	Value float64 `json:",string"`
	Width float64 `json:",string"`
	Delta float64 `json:",string"`
}

// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample

func (a Samples) Len() int {
	return len(a)
}

func (a Samples) Less(i, j int) bool {
	return a[i].Value < a[j].Value
}

func (a Samples) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type invariant func(s *stream, r float64) float64

// NewBiased returns an initialized Stream for high-biased quantiles (e.g.
// 50th, 90th, 99th) not known a priori with ﬁner error guarantees for the
// higher ranks of the data distribution.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewBiased() *Stream {
	ƒ := func(s *stream, r float64) float64 {
		return 2 * s.epsilon * r
	}
	return newStream(ƒ)
}

// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(quantiles ...float64) *Stream {
	ƒ := func(s *stream, r float64) float64 {
		var m float64 = math.MaxFloat64
		var f float64
		for _, q := range quantiles {
			if q*s.n <= r {
				f = (2 * s.epsilon * r) / q
			} else {
				f = (2 * s.epsilon * (s.n - r)) / (1 - q)
			}
			m = math.Min(m, f)
		}
		return m
	}
	return newStream(ƒ)
}

// Stream computes quantiles for a stream of float64s. It is not thread-safe.
type Stream struct {
	*stream
	b Samples
}

func newStream(ƒ invariant) *Stream {
	const defaultEpsilon = 0.01
	x := &stream{epsilon: defaultEpsilon, ƒ: ƒ, l: list.New()}
	return &Stream{x, make(Samples, 0, 500)}
}

// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
	s.insert(Sample{Value: v, Width: 1})
}

func (s *Stream) insert(sample Sample) {
	s.b = append(s.b, sample)
	if len(s.b) == cap(s.b) {
		s.flush()
		s.compress()
	}
}

// Query returns the computed qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
	if s.flushed() {
		// Fast path when there hasn't been enough data for a flush;
		// this also yeilds better accuracy for small sets of data.
		i := float64(len(s.b)) * q
		return s.b[int(i)-1].Value
	}
	s.flush()
	return s.stream.query(q)
}

// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
func (s *Stream) Merge(samples Samples) {
	s.stream.merge(samples)
}

// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
	s.stream.reset()
	s.b = s.b[:0]
}

// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
	if !s.flushed() {
		return s.b
	}
	return s.stream.samples()
}

// Count returns the total number of samples observed in the stream
// since initialization.
func (s *Stream) Count() int {
	return len(s.b) + s.stream.count()
}

func (s *Stream) flush() {
	sort.Sort(s.b)
	s.stream.merge(s.b)
	s.b = s.b[:0]
}

func (s *Stream) flushed() bool {
	return s.stream.l.Len() == 0
}

type stream struct {
	epsilon float64
	n       float64
	l       *list.List
	ƒ       invariant
}

// SetEpsilon sets the error epsilon for the Stream. The default epsilon is
// 0.01 and is usually satisfactory. If needed, this must be called before all
// Inserts.
// To learn more, see: http://www.cs.rutgers.edu/~muthu/bquant.pdf
func (s *stream) SetEpsilon(epsilon float64) {
	s.epsilon = epsilon
}

func (s *stream) reset() {
	s.l.Init()
	s.n = 0
}

func (s *stream) insert(v float64) {
	fn := s.mergeFunc()
	fn(v, 1)
}

func (s *stream) merge(samples Samples) {
	fn := s.mergeFunc()
	for _, s := range samples {
		fn(s.Value, s.Width)
	}
}

func (s *stream) mergeFunc() func(v, w float64) {
	// NOTE: I used a goto over defer because it bought me a few extra
	// nanoseconds. I know. I know.
	var r float64
	e := s.l.Front()
	return func(v, w float64) {
		for ; e != nil; e = e.Next() {
			c := e.Value.(*Sample)
			if c.Value > v {
				sm := &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1}
				s.l.InsertBefore(sm, e)
				goto inserted
			}
			r += c.Width
		}
		s.l.PushBack(&Sample{v, w, 0})
	inserted:
		s.n += w
	}
}

func (s *stream) count() int {
	return int(s.n)
}

func (s *stream) query(q float64) float64 {
	e := s.l.Front()
	t := math.Ceil(q * s.n)
	t += math.Ceil(s.ƒ(s, t) / 2)
	p := e.Value.(*Sample)
	e = e.Next()
	r := float64(0)
	for e != nil {
		c := e.Value.(*Sample)
		if r+c.Width+c.Delta > t {
			return p.Value
		}
		r += p.Width
		p = c
		e = e.Next()
	}
	return p.Value
}

func (s *stream) compress() {
	if s.l.Len() < 2 {
		return
	}
	e := s.l.Back()
	x := e.Value.(*Sample)
	r := s.n - 1 - x.Width
	e = e.Prev()
	for e != nil {
		c := e.Value.(*Sample)
		if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
			x.Width += c.Width
			o := e
			e = e.Prev()
			s.l.Remove(o)
		} else {
			x = c
			e = e.Prev()
		}
		r -= c.Width
	}
}

func (s *stream) samples() Samples {
	samples := make(Samples, 0, s.l.Len())
	for e := s.l.Front(); e != nil; e = e.Next() {
		samples = append(samples, *e.Value.(*Sample))
	}
	return samples
}
<commit_msg>fix #1 - index out of range<commit_after>// Package quantile computes approximate quantiles over an unbounded data
// stream within low memory and CPU bounds.
//
// A small amount of accuracy is traded to achieve the above properties.
//
// Multiple streams can be merged before calling Query to generate a single set
// of results. This is meaningful when the streams represent the same type of
// data. See Merge and Samples.
//
// For more detailed information about the algorithm used, see:
//
// Effective Computation of Biased Quantiles over Data Streams
//
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
package quantile

import (
	"container/list"
	"math"
	"sort"
)

// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
	Value float64 `json:",string"`
	Width float64 `json:",string"`
	Delta float64 `json:",string"`
}

// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample

func (a Samples) Len() int {
	return len(a)
}

func (a Samples) Less(i, j int) bool {
	return a[i].Value < a[j].Value
}

func (a Samples) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type invariant func(s *stream, r float64) float64

// NewBiased returns an initialized Stream for high-biased quantiles (e.g.
// 50th, 90th, 99th) not known a priori with ﬁner error guarantees for the
// higher ranks of the data distribution.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewBiased() *Stream {
	ƒ := func(s *stream, r float64) float64 {
		return 2 * s.epsilon * r
	}
	return newStream(ƒ)
}

// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(quantiles ...float64) *Stream {
	ƒ := func(s *stream, r float64) float64 {
		var m float64 = math.MaxFloat64
		var f float64
		for _, q := range quantiles {
			if q*s.n <= r {
				f = (2 * s.epsilon * r) / q
			} else {
				f = (2 * s.epsilon * (s.n - r)) / (1 - q)
			}
			m = math.Min(m, f)
		}
		return m
	}
	return newStream(ƒ)
}

// Stream computes quantiles for a stream of float64s. It is not thread-safe.
type Stream struct {
	*stream
	b Samples
}

func newStream(ƒ invariant) *Stream {
	const defaultEpsilon = 0.01
	x := &stream{epsilon: defaultEpsilon, ƒ: ƒ, l: list.New()}
	return &Stream{x, make(Samples, 0, 500)}
}

// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
	s.insert(Sample{Value: v, Width: 1})
}

func (s *Stream) insert(sample Sample) {
	s.b = append(s.b, sample)
	if len(s.b) == cap(s.b) {
		s.flush()
		s.compress()
	}
}

// Query returns the computed qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
	if s.flushed() {
		// Fast path when there hasn't been enough data for a flush;
		// this also yeilds better accuracy for small sets of data.
		i := int(float64(len(s.b)) * q)
		if i > 0 {
			i -= 1
		}
		return s.b[i].Value
	}
	s.flush()
	return s.stream.query(q)
}

// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
func (s *Stream) Merge(samples Samples) {
	s.stream.merge(samples)
}

// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
	s.stream.reset()
	s.b = s.b[:0]
}

// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
	if !s.flushed() {
		return s.b
	}
	return s.stream.samples()
}

// Count returns the total number of samples observed in the stream
// since initialization.
func (s *Stream) Count() int {
	return len(s.b) + s.stream.count()
}

func (s *Stream) flush() {
	sort.Sort(s.b)
	s.stream.merge(s.b)
	s.b = s.b[:0]
}

func (s *Stream) flushed() bool {
	return s.stream.l.Len() == 0
}

type stream struct {
	epsilon float64
	n       float64
	l       *list.List
	ƒ       invariant
}

// SetEpsilon sets the error epsilon for the Stream. The default epsilon is
// 0.01 and is usually satisfactory. If needed, this must be called before all
// Inserts.
// To learn more, see: http://www.cs.rutgers.edu/~muthu/bquant.pdf
func (s *stream) SetEpsilon(epsilon float64) {
	s.epsilon = epsilon
}

func (s *stream) reset() {
	s.l.Init()
	s.n = 0
}

func (s *stream) insert(v float64) {
	fn := s.mergeFunc()
	fn(v, 1)
}

func (s *stream) merge(samples Samples) {
	fn := s.mergeFunc()
	for _, s := range samples {
		fn(s.Value, s.Width)
	}
}

func (s *stream) mergeFunc() func(v, w float64) {
	// NOTE: I used a goto over defer because it bought me a few extra
	// nanoseconds. I know. I know.
	var r float64
	e := s.l.Front()
	return func(v, w float64) {
		for ; e != nil; e = e.Next() {
			c := e.Value.(*Sample)
			if c.Value > v {
				sm := &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1}
				s.l.InsertBefore(sm, e)
				goto inserted
			}
			r += c.Width
		}
		s.l.PushBack(&Sample{v, w, 0})
	inserted:
		s.n += w
	}
}

func (s *stream) count() int {
	return int(s.n)
}

func (s *stream) query(q float64) float64 {
	e := s.l.Front()
	t := math.Ceil(q * s.n)
	t += math.Ceil(s.ƒ(s, t) / 2)
	p := e.Value.(*Sample)
	e = e.Next()
	r := float64(0)
	for e != nil {
		c := e.Value.(*Sample)
		if r+c.Width+c.Delta > t {
			return p.Value
		}
		r += p.Width
		p = c
		e = e.Next()
	}
	return p.Value
}

func (s *stream) compress() {
	if s.l.Len() < 2 {
		return
	}
	e := s.l.Back()
	x := e.Value.(*Sample)
	r := s.n - 1 - x.Width
	e = e.Prev()
	for e != nil {
		c := e.Value.(*Sample)
		if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
			x.Width += c.Width
			o := e
			e = e.Prev()
			s.l.Remove(o)
		} else {
			x = c
			e = e.Prev()
		}
		r -= c.Width
	}
}

func (s *stream) samples() Samples {
	samples := make(Samples, 0, s.l.Len())
	for e := s.l.Front(); e != nil; e = e.Next() {
		samples = append(samples, *e.Value.(*Sample))
	}
	return samples
}
<|endoftext|>


<commit_before>// Package quantile computes approximate quantiles over an unbounded data
// stream within low memory and CPU bounds.
//
// A small amount of accuracy is traded to achieve the above properties.
//
// Multiple streams can be merged before calling Query to generate a single set
// of results. This is meaningful when the streams represent the same type of
// data. See Merge and Samples.
//
// For more detailed information about the algorithm, see:
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
// The quantile package implements the algorithm in the paper Effective
// Computation of Biased Quantiles over Data Streams with both invariants.
package quantile

import (
	"container/list"
	"math"
	"sort"
)

// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
	Value float64 `json:",string"`
	Width float64 `json:",string"`
	Delta float64 `json:",string"`
}

// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample

func (a Samples) Len() int {
	return len(a)
}

func (a Samples) Less(i, j int) bool {
	return a[i].Value < a[j].Value
}

func (a Samples) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type invariant func(s *stream, r float64) float64

// NewBiased returns an initialized Stream for high-biased quantiles (e.g.
// 50th, 90th, 99th) not known a priori with ﬁner error guarantees for the
// higher ranks of the data distribution.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewBiased() *Stream {
	f := func(s *stream, r float64) float64 {
		return 2 * s.epsilon * r
	}
	return newStream(f)
}

// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(quantiles ...float64) *Stream {
	f := func(s *stream, r float64) float64 {
		var m float64 = math.MaxFloat64
		var f float64
		for _, q := range quantiles {
			if q*s.n <= r {
				f = (2 * s.epsilon * r) / q
			} else {
				f = (2 * s.epsilon * (s.n - r)) / (1 - q)
			}
			m = math.Min(m, f)
		}
		return m
	}
	return newStream(f)
}

// Stream computes quantiles for a stream of float64s. It is not thread-safe.
type Stream struct {
	*stream
	b Samples
}

func newStream(ƒ invariant) *Stream {
	const defaultEpsilon = 0.01
	x := &stream{epsilon: defaultEpsilon, ƒ: ƒ, l: list.New()}
	return &Stream{x, make(Samples, 0, 500)}
}

// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
	s.insert(Sample{Value: v, Width: 1})
}

func (s *Stream) insert(sample Sample) {
	s.b = append(s.b, sample)
	if len(s.b) == cap(s.b) {
		s.flush()
		s.compress()
	}
}

// Query returns the computes qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
	if s.flushed() {
		// Fast path when there hasn't been enough data for a flush;
		// this also yeilds better accuracy for small sets of data.
		i := float64(len(s.b)) * q
		return s.b[int(i)-1].Value
	}
	s.flush()
	return s.stream.query(q)
}

// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
func (s *Stream) Merge(samples Samples) {
	s.stream.merge(samples)
}

// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
	s.stream.reset()
	s.b = s.b[:0]
}

// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
	if !s.flushed() {
		return s.b
	}
	return s.stream.samples()
}

// Count returns the total number of samples observed in the stream
// since initialization.
func (s *Stream) Count() int {
	return len(s.b) + s.stream.count()
}

func (s *Stream) flush() {
	sort.Sort(s.b)
	s.stream.merge(s.b)
	s.b = s.b[:0]
}

func (s *Stream) flushed() bool {
	return s.stream.l.Len() == 0
}

type stream struct {
	epsilon float64
	n       float64
	l       *list.List
	ƒ       invariant
}

// SetEpsilon sets the error epsilon for the Stream. The default epsilon is
// 0.01 and is usually satisfactory. If needed, this must be called before all
// Inserts.
// To learn more, see: http://www.cs.rutgers.edu/~muthu/bquant.pdf
func (s *stream) SetEpsilon(epsilon float64) {
	s.epsilon = epsilon
}

func (s *stream) reset() {
	s.l.Init()
	s.n = 0
}

func (s *stream) insert(v float64) {
	fn := s.mergeFunc()
	fn(v, 1)
}

func (s *stream) merge(samples Samples) {
	fn := s.mergeFunc()
	for _, s := range samples {
		fn(s.Value, s.Width)
	}
}

func (s *stream) mergeFunc() func(v, w float64) {
	// NOTE: I used a goto over defer because it bought me a few extra
	// nanoseconds. I know. I know.
	var r float64
	e := s.l.Front()
	return func(v, w float64) {
		for ; e != nil; e = e.Next() {
			c := e.Value.(*Sample)
			if c.Value > v {
				sm := &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1}
				s.l.InsertBefore(sm, e)
				goto inserted
			}
			r += c.Width
		}
		s.l.PushBack(&Sample{v, w, 0})
	inserted:
		s.n += w
	}
}

func (s *stream) count() int {
	return int(s.n)
}

func (s *stream) query(q float64) float64 {
	e := s.l.Front()
	t := math.Ceil(q * s.n)
	t += math.Ceil(s.ƒ(s, t) / 2)
	p := e.Value.(*Sample)
	e = e.Next()
	r := float64(0)
	for e != nil {
		c := e.Value.(*Sample)
		if r+c.Width+c.Delta > t {
			return p.Value
		}
		r += p.Width
		p = c
		e = e.Next()
	}
	return p.Value
}

func (s *stream) compress() {
	if s.l.Len() < 2 {
		return
	}
	e := s.l.Back()
	x := e.Value.(*Sample)
	r := s.n - 1 - x.Width
	e = e.Prev()
	for e != nil {
		c := e.Value.(*Sample)
		if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
			x.Width += c.Width
			o := e
			e = e.Prev()
			s.l.Remove(o)
		} else {
			x = c
			e = e.Prev()
		}
		r -= c.Width
	}
}

func (s *stream) samples() Samples {
	samples := make(Samples, 0, s.l.Len())
	for e := s.l.Front(); e != nil; e = e.Next() {
		samples = append(samples, *e.Value.(*Sample))
	}
	return samples
}
<commit_msg>fix typo<commit_after>// Package quantile computes approximate quantiles over an unbounded data
// stream within low memory and CPU bounds.
//
// A small amount of accuracy is traded to achieve the above properties.
//
// Multiple streams can be merged before calling Query to generate a single set
// of results. This is meaningful when the streams represent the same type of
// data. See Merge and Samples.
//
// For more detailed information about the algorithm, see:
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
// The quantile package implements the algorithm in the paper Effective
// Computation of Biased Quantiles over Data Streams with both invariants.
package quantile

import (
	"container/list"
	"math"
	"sort"
)

// Sample holds an observed value and meta information for compression. JSON
// tags have been added for convenience.
type Sample struct {
	Value float64 `json:",string"`
	Width float64 `json:",string"`
	Delta float64 `json:",string"`
}

// Samples represents a slice of samples. It implements sort.Interface.
type Samples []Sample

func (a Samples) Len() int {
	return len(a)
}

func (a Samples) Less(i, j int) bool {
	return a[i].Value < a[j].Value
}

func (a Samples) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

type invariant func(s *stream, r float64) float64

// NewBiased returns an initialized Stream for high-biased quantiles (e.g.
// 50th, 90th, 99th) not known a priori with ﬁner error guarantees for the
// higher ranks of the data distribution.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewBiased() *Stream {
	f := func(s *stream, r float64) float64 {
		return 2 * s.epsilon * r
	}
	return newStream(f)
}

// NewTargeted returns an initialized Stream concerned with a particular set of
// quantile values that are supplied a priori. Knowing these a priori reduces
// space and computation time.
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
func NewTargeted(quantiles ...float64) *Stream {
	f := func(s *stream, r float64) float64 {
		var m float64 = math.MaxFloat64
		var f float64
		for _, q := range quantiles {
			if q*s.n <= r {
				f = (2 * s.epsilon * r) / q
			} else {
				f = (2 * s.epsilon * (s.n - r)) / (1 - q)
			}
			m = math.Min(m, f)
		}
		return m
	}
	return newStream(f)
}

// Stream computes quantiles for a stream of float64s. It is not thread-safe.
type Stream struct {
	*stream
	b Samples
}

func newStream(ƒ invariant) *Stream {
	const defaultEpsilon = 0.01
	x := &stream{epsilon: defaultEpsilon, ƒ: ƒ, l: list.New()}
	return &Stream{x, make(Samples, 0, 500)}
}

// Insert inserts v into the stream.
func (s *Stream) Insert(v float64) {
	s.insert(Sample{Value: v, Width: 1})
}

func (s *Stream) insert(sample Sample) {
	s.b = append(s.b, sample)
	if len(s.b) == cap(s.b) {
		s.flush()
		s.compress()
	}
}

// Query returns the computed qth percentiles value. If s was created with
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
// will return an unspecified result.
func (s *Stream) Query(q float64) float64 {
	if s.flushed() {
		// Fast path when there hasn't been enough data for a flush;
		// this also yeilds better accuracy for small sets of data.
		i := float64(len(s.b)) * q
		return s.b[int(i)-1].Value
	}
	s.flush()
	return s.stream.query(q)
}

// Merge merges samples into the underlying streams samples. This is handy when
// merging multiple streams from separate threads, database shards, etc.
func (s *Stream) Merge(samples Samples) {
	s.stream.merge(samples)
}

// Reset reinitializes and clears the list reusing the samples buffer memory.
func (s *Stream) Reset() {
	s.stream.reset()
	s.b = s.b[:0]
}

// Samples returns stream samples held by s.
func (s *Stream) Samples() Samples {
	if !s.flushed() {
		return s.b
	}
	return s.stream.samples()
}

// Count returns the total number of samples observed in the stream
// since initialization.
func (s *Stream) Count() int {
	return len(s.b) + s.stream.count()
}

func (s *Stream) flush() {
	sort.Sort(s.b)
	s.stream.merge(s.b)
	s.b = s.b[:0]
}

func (s *Stream) flushed() bool {
	return s.stream.l.Len() == 0
}

type stream struct {
	epsilon float64
	n       float64
	l       *list.List
	ƒ       invariant
}

// SetEpsilon sets the error epsilon for the Stream. The default epsilon is
// 0.01 and is usually satisfactory. If needed, this must be called before all
// Inserts.
// To learn more, see: http://www.cs.rutgers.edu/~muthu/bquant.pdf
func (s *stream) SetEpsilon(epsilon float64) {
	s.epsilon = epsilon
}

func (s *stream) reset() {
	s.l.Init()
	s.n = 0
}

func (s *stream) insert(v float64) {
	fn := s.mergeFunc()
	fn(v, 1)
}

func (s *stream) merge(samples Samples) {
	fn := s.mergeFunc()
	for _, s := range samples {
		fn(s.Value, s.Width)
	}
}

func (s *stream) mergeFunc() func(v, w float64) {
	// NOTE: I used a goto over defer because it bought me a few extra
	// nanoseconds. I know. I know.
	var r float64
	e := s.l.Front()
	return func(v, w float64) {
		for ; e != nil; e = e.Next() {
			c := e.Value.(*Sample)
			if c.Value > v {
				sm := &Sample{v, w, math.Floor(s.ƒ(s, r)) - 1}
				s.l.InsertBefore(sm, e)
				goto inserted
			}
			r += c.Width
		}
		s.l.PushBack(&Sample{v, w, 0})
	inserted:
		s.n += w
	}
}

func (s *stream) count() int {
	return int(s.n)
}

func (s *stream) query(q float64) float64 {
	e := s.l.Front()
	t := math.Ceil(q * s.n)
	t += math.Ceil(s.ƒ(s, t) / 2)
	p := e.Value.(*Sample)
	e = e.Next()
	r := float64(0)
	for e != nil {
		c := e.Value.(*Sample)
		if r+c.Width+c.Delta > t {
			return p.Value
		}
		r += p.Width
		p = c
		e = e.Next()
	}
	return p.Value
}

func (s *stream) compress() {
	if s.l.Len() < 2 {
		return
	}
	e := s.l.Back()
	x := e.Value.(*Sample)
	r := s.n - 1 - x.Width
	e = e.Prev()
	for e != nil {
		c := e.Value.(*Sample)
		if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
			x.Width += c.Width
			o := e
			e = e.Prev()
			s.l.Remove(o)
		} else {
			x = c
			e = e.Prev()
		}
		r -= c.Width
	}
}

func (s *stream) samples() Samples {
	samples := make(Samples, 0, s.l.Len())
	for e := s.l.Front(); e != nil; e = e.Next() {
		samples = append(samples, *e.Value.(*Sample))
	}
	return samples
}
<|endoftext|>


<commit_before>package raft

import (
	"encoding/json"
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/HelloCodeMing/raft-rocks/pb"
	"github.com/HelloCodeMing/raft-rocks/store"
	"github.com/HelloCodeMing/raft-rocks/utils"
	"github.com/golang/glog"
)

// Hold state of raft, coordinate the updating and reading
type raftState struct {
	sync.RWMutex
	log       *store.LogStorage
	persister store.Persister
	applyCh   chan<- *ApplyMsg

	// once initialized, they will not be changed
	Me       int
	NumPeers int

	Role        pb.RaftRole
	CurrentTerm int32
	VotedFor    int32

	// maintained by leader, record follower's repliate and commit state
	MatchIndex []int
	NextIndex  []int

	// in raft paper, these two field is volatile
	// when restart, we need replay all WAL to recover state
	// otherwise, we have to do checkpoint, and record where has beed commited and applied to state machine
	// but when it comes to RocksDB like storage engine, it has its own WAL, so the storage itself is durable
	// but disappointing, even if reading RocksDB's WAL to get LastApplied is possible, it's too tricky
	// An elegant way to avoid 'double WAL' may be stop use WAL in RocksDB, but only in Raft,
	// and in raft we could perioidly do checkpoint/snapshot, persist LastApplied, as a result, we just need to
	// restore state machine from checkpoint, and replay WAL.
	//
	// All above is just my imagination, the real implementation is too naive. When LastApplied is updated, store it in persister.
	CommitIndex int // index of highest log entry known to be committed
	LastApplied int // index of highest log entry applied to state machine

	readLease time.Time
}

func (s *raftState) getRole() pb.RaftRole {
	s.RLock()
	defer s.RUnlock()
	return s.Role
}

// maybe we could cache this field in Raft, to avoid lock
func (s *raftState) getTerm() int32 {
	s.RLock()
	defer s.RUnlock()
	return s.CurrentTerm
}

func (s *raftState) getCommited() int {
	s.RLock()
	defer s.RUnlock()
	return s.CommitIndex
}

const (
	currentTerm = "currentTerm"
	votedFor    = "votedFor"
	commitIndex = "commitIndex"
	lastApplied = "lastApplied"
)

func (s *raftState) persist() {
	s.persister.StoreInt32(currentTerm, s.CurrentTerm)
	s.persister.StoreInt32(votedFor, s.VotedFor)
}

func (s *raftState) restore() {
	var ok bool
	s.CurrentTerm, ok = s.persister.LoadInt32(currentTerm)
	s.VotedFor, ok = s.persister.LoadInt32(votedFor)
	if !ok {
		s.VotedFor = -1
	}
	commit, ok := s.persister.LoadInt32(commitIndex)
	if ok {
		s.CommitIndex = int(commit)
	}
	apply, ok := s.persister.LoadInt32(lastApplied)
	if ok {
		s.LastApplied = int(apply)
	}
}

func (s *raftState) commitUntil(index int) {
	s.CommitIndex = index
	s.persister.StoreInt32(commitIndex, int32(index))
}

func (s *raftState) applyOne() {
	s.LastApplied++
	s.persister.StoreInt32(lastApplied, int32(s.LastApplied))
}

func (s *raftState) checkNewTerm(newTerm int32) bool {
	if s.getTerm() < newTerm {
		s.becomeFollower(-1, newTerm)
		return true
	}
	return false
}
func (s *raftState) checkFollowerCommit(leaderCommit int) bool {
	s.Lock()
	defer s.Unlock()
	// Have uncommited log, but leader has commited them
	if s.CommitIndex < leaderCommit && s.CommitIndex < s.log.LastIndex() {
		s.commitUntil(minInt(leaderCommit, int(s.log.LastIndex())))
		s.checkApply()
		return true
	}
	return false
}

// any command to apply
func (s *raftState) checkApply() {
	old := s.LastApplied
	log := s.log
	for s.CommitIndex > s.LastApplied {
		s.applyOne()
		if log.LastIndex() < s.LastApplied {
			panic(s)
		}
		entry := log.At(s.LastApplied)
		if entry == nil {
			break
		}
		msg := &ApplyMsg{Command: entry}
		s.applyCh <- msg
	}
	if s.LastApplied > old {
		glog.V(utils.VDebug).Infof("%s Applyed commands until index=%d", s.String(), s.LastApplied)
	}
}

func (s *raftState) changeRole(role pb.RaftRole) {
	s.Lock()
	defer s.Unlock()
	s.Role = role

}

func (s *raftState) becomeFollowerUnlocked(candidateID int32, term int32) {
	s.CurrentTerm = term
	s.VotedFor = candidateID
	s.Role = pb.RaftRole_Follower
	s.persist()
}

func (s *raftState) becomeFollower(candidateID int32, term int32) {
	s.Lock()
	defer s.Unlock()
	s.CurrentTerm = term
	s.VotedFor = candidateID
	s.Role = pb.RaftRole_Follower
	s.MatchIndex = nil
	s.NextIndex = nil
	s.persist()
}

func (s *raftState) becomeCandidate() int32 {
	s.Lock()
	defer s.Unlock()
	s.Role = pb.RaftRole_Candidate
	s.CurrentTerm++
	s.VotedFor = int32(s.Me)
	s.MatchIndex = nil
	s.NextIndex = nil
	s.persist()
	return s.CurrentTerm
}

func (s *raftState) becomeLeader() {
	s.Lock()
	defer s.Unlock()
	s.Role = pb.RaftRole_Leader
	s.MatchIndex = make([]int, s.NumPeers)
	s.NextIndex = make([]int, s.NumPeers)
	lastIndex := s.log.LastIndex()
	for i := range s.NextIndex {
		s.NextIndex[i] = lastIndex + 1
	}
}

func (s *raftState) replicatedToPeer(peer int, index int) {
	s.Lock()
	defer s.Unlock()
	s.MatchIndex[peer] = maxInt(index, s.MatchIndex[peer])
	s.NextIndex[peer] = s.MatchIndex[peer] + 1
	if s.checkLeaderCommit() {
		s.checkApply()
	}
}

// update  commit index, use matchIndex from peers
// NOTE: It's not thread-safe, should be synchronized by external lock
func (s *raftState) checkLeaderCommit() (updated bool) {
	lowerIndex := s.CommitIndex + 1
	upperIndex := 0
	// find max matchIndex
	for _, x := range s.MatchIndex {
		if x > upperIndex {
			upperIndex = x
		}
	}
	// if N > commitIndex, a majority of match[i] >= N, and log[N].term == currentTerm
	// set commitIndex = N
	for N := upperIndex; N >= lowerIndex && s.log.At(N).Term == s.CurrentTerm; N-- {
		// count match[i] >= N
		cnt := 1
		for i, x := range s.MatchIndex {
			if i != s.Me && x >= N {
				cnt++
			}
		}
		if cnt >= s.majority() {
			s.commitUntil(N)
			glog.V(utils.VDebug).Infof("%s Leader update commitIndex: %d", s.String(), s.CommitIndex)
			updated = true
			break
		}
	}
	return
}

func (s *raftState) retreatForPeer(peer int) int {
	s.Lock()
	defer s.Unlock()
	idx := &s.NextIndex[peer]
	log := s.log
	*idx = minInt(*idx, log.LastIndex())
	oldTerm := log.At(*idx).Term
	for *idx > 0 && log.At(*idx).Term == oldTerm {
		*idx--
	}
	*idx = maxInt(*idx, 1)
	return *idx
}

func (s *raftState) toReplicate(peer int) int {
	return s.NextIndex[peer]
}

// If lease is granted in this term, and later than the old one, extend the lease
func (s *raftState) updateReadLease(term int32, lease time.Time) {
	if s.readLease.Before(time.Now()) && term == s.CurrentTerm && lease.After(s.readLease) {
		s.readLease = lease
		glog.Infof("%s Update read lease to %v", s.String(), lease)
	}
}

func (s *raftState) String() string {
	return fmt.Sprintf("Raft<%d:%d>", s.Me, s.CurrentTerm)
}

func (s *raftState) dump(writer io.Writer) {
	state := map[string]interface{}{
		"meta":         s,
		"LogLastIndex": s.log.LastIndex(),
	}
	buf, err := json.MarshalIndent(state, "", "\t")
	if err != nil {
		panic(err)
	}
	writer.Write(buf)
}

func (s *raftState) majority() int {
	return s.NumPeers/2 + 1
}

func makeRaftState(log *store.LogStorage, persister store.Persister, applyCh chan<- *ApplyMsg, numPeers int, me int) *raftState {
	s := &raftState{
		log:         log,
		applyCh:     applyCh,
		NumPeers:    numPeers,
		Me:          me,
		CurrentTerm: 0,
		VotedFor:    -1,
		Role:        pb.RaftRole_Follower,
		readLease:   time.Now(),
		persister:   persister,
	}
	s.restore()
	glog.Infof("Restore raft state: CurrentTerm: %d, CommitIndex: %d, LastApplied: %d", s.CurrentTerm, s.CommitIndex, s.LastApplied)
	return s
}
<commit_msg>commitIndex volatile<commit_after>package raft

import (
	"encoding/json"
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/HelloCodeMing/raft-rocks/pb"
	"github.com/HelloCodeMing/raft-rocks/store"
	"github.com/HelloCodeMing/raft-rocks/utils"
	"github.com/golang/glog"
)

// Hold state of raft, coordinate the updating and reading
type raftState struct {
	sync.RWMutex
	log       *store.LogStorage
	persister store.Persister
	applyCh   chan<- *ApplyMsg

	// once initialized, they will not be changed
	Me       int
	NumPeers int

	Role        pb.RaftRole
	CurrentTerm int32
	VotedFor    int32

	// maintained by leader, record follower's repliate and commit state
	MatchIndex []int
	NextIndex  []int

	// in raft paper, these two field is volatile
	// when restart, we need replay all WAL to recover state
	// otherwise, we have to do checkpoint, and record where has beed commited and applied to state machine
	// but when it comes to RocksDB like storage engine, it has its own WAL, so the storage itself is durable
	// but disappointing, even if reading RocksDB's WAL to get LastApplied is possible, it's too tricky
	// An elegant way to avoid 'double WAL' may be stop use WAL in RocksDB, but only in Raft,
	// and in raft we could perioidly do checkpoint/snapshot, persist LastApplied, as a result, we just need to
	// restore state machine from checkpoint, and replay WAL.
	//
	// All above is just my imagination, the real implementation is too naive. When LastApplied is updated, store it in persister.
	CommitIndex int // index of highest log entry known to be committed
	LastApplied int // index of highest log entry applied to state machine

	readLease time.Time
}

func (s *raftState) getRole() pb.RaftRole {
	s.RLock()
	defer s.RUnlock()
	return s.Role
}

// maybe we could cache this field in Raft, to avoid lock
func (s *raftState) getTerm() int32 {
	s.RLock()
	defer s.RUnlock()
	return s.CurrentTerm
}

func (s *raftState) getCommited() int {
	s.RLock()
	defer s.RUnlock()
	return s.CommitIndex
}

const (
	currentTerm = "currentTerm"
	votedFor    = "votedFor"
	lastApplied = "lastApplied"
)

func (s *raftState) persist() {
	s.persister.StoreInt32(currentTerm, s.CurrentTerm)
	s.persister.StoreInt32(votedFor, s.VotedFor)
}

func (s *raftState) restore() {
	var ok bool
	s.CurrentTerm, ok = s.persister.LoadInt32(currentTerm)
	s.VotedFor, ok = s.persister.LoadInt32(votedFor)
	if !ok {
		s.VotedFor = -1
	}
	apply, ok := s.persister.LoadInt32(lastApplied)
	if ok {
		s.LastApplied = int(apply)
	}
}

func (s *raftState) commitUntil(index int) {
	s.CommitIndex = index
}

func (s *raftState) applyOne() {
	s.LastApplied++
	s.persister.StoreInt32(lastApplied, int32(s.LastApplied))
}

func (s *raftState) checkNewTerm(newTerm int32) bool {
	if s.getTerm() < newTerm {
		s.becomeFollower(-1, newTerm)
		return true
	}
	return false
}
func (s *raftState) checkFollowerCommit(leaderCommit int) bool {
	s.Lock()
	defer s.Unlock()
	// Have uncommited log, but leader has commited them
	if s.CommitIndex < leaderCommit && s.CommitIndex < s.log.LastIndex() {
		s.commitUntil(minInt(leaderCommit, int(s.log.LastIndex())))
		s.checkApply()
		return true
	}
	return false
}

// any command to apply
func (s *raftState) checkApply() {
	old := s.LastApplied
	log := s.log
	for s.CommitIndex > s.LastApplied {
		s.applyOne()
		if log.LastIndex() < s.LastApplied {
			panic(s)
		}
		entry := log.At(s.LastApplied)
		if entry == nil {
			break
		}
		msg := &ApplyMsg{Command: entry}
		s.applyCh <- msg
	}
	if s.LastApplied > old {
		glog.V(utils.VDebug).Infof("%s Applyed commands until index=%d", s.String(), s.LastApplied)
	}
}

func (s *raftState) changeRole(role pb.RaftRole) {
	s.Lock()
	defer s.Unlock()
	s.Role = role

}

func (s *raftState) becomeFollowerUnlocked(candidateID int32, term int32) {
	s.CurrentTerm = term
	s.VotedFor = candidateID
	s.Role = pb.RaftRole_Follower
	s.persist()
}

func (s *raftState) becomeFollower(candidateID int32, term int32) {
	s.Lock()
	defer s.Unlock()
	s.CurrentTerm = term
	s.VotedFor = candidateID
	s.Role = pb.RaftRole_Follower
	s.MatchIndex = nil
	s.NextIndex = nil
	s.persist()
}

func (s *raftState) becomeCandidate() int32 {
	s.Lock()
	defer s.Unlock()
	s.Role = pb.RaftRole_Candidate
	s.CurrentTerm++
	s.VotedFor = int32(s.Me)
	s.MatchIndex = nil
	s.NextIndex = nil
	s.persist()
	return s.CurrentTerm
}

func (s *raftState) becomeLeader() {
	s.Lock()
	defer s.Unlock()
	s.Role = pb.RaftRole_Leader
	s.MatchIndex = make([]int, s.NumPeers)
	s.NextIndex = make([]int, s.NumPeers)
	lastIndex := s.log.LastIndex()
	for i := range s.NextIndex {
		s.NextIndex[i] = lastIndex + 1
	}
}

func (s *raftState) replicatedToPeer(peer int, index int) {
	s.Lock()
	defer s.Unlock()
	s.MatchIndex[peer] = maxInt(index, s.MatchIndex[peer])
	s.NextIndex[peer] = s.MatchIndex[peer] + 1
	if s.checkLeaderCommit() {
		s.checkApply()
	}
}

// update  commit index, use matchIndex from peers
// NOTE: It's not thread-safe, should be synchronized by external lock
func (s *raftState) checkLeaderCommit() (updated bool) {
	lowerIndex := s.CommitIndex + 1
	upperIndex := 0
	// find max matchIndex
	for _, x := range s.MatchIndex {
		if x > upperIndex {
			upperIndex = x
		}
	}
	// if N > commitIndex, a majority of match[i] >= N, and log[N].term == currentTerm
	// set commitIndex = N
	for N := upperIndex; N >= lowerIndex && s.log.At(N).Term == s.CurrentTerm; N-- {
		// count match[i] >= N
		cnt := 1
		for i, x := range s.MatchIndex {
			if i != s.Me && x >= N {
				cnt++
			}
		}
		if cnt >= s.majority() {
			s.commitUntil(N)
			glog.V(utils.VDebug).Infof("%s Leader update commitIndex: %d", s.String(), s.CommitIndex)
			updated = true
			break
		}
	}
	return
}

func (s *raftState) retreatForPeer(peer int) int {
	s.Lock()
	defer s.Unlock()
	idx := &s.NextIndex[peer]
	log := s.log
	*idx = minInt(*idx, log.LastIndex())
	oldTerm := log.At(*idx).Term
	for *idx > 0 && log.At(*idx).Term == oldTerm {
		*idx--
	}
	*idx = maxInt(*idx, 1)
	return *idx
}

func (s *raftState) toReplicate(peer int) int {
	return s.NextIndex[peer]
}

// If lease is granted in this term, and later than the old one, extend the lease
func (s *raftState) updateReadLease(term int32, lease time.Time) {
	if s.readLease.Before(time.Now()) && term == s.CurrentTerm && lease.After(s.readLease) {
		s.readLease = lease
		glog.Infof("%s Update read lease to %v", s.String(), lease)
	}
}

func (s *raftState) String() string {
	return fmt.Sprintf("Raft<%d:%d>", s.Me, s.CurrentTerm)
}

func (s *raftState) dump(writer io.Writer) {
	state := map[string]interface{}{
		"meta":         s,
		"LogLastIndex": s.log.LastIndex(),
	}
	buf, err := json.MarshalIndent(state, "", "\t")
	if err != nil {
		panic(err)
	}
	writer.Write(buf)
}

func (s *raftState) majority() int {
	return s.NumPeers/2 + 1
}

func makeRaftState(log *store.LogStorage, persister store.Persister, applyCh chan<- *ApplyMsg, numPeers int, me int) *raftState {
	s := &raftState{
		log:         log,
		applyCh:     applyCh,
		NumPeers:    numPeers,
		Me:          me,
		CurrentTerm: 0,
		VotedFor:    -1,
		Role:        pb.RaftRole_Follower,
		readLease:   time.Now(),
		persister:   persister,
	}
	s.restore()
	glog.Infof("Restore raft state: CurrentTerm: %d, CommitIndex: %d, LastApplied: %d", s.CurrentTerm, s.CommitIndex, s.LastApplied)
	return s
}
<|endoftext|>


<commit_before>package tme

import (
	"testing"
	"time"
)

const dur = 500 * time.Millisecond

func TestDone(t *testing.T) {
	now := time.Now()
	timer := NewRealTimer(dur)
	select {
	case done := <-timer.Done():
		if done.Time.After(now.Add(dur * 2)) {
			t.Fatalf("timer ended after %s", dur)
		}
	case <-time.After(dur * 2):
		t.Fatalf("timer didn't end before %s", dur*2)
	}
}

func TestMultipleStop(t *testing.T) {
	timer := NewRealTimer(dur)
	if !timer.Stop() {
		t.Fatal("first stop call returned false")
	}
	for i := 0; i < 10; i++ {
		go func(i int) {
			if timer.Stop() {
				t.Errorf("stop # %d returned true", i)
			}
		}(i)
	}
}

func TestDoneAfterStop(t *testing.T) {
	timer := NewRealTimer(dur)
	if !timer.Stop() {
		t.Fatal("stop returned false")
	}
	ch := timer.Done()
	select {
	case <-time.After(dur):
	case <-ch:
		t.Errorf("channel returned after stop called")
	}
}
<commit_msg>func name change<commit_after>package tme

import (
	"testing"
	"time"
)

const dur = 500 * time.Millisecond

func TestRealTimerDone(t *testing.T) {
	now := time.Now()
	timer := NewRealTimer(dur)
	select {
	case done := <-timer.Done():
		if done.Time.After(now.Add(dur * 2)) {
			t.Fatalf("timer ended after %s", dur)
		}
	case <-time.After(dur * 2):
		t.Fatalf("timer didn't end before %s", dur*2)
	}
}

func TestMultipleStop(t *testing.T) {
	timer := NewRealTimer(dur)
	if !timer.Stop() {
		t.Fatal("first stop call returned false")
	}
	for i := 0; i < 10; i++ {
		go func(i int) {
			if timer.Stop() {
				t.Errorf("stop # %d returned true", i)
			}
		}(i)
	}
}

func TestDoneAfterStop(t *testing.T) {
	timer := NewRealTimer(dur)
	if !timer.Stop() {
		t.Fatal("stop returned false")
	}
	ch := timer.Done()
	select {
	case <-time.After(dur):
	case <-ch:
		t.Errorf("channel returned after stop called")
	}
}
<|endoftext|>


<commit_before>// Package regserv implements a registration server kite. Users can register
// to a kite infrastructure by running "kite register" command.
package regserv

import (
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/nu7hatch/gouuid"
	"kite"
	"kite/kitekey"
	"os"
	"time"
)

const version = "0.0.1"

// RegServ is a registration kite. Users can register their machines by
// running "kite register" command.
type RegServ struct {
	Environment string
	Region      string
	PublicIP    string
	Port        string
	backend     Backend
	kite        *kite.Kite
}

func New(backend Backend) *RegServ {
	return &RegServ{
		Environment: "production",
		Region:      "localhost",
		backend:     backend,
	}
}

func (s *RegServ) Run() {
	if s.Environment == "" || s.Region == "" || s.PublicIP == "" || s.Port == "" {
		panic("RegServ is not initialized properly")
	}

	_, err := kitekey.Parse()
	if err != nil {
		s.RegisterSelf() // Need to do this before creating new kite
	}

	// Create a kite and run it.
	opts := &kite.Options{
		Kitename:    "regserv",
		Version:     version,
		Environment: s.Environment,
		Region:      s.Region,
		PublicIP:    s.PublicIP,
		Port:        s.Port,
		Path:        "/regserv",
		DisableAuthentication: true,
	}
	s.kite = kite.New(opts)
	s.kite.HandleFunc("register", s.handleRegister)

	ready := s.kite.ReadyNotify()
	go func() {
		<-ready
		fmt.Println("Users can register with the following command:")
		fmt.Printf("kite register -to '%s'\n", s.kite.URL.String())
	}()

	s.kite.Run()
}

// RegisterSelf registers this host and writes a key to ~/.kite/kite.key
func (s *RegServ) RegisterSelf() error {
	hostname, err := os.Hostname()
	if err != nil {
		return err
	}
	key, err := s.register(s.backend.Username(), hostname)
	if err != nil {
		return err
	}
	return kitekey.Write(key)
}

// Backend is the interface that is passed to New() function.
type Backend interface {
	Username() string
	KontrolURL() string
	PublicKey() string
	PrivateKey() string

	// Authenticate the user and return username.
	Authenticate(r *kite.Request) (string, error)
}

func (s *RegServ) handleRegister(r *kite.Request) (interface{}, error) {
	var args struct {
		Hostname string
	}
	r.Args.One().MustUnmarshal(&args)

	username, err := s.backend.Authenticate(r)
	if err != nil {
		return nil, errors.New("cannot authenticate user")
	}

	return s.register(username, args.Hostname)
}

func (s *RegServ) register(username, hostname string) (kiteKey string, err error) {
	tknID, err := uuid.NewV4()
	if err != nil {
		return "", errors.New("cannot generate a token")
	}

	token := jwt.New(jwt.GetSigningMethod("RS256"))

	token.Claims = map[string]interface{}{
		"iss":        s.backend.Username(),    // Issuer
		"sub":        username,                // Subject
		"iat":        time.Now().UTC().Unix(), // Issued At
		"hostname":   hostname,                // Hostname of registered machine
		"kontrolURL": s.backend.KontrolURL(),  // Kontrol URL
		"kontrolKey": s.backend.PublicKey(),   // Public key of kontrol
		"jti":        tknID.String(),          // JWT ID
	}

	return token.SignedString([]byte(s.backend.PrivateKey()))
}
<commit_msg>print log message when creating new kite.key<commit_after>// Package regserv implements a registration server kite. Users can register
// to a kite infrastructure by running "kite register" command.
package regserv

import (
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/nu7hatch/gouuid"
	"kite"
	"kite/kitekey"
	"os"
	"time"
)

const version = "0.0.1"

// RegServ is a registration kite. Users can register their machines by
// running "kite register" command.
type RegServ struct {
	Environment string
	Region      string
	PublicIP    string
	Port        string
	backend     Backend
	kite        *kite.Kite
}

func New(backend Backend) *RegServ {
	return &RegServ{
		Environment: "production",
		Region:      "localhost",
		backend:     backend,
	}
}

func (s *RegServ) Run() {
	if s.Environment == "" || s.Region == "" || s.PublicIP == "" || s.Port == "" {
		panic("RegServ is not initialized properly")
	}

	_, err := kitekey.Parse()
	if err != nil {
		fmt.Println("!!! kite.key is not found. Generating one...") // TODO show this message in yellow color
		s.RegisterSelf()                                            // Need to do this before creating new kite
	}

	// Create a kite and run it.
	opts := &kite.Options{
		Kitename:    "regserv",
		Version:     version,
		Environment: s.Environment,
		Region:      s.Region,
		PublicIP:    s.PublicIP,
		Port:        s.Port,
		Path:        "/regserv",
		DisableAuthentication: true,
	}
	s.kite = kite.New(opts)
	s.kite.HandleFunc("register", s.handleRegister)

	ready := s.kite.ReadyNotify()
	go func() {
		<-ready
		fmt.Println("Users can register with the following command:")
		fmt.Printf("kite register -to '%s'\n", s.kite.URL.String())
	}()

	s.kite.Run()
}

// RegisterSelf registers this host and writes a key to ~/.kite/kite.key
func (s *RegServ) RegisterSelf() error {
	hostname, err := os.Hostname()
	if err != nil {
		return err
	}
	key, err := s.register(s.backend.Username(), hostname)
	if err != nil {
		return err
	}
	return kitekey.Write(key)
}

// Backend is the interface that is passed to New() function.
type Backend interface {
	Username() string
	KontrolURL() string
	PublicKey() string
	PrivateKey() string

	// Authenticate the user and return username.
	Authenticate(r *kite.Request) (string, error)
}

func (s *RegServ) handleRegister(r *kite.Request) (interface{}, error) {
	var args struct {
		Hostname string
	}
	r.Args.One().MustUnmarshal(&args)

	username, err := s.backend.Authenticate(r)
	if err != nil {
		return nil, errors.New("cannot authenticate user")
	}

	return s.register(username, args.Hostname)
}

func (s *RegServ) register(username, hostname string) (kiteKey string, err error) {
	tknID, err := uuid.NewV4()
	if err != nil {
		return "", errors.New("cannot generate a token")
	}

	token := jwt.New(jwt.GetSigningMethod("RS256"))

	token.Claims = map[string]interface{}{
		"iss":        s.backend.Username(),    // Issuer
		"sub":        username,                // Subject
		"iat":        time.Now().UTC().Unix(), // Issued At
		"hostname":   hostname,                // Hostname of registered machine
		"kontrolURL": s.backend.KontrolURL(),  // Kontrol URL
		"kontrolKey": s.backend.PublicKey(),   // Public key of kontrol
		"jti":        tknID.String(),          // JWT ID
	}

	return token.SignedString([]byte(s.backend.PrivateKey()))
}
<|endoftext|>


<commit_before>package gorma

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/dslengine"
)

// NewRelationalFieldDefinition returns an initialized
// RelationalFieldDefinition.
func NewRelationalFieldDefinition() *RelationalFieldDefinition {
	m := &RelationalFieldDefinition{
		Mappings: make(map[string]*MapDefinition),
	}
	return m
}

// Context returns the generic definition name used in error messages.
func (f *RelationalFieldDefinition) Context() string {
	if f.FieldName != "" {
		return fmt.Sprintf("RelationalField %#v", f.FieldName)
	}
	return "unnamed RelationalField"
}

// DSL returns this object's DSL.
func (f *RelationalFieldDefinition) DSL() func() {
	return f.DefinitionDSL
}

// Children returns a slice of this objects children.
func (f RelationalFieldDefinition) Children() []dslengine.Definition {
	// no children yet
	return []dslengine.Definition{}
}

// Attribute implements the Container interface of the goa Attribute
// model.
func (f *RelationalFieldDefinition) Attribute() *design.AttributeDefinition {
	return f.a
}

// FieldDefinition returns the field's struct definition.
func (f *RelationalFieldDefinition) FieldDefinition() string {
	var comment string
	if f.Description != "" {
		comment = "// " + f.Description
	}
	def := fmt.Sprintf("%s\t%s %s %s\n", f.FieldName, goDatatype(f, true), tags(f), comment)
	return def
}

// Tags returns the sql and gorm struct tags for the Definition.
func (f *RelationalFieldDefinition) Tags() string {
	return tags(f)
}

// LowerName returns the field name as a lowercase string.
func (f *RelationalFieldDefinition) LowerName() string {
	return strings.ToLower(f.FieldName)
}

// Underscore returns the field name as a lowercase string in snake case.
func (f *RelationalFieldDefinition) Underscore() string {
	runes := []rune(f.FieldName)
	length := len(runes)

	var out []rune
	for i := 0; i < length; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}

func goDatatype(f *RelationalFieldDefinition, includePtr bool) string {
	var ptr string
	if f.Nullable && includePtr {
		ptr = "*"
	}
	switch f.Datatype {
	case Boolean:
		return ptr + "bool"
	case Integer, BigInteger:
		return ptr + "int"
	case AutoInteger, AutoBigInteger:
		return ptr + "int " // sql/gorm tags later
	case Decimal:
		return ptr + "float32"
	case BigDecimal:
		return ptr + "float64"
	case String:
		return ptr + "string"
	case Text:
		return ptr + "string"
	case UUID:
		return ptr + "uuid.UUID"
	case Timestamp, NullableTimestamp:
		return ptr + "time.Time"
	case BelongsTo:
		return ptr + belongsToIDType(f, includePtr)
	case HasMany:
		return fmt.Sprintf("[]%s", f.HasMany)
	case HasManyKey:
		return ptr + hasManyIDType(f, includePtr)
	case HasOneKey:
		return ptr + hasOneIDType(f, includePtr)
	case HasOne:
		return fmt.Sprintf("%s", f.HasOne)
	default:

		if f.Many2Many != "" {
			return fmt.Sprintf("[]%s", f.Many2Many)
		}
	}

	return "UNKNOWN TYPE"
}

func belongsToIDType(f *RelationalFieldDefinition, includePtr bool) string {
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.BelongsTo[modelName]
	return relatedIDType(model, includePtr)
}

func hasOneIDType(f *RelationalFieldDefinition, includePtr bool) string {
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.HasOne[modelName]
	return relatedIDType(model, includePtr)
}

func hasManyIDType(f *RelationalFieldDefinition, includePtr bool) string {
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.HasMany[modelName]
	return relatedIDType(model, includePtr)
}

func relatedIDType(m *RelationalModelDefinition, includePtr bool) string {
	if m == nil {
		return "int"
	}
	if len(m.PrimaryKeys) > 1 {
		panic("Can't determine field Type when using multiple primary keys")
	}
	return goDatatype(m.PrimaryKeys[0], includePtr)
}

func tags(f *RelationalFieldDefinition) string {
	var sqltags []string
	if f.SQLTag != "" {
		sqltags = append(sqltags, f.SQLTag)
	}

	var gormtags []string
	if f.DatabaseFieldName != "" && f.DatabaseFieldName != f.Underscore() {
		gormtags = append(gormtags, "column:"+f.DatabaseFieldName)
	}
	if f.PrimaryKey {
		gormtags = append(gormtags, "primary_key")
	}
	if f.Many2Many != "" {
		p := strings.ToLower(f.Parent.ModelName)
		j := strings.ToLower(f.Many2Many)
		gormtags = append(gormtags, "many2many:"+p+"_"+j)
	}

	var tags []string
	if len(sqltags) > 0 {
		sqltag := "sql:\"" + strings.Join(sqltags, ";") + "\""
		tags = append(tags, sqltag)
	}
	if len(gormtags) > 0 {
		gormtag := "gorm:\"" + strings.Join(gormtags, ";") + "\""
		tags = append(tags, gormtag)
	}

	if len(tags) > 0 {
		return "`" + strings.Join(tags, " ") + "`"
	}
	return ""
}
<commit_msg>Add nil check on Parent to satisfy testsuite<commit_after>package gorma

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/dslengine"
)

// NewRelationalFieldDefinition returns an initialized
// RelationalFieldDefinition.
func NewRelationalFieldDefinition() *RelationalFieldDefinition {
	m := &RelationalFieldDefinition{
		Mappings: make(map[string]*MapDefinition),
	}
	return m
}

// Context returns the generic definition name used in error messages.
func (f *RelationalFieldDefinition) Context() string {
	if f.FieldName != "" {
		return fmt.Sprintf("RelationalField %#v", f.FieldName)
	}
	return "unnamed RelationalField"
}

// DSL returns this object's DSL.
func (f *RelationalFieldDefinition) DSL() func() {
	return f.DefinitionDSL
}

// Children returns a slice of this objects children.
func (f RelationalFieldDefinition) Children() []dslengine.Definition {
	// no children yet
	return []dslengine.Definition{}
}

// Attribute implements the Container interface of the goa Attribute
// model.
func (f *RelationalFieldDefinition) Attribute() *design.AttributeDefinition {
	return f.a
}

// FieldDefinition returns the field's struct definition.
func (f *RelationalFieldDefinition) FieldDefinition() string {
	var comment string
	if f.Description != "" {
		comment = "// " + f.Description
	}
	def := fmt.Sprintf("%s\t%s %s %s\n", f.FieldName, goDatatype(f, true), tags(f), comment)
	return def
}

// Tags returns the sql and gorm struct tags for the Definition.
func (f *RelationalFieldDefinition) Tags() string {
	return tags(f)
}

// LowerName returns the field name as a lowercase string.
func (f *RelationalFieldDefinition) LowerName() string {
	return strings.ToLower(f.FieldName)
}

// Underscore returns the field name as a lowercase string in snake case.
func (f *RelationalFieldDefinition) Underscore() string {
	runes := []rune(f.FieldName)
	length := len(runes)

	var out []rune
	for i := 0; i < length; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}

func goDatatype(f *RelationalFieldDefinition, includePtr bool) string {
	var ptr string
	if f.Nullable && includePtr {
		ptr = "*"
	}
	switch f.Datatype {
	case Boolean:
		return ptr + "bool"
	case Integer, BigInteger:
		return ptr + "int"
	case AutoInteger, AutoBigInteger:
		return ptr + "int " // sql/gorm tags later
	case Decimal:
		return ptr + "float32"
	case BigDecimal:
		return ptr + "float64"
	case String:
		return ptr + "string"
	case Text:
		return ptr + "string"
	case UUID:
		return ptr + "uuid.UUID"
	case Timestamp, NullableTimestamp:
		return ptr + "time.Time"
	case BelongsTo:
		return ptr + belongsToIDType(f, includePtr)
	case HasMany:
		return fmt.Sprintf("[]%s", f.HasMany)
	case HasManyKey:
		return ptr + hasManyIDType(f, includePtr)
	case HasOneKey:
		return ptr + hasOneIDType(f, includePtr)
	case HasOne:
		return fmt.Sprintf("%s", f.HasOne)
	default:

		if f.Many2Many != "" {
			return fmt.Sprintf("[]%s", f.Many2Many)
		}
	}

	return "UNKNOWN TYPE"
}

func belongsToIDType(f *RelationalFieldDefinition, includePtr bool) string {
	if f.Parent == nil {
		return "int"
	}
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.BelongsTo[modelName]
	return relatedIDType(model, includePtr)
}

func hasOneIDType(f *RelationalFieldDefinition, includePtr bool) string {
	if f.Parent == nil {
		return "int"
	}
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.HasOne[modelName]
	return relatedIDType(model, includePtr)
}

func hasManyIDType(f *RelationalFieldDefinition, includePtr bool) string {
	if f.Parent == nil {
		return "int"
	}
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.HasMany[modelName]
	return relatedIDType(model, includePtr)
}

func relatedIDType(m *RelationalModelDefinition, includePtr bool) string {
	if m == nil {
		return "int"
	}
	if len(m.PrimaryKeys) > 1 {
		panic("Can't determine field Type when using multiple primary keys")
	}
	return goDatatype(m.PrimaryKeys[0], includePtr)
}

func tags(f *RelationalFieldDefinition) string {
	var sqltags []string
	if f.SQLTag != "" {
		sqltags = append(sqltags, f.SQLTag)
	}

	var gormtags []string
	if f.DatabaseFieldName != "" && f.DatabaseFieldName != f.Underscore() {
		gormtags = append(gormtags, "column:"+f.DatabaseFieldName)
	}
	if f.PrimaryKey {
		gormtags = append(gormtags, "primary_key")
	}
	if f.Many2Many != "" {
		p := strings.ToLower(f.Parent.ModelName)
		j := strings.ToLower(f.Many2Many)
		gormtags = append(gormtags, "many2many:"+p+"_"+j)
	}

	var tags []string
	if len(sqltags) > 0 {
		sqltag := "sql:\"" + strings.Join(sqltags, ";") + "\""
		tags = append(tags, sqltag)
	}
	if len(gormtags) > 0 {
		gormtag := "gorm:\"" + strings.Join(gormtags, ";") + "\""
		tags = append(tags, gormtag)
	}

	if len(tags) > 0 {
		return "`" + strings.Join(tags, " ") + "`"
	}
	return ""
}
<|endoftext|>


<commit_before>package alita

import (
	"bufio"
	"fmt"
	"io"
)

type Aligner struct {
	w         io.Writer
	Space     *Space
	Margin    *Margin
	Delimiter *Delimiter
	Padding   *Padding
	lines     [][]string
}

func NewAligner(w io.Writer) *Aligner {
	return &Aligner{
		w:         w,
		Space:     NewSpace(),
		Margin:    NewMargin(),
		Delimiter: NewDelimiter(),
		Padding:   NewPadding(),
	}
}

func (a *Aligner) SetOutput(w io.Writer) {
	a.w = w
}

func (a *Aligner) appendLine(s string) {
	sp := a.Delimiter.Split(a.Space.Strip(s))
	a.lines = append(a.lines, sp)

	if len(sp) > 1 {
		a.Space.UpdateHeadWidth(s)
		a.Padding.UpdateWidth(sp)
	}
}

func (a *Aligner) ReadAll(r io.Reader) error {
	s := bufio.NewScanner(r)
	for s.Scan() {
		a.appendLine(s.Text())
	}
	return s.Err()
}

func (a *Aligner) format(sp []string) string {
	if len(sp) == 1 {
		return sp[0]
	}
	return a.Space.Adjust(a.Margin.Join(a.Padding.Format(sp)))
}

func (a *Aligner) Flush() error {
	for _, sp := range a.lines {
		_, err := fmt.Fprintln(a.w, a.format(sp))
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>To public Aligner.appendLine<commit_after>package alita

import (
	"bufio"
	"fmt"
	"io"
)

type Aligner struct {
	w         io.Writer
	Space     *Space
	Margin    *Margin
	Delimiter *Delimiter
	Padding   *Padding
	lines     [][]string
}

func NewAligner(w io.Writer) *Aligner {
	return &Aligner{
		w:         w,
		Space:     NewSpace(),
		Margin:    NewMargin(),
		Delimiter: NewDelimiter(),
		Padding:   NewPadding(),
	}
}

func (a *Aligner) SetOutput(w io.Writer) {
	a.w = w
}

func (a *Aligner) AppendLine(s string) {
	sp := a.Delimiter.Split(a.Space.Strip(s))
	a.lines = append(a.lines, sp)

	if len(sp) > 1 {
		a.Space.UpdateHeadWidth(s)
		a.Padding.UpdateWidth(sp)
	}
}

func (a *Aligner) ReadAll(r io.Reader) error {
	s := bufio.NewScanner(r)
	for s.Scan() {
		a.AppendLine(s.Text())
	}
	return s.Err()
}

func (a *Aligner) format(sp []string) string {
	if len(sp) == 1 {
		return sp[0]
	}
	return a.Space.Adjust(a.Margin.Join(a.Padding.Format(sp)))
}

func (a *Aligner) Flush() error {
	for _, sp := range a.lines {
		_, err := fmt.Fprintln(a.w, a.format(sp))
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Package api contains interfaces and low level structs required across
// multiple packages or repositories.
package api

import (
	"encoding/json"
	"errors"
	"os"
	"regexp"
	"time"
)

const (
	// Folder containing the maxmind files
	MaxmindPrefix = "Maxmind/"
)

var (
	// MaxmindBucketName is the bucket containing maxmind files.
	MaxmindBucketName = "downloader-" + os.Getenv("GCLOUD_PROJECT")
)

/*************************************************************************
*                             Annotation Structs                         *
*************************************************************************/

// The GeolocationIP struct contains all the information needed for the
// geolocation data that will be inserted into big query. The fields are
// capitalized for exporting, although the originals in the DB schema
// are not.
// This is in common because it is used by the etl repository.
type GeolocationIP struct {
	ContinentCode string  `json:"continent_code,,omitempty" bigquery:"continent_code"` // Gives a shorthand for the continent
	CountryCode   string  `json:"country_code,,omitempty"   bigquery:"country_code"`   // Gives a shorthand for the country
	CountryCode3  string  `json:"country_code3,,omitempty"  bigquery:"country_code3"`  // Gives a shorthand for the country
	CountryName   string  `json:"country_name,,omitempty"   bigquery:"country_name"`   // Name of the country
	Region        string  `json:"region,,omitempty"         bigquery:"region"`         // Region or State within the country
	MetroCode     int64   `json:"metro_code,,omitempty"     bigquery:"metro_code"`     // Metro code within the country
	City          string  `json:"city,,omitempty"           bigquery:"city"`           // City within the region
	AreaCode      int64   `json:"area_code,,omitempty"      bigquery:"area_code"`      // Area code, similar to metro code
	PostalCode    string  `json:"postal_code,,omitempty"    bigquery:"postal_code"`    // Postal code, again similar to metro
	Latitude      float64 `json:"latitude,,omitempty"       bigquery:"latitude"`       // Latitude
	Longitude     float64 `json:"longitude,,omitempty"      bigquery:"longitude"`      // Longitude
}

/************************************************************************
*                            ASN Annotations                            *
************************************************************************/

// We are currently using CAIDA Routeviews data to populate ASN annotations.
// See documentation at:
// http://data.caida.org/datasets/routing/routeviews-prefix2as/README.txt

// An AS mapping is either a single ASN or AS set, or a Multi-Origin AS, with 2 or more elements, each of which
// might be a single ASN or an AS set.
// The following scenarios are possible:
//
// Single ASN belongs to the IP:
//   - Example input: `"14061"`
//   - Example GeoData.ASData.Systems: `[{"ASNs": [14061]}]`
// An AS set for the IP:
//   - Example input: `"367,1479,1504"`
//   - Example GeoData.ASData.Systems: `[
//       {"ASNs": [367, 1479, 1504]}
//     ]`
// A multi-origin AS, consisting of multiple of AS sets:
//   - Example input: `"55967_38365_38365,64512,65323"`
//   - Example GeoData.Systems: `[
//       {"ASNs": [55967]},              // Appears most frequently
//       {"ASNs": [38365]},              // Appears less frequently
//       {"ASNs": [38365, 64512, 65323]} // Appears least frequently.
//     ]`
// Another multi-origin AS, consisting of multiple of AS sets:
//   - Example input: `"8508,199279_15744"`
//   - Example GeoData.Systems: `[
//       {"ASNs": [8508, 199279]},  // Appears most frequently
//       {"ASNs": [15744]},         // Appears less frequently
//     ]`

// A System is the base element.  It may contain a single ASN, or multiple ASNs comprising an AS set.
type System struct {
	// ASNs contains a single ASN, or AS set.  There must always be at least one ASN.
	// If there are more than one ASN, they are (arbitrarily) listed in increasing numerical order.
	ASNs []uint32
}

// ASData contains the Autonomous System information associated with the IP prefix.
// Roughly 99% of mappings consist of a single System with a single ASN.
//
// Looking at Routeviews data from 2019/01/01, the MOAS and AS set stats look like:
// IPv4:   Single: 99%    MOAS: 1%     AS set: .005% (of entries) 1/2 of AS sets start with MOAS
// IPv6:   Single: 99.3%  MOAS: 0.6%   AS set: .01%  (of entries) 1/3 of AS sets start with MOAS
// NOTE: This is NOT intended to be used directly as the BigQuery schema.
type ASData struct {
	IPPrefix string // the IP prefix found in the table.

	// One or more "Systems".  There must always be at least one System.  If there are more than one,
	// then this is a Multi-Origin AS, and the component Systems are in order of frequency in routing tables,
	// most common first.
	Systems []System
}

// ErrNilOrEmptyASData is returned by BestASN if the ASData is nil or empty.
var ErrNilOrEmptyASData = errors.New("Empty or Nil ASData")

// BestASN returns a plausible ASN from a possibly complex ASData.
func (as *ASData) BestASN() (int64, error) {
	if as == nil || len(as.Systems) == 0 {
		return 0, ErrNilOrEmptyASData
	}
	sys0 := as.Systems[0]
	if len(sys0.ASNs) == 0 {
		return 0, ErrNilOrEmptyASData
	}
	return int64(sys0.ASNs[0]), nil
}

// GeoData is the main struct for the geo metadata, which holds pointers to the
// Geolocation data and the IP/ASN data. This is what we parse the JSON
// response from the annotator into.
// TODO - replace this with type Annotations struct.
type GeoData struct {
	Geo     *GeolocationIP // Holds the geolocation data
	Network *ASData        // Holds the associated network Autonomous System data.
}

/*************************************************************************
*                       Request/Response Structs                         *
*************************************************************************/

// The RequestData schema is the schema for the json that we will send
// down the pipe to the annotation service.
// DEPRECATED
// Should instead use a single Date (time.Time) and array of net.IP.  See the v2 API.
type RequestData struct {
	IP        string    // Holds the IP from an incoming request
	IPFormat  int       // Holds the ip format, 4 or 6
	Timestamp time.Time // Holds the timestamp from an incoming request
}

// RequestWrapper will be used for all future request types.
type RequestWrapper struct {
	RequestType string
	Body        json.RawMessage
}

/*************************************************************************
*                           Local Annotator API                          *
*************************************************************************/

// Annotator defines the methods required annotating
type Annotator interface {
	// Annotate populates one or more annotation fields in the GeoData object.
	// If it fails, it will return a non-nil error and will leave the target unmodified.
	Annotate(ip string, ann *GeoData) error

	// The date associated with the dataset.
	AnnotatorDate() time.Time
}

var dateRE = regexp.MustCompile(`[0-9]{8}T`)

// ExtractDateFromFilename return the date for a filename like
// gs://downloader-mlab-oti/Maxmind/2017/05/08/20170508T080000Z-GeoLiteCity.dat.gz
// TODO: both geoloader and geolite2 package use this func, so leave it here for now.
func ExtractDateFromFilename(filename string) (time.Time, error) {
	filedate := dateRE.FindAllString(filename, -1)
	if len(filedate) != 1 {
		return time.Time{}, errors.New("cannot extract date from input filename")
	}
	return time.Parse(time.RFC3339, filedate[0][0:4]+"-"+filedate[0][4:6]+"-"+filedate[0][6:8]+"T00:00:00Z")
}

/*************************************************************************
*                            Annotator Loader                            *
*************************************************************************/

// CachingLoader keeps a cache of loaded annotators, updates the cache on request, and returns a copy
// of the cache on request.
type CachingLoader interface {
	// UpdateCache causes the loader to load any new annotators and add them to the cached list.
	UpdateCache() error

	// Fetch returns a copy of the current list of annotators.
	// May return an empty slice, but must not return nil.
	Fetch() []Annotator
}
<commit_msg>omitempty IPPrefix field<commit_after>// Package api contains interfaces and low level structs required across
// multiple packages or repositories.
package api

import (
	"encoding/json"
	"errors"
	"os"
	"regexp"
	"time"
)

const (
	// Folder containing the maxmind files
	MaxmindPrefix = "Maxmind/"
)

var (
	// MaxmindBucketName is the bucket containing maxmind files.
	MaxmindBucketName = "downloader-" + os.Getenv("GCLOUD_PROJECT")
)

/*************************************************************************
*                             Annotation Structs                         *
*************************************************************************/

// The GeolocationIP struct contains all the information needed for the
// geolocation data that will be inserted into big query. The fields are
// capitalized for exporting, although the originals in the DB schema
// are not.
// This is in common because it is used by the etl repository.
type GeolocationIP struct {
	ContinentCode string  `json:"continent_code,,omitempty" bigquery:"continent_code"` // Gives a shorthand for the continent
	CountryCode   string  `json:"country_code,,omitempty"   bigquery:"country_code"`   // Gives a shorthand for the country
	CountryCode3  string  `json:"country_code3,,omitempty"  bigquery:"country_code3"`  // Gives a shorthand for the country
	CountryName   string  `json:"country_name,,omitempty"   bigquery:"country_name"`   // Name of the country
	Region        string  `json:"region,,omitempty"         bigquery:"region"`         // Region or State within the country
	MetroCode     int64   `json:"metro_code,,omitempty"     bigquery:"metro_code"`     // Metro code within the country
	City          string  `json:"city,,omitempty"           bigquery:"city"`           // City within the region
	AreaCode      int64   `json:"area_code,,omitempty"      bigquery:"area_code"`      // Area code, similar to metro code
	PostalCode    string  `json:"postal_code,,omitempty"    bigquery:"postal_code"`    // Postal code, again similar to metro
	Latitude      float64 `json:"latitude,,omitempty"       bigquery:"latitude"`       // Latitude
	Longitude     float64 `json:"longitude,,omitempty"      bigquery:"longitude"`      // Longitude
}

/************************************************************************
*                            ASN Annotations                            *
************************************************************************/

// We are currently using CAIDA Routeviews data to populate ASN annotations.
// See documentation at:
// http://data.caida.org/datasets/routing/routeviews-prefix2as/README.txt

// An AS mapping is either a single ASN or AS set, or a Multi-Origin AS, with 2 or more elements, each of which
// might be a single ASN or an AS set.
// The following scenarios are possible:
//
// Single ASN belongs to the IP:
//   - Example input: `"14061"`
//   - Example GeoData.ASData.Systems: `[{"ASNs": [14061]}]`
// An AS set for the IP:
//   - Example input: `"367,1479,1504"`
//   - Example GeoData.ASData.Systems: `[
//       {"ASNs": [367, 1479, 1504]}
//     ]`
// A multi-origin AS, consisting of multiple of AS sets:
//   - Example input: `"55967_38365_38365,64512,65323"`
//   - Example GeoData.Systems: `[
//       {"ASNs": [55967]},              // Appears most frequently
//       {"ASNs": [38365]},              // Appears less frequently
//       {"ASNs": [38365, 64512, 65323]} // Appears least frequently.
//     ]`
// Another multi-origin AS, consisting of multiple of AS sets:
//   - Example input: `"8508,199279_15744"`
//   - Example GeoData.Systems: `[
//       {"ASNs": [8508, 199279]},  // Appears most frequently
//       {"ASNs": [15744]},         // Appears less frequently
//     ]`

// A System is the base element.  It may contain a single ASN, or multiple ASNs comprising an AS set.
type System struct {
	// ASNs contains a single ASN, or AS set.  There must always be at least one ASN.
	// If there are more than one ASN, they are (arbitrarily) listed in increasing numerical order.
	ASNs []uint32
}

// ASData contains the Autonomous System information associated with the IP prefix.
// Roughly 99% of mappings consist of a single System with a single ASN.
//
// Looking at Routeviews data from 2019/01/01, the MOAS and AS set stats look like:
// IPv4:   Single: 99%    MOAS: 1%     AS set: .005% (of entries) 1/2 of AS sets start with MOAS
// IPv6:   Single: 99.3%  MOAS: 0.6%   AS set: .01%  (of entries) 1/3 of AS sets start with MOAS
// NOTE: This is NOT intended to be used directly as the BigQuery schema.
type ASData struct {
	IPPrefix string `json:",omitempty"` // the IP prefix found in the table.

	// One or more "Systems".  There must always be at least one System.  If there are more than one,
	// then this is a Multi-Origin AS, and the component Systems are in order of frequency in routing tables,
	// most common first.
	Systems []System
}

// ErrNilOrEmptyASData is returned by BestASN if the ASData is nil or empty.
var ErrNilOrEmptyASData = errors.New("Empty or Nil ASData")

// BestASN returns a plausible ASN from a possibly complex ASData.
func (as *ASData) BestASN() (int64, error) {
	if as == nil || len(as.Systems) == 0 {
		return 0, ErrNilOrEmptyASData
	}
	sys0 := as.Systems[0]
	if len(sys0.ASNs) == 0 {
		return 0, ErrNilOrEmptyASData
	}
	return int64(sys0.ASNs[0]), nil
}

// GeoData is the main struct for the geo metadata, which holds pointers to the
// Geolocation data and the IP/ASN data. This is what we parse the JSON
// response from the annotator into.
// TODO - replace this with type Annotations struct.
type GeoData struct {
	Geo     *GeolocationIP // Holds the geolocation data
	Network *ASData        // Holds the associated network Autonomous System data.
}

/*************************************************************************
*                       Request/Response Structs                         *
*************************************************************************/

// The RequestData schema is the schema for the json that we will send
// down the pipe to the annotation service.
// DEPRECATED
// Should instead use a single Date (time.Time) and array of net.IP.  See the v2 API.
type RequestData struct {
	IP        string    // Holds the IP from an incoming request
	IPFormat  int       // Holds the ip format, 4 or 6
	Timestamp time.Time // Holds the timestamp from an incoming request
}

// RequestWrapper will be used for all future request types.
type RequestWrapper struct {
	RequestType string
	Body        json.RawMessage
}

/*************************************************************************
*                           Local Annotator API                          *
*************************************************************************/

// Annotator defines the methods required annotating
type Annotator interface {
	// Annotate populates one or more annotation fields in the GeoData object.
	// If it fails, it will return a non-nil error and will leave the target unmodified.
	Annotate(ip string, ann *GeoData) error

	// The date associated with the dataset.
	AnnotatorDate() time.Time
}

var dateRE = regexp.MustCompile(`[0-9]{8}T`)

// ExtractDateFromFilename return the date for a filename like
// gs://downloader-mlab-oti/Maxmind/2017/05/08/20170508T080000Z-GeoLiteCity.dat.gz
// TODO: both geoloader and geolite2 package use this func, so leave it here for now.
func ExtractDateFromFilename(filename string) (time.Time, error) {
	filedate := dateRE.FindAllString(filename, -1)
	if len(filedate) != 1 {
		return time.Time{}, errors.New("cannot extract date from input filename")
	}
	return time.Parse(time.RFC3339, filedate[0][0:4]+"-"+filedate[0][4:6]+"-"+filedate[0][6:8]+"T00:00:00Z")
}

/*************************************************************************
*                            Annotator Loader                            *
*************************************************************************/

// CachingLoader keeps a cache of loaded annotators, updates the cache on request, and returns a copy
// of the cache on request.
type CachingLoader interface {
	// UpdateCache causes the loader to load any new annotators and add them to the cached list.
	UpdateCache() error

	// Fetch returns a copy of the current list of annotators.
	// May return an empty slice, but must not return nil.
	Fetch() []Annotator
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/gorilla/mux"

	"github.com/reviewboard/rb-gateway/config"
	"github.com/reviewboard/rb-gateway/repositories"
)

const (
	PrivateTokenHeader = "PRIVATE-TOKEN"
)

type API struct {
	config config.Config
	router *mux.Router
}

// Return a new router for the API.
func New(cfg config.Config) API {
	api := API{
		config: cfg,
		router: mux.NewRouter(),
	}

	api.router.Use(loggingMiddleware)

	api.router.Path("/session").
		Methods("GET").
		HandlerFunc(api.getSession)

	// The following routes all require authorization.
	repoRoutes := api.router.PathPrefix("/repos/{repo}").Subrouter()
	repoRoutes.Use(api.withAuthorizationRequired)
	repoRoutes.Use(api.withRepository)

	routeTable := []struct {
		methods []string
		path    string
		handler http.Handler
	}{
		{[]string{"GET"}, "/branches", http.HandlerFunc(api.getBranches)},
		{[]string{"GET"}, "/branches/{branch}/commits", http.HandlerFunc(api.getCommits)},
		{[]string{"GET"}, "/commits/{commit-id}", http.HandlerFunc(api.getCommit)},
		{[]string{"GET"}, "/commits/{commit-id}/path/{path}", http.HandlerFunc(api.getFileByCommit)},
		{[]string{"HEAD"}, "/commits/{commit-id}/path/{path}", http.HandlerFunc(api.getFileExistsByCommit)},
		{[]string{"GET"}, "/file/{file-id}", http.HandlerFunc(api.getFile)},
		{[]string{"HEAD"}, "/file/{file-id}", http.HandlerFunc(api.getFileExists)},
	}

	for _, route := range routeTable {
		repoRoutes.Path(route.path).
			Methods(route.methods...).
			Handler(route.handler)
	}

	return api
}

func (api *API) SetConfig(newConfig config.Config) {
	api.config = newConfig
}

// A middleware for wrapping routes that require a repository.
//
// If the requested repository exists, it will be provided through the context
// as `"repo"`. Otherwise, an appropriate error will be returned.
func (api API) withRepository(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		repoName := mux.Vars(r)["repo"]

		var repo repositories.Repository
		var exists bool

		if len(repoName) == 0 {
			http.Error(w, "Repository not provided.", http.StatusBadRequest)
		} else if repo, exists = api.config.Repositories[repoName]; !exists {
			http.Error(w, "Repository not found.", http.StatusNotFound)
		} else {
			ctx := context.WithValue(r.Context(), "repo", repo)
			next.ServeHTTP(w, r.WithContext(ctx))
		}
	})
}

// TODO: Replace this with actual token logic.
func (api API) withAuthorizationRequired(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		privateToken := r.Header.Get(PrivateTokenHeader)

		var payload []byte
		var err error
		var pair []string

		if len(privateToken) == 0 {
			http.Error(w, "Bad private token.", http.StatusBadRequest)
		} else if payload, err = base64.StdEncoding.DecodeString(privateToken); err != nil {
			http.Error(w, "Bad private token.", http.StatusBadRequest)
		} else if pair = strings.SplitN(string(payload), ":", 2); len(pair) != 2 {
			http.Error(w, "Bad private token.", http.StatusBadRequest)
		} else if !api.validateCredentials(pair[0], pair[1]) {
			http.Error(w, "Authorization failed.", http.StatusUnauthorized)
		} else {
			next.ServeHTTP(w, r)
		}
	})
}

// Serve the HTTP request.
func (api API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	api.router.ServeHTTP(w, r)
}

func (api *API) validateCredentials(username, password string) bool {
	return username == api.config.Username && password == api.config.Password
}

func (api *API) CreateSession(r *http.Request) (*Session, error) {
	username, password, ok := r.BasicAuth()
	if !ok {
		return nil, errors.New("Invalid Authorization header.")
	}

	if !api.validateCredentials(username, password) {
		return nil, errors.New("Authorization failed.")
	}

	// TODO: replace with an actual token.
	credentials := []byte(fmt.Sprintf("%s:%s", username, password))
	token := base64.StdEncoding.EncodeToString(credentials)
	return &Session{token}, nil
}
<commit_msg>Ensure we log all requests<commit_after>package api

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/gorilla/mux"

	"github.com/reviewboard/rb-gateway/config"
	"github.com/reviewboard/rb-gateway/repositories"
)

const (
	PrivateTokenHeader = "PRIVATE-TOKEN"
)

type API struct {
	config config.Config
	router *mux.Router
}

// Return a new router for the API.
func New(cfg config.Config) API {
	api := API{
		config: cfg,
		router: mux.NewRouter(),
	}

	api.router.Path("/session").
		Methods("GET").
		HandlerFunc(api.getSession)

	// The following routes all require authorization.
	repoRoutes := api.router.PathPrefix("/repos/{repo}").Subrouter()
	repoRoutes.Use(api.withAuthorizationRequired)
	repoRoutes.Use(api.withRepository)

	routeTable := []struct {
		methods []string
		path    string
		handler http.Handler
	}{
		{[]string{"GET"}, "/branches", http.HandlerFunc(api.getBranches)},
		{[]string{"GET"}, "/branches/{branch}/commits", http.HandlerFunc(api.getCommits)},
		{[]string{"GET"}, "/commits/{commit-id}", http.HandlerFunc(api.getCommit)},
		{[]string{"GET"}, "/commits/{commit-id}/path/{path}", http.HandlerFunc(api.getFileByCommit)},
		{[]string{"HEAD"}, "/commits/{commit-id}/path/{path}", http.HandlerFunc(api.getFileExistsByCommit)},
		{[]string{"GET"}, "/file/{file-id}", http.HandlerFunc(api.getFile)},
		{[]string{"HEAD"}, "/file/{file-id}", http.HandlerFunc(api.getFileExists)},
	}

	for _, route := range routeTable {
		repoRoutes.Path(route.path).
			Methods(route.methods...).
			Handler(route.handler)
	}

	return api
}

func (api *API) SetConfig(newConfig config.Config) {
	api.config = newConfig
}

// A middleware for wrapping routes that require a repository.
//
// If the requested repository exists, it will be provided through the context
// as `"repo"`. Otherwise, an appropriate error will be returned.
func (api API) withRepository(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		repoName := mux.Vars(r)["repo"]

		var repo repositories.Repository
		var exists bool

		if len(repoName) == 0 {
			http.Error(w, "Repository not provided.", http.StatusBadRequest)
		} else if repo, exists = api.config.Repositories[repoName]; !exists {
			http.Error(w, "Repository not found.", http.StatusNotFound)
		} else {
			ctx := context.WithValue(r.Context(), "repo", repo)
			next.ServeHTTP(w, r.WithContext(ctx))
		}
	})
}

// TODO: Replace this with actual token logic.
func (api API) withAuthorizationRequired(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		privateToken := r.Header.Get(PrivateTokenHeader)

		var payload []byte
		var err error
		var pair []string

		if len(privateToken) == 0 {
			http.Error(w, "Bad private token.", http.StatusBadRequest)
		} else if payload, err = base64.StdEncoding.DecodeString(privateToken); err != nil {
			http.Error(w, "Bad private token.", http.StatusBadRequest)
		} else if pair = strings.SplitN(string(payload), ":", 2); len(pair) != 2 {
			http.Error(w, "Bad private token.", http.StatusBadRequest)
		} else if !api.validateCredentials(pair[0], pair[1]) {
			http.Error(w, "Authorization failed.", http.StatusUnauthorized)
		} else {
			next.ServeHTTP(w, r)
		}
	})
}

// Serve the HTTP request.
func (api API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	loggingMiddleware(api.router).ServeHTTP(w, r)
}

func (api *API) validateCredentials(username, password string) bool {
	return username == api.config.Username && password == api.config.Password
}

func (api *API) CreateSession(r *http.Request) (*Session, error) {
	username, password, ok := r.BasicAuth()
	if !ok {
		return nil, errors.New("Invalid Authorization header.")
	}

	if !api.validateCredentials(username, password) {
		return nil, errors.New("Authorization failed.")
	}

	// TODO: replace with an actual token.
	credentials := []byte(fmt.Sprintf("%s:%s", username, password))
	token := base64.StdEncoding.EncodeToString(credentials)
	return &Session{token}, nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"gitlab.transip.us/swiltink/go-MusicBot/player"
	"gitlab.transip.us/swiltink/go-MusicBot/playlist"
	"log"
	"net/http"
)

type API struct {
	Router   *mux.Router
	playlist playlist.ListInterface
	Routes   []Route
}

func NewAPI(playlist playlist.ListInterface) *API {
	return &API{
		Router:   mux.NewRouter(),
		playlist: playlist,
	}
}

func (api *API) Start() {
	api.initializeRoutes()

	// Register all routes
	for _, r := range api.Routes {
		api.registerRoute(r)
	}

	log.Fatal(http.ListenAndServe(":7070", api.Router))
}

func (api *API) initializeRoutes() {
	api.Routes = []Route{
		{
			Pattern: "/list",
			Method:  http.MethodGet,
			handler: api.ListHandler,
		}, {
			Pattern: "/current",
			Method:  http.MethodGet,
			handler: api.CurrentHandler,
		}, {
			Pattern: "/play",
			Method:  http.MethodGet,
			handler: api.PlayHandler,
		}, {
			Pattern: "/pause",
			Method:  http.MethodGet,
			handler: api.PauseHandler,
		}, {
			Pattern: "/stop",
			Method:  http.MethodGet,
			handler: api.StopHandler,
		}, {
			Pattern: "/next",
			Method:  http.MethodGet,
			handler: api.NextHandler,
		}, {
			Pattern: "/add",
			Method:  http.MethodGet,
			handler: api.AddHandler,
		},
	}
}

// registerRoute - Register a rout with the
func (api *API) registerRoute(route Route) bool {
	api.Router.HandleFunc(route.Pattern, route.handler).Methods(route.Method)

	return true
}

func (api *API) ListHandler(w http.ResponseWriter, r *http.Request) {
	items := api.playlist.GetItems()
	err := json.NewEncoder(w).Encode(api.convertItems(items))
	if err != nil {
		fmt.Printf("API list encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) CurrentHandler(w http.ResponseWriter, r *http.Request) {
	itm := api.playlist.GetCurrentItem()
	err := json.NewEncoder(w).Encode(api.convertItem(itm))
	if err != nil {
		fmt.Printf("API current encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) PlayHandler(w http.ResponseWriter, r *http.Request) {
	itm, err := api.playlist.Play()
	if err != nil {
		fmt.Printf("API play error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	err = json.NewEncoder(w).Encode(api.convertItem(itm))
	if err != nil {
		fmt.Printf("API next encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) PauseHandler(w http.ResponseWriter, r *http.Request) {
	err := api.playlist.Pause()
	if err != nil {
		fmt.Printf("API pause error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) StopHandler(w http.ResponseWriter, r *http.Request) {
	err := api.playlist.Stop()
	if err != nil {
		fmt.Printf("API stop error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) NextHandler(w http.ResponseWriter, r *http.Request) {
	itm, err := api.playlist.Next()
	if err != nil {
		fmt.Printf("API next error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	err = json.NewEncoder(w).Encode(api.convertItem(itm))
	if err != nil {
		fmt.Printf("API next encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) AddHandler(w http.ResponseWriter, r *http.Request) {
	items, err := api.playlist.AddItems(r.URL.Query().Get("url"))
	if err != nil {
		fmt.Printf("API add error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	err = json.NewEncoder(w).Encode(api.convertItems(items))
	if err != nil {
		fmt.Printf("API add encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) convertItem(itm playlist.ItemInterface) (newItem *player.ListItem) {
	if itm != nil {
		newItem = player.NewListItem(itm.GetTitle(), itm.GetDuration(), itm.GetURL())
	}
	return
}

func (api *API) convertItems(itms []playlist.ItemInterface) (newItems []player.ListItem) {
	for _, itm := range itms {
		if itm == nil {
			continue
		}
		newItems = append(newItems, *api.convertItem(itm))
	}
	return
}
<commit_msg>Add status api<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"gitlab.transip.us/swiltink/go-MusicBot/player"
	"gitlab.transip.us/swiltink/go-MusicBot/playlist"
	"log"
	"net/http"
)

type API struct {
	Router   *mux.Router
	playlist playlist.ListInterface
	Routes   []Route
}

type Status struct {
	Status  playlist.Status
	Current *player.ListItem
	List    []player.ListItem
}

func NewAPI(playlist playlist.ListInterface) *API {
	return &API{
		Router:   mux.NewRouter(),
		playlist: playlist,
	}
}

func (api *API) Start() {
	api.initializeRoutes()

	// Register all routes
	for _, r := range api.Routes {
		api.registerRoute(r)
	}

	log.Fatal(http.ListenAndServe(":7070", api.Router))
}

func (api *API) initializeRoutes() {
	api.Routes = []Route{
		{
			Pattern: "/status",
			Method:  http.MethodGet,
			handler: api.StatusHandler,
		}, {
			Pattern: "/list",
			Method:  http.MethodGet,
			handler: api.ListHandler,
		}, {
			Pattern: "/current",
			Method:  http.MethodGet,
			handler: api.CurrentHandler,
		}, {
			Pattern: "/play",
			Method:  http.MethodGet,
			handler: api.PlayHandler,
		}, {
			Pattern: "/pause",
			Method:  http.MethodGet,
			handler: api.PauseHandler,
		}, {
			Pattern: "/stop",
			Method:  http.MethodGet,
			handler: api.StopHandler,
		}, {
			Pattern: "/next",
			Method:  http.MethodGet,
			handler: api.NextHandler,
		}, {
			Pattern: "/add",
			Method:  http.MethodGet,
			handler: api.AddHandler,
		},
	}
}

// registerRoute - Register a rout with the
func (api *API) registerRoute(route Route) bool {
	api.Router.HandleFunc(route.Pattern, route.handler).Methods(route.Method)

	return true
}

func (api *API) StatusHandler(w http.ResponseWriter, r *http.Request) {
	s := Status{
		Status:  api.playlist.GetStatus(),
		Current: api.convertItem(api.playlist.GetCurrentItem()),
		List:    api.convertItems(api.playlist.GetItems()),
	}
	err := json.NewEncoder(w).Encode(s)
	if err != nil {
		fmt.Printf("API status encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) ListHandler(w http.ResponseWriter, r *http.Request) {
	items := api.playlist.GetItems()
	err := json.NewEncoder(w).Encode(api.convertItems(items))
	if err != nil {
		fmt.Printf("API list encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) CurrentHandler(w http.ResponseWriter, r *http.Request) {
	itm := api.playlist.GetCurrentItem()
	err := json.NewEncoder(w).Encode(api.convertItem(itm))
	if err != nil {
		fmt.Printf("API current encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) PlayHandler(w http.ResponseWriter, r *http.Request) {
	itm, err := api.playlist.Play()
	if err != nil {
		fmt.Printf("API play error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	err = json.NewEncoder(w).Encode(api.convertItem(itm))
	if err != nil {
		fmt.Printf("API next encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) PauseHandler(w http.ResponseWriter, r *http.Request) {
	err := api.playlist.Pause()
	if err != nil {
		fmt.Printf("API pause error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) StopHandler(w http.ResponseWriter, r *http.Request) {
	err := api.playlist.Stop()
	if err != nil {
		fmt.Printf("API stop error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) NextHandler(w http.ResponseWriter, r *http.Request) {
	itm, err := api.playlist.Next()
	if err != nil {
		fmt.Printf("API next error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	err = json.NewEncoder(w).Encode(api.convertItem(itm))
	if err != nil {
		fmt.Printf("API next encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) AddHandler(w http.ResponseWriter, r *http.Request) {
	items, err := api.playlist.AddItems(r.URL.Query().Get("url"))
	if err != nil {
		fmt.Printf("API add error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	err = json.NewEncoder(w).Encode(api.convertItems(items))
	if err != nil {
		fmt.Printf("API add encode error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

func (api *API) convertItem(itm playlist.ItemInterface) (newItem *player.ListItem) {
	if itm != nil {
		newItem = player.NewListItem(itm.GetTitle(), itm.GetDuration(), itm.GetURL())
	}
	return
}

func (api *API) convertItems(itms []playlist.ItemInterface) (newItems []player.ListItem) {
	for _, itm := range itms {
		if itm == nil {
			continue
		}
		newItems = append(newItems, *api.convertItem(itm))
	}
	return
}
<|endoftext|>