<commit_before>package gosubway

import (
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
	"time"

	"code.google.com/p/goprotobuf/proto"

	_ "github.com/jprobinson/gtfs/nyct_subway_proto"
	"github.com/jprobinson/gtfs/transit_realtime"
)

type FeedMessage struct {
	transit_realtime.FeedMessage
}

// GetFeed takes an API key generated from http://datamine.mta.info/user/register
// and a boolean specifying which feed (1,2,3,4,5,6,S trains OR L train) and
// it will return a transit_realtime.FeedMessage with NYCT extensions.
func GetFeed(key string, lTrain bool) (*FeedMessage, error) {
	url := "http://datamine.mta.info/mta_esi.php?key=" + key
	if lTrain {
		url = url + "&feed_id=2"
	} else {
		url = url + "&feed_id=1"
	}
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	transit := &transit_realtime.FeedMessage{}
	err = proto.Unmarshal(body, transit)
	if err != nil {
		return nil, err
	}
	return &FeedMessage{*transit}, nil
}

type StopTimeUpdate struct {
	transit_realtime.TripUpdate_StopTimeUpdate
}

// Trains will accept a stopId (found here: http://web.mta.info/developers/data/nyct/subway/google_transit.zip)
// and returns a list of updates from northbound and southbound trains
func (f *FeedMessage) Trains(stopId string) (northbound, southbound []*StopTimeUpdate) {

	for _, ent := range f.Entity {
		if ent.TripUpdate != nil {
			for _, upd := range ent.TripUpdate.StopTimeUpdate {
				if strings.HasPrefix(*upd.StopId, stopId) {
					if strings.HasSuffix(*upd.StopId, "N") {
						northbound = append(northbound, &StopTimeUpdate{*upd})
					} else if strings.HasSuffix(*upd.StopId, "S") {
						southbound = append(southbound, &StopTimeUpdate{*upd})
					}
				}
			}
		}

	}

	return
}

// NextTrainTimes will return an ordered slice of upcoming train departure times
// in either direction.
func (f *FeedMessage) NextTrainTimes(stopId string) (northbound, southbound []time.Time) {
	north, south := f.Trains(stopId)
	northbound = NextTrainTimes(north)
	southbound = NextTrainTimes(south)
	return
}

// NextTrainTimes will extract the departure times from the given
// update slice, order and return them.
func NextTrainTimes(updates []*StopTimeUpdate) []time.Time {
	var times []time.Time

	for _, upd := range updates {
		if upd.Departure == nil {
			continue
		}
		unix := *upd.Departure.Time
		if upd.Departure.Delay != nil {
			unix += int64(*upd.Departure.Delay * 1000)
		}
		dept := time.Unix(unix, 0)
		if dept.After(time.Now()) {
			times = append(times, dept)
		}
	}
	sort.Sort(timeSlice(times))
	if len(times) > 5 {
		times = times[:5]
	}
	return times
}

type timeSlice []time.Time

func (t timeSlice) Len() int {
	return len(t)
}

func (t timeSlice) Less(i, j int) bool {
	return t[i].Before(t[j])
}

func (t timeSlice) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
<commit_msg>proto import update<commit_after>package gosubway

import (
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/golang/protobuf/proto"
	_ "github.com/jprobinson/gtfs/nyct_subway_proto"
	"github.com/jprobinson/gtfs/transit_realtime"
)

type FeedMessage struct {
	transit_realtime.FeedMessage
}

// GetFeed takes an API key generated from http://datamine.mta.info/user/register
// and a boolean specifying which feed (1,2,3,4,5,6,S trains OR L train) and
// it will return a transit_realtime.FeedMessage with NYCT extensions.
func GetFeed(key string, lTrain bool) (*FeedMessage, error) {
	url := "http://datamine.mta.info/mta_esi.php?key=" + key
	if lTrain {
		url = url + "&feed_id=2"
	} else {
		url = url + "&feed_id=1"
	}
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	transit := &transit_realtime.FeedMessage{}
	err = proto.Unmarshal(body, transit)
	if err != nil {
		return nil, err
	}
	return &FeedMessage{*transit}, nil
}

type StopTimeUpdate struct {
	transit_realtime.TripUpdate_StopTimeUpdate
}

// Trains will accept a stopId (found here: http://web.mta.info/developers/data/nyct/subway/google_transit.zip)
// and returns a list of updates from northbound and southbound trains
func (f *FeedMessage) Trains(stopId string) (northbound, southbound []*StopTimeUpdate) {

	for _, ent := range f.Entity {
		if ent.TripUpdate != nil {
			for _, upd := range ent.TripUpdate.StopTimeUpdate {
				if strings.HasPrefix(*upd.StopId, stopId) {
					if strings.HasSuffix(*upd.StopId, "N") {
						northbound = append(northbound, &StopTimeUpdate{*upd})
					} else if strings.HasSuffix(*upd.StopId, "S") {
						southbound = append(southbound, &StopTimeUpdate{*upd})
					}
				}
			}
		}

	}

	return
}

// NextTrainTimes will return an ordered slice of upcoming train departure times
// in either direction.
func (f *FeedMessage) NextTrainTimes(stopId string) (northbound, southbound []time.Time) {
	north, south := f.Trains(stopId)
	northbound = NextTrainTimes(north)
	southbound = NextTrainTimes(south)
	return
}

// NextTrainTimes will extract the departure times from the given
// update slice, order and return them.
func NextTrainTimes(updates []*StopTimeUpdate) []time.Time {
	var times []time.Time

	for _, upd := range updates {
		if upd.Departure == nil {
			continue
		}
		unix := *upd.Departure.Time
		if upd.Departure.Delay != nil {
			unix += int64(*upd.Departure.Delay * 1000)
		}
		dept := time.Unix(unix, 0)
		if dept.After(time.Now()) {
			times = append(times, dept)
		}
	}
	sort.Sort(timeSlice(times))
	if len(times) > 5 {
		times = times[:5]
	}
	return times
}

type timeSlice []time.Time

func (t timeSlice) Len() int {
	return len(t)
}

func (t timeSlice) Less(i, j int) bool {
	return t[i].Before(t[j])
}

func (t timeSlice) Swap(i, j int) {
	t[i], t[j] = t[j], t[i]
}
<|endoftext|>


<commit_before>// Package sudoku is a generic solver for sudoku type puzzles. It can solve a puzzle of any shape and size.
package sudoku

var (
	s4 = []Constraint{
		Unique{0, 1, 2, 3},
		Unique{4, 5, 6, 7},
		Unique{8, 9, 10, 11},
		Unique{12, 13, 14, 15},
		Unique{0, 4, 8, 12},
		Unique{1, 5, 9, 13},
		Unique{2, 6, 10, 14},
		Unique{3, 7, 11, 15},
		Unique{0, 1, 4, 5},
		Unique{2, 3, 6, 7},
		Unique{8, 9, 12, 13},
		Unique{10, 11, 14, 15},
	}
	s9 = []Constraint{
		Unique{0, 1, 2, 3, 4, 5, 6, 7, 8},
		Unique{9, 10, 11, 12, 13, 14, 15, 16, 17},
		Unique{18, 19, 20, 21, 22, 23, 24, 25, 26},
		Unique{27, 28, 29, 30, 31, 32, 33, 34, 35},
		Unique{36, 37, 38, 39, 40, 41, 42, 43, 44},
		Unique{45, 46, 47, 48, 49, 50, 51, 52, 53},
		Unique{54, 55, 56, 57, 58, 59, 60, 61, 62},
		Unique{63, 64, 65, 66, 67, 68, 69, 70, 71},
		Unique{72, 73, 74, 75, 76, 77, 78, 79, 80},
		Unique{0, 9, 18, 27, 36, 45, 54, 63, 72},
		Unique{1, 10, 19, 28, 37, 46, 55, 64, 73},
		Unique{2, 11, 20, 29, 38, 47, 56, 65, 74},
		Unique{3, 12, 21, 30, 39, 48, 57, 66, 75},
		Unique{4, 13, 22, 31, 40, 49, 58, 67, 76},
		Unique{5, 14, 23, 32, 41, 50, 59, 68, 77},
		Unique{6, 15, 24, 33, 42, 51, 60, 69, 78},
		Unique{7, 16, 25, 34, 43, 52, 61, 70, 79},
		Unique{8, 17, 26, 35, 44, 53, 62, 71, 80},
		Unique{0, 1, 2, 9, 10, 11, 18, 19, 20},
		Unique{3, 4, 5, 12, 13, 14, 21, 22, 23},
		Unique{6, 7, 8, 15, 16, 17, 24, 25, 26},
		Unique{27, 28, 29, 36, 37, 38, 45, 46, 47},
		Unique{30, 31, 32, 39, 40, 41, 48, 49, 50},
		Unique{33, 34, 35, 42, 43, 44, 51, 52, 53},
		Unique{54, 55, 56, 63, 64, 65, 72, 73, 74},
		Unique{57, 58, 59, 66, 67, 68, 75, 76, 77},
		Unique{60, 61, 62, 69, 70, 71, 78, 79, 80},
	}
)

// MakeBox is a helper function to make it easier to create the sections in standard rectangular puzzles
func MakeBox(gridWidth, gridHeight, boxWidth, boxHeight int) []Constraint {
	toRet := make([]Constraint, 0, gridWidth/boxWidth*gridHeight/boxHeight)
	for j := 0; j < gridHeight; j += boxHeight {
		for i := 0; i < gridWidth; i += boxWidth {
			thisGrid := make(Unique, 0, boxWidth*boxHeight)
			for y := 0; y < boxHeight; y++ {
				for x := 0; x < boxWidth; x++ {
					thisGrid = append(thisGrid, i+x+(j+y)*gridWidth)
				}
			}
			toRet = append(toRet, thisGrid)
		}
	}
	return toRet
}

// Solve9 is a sudoku puzzle of the standard 9x9 format
func Solve9(data []int) bool {
	if len(data) != 81 {
		return false
	}
	s := Sudoku{
		data:        data,
		chars:       9,
		constraints: s9,
	}
	return s.Solve()
}

// Solve4 is a sudoku puzzle of the 4x4 format
func Solve4(data []int) bool {
	if len(data) != 16 {
		return false
	}
	s := Sudoku{
		data:        data,
		chars:       4,
		constraints: s4,
	}
	return s.Solve()
}

// Solve allows the creation of a non-standard Sudoku puzzle and solves it.
//
// data is the puzzle information, layed out left to right, then top to bottom
//
// chars is any valid 'character' the puzzle uses - 0 is used for an unfilled space
//
// structure is a slice of sections, each of which is a slice of positions, len(chars)
// in length, which describes the rows, columns, boxes or other shapes in which there
// can only be one of each character
//
// Will return true if puzzle is solveable and the solution will be stored in the data slice.
// Upon a failure, will return false and the data slice will be as original.
func Solve(data []int, chars int, constraints []Constraint) bool {
	s := Sudoku{data, chars, constraints}
	return s.Solve()
}

func slicePos(s []int, p int) int {
	for n, sp := range s {
		if sp == p {
			return n
		}
	}
	return -1
}

// Constraint defines the interface through which the character constraints are processed
type Constraint interface {
	Constrain(*Sudoku, int, []bool) bool
}

// Sudoku puzzle information
type Sudoku struct {
	data        []int
	chars       int
	constraints []Constraint
}

// Chars returns the number of different characters used in the puzzle
func (s *Sudoku) Chars() int {
	return s.chars
}

// Pos return the character at the given position
func (s *Sudoku) Pos(i int) int {
	return s.data[i]
}

func (s *Sudoku) possible(pos int) []int {
	if pos < 0 || pos > len(s.data) || s.data[pos] != 0 {
		return nil
	}
	marked := make([]bool, s.chars+1)
	for _, c := range s.constraints {
		any := c.Constrain(s, pos, marked)
		if !any {
			return []int{}
		}
	}
	toRet := make([]int, 0, s.chars)
	for p, m := range marked[1:] {
		if !m {
			toRet = append(toRet, p+1)
		}
	}
	return toRet
}

// Solve will solve any solveable puzzle and return whether is was sucessful.
func (s *Sudoku) Solve() bool {
	l := len(s.data)
	possibilities := make([][]int, l)
	var pos int
	for ; pos < l; pos++ {
		if p := s.possible(pos); p != nil {
			possibilities[pos] = p
			break
		}
	}
	for pos < l {
		if len(possibilities[pos]) == 0 {
			s.data[pos] = 0
			for pos--; pos >= 0 && len(possibilities[pos]) == 0; pos-- {
				if possibilities[pos] != nil {
					s.data[pos] = 0
				}
			}
			if pos < 0 {
				return false
			}
			continue
		}
		s.data[pos] = possibilities[pos][0]
		possibilities[pos] = possibilities[pos][1:]
		for pos++; pos < l; pos++ {
			if p := s.possible(pos); p != nil {
				possibilities[pos] = p
				break
			}
		}
	}
	return true
}
<commit_msg>updated to vanity urls<commit_after>// Package sudoku is a generic solver for sudoku type puzzles. It can solve a puzzle of any shape and size.
package sudoku // import "vimagination.zapto.org/sudoku"

var (
	s4 = []Constraint{
		Unique{0, 1, 2, 3},
		Unique{4, 5, 6, 7},
		Unique{8, 9, 10, 11},
		Unique{12, 13, 14, 15},
		Unique{0, 4, 8, 12},
		Unique{1, 5, 9, 13},
		Unique{2, 6, 10, 14},
		Unique{3, 7, 11, 15},
		Unique{0, 1, 4, 5},
		Unique{2, 3, 6, 7},
		Unique{8, 9, 12, 13},
		Unique{10, 11, 14, 15},
	}
	s9 = []Constraint{
		Unique{0, 1, 2, 3, 4, 5, 6, 7, 8},
		Unique{9, 10, 11, 12, 13, 14, 15, 16, 17},
		Unique{18, 19, 20, 21, 22, 23, 24, 25, 26},
		Unique{27, 28, 29, 30, 31, 32, 33, 34, 35},
		Unique{36, 37, 38, 39, 40, 41, 42, 43, 44},
		Unique{45, 46, 47, 48, 49, 50, 51, 52, 53},
		Unique{54, 55, 56, 57, 58, 59, 60, 61, 62},
		Unique{63, 64, 65, 66, 67, 68, 69, 70, 71},
		Unique{72, 73, 74, 75, 76, 77, 78, 79, 80},
		Unique{0, 9, 18, 27, 36, 45, 54, 63, 72},
		Unique{1, 10, 19, 28, 37, 46, 55, 64, 73},
		Unique{2, 11, 20, 29, 38, 47, 56, 65, 74},
		Unique{3, 12, 21, 30, 39, 48, 57, 66, 75},
		Unique{4, 13, 22, 31, 40, 49, 58, 67, 76},
		Unique{5, 14, 23, 32, 41, 50, 59, 68, 77},
		Unique{6, 15, 24, 33, 42, 51, 60, 69, 78},
		Unique{7, 16, 25, 34, 43, 52, 61, 70, 79},
		Unique{8, 17, 26, 35, 44, 53, 62, 71, 80},
		Unique{0, 1, 2, 9, 10, 11, 18, 19, 20},
		Unique{3, 4, 5, 12, 13, 14, 21, 22, 23},
		Unique{6, 7, 8, 15, 16, 17, 24, 25, 26},
		Unique{27, 28, 29, 36, 37, 38, 45, 46, 47},
		Unique{30, 31, 32, 39, 40, 41, 48, 49, 50},
		Unique{33, 34, 35, 42, 43, 44, 51, 52, 53},
		Unique{54, 55, 56, 63, 64, 65, 72, 73, 74},
		Unique{57, 58, 59, 66, 67, 68, 75, 76, 77},
		Unique{60, 61, 62, 69, 70, 71, 78, 79, 80},
	}
)

// MakeBox is a helper function to make it easier to create the sections in standard rectangular puzzles
func MakeBox(gridWidth, gridHeight, boxWidth, boxHeight int) []Constraint {
	toRet := make([]Constraint, 0, gridWidth/boxWidth*gridHeight/boxHeight)
	for j := 0; j < gridHeight; j += boxHeight {
		for i := 0; i < gridWidth; i += boxWidth {
			thisGrid := make(Unique, 0, boxWidth*boxHeight)
			for y := 0; y < boxHeight; y++ {
				for x := 0; x < boxWidth; x++ {
					thisGrid = append(thisGrid, i+x+(j+y)*gridWidth)
				}
			}
			toRet = append(toRet, thisGrid)
		}
	}
	return toRet
}

// Solve9 is a sudoku puzzle of the standard 9x9 format
func Solve9(data []int) bool {
	if len(data) != 81 {
		return false
	}
	s := Sudoku{
		data:        data,
		chars:       9,
		constraints: s9,
	}
	return s.Solve()
}

// Solve4 is a sudoku puzzle of the 4x4 format
func Solve4(data []int) bool {
	if len(data) != 16 {
		return false
	}
	s := Sudoku{
		data:        data,
		chars:       4,
		constraints: s4,
	}
	return s.Solve()
}

// Solve allows the creation of a non-standard Sudoku puzzle and solves it.
//
// data is the puzzle information, layed out left to right, then top to bottom
//
// chars is any valid 'character' the puzzle uses - 0 is used for an unfilled space
//
// structure is a slice of sections, each of which is a slice of positions, len(chars)
// in length, which describes the rows, columns, boxes or other shapes in which there
// can only be one of each character
//
// Will return true if puzzle is solveable and the solution will be stored in the data slice.
// Upon a failure, will return false and the data slice will be as original.
func Solve(data []int, chars int, constraints []Constraint) bool {
	s := Sudoku{data, chars, constraints}
	return s.Solve()
}

func slicePos(s []int, p int) int {
	for n, sp := range s {
		if sp == p {
			return n
		}
	}
	return -1
}

// Constraint defines the interface through which the character constraints are processed
type Constraint interface {
	Constrain(*Sudoku, int, []bool) bool
}

// Sudoku puzzle information
type Sudoku struct {
	data        []int
	chars       int
	constraints []Constraint
}

// Chars returns the number of different characters used in the puzzle
func (s *Sudoku) Chars() int {
	return s.chars
}

// Pos return the character at the given position
func (s *Sudoku) Pos(i int) int {
	return s.data[i]
}

func (s *Sudoku) possible(pos int) []int {
	if pos < 0 || pos > len(s.data) || s.data[pos] != 0 {
		return nil
	}
	marked := make([]bool, s.chars+1)
	for _, c := range s.constraints {
		any := c.Constrain(s, pos, marked)
		if !any {
			return []int{}
		}
	}
	toRet := make([]int, 0, s.chars)
	for p, m := range marked[1:] {
		if !m {
			toRet = append(toRet, p+1)
		}
	}
	return toRet
}

// Solve will solve any solveable puzzle and return whether is was sucessful.
func (s *Sudoku) Solve() bool {
	l := len(s.data)
	possibilities := make([][]int, l)
	var pos int
	for ; pos < l; pos++ {
		if p := s.possible(pos); p != nil {
			possibilities[pos] = p
			break
		}
	}
	for pos < l {
		if len(possibilities[pos]) == 0 {
			s.data[pos] = 0
			for pos--; pos >= 0 && len(possibilities[pos]) == 0; pos-- {
				if possibilities[pos] != nil {
					s.data[pos] = 0
				}
			}
			if pos < 0 {
				return false
			}
			continue
		}
		s.data[pos] = possibilities[pos][0]
		possibilities[pos] = possibilities[pos][1:]
		for pos++; pos < l; pos++ {
			if p := s.possible(pos); p != nil {
				possibilities[pos] = p
				break
			}
		}
	}
	return true
}
<|endoftext|>


<commit_before>package images

import(
  "fmt"
  "io"
  "io/ioutil"
  "encoding/json"
  "github.com/ricallinson/forgery"
  "github.com/spacedock-io/registry/db"
  "github.com/spacedock-io/registry/models"
  "github.com/spacedock-io/registry/cloudfiles"
)

func GetJson(req *f.Request, res *f.Response) {
  var image models.Image
  q := db.DB.Where(&models.Image{Uuid: req.Params["id"]}).First(&image)
  if q.Error != nil {
    res.Send(404)
    return
  }

  res.Set("X-Docker-Size", string(image.Size))
  res.Set("X-Docker-Checksum", image.Checksum)

  res.Send(image.Json)
}

func PutJson(req *f.Request, res *f.Response) {
  var image models.Image
  var err error

  q := db.DB.Where(&models.Image{Uuid: req.Params["id"]}).First(&image)
  fmt.Printf("q: %+v\n", q)
  if q.Error != nil {
    if q.RecordNotFound() == false {
      res.Send(404)
      return
    }
  }

  image.Json, err = ioutil.ReadAll(req.Request.Request.Body)

  if err != nil {
    res.Send(500)
    return
  }

  fmt.Printf("image: %+v\n", image)
  q = db.DB.Save(&image)
  fmt.Printf("q: %+v\n", q)
  if q.Error != nil {
    res.Send(500)
    return
  }

  res.Send(200)
}

func GetLayer(req *f.Request, res *f.Response) {
  _, err := cloudfiles.Cloudfiles.ObjectGet(
    "default", req.Params["id"], res.Response.Writer, true, nil)
  if err == nil {
    res.Send(200)
  } else { res.Send(500) }
}

func PutLayer(req *f.Request, res *f.Response) {
  obj, err := cloudfiles.Cloudfiles.ObjectCreate(
    "default", req.Params["id"], true, "", "", nil)
  if err == nil {
    io.Copy(obj, req.Request.Request.Body)
    res.Send(200)
  } else { res.Send(500) }
}

func GetAncestry(req *f.Request, res *f.Response) {
  var image models.Image
  q := db.DB.First(&models.Image{Uuid: req.Params["id"]}).First(&image)
  if q.Error != nil {
    res.Send(404)
    return
  }

  data, err := json.Marshal(image.Ancestry)

  if err == nil {
    res.Send(data)
  } else { res.Send(500) }
}
<commit_msg>Change container name<commit_after>package images

import(
  "fmt"
  "io"
  "io/ioutil"
  "encoding/json"
  "github.com/ricallinson/forgery"
  "github.com/spacedock-io/registry/db"
  "github.com/spacedock-io/registry/models"
  "github.com/spacedock-io/registry/cloudfiles"
)

func GetJson(req *f.Request, res *f.Response) {
  var image models.Image
  q := db.DB.Where(&models.Image{Uuid: req.Params["id"]}).First(&image)
  if q.Error != nil {
    res.Send(404)
    return
  }

  res.Set("X-Docker-Size", string(image.Size))
  res.Set("X-Docker-Checksum", image.Checksum)

  res.Send(image.Json)
}

func PutJson(req *f.Request, res *f.Response) {
  var image models.Image
  var err error

  q := db.DB.Where(&models.Image{Uuid: req.Params["id"]}).First(&image)
  fmt.Printf("q: %+v\n", q)
  if q.Error != nil {
    if q.RecordNotFound() == false {
      res.Send(404)
      return
    }
  }

  image.Json, err = ioutil.ReadAll(req.Request.Request.Body)

  if err != nil {
    res.Send(500)
    return
  }

  fmt.Printf("image: %+v\n", image)
  q = db.DB.Save(&image)
  fmt.Printf("q: %+v\n", q)
  if q.Error != nil {
    res.Send(500)
    return
  }

  res.Send(200)
}

func GetLayer(req *f.Request, res *f.Response) {
  _, err := cloudfiles.Cloudfiles.ObjectGet(
    "default", req.Params["id"], res.Response.Writer, true, nil)
  if err == nil {
    res.Send(200)
  } else { res.Send(500) }
}

func PutLayer(req *f.Request, res *f.Response) {
  obj, err := cloudfiles.Cloudfiles.ObjectCreate(
    "spacedock", req.Params["id"], true, "", "", nil)
  if err == nil {
    io.Copy(obj, req.Request.Request.Body)
    res.Send(200)
  } else { res.Send(500) }
}

func GetAncestry(req *f.Request, res *f.Response) {
  var image models.Image
  q := db.DB.First(&models.Image{Uuid: req.Params["id"]}).First(&image)
  if q.Error != nil {
    res.Send(404)
    return
  }

  data, err := json.Marshal(image.Ancestry)

  if err == nil {
    res.Send(data)
  } else { res.Send(500) }
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/mitchellh/mapstructure"
	"gopkg.in/mgo.v2/bson"
)

var errUserNotFound = errors.New("User Not Found")

type userDB struct {
	list map[string]*user
	lock sync.RWMutex
}

func (u *userDB) update() {
	log.Println("Refreshing User List")
	u.lock.Lock()
	defer u.lock.Unlock()
	resp, err := http.Get(userListAddress)
	if err != nil {
		log.Printf("Error fetching userlist from %s: %s", userListAddress, err.Error())
		return
	}
	defer resp.Body.Close()
	var data struct {
		Members []struct {
			UserName string `json:"name"`
			UserID   string `json:"id"`
			Deleted  bool   `json:"deleted"`
			Bot      bool   `json:"is_bot"`
			Profile  struct {
				Gamertag string `json:"first_name"`
			} `json:"profile"`
		} `json:"members"`
	}
	dec := json.NewDecoder(resp.Body)
	if dec.Decode(&data); err != nil {
		log.Printf("Error decoding userlist: %s", err.Error())
		return
	}
	found := map[string]bool{}
	re := regexp.MustCompile("[^0-9a-z- ]")
	for _, m := range data.Members {
		if m.Deleted {
			continue
		}
		if m.Bot {
			continue
		}
		name := strings.TrimSpace(m.Profile.Gamertag)
		key := re.ReplaceAllString(strings.ToLower(name), "_")
		found[key] = true
		if _, ok := u.list[key]; !ok {
			u.list[key] = &user{
				name:     name,
				userName: m.UserName,
				userID:   m.UserID,
				data:     map[string]*userBit{},
			}
			log.Printf("+ %s", key)
		}
	}
	for name, _ := range u.list {
		if _, ok := found[name]; !ok {
			log.Printf("- %s", name)
			delete(u.list, name)
		}
	}

}

type userBit struct {
	when time.Time
	data interface{}
}

type user struct {
	name     string
	userName string
	userID   string
	platform int
	account  string
	data     map[string]*userBit
}

func (u *user) set(what string, info interface{}, err error) {
	if _, ok := u.data[what]; !ok {
		u.data[what] = &userBit{}
	}
	if err != nil {
		log.Printf("Error fetching %s for %s: %s", what, u.name, err.Error())
		return
	}
	u.data[what].data = info
	u.data[what].when = time.Now()
}

func (u *user) cUpsert(collection string, query, data interface{}) error {
	_, err := mgoDB.DB("fof").C(collection).Upsert(query, data)
	return err
}

func (u *user) pull() error {
	defer func() {
		var doc = map[string]interface{}{}
		for k, v := range u.data {
			doc[k] = v.data
			delete(u.data, k)
		}
		if bytes, err := json.Marshal(doc); err != nil {
			log.Printf("Error marshalling data for %s: %s", u.name, err.Error())
		} else {
			err = ioutil.WriteFile(fmt.Sprintf("data/%d.%s.json", u.platform, u.account), bytes, 0644)
			if err != nil {
				log.Printf("Error writing datafile for user data/%d.%s.json: %s", u.platform, u.account, err.Error())
			}
		}
		c := mgoDB.DB("fof").C("memberDestinyStats")
		_, err := c.Upsert(bson.M{"member": u.name}, &struct {
			Member string
			Data   map[string]interface{}
		}{
			Member: u.name,
			Data:   doc,
		})
		if err != nil {
			log.Printf("Error inserting document into MongoDB: %s", err.Error())
		}
	}()

	if u.platform == 0 {
		var search = []struct {
			MembershipType int    `mapstructure:"membershipType"`
			MembershipId   string `mapstructure:"membershipId"`
		}{}
		// Try XBL First
		info, buf, err := client.get(playerURL(platformXBL, u.name))
		if err != nil {
			return err
		} else {
			if err := mapstructure.Decode(info, &search); err != nil {
				return err
			}
		}
		if len(search) < 1 {
			// Try PSN Second
			info, buf, err = client.get(playerURL(platformPSN, u.name))
			if err != nil {
				return err
			} else {
				if err := mapstructure.Decode(info, &search); err != nil {
					return err
				}
			}
		}
		if len(search) < 1 {
			return errUserNotFound
		}
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "player",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "player",
				"data":     info.([]interface{})[0],
				"raw":      buf,
			},
		)
		u.platform = search[0].MembershipType
		u.account = search[0].MembershipId
		u.data["player"] = &userBit{
			when: time.Now(),
			data: info.([]interface{})[0],
		}
	}

	if bytes, err := ioutil.ReadFile(fmt.Sprintf("data/%d.%s.json", u.platform, u.account)); err != nil {
		if !os.IsNotExist(err) {
			log.Printf("Error reading %s: %s", fmt.Sprintf("data/%d.%s.json", u.platform, u.account), err.Error())
		}
	} else {
		var doc = map[string]interface{}{}
		if err := json.Unmarshal(bytes, &doc); err != nil {
			log.Printf("Error unmarshalling data for %d/%s (%s): %s", u.platform, u.account, u.name, err.Error())
		} else {
			for k, v := range doc {
				u.set(k, v, nil)
			}
		}
	}

	astats, buf, err := client.get(accountStatsURL(u.platform, u.account))
	u.set("accountStats", astats, err)
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "accountStats",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "accountStats",
				"data":     astats,
				"raw":      buf,
			},
		)
		if astatsDocs, err := pullAllTimeStatsDocs(u.name, u.account, astats); err != nil {
			log.Printf("Error pulling all time stats: %s", err.Error())
		} else {
			for _, v := range astatsDocs {
				if err := u.cUpsert("accountStats", bson.M{"account": u.account, "section": v.Section, "stat": v.Stat}, v); err != nil {
					log.Printf("Error inserting into accountStats: %s", err.Error())
				}
			}
		}
	}

	grim, buf, err := client.get(grimoireURL(u.platform, u.account))
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "grimoire",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "grimoire",
				"data":     grim,
				"raw":      buf,
			},
		)
	}
	u.set("grimoire", grim, err)

	t, buf, err := client.get(triumphsURL(u.platform, u.account))
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "triumphs",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "triumphs",
				"data":     t,
				"raw":      buf,
			},
		)
	}
	u.set("triumphs", t, err)

	account, buf, err := client.get(accountURL(u.platform, u.account))
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "account",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "account",
				"data":     account,
				"raw":      buf,
			},
		)
	}
	u.set("account", account, err)

	if err != nil {
		// todo... decide how to handle error handing in this function...
		return nil
	}

	var acctResponse struct {
		Data struct {
			Characters []struct {
				CharacterBase struct {
					CharacterId string `mapstructure:"characterId"`
				} `mapstructure:"characterBase"`
			} `mapstructure:"characters"`
		} `mapstructure:"data"`
	}

	if err := mapstructure.Decode(account, &acctResponse); err != nil {
		return err
	}

	if _, ok := u.data["characters"]; !ok {
		u.data["characters"] = &userBit{
			data: map[string]map[string]interface{}{},
		}
	}
	var characterDoc map[string]map[string]interface{}
	mapstructure.Decode(u.data["characters"].data, &characterDoc)
	for _, c := range acctResponse.Data.Characters {
		if _, ok := characterDoc[c.CharacterBase.CharacterId]; !ok {
			characterDoc[c.CharacterBase.CharacterId] = map[string]interface{}{}
		}
		cdoc := characterDoc[c.CharacterBase.CharacterId]

		if ch, _, err := client.get(charActivityHistoryURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["activityHistory"] = ch
		}
		if ca, _, err := client.get(charActivitiesURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["activities"] = ca
		}
		if cp, _, err := client.get(charProgressionURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["progression"] = cp
		}
		if cas, _, err := client.get(charActivityStatsURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["activityStats"] = cas
		}
		if cs, _, err := client.get(charStatsURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["stats"] = cs
		}
		if cu, _, err := client.get(charUniqueWeaponsStateURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["uniqueWeapons"] = cu
		}
		if ci, _, err := client.get(charInventoryURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["inventory"] = ci
			// Enumerate Characters Inventiry -- maybe not important?
			//		InventoryItem
		}
		buf, _ := json.Marshal(cdoc)
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "character-" + c.CharacterBase.CharacterId,
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "character-" + c.CharacterBase.CharacterId,
				"data":     cdoc,
				"raw":      buf,
			},
		)
		characterDoc[c.CharacterBase.CharacterId] = cdoc
	}

	buf, _ = json.Marshal(characterDoc)
	u.cUpsert(
		"userData",
		bson.M{
			"account": u.account,
			"name":    u.name,
			"type":    "characters",
		},
		map[string]interface{}{
			"username": u.userName,
			"userid":   u.userID,
			"account":  u.account,
			"name":     u.name,
			"type":     "characters",
			"data":     characterDoc,
			"raw":      buf,
		},
	)

	exoticStatsDocs, err := pullExoticStatsDocs(u.name, u.account, characterDoc)
	if err != nil {
		log.Printf("Error pulling exotic stats for %s: %s", u.name, err.Error())
	} else {
		for _, v := range exoticStatsDocs {
			if err := u.cUpsert("accountStats", bson.M{"account": u.account, "section": v.Section, "stat": v.Stat}, v); err != nil {
				log.Printf("Error inserting into accountStats: %s", err.Error())
			}
		}
	}
	u.set("characters", characterDoc, nil)

	// Merge into single document
	return nil
}
<commit_msg>no longer recording whole json... no longer storing net data in memberDestinyStats<commit_after>package main

import (
	"encoding/json"
	"errors"

	"log"
	"net/http"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/mitchellh/mapstructure"
	"gopkg.in/mgo.v2/bson"
)

var errUserNotFound = errors.New("User Not Found")

type userDB struct {
	list map[string]*user
	lock sync.RWMutex
}

func (u *userDB) update() {
	log.Println("Refreshing User List")
	u.lock.Lock()
	defer u.lock.Unlock()
	resp, err := http.Get(userListAddress)
	if err != nil {
		log.Printf("Error fetching userlist from %s: %s", userListAddress, err.Error())
		return
	}
	defer resp.Body.Close()
	var data struct {
		Members []struct {
			UserName string `json:"name"`
			UserID   string `json:"id"`
			Deleted  bool   `json:"deleted"`
			Bot      bool   `json:"is_bot"`
			Profile  struct {
				Gamertag string `json:"first_name"`
			} `json:"profile"`
		} `json:"members"`
	}
	dec := json.NewDecoder(resp.Body)
	if dec.Decode(&data); err != nil {
		log.Printf("Error decoding userlist: %s", err.Error())
		return
	}
	found := map[string]bool{}
	re := regexp.MustCompile("[^0-9a-z- ]")
	for _, m := range data.Members {
		if m.Deleted {
			continue
		}
		if m.Bot {
			continue
		}
		name := strings.TrimSpace(m.Profile.Gamertag)
		key := re.ReplaceAllString(strings.ToLower(name), "_")
		found[key] = true
		if _, ok := u.list[key]; !ok {
			u.list[key] = &user{
				name:     name,
				userName: m.UserName,
				userID:   m.UserID,
				data:     map[string]*userBit{},
			}
			log.Printf("+ %s", key)
		}
	}
	for name, _ := range u.list {
		if _, ok := found[name]; !ok {
			log.Printf("- %s", name)
			delete(u.list, name)
		}
	}

}

type userBit struct {
	when time.Time
	data interface{}
}

type user struct {
	name     string
	userName string
	userID   string
	platform int
	account  string
	data     map[string]*userBit
}

func (u *user) cUpsert(collection string, query, data interface{}) error {
	_, err := mgoDB.DB("fof").C(collection).Upsert(query, data)
	return err
}

func (u *user) pull() error {
	var search = []struct {
		MembershipType int    `mapstructure:"membershipType"`
		MembershipId   string `mapstructure:"membershipId"`
	}{}
	// Try XBL First
	info, buf, err := client.get(playerURL(platformXBL, u.name))
	if err != nil {
		return err
	} else {
		if err := mapstructure.Decode(info, &search); err != nil {
			return err
		}
	}
	if len(search) < 1 {
		// Try PSN Second
		info, buf, err = client.get(playerURL(platformPSN, u.name))
		if err != nil {
			return err
		} else {
			if err := mapstructure.Decode(info, &search); err != nil {
				return err
			}
		}
	}
	if len(search) < 1 {
		return errUserNotFound
	}
	u.cUpsert(
		"userData",
		bson.M{
			"account": u.account,
			"name":    u.name,
			"type":    "player",
		},
		map[string]interface{}{
			"username": u.userName,
			"userid":   u.userID,
			"account":  u.account,
			"name":     u.name,
			"type":     "player",
			"data":     info.([]interface{})[0],
			"raw":      buf,
		},
	)
	u.platform = search[0].MembershipType
	u.account = search[0].MembershipId
	u.data["player"] = &userBit{
		when: time.Now(),
		data: info.([]interface{})[0],
	}

	astats, buf, err := client.get(accountStatsURL(u.platform, u.account))
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "accountStats",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "accountStats",
				"data":     astats,
				"raw":      buf,
			},
		)
		if astatsDocs, err := pullAllTimeStatsDocs(u.name, u.account, astats); err != nil {
			log.Printf("Error pulling all time stats: %s", err.Error())
		} else {
			for _, v := range astatsDocs {
				if err := u.cUpsert("accountStats", bson.M{"account": u.account, "section": v.Section, "stat": v.Stat}, v); err != nil {
					log.Printf("Error inserting into accountStats: %s", err.Error())
				}
			}
		}
	}

	grim, buf, err := client.get(grimoireURL(u.platform, u.account))
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "grimoire",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "grimoire",
				"data":     grim,
				"raw":      buf,
			},
		)
	}

	t, buf, err := client.get(triumphsURL(u.platform, u.account))
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "triumphs",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "triumphs",
				"data":     t,
				"raw":      buf,
			},
		)
	}

	account, buf, err := client.get(accountURL(u.platform, u.account))
	if err == nil {
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "account",
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "account",
				"data":     account,
				"raw":      buf,
			},
		)
	}

	if err != nil {
		// todo... decide how to handle error handing in this function...
		return nil
	}

	var acctResponse struct {
		Data struct {
			Characters []struct {
				CharacterBase struct {
					CharacterId string `mapstructure:"characterId"`
				} `mapstructure:"characterBase"`
			} `mapstructure:"characters"`
		} `mapstructure:"data"`
	}

	if err := mapstructure.Decode(account, &acctResponse); err != nil {
		return err
	}

	if _, ok := u.data["characters"]; !ok {
		u.data["characters"] = &userBit{
			data: map[string]map[string]interface{}{},
		}
	}
	var characterDoc map[string]map[string]interface{}
	mapstructure.Decode(u.data["characters"].data, &characterDoc)
	for _, c := range acctResponse.Data.Characters {
		if _, ok := characterDoc[c.CharacterBase.CharacterId]; !ok {
			characterDoc[c.CharacterBase.CharacterId] = map[string]interface{}{}
		}
		cdoc := characterDoc[c.CharacterBase.CharacterId]

		if ch, _, err := client.get(charActivityHistoryURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["activityHistory"] = ch
		}
		if ca, _, err := client.get(charActivitiesURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["activities"] = ca
		}
		if cp, _, err := client.get(charProgressionURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["progression"] = cp
		}
		if cas, _, err := client.get(charActivityStatsURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["activityStats"] = cas
		}
		if cs, _, err := client.get(charStatsURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["stats"] = cs
		}
		if cu, _, err := client.get(charUniqueWeaponsStateURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["uniqueWeapons"] = cu
		}
		if ci, _, err := client.get(charInventoryURL(u.platform, u.account, c.CharacterBase.CharacterId)); err == nil {
			cdoc["inventory"] = ci
			// Enumerate Characters Inventiry -- maybe not important?
			//		InventoryItem
		}
		buf, _ := json.Marshal(cdoc)
		u.cUpsert(
			"userData",
			bson.M{
				"account": u.account,
				"name":    u.name,
				"type":    "character-" + c.CharacterBase.CharacterId,
			},
			map[string]interface{}{
				"username": u.userName,
				"userid":   u.userID,
				"account":  u.account,
				"name":     u.name,
				"type":     "character-" + c.CharacterBase.CharacterId,
				"data":     cdoc,
				"raw":      buf,
			},
		)
		characterDoc[c.CharacterBase.CharacterId] = cdoc
	}

	buf, _ = json.Marshal(characterDoc)
	u.cUpsert(
		"userData",
		bson.M{
			"account": u.account,
			"name":    u.name,
			"type":    "characters",
		},
		map[string]interface{}{
			"username": u.userName,
			"userid":   u.userID,
			"account":  u.account,
			"name":     u.name,
			"type":     "characters",
			"data":     characterDoc,
			"raw":      buf,
		},
	)

	exoticStatsDocs, err := pullExoticStatsDocs(u.name, u.account, characterDoc)
	if err != nil {
		log.Printf("Error pulling exotic stats for %s: %s", u.name, err.Error())
	} else {
		for _, v := range exoticStatsDocs {
			if err := u.cUpsert("accountStats", bson.M{"account": u.account, "section": v.Section, "stat": v.Stat}, v); err != nil {
				log.Printf("Error inserting into accountStats: %s", err.Error())
			}
		}
	}

	// Merge into single document
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"
)

func greet(s string) {
	fmt.Println(s)
}

func main() {
	// A goroutinue is an independent and concurrently executing computation.

	// Some intresting properties of a goroutinue are:
	// 1. They are extremly light weight and cheap when compared to threads.
	// 2. Hence you can create hundreds even millions on a commodity hardware.
	// 3. All goroutinues spwanned by an application runs in the same address space
	// 4. They are mutiplexed on to the OS threads by Go runtime scheduler.
	// 5. They run independently of other goroutines, in the same address space.
	// 6. When the main goroutinue exits the program terminates.
	// 7. Hence you must arrange for the main goroutinue to hang around until all
	// other gorountinues either terminate or are cancelled.
	// 8. All Go programs have atleast one goroutinue (main) associated with it.

	// A "go" statement starts the execution of a function call as an
	// independent concurrent thread of control, or goroutine, within the
	// same address space. The passes function, and its arguments are
	// evaluated in the context of current goroutinue and the passed function
	// itself is executed in a new separete goroutinue.
	go greet("there!")
	fmt.Println("Hi")

	// Since the program terminates when the main goroutinue returns, if we
	// to remove the cal to sleep  we will never see the output of the function
	// greet, since it is executing in a separate goroutine
	time.Sleep(10 * time.Nanosecond)
}
<commit_msg>first concurrancy sample<commit_after>package main

import (
	"fmt"
	"time"
)

func greet(s string) {
	fmt.Println(s)
}

func main() {
	// A goroutinue is an independent and concurrently executing computation.

	// Some intresting properties of a goroutinue are:
	// 1. They are extremly light weight and cheap when compared to threads.
	// 2. Hence you can create hundreds even millions on a commodity hardware.
	// 3. All goroutinues spwanned by an application runs in the same address space
	// 4. They are mutiplexed on to the OS threads by Go runtime scheduler.
	// 5. They run independently of other goroutines, in the same address space.
	// 6. When the main goroutinue exits the program terminates.
	// 7. Hence you must arrange for the main goroutinue to hang around until all
	// other gorountinues either terminate or are cancelled.
	// 8. All Go programs have atleast one goroutinue (main goroutinue)
	// associated with it. The main function runs in its own goroutinue.

	// A "go" statement starts the execution of a function call as an
	// independent concurrent thread of control, or goroutine, within the
	// same address space. The passes function, and its arguments are
	// evaluated in the context of current goroutinue and the passed function
	// itself is executed in a new separete goroutinue.
	go greet("there!")
	fmt.Println("Hi")

	// Since the program terminates when the main goroutinue returns, if we
	// to remove the cal to sleep  we will never see the output of the function
	// greet, since it is executing in a separate goroutine
	time.Sleep(10 * time.Nanosecond)
}
<|endoftext|>


<commit_before>// Package object is an abstract data record which tracks state changes.  It's
// meant to make it easier to map key-value records into ORM / RDBMS systems.
// The state change tracking can be useful when the values of primary keys need
// to be updated. (Changing a foreign key on a table with a composite key, for
// example)
package object

import (
	"errors"
	"fmt"
	"strconv"
)

var (
	// ErrValueWasNil is returned by the Get* family of functions in
	// situations where a value must be returned, but we would like to
	// signal that it was originally nil.
	ErrValueWasNil = errors.New("object: value was nil")
)

// Array is our 'object array container' to assist with a couple of instances
// where slices are needed.
type Array []*Object

// Object struct encapsulates our key-value pairs (KV) and a single-item
// per-key history of the previous value stored for a given key
// (ChangedFields).  We also store any instances of 'child records' which may
// be relevant (for instance, when saving with nested transactions).  'saved'
// is used to track the internal state of whether an object was recently
// retrieved or remapped from internal database state.
type Object struct {
	Type          string
	KV            map[string]interface{}
	HiddenKV      map[string]interface{}
	ChangedFields map[string]interface{}
	Children      map[string]Array
	saved         bool
}

// New is an empty constructor
func New(objType string) *Object {
	return &Object{Type: objType, KV: makeEmptyMap(), HiddenKV: nil, ChangedFields: makeEmptyMap(), Children: makeEmptyChildrenMap(), saved: false}
}

// MakeArray will construct an array of length 'size'
func MakeArray(size int) Array {
	objAry := make(Array, size)
	return objAry
}

// NewArray is our single-object array constructor
func NewArray(val *Object) Array {
	objAry := make(Array, 1)
	objAry[0] = val
	return objAry
}

// NewEmptyArray is an empty array constructor
func NewEmptyArray() Array {
	objAry := make(Array, 0)
	return objAry
}

// just a bit of shorthand for the methods below
func makeEmptyMap() map[string]interface{} {
	return make(map[string]interface{})
}

func makeEmptyChildrenMap() map[string]Array {
	return make(map[string]Array)
}

func (o *Object) MakeHiddenKVIfNeeded() {
	if o.HiddenKV == nil {
		o.HiddenKV = makeEmptyMap()
	}
}

// Get is the most basic accessor, for cases
// that may not be handled by other methods
func (o Object) Get(k string) interface{} {
	return o.KV[k]
}

// GetWithFlag is the second most basic accessor, for cases
// that may not be handled by other methods
func (o Object) GetWithFlag(k string) (interface{}, bool) {
	v, ok := o.KV[k]
	return v, ok
}

// GetBool is a safe, typed bool accessor
func (o Object) GetBool(k string) (bool, bool) {
	v, ok := o.KV[k].(bool)
	return v, ok
}

// GetString is a safe, typed string accessor
func (o Object) GetString(k string) (string, bool) {
	v, ok := o.KV[k].(string)
	return v, ok
}

// GetInt is a safe, typed int64 accessor
func (o Object) GetInt(k string) (int64, bool) {
	v, ok := o.KV[k].(int64)
	return v, ok
}

// GetFloat is a safe, typed float64 accessor
func (o Object) GetFloat(k string) (float64, bool) {
	v, ok := o.KV[k].(float64)
	return v, ok
}

// HiddenGetStringAlways is a safe, typed string accessor for the Hidden KV. It
// will force conversion away from float64, int64, uint64, string, and nil
// values. Nils and unrecognized values are marked as an error (nil values will
// return 0 and ErrValueWasNil)
func (o Object) HiddenGetStringAlways(k string) (string, error) {
	switch v := o.HiddenKV[k].(type) {
	case float64:
		fl := o.HiddenKV[k].(float64)
		return fmt.Sprintf("%f", fl), nil
	case int64:
		fl := o.HiddenKV[k].(int64)
		return fmt.Sprintf("%d", fl), nil
	case uint64:
		fl := o.HiddenKV[k].(uint64)
		return fmt.Sprintf("%d", fl), nil
	case string:
		fl := o.HiddenKV[k].(string)
		return fl, nil
	case nil:
		return "", ErrValueWasNil
	// TODO: what about booleans?
	default:
		return "", fmt.Errorf("HiddenGetStringAlways: unrecognized type %v", v)
	}
}

// GetStringAlways is a safe, typed string accessor. It will force conversion away
// from float64, int64, uint64, string, and nil values. Nils and unrecognized values
// are marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetStringAlways(k string) (string, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return fmt.Sprintf("%f", fl), nil
	case int64:
		fl := o.KV[k].(int64)
		return fmt.Sprintf("%d", fl), nil
	case uint64:
		fl := o.KV[k].(uint64)
		return fmt.Sprintf("%d", fl), nil
	case string:
		fl := o.KV[k].(string)
		return fl, nil
	case nil:
		return "", ErrValueWasNil
	// TODO: what about booleans?
	default:
		return "", fmt.Errorf("GetStringAlways: unrecognized type %v", v)
	}
}

// GetFloatAlways is a safe, typed float64 accessor. It will force conversion away
// from float64, int64, uint64, string, and nil values. Nils and unrecognized values
// are marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetFloatAlways(k string) (float64, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return fl, nil
	case int64:
		fl := o.KV[k].(float64)
		return fl, nil
	case uint64:
		fl := o.KV[k].(uint64)
		return float64(fl), nil
	case string:
		fl := o.KV[k].(string)
		return strconv.ParseFloat(fl, 64)
	case nil:
		return 0, ErrValueWasNil
	// TODO: what about booleans?
	default:
		return 0, fmt.Errorf("GetFloatAlways: unrecognized type %v", v)
	}
}

// GetIntAlways is a safe, typed int64 accessor. It will force conversion away
// from float64, uint64, int64 and string values. Nils and unrecognized values are
// marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetIntAlways(k string) (int64, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return int64(fl), nil
	case int64:
		fl := o.KV[k].(int64)
		return fl, nil
	case uint64:
		fl := o.KV[k].(uint64)
		return int64(fl), nil
	case string:
		fl := o.KV[k].(string)
		return strconv.ParseInt(fl, 10, 64)
	case nil:
		return 0, ErrValueWasNil
	// TODO: what about booleans?
	default:
		return 0, fmt.Errorf("GetIntAlways: unrecognized type %v", v)
	}
}

// GetUintAlways is a safe, typed uint64 accessor. It will force conversion
// away from float64, int64, and string values. Nils and unrecognized values
// are marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetUintAlways(k string) (uint64, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return uint64(fl), nil
	case int64:
		fl := o.KV[k].(int64)
		return uint64(fl), nil
	case uint64:
		fl := o.KV[k].(uint64)
		return fl, nil
	case string:
		fl := o.KV[k].(string)
		return strconv.ParseUint(fl, 10, 64)
	case nil:
		return uint64(0), ErrValueWasNil
	// TODO: what about booleans?
	default:
		return 0, fmt.Errorf("GetUintAlways: unrecognized type %v", v)
	}
}

// Set is our typical setter. It attempts to track changes in records and the
// current state of whether an object appears to have been modified from what
// the database had (or should have).
func (o *Object) Set(k string, v interface{}) {
	oldVal := o.Get(k)

	if oldVal != nil {
		// Avoid redundant Set()s
		if oldVal == v {
			return
		}
		o.FieldChanged(k, oldVal)
	}
	if o.GetSaved() {
		o.SetSaved(false)
	}
	o.SetCore(k, v)
}

// FieldChanged records the previous value for something that is about to be
// set
func (o *Object) FieldChanged(k string, oldVal interface{}) {
	o.ChangedFields[k] = oldVal
}

// SetCore just mutates the internal object KV without any of the usual
// tracking that occurs when Set is called.
func (o *Object) SetCore(k string, v interface{}) {
	o.KV[k] = v
}

// SetWhereNeeded is useful in situations where you aren't quite sure what Set()
// method to use (because yes, sometimes, it is not super clear.) If you are forcing
// UPDATEs to an entire row by turning SetSaved() off, then calling Set() will populate
// ChangedFields, and you will lose the rest of your UPDATE statement. If the object
// is already changed, then Set() is fine. Sometimes, you would want SetCore().
// This has the necessary logic to determine the approach Set() method to call.
// This can be particularly useful when constructing hook functions as part of a
// data pipeline. I consider this a design smell with dyndao and am working on solving
// this in a more sane manner.
func (o * Object) SetWhereNeeded(k string, v interface{}) {
	if len(o.ChangedFields) > 0 {
		o.Set(k, v)
	} else if !o.GetSaved() && len(o.KV) > 0 {
		// If the object is a forced UPDATE (meaning ChangedFields are not set),
		// then we have to set this value in the KV without affecting a change that
		// will force us away from updating the other values. (Due to how
		// Update is coded in Dyndao)
		o.SetCore(k, v)
	}

}

// ResetChangedFields can be used in conjunction with an ORM... For instance,
// once a Save() method is invoked
func (o *Object) ResetChangedFields() {
	o.ChangedFields = make(map[string]interface{})
}

// SetSaved is our setter for the 'object is saved' status field
func (o *Object) SetSaved(status bool) {
	o.saved = status
}

// GetSaved is our getter for the 'object is saved' bool field
func (o Object) GetSaved() bool {
	return o.saved
}
<commit_msg>more comments on SetWhereNeeded, not sure if clear yet<commit_after>// Package object is an abstract data record which tracks state changes.  It's
// meant to make it easier to map key-value records into ORM / RDBMS systems.
// The state change tracking can be useful when the values of primary keys need
// to be updated. (Changing a foreign key on a table with a composite key, for
// example)
package object

import (
	"errors"
	"fmt"
	"strconv"
)

var (
	// ErrValueWasNil is returned by the Get* family of functions in
	// situations where a value must be returned, but we would like to
	// signal that it was originally nil.
	ErrValueWasNil = errors.New("object: value was nil")
)

// Array is our 'object array container' to assist with a couple of instances
// where slices are needed.
type Array []*Object

// Object struct encapsulates our key-value pairs (KV) and a single-item
// per-key history of the previous value stored for a given key
// (ChangedFields).  We also store any instances of 'child records' which may
// be relevant (for instance, when saving with nested transactions).  'saved'
// is used to track the internal state of whether an object was recently
// retrieved or remapped from internal database state.
type Object struct {
	Type          string
	KV            map[string]interface{}
	HiddenKV      map[string]interface{}
	ChangedFields map[string]interface{}
	Children      map[string]Array
	saved         bool
}

// New is an empty constructor
func New(objType string) *Object {
	return &Object{Type: objType, KV: makeEmptyMap(), HiddenKV: nil, ChangedFields: makeEmptyMap(), Children: makeEmptyChildrenMap(), saved: false}
}

// MakeArray will construct an array of length 'size'
func MakeArray(size int) Array {
	objAry := make(Array, size)
	return objAry
}

// NewArray is our single-object array constructor
func NewArray(val *Object) Array {
	objAry := make(Array, 1)
	objAry[0] = val
	return objAry
}

// NewEmptyArray is an empty array constructor
func NewEmptyArray() Array {
	objAry := make(Array, 0)
	return objAry
}

// just a bit of shorthand for the methods below
func makeEmptyMap() map[string]interface{} {
	return make(map[string]interface{})
}

func makeEmptyChildrenMap() map[string]Array {
	return make(map[string]Array)
}

func (o *Object) MakeHiddenKVIfNeeded() {
	if o.HiddenKV == nil {
		o.HiddenKV = makeEmptyMap()
	}
}

// Get is the most basic accessor, for cases
// that may not be handled by other methods
func (o Object) Get(k string) interface{} {
	return o.KV[k]
}

// GetWithFlag is the second most basic accessor, for cases
// that may not be handled by other methods
func (o Object) GetWithFlag(k string) (interface{}, bool) {
	v, ok := o.KV[k]
	return v, ok
}

// GetBool is a safe, typed bool accessor
func (o Object) GetBool(k string) (bool, bool) {
	v, ok := o.KV[k].(bool)
	return v, ok
}

// GetString is a safe, typed string accessor
func (o Object) GetString(k string) (string, bool) {
	v, ok := o.KV[k].(string)
	return v, ok
}

// GetInt is a safe, typed int64 accessor
func (o Object) GetInt(k string) (int64, bool) {
	v, ok := o.KV[k].(int64)
	return v, ok
}

// GetFloat is a safe, typed float64 accessor
func (o Object) GetFloat(k string) (float64, bool) {
	v, ok := o.KV[k].(float64)
	return v, ok
}

// HiddenGetStringAlways is a safe, typed string accessor for the Hidden KV. It
// will force conversion away from float64, int64, uint64, string, and nil
// values. Nils and unrecognized values are marked as an error (nil values will
// return 0 and ErrValueWasNil)
func (o Object) HiddenGetStringAlways(k string) (string, error) {
	switch v := o.HiddenKV[k].(type) {
	case float64:
		fl := o.HiddenKV[k].(float64)
		return fmt.Sprintf("%f", fl), nil
	case int64:
		fl := o.HiddenKV[k].(int64)
		return fmt.Sprintf("%d", fl), nil
	case uint64:
		fl := o.HiddenKV[k].(uint64)
		return fmt.Sprintf("%d", fl), nil
	case string:
		fl := o.HiddenKV[k].(string)
		return fl, nil
	case nil:
		return "", ErrValueWasNil
	// TODO: what about booleans?
	default:
		return "", fmt.Errorf("HiddenGetStringAlways: unrecognized type %v", v)
	}
}

// GetStringAlways is a safe, typed string accessor. It will force conversion away
// from float64, int64, uint64, string, and nil values. Nils and unrecognized values
// are marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetStringAlways(k string) (string, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return fmt.Sprintf("%f", fl), nil
	case int64:
		fl := o.KV[k].(int64)
		return fmt.Sprintf("%d", fl), nil
	case uint64:
		fl := o.KV[k].(uint64)
		return fmt.Sprintf("%d", fl), nil
	case string:
		fl := o.KV[k].(string)
		return fl, nil
	case nil:
		return "", ErrValueWasNil
	// TODO: what about booleans?
	default:
		return "", fmt.Errorf("GetStringAlways: unrecognized type %v", v)
	}
}

// GetFloatAlways is a safe, typed float64 accessor. It will force conversion away
// from float64, int64, uint64, string, and nil values. Nils and unrecognized values
// are marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetFloatAlways(k string) (float64, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return fl, nil
	case int64:
		fl := o.KV[k].(float64)
		return fl, nil
	case uint64:
		fl := o.KV[k].(uint64)
		return float64(fl), nil
	case string:
		fl := o.KV[k].(string)
		return strconv.ParseFloat(fl, 64)
	case nil:
		return 0, ErrValueWasNil
	// TODO: what about booleans?
	default:
		return 0, fmt.Errorf("GetFloatAlways: unrecognized type %v", v)
	}
}

// GetIntAlways is a safe, typed int64 accessor. It will force conversion away
// from float64, uint64, int64 and string values. Nils and unrecognized values are
// marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetIntAlways(k string) (int64, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return int64(fl), nil
	case int64:
		fl := o.KV[k].(int64)
		return fl, nil
	case uint64:
		fl := o.KV[k].(uint64)
		return int64(fl), nil
	case string:
		fl := o.KV[k].(string)
		return strconv.ParseInt(fl, 10, 64)
	case nil:
		return 0, ErrValueWasNil
	// TODO: what about booleans?
	default:
		return 0, fmt.Errorf("GetIntAlways: unrecognized type %v", v)
	}
}

// GetUintAlways is a safe, typed uint64 accessor. It will force conversion
// away from float64, int64, and string values. Nils and unrecognized values
// are marked as an error (nil values will return 0 and ErrValueWasNil)
func (o Object) GetUintAlways(k string) (uint64, error) {
	switch v := o.KV[k].(type) {
	case float64:
		fl := o.KV[k].(float64)
		return uint64(fl), nil
	case int64:
		fl := o.KV[k].(int64)
		return uint64(fl), nil
	case uint64:
		fl := o.KV[k].(uint64)
		return fl, nil
	case string:
		fl := o.KV[k].(string)
		return strconv.ParseUint(fl, 10, 64)
	case nil:
		return uint64(0), ErrValueWasNil
	// TODO: what about booleans?
	default:
		return 0, fmt.Errorf("GetUintAlways: unrecognized type %v", v)
	}
}

// Set is our typical setter. It attempts to track changes in records and the
// current state of whether an object appears to have been modified from what
// the database had (or should have).
func (o *Object) Set(k string, v interface{}) {
	oldVal := o.Get(k)

	if oldVal != nil {
		// Avoid redundant Set()s
		if oldVal == v {
			return
		}
		o.FieldChanged(k, oldVal)
	}
	if o.GetSaved() {
		o.SetSaved(false)
	}
	o.SetCore(k, v)
}

// FieldChanged records the previous value for something that is about to be
// set
func (o *Object) FieldChanged(k string, oldVal interface{}) {
	o.ChangedFields[k] = oldVal
}

// SetCore just mutates the internal object KV without any of the usual
// tracking that occurs when Set is called.
func (o *Object) SetCore(k string, v interface{}) {
	o.KV[k] = v
}

// SetWhereNeeded is useful in situations where you aren't quite sure what Set()
// method to use (because yes, sometimes, it is not super clear.) If you are forcing
// UPDATEs to an entire row by turning SetSaved() off, then calling Set() will populate
// ChangedFields, and you will lose the rest of your UPDATE statement. If the object
// is already changed, then Set() is fine. Sometimes, you would want SetCore().
// This has the necessary logic to determine the approach Set() method to call.
// This can be particularly useful when constructing hook functions as part of a
// data pipeline. I consider this a design smell with dyndao and am working on solving
// this in a more sane manner.
func (o * Object) SetWhereNeeded(k string, v interface{}) {
	// If the object has already been changed, then we need to try to use Set().
	// Otherwise, SetCore() is the right method to use.
	if len(o.ChangedFields) > 0 {
		o.Set(k, v)
	} else if !o.GetSaved() && len(o.KV) > 0 {
		// If the object is a forced UPDATE (meaning ChangedFields are
		// not set), for example, then we have to set this value in the
		// KV without affecting a change that will force us away from
		// updating the other values. (Due to how Update is coded in
		// Dyndao)
		o.SetCore(k, v)
	}

}

// ResetChangedFields can be used in conjunction with an ORM... For instance,
// once a Save() method is invoked
func (o *Object) ResetChangedFields() {
	o.ChangedFields = make(map[string]interface{})
}

// SetSaved is our setter for the 'object is saved' status field
func (o *Object) SetSaved(status bool) {
	o.saved = status
}

// GetSaved is our getter for the 'object is saved' bool field
func (o Object) GetSaved() bool {
	return o.saved
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/net/context"

	datakit "github.com/docker/datakit/api/go-datakit"
)

type GithubHeaders struct {
	GitHubEvent    string
	GitHubDelivery string
	HubSignature   string
}

func (h *Server) PRDir(e github.PullRequestEvent) ([]string, error) {
	n := e.Number
	if n == nil {
		return nil, fmt.Errorf("Invalid PR number")
	}

	if e.Repo.Owner.Login == nil {
		return nil, fmt.Errorf("Empty user")
	}
	user := *e.Repo.Owner.Login

	if e.Repo.Name == nil {
		return nil, fmt.Errorf("Empty repo")
	}
	repo := *e.Repo.Name

	h.logger.Debugf("user=%s, repo=%s", user, repo)
	prDir := []string{user, repo, "prs", strconv.Itoa(*n)}
	return prDir, nil
}

func (h *Server) HandlePullRequestEvent(g GithubHeaders, e github.PullRequestEvent) error {

	// do nothing if the pull request's head didn't change
	if e.Action == nil {
		return nil
	}
	h.logger.Debugf("action: %s", *e.Action)
	switch *e.Action {
	case "opened":
	case "closed":
	case "reopened":
	case "synchronize":
	default:
		return nil
	}

	// maybe we want to move Dial in the parent function
	ctx := context.Background()
	client, err := datakit.Dial(ctx, h.proto, h.address)
	if err != nil {
		return err
	}

	// create a new transaction in the DB
	tr, err := datakit.NewTransaction(ctx, client, h.branch, h.branch+"-"+g.GitHubDelivery)
	if err != nil {
		return err
	}

	dir, err := h.PRDir(e)
	if err != nil {
		return err
	}

	if *e.Action == "closed" {
		// we only store open PRs
		tr.Remove(ctx, dir)
	} else {
		// the PR's head has changed, so store the new head
		head := e.PullRequest.Head.SHA
		if head == nil {
			return fmt.Errorf("PR %d has an invalid head", *e.Number)
		}
		tr.Write(ctx, append(dir, "head"), *head)
	}

	// commit the changes to the hook's branch
	err = tr.Commit(ctx, fmt.Sprintf("Pull request event: %s", g.GitHubDelivery))
	if err != nil {
		return err
	}

	client.Close(ctx)
	return nil
}

func (h *Server) CommitDir(e github.StatusEvent) ([]string, error) {

	if e.Repo.Owner.Login == nil {
		return nil, fmt.Errorf("Empty user")
	}
	user := *e.Repo.Owner.Login

	if e.Repo.Name == nil {
		return nil, fmt.Errorf("Empty repo")
	}
	repo := *e.Repo.Name

	if e.SHA == nil {
		return nil, fmt.Errorf("Empty SHA")
	}
	sha := *e.SHA

	h.logger.Debugf("user=%s, repo=%s", user, repo)
	commitDir := []string{user, repo, "commits", sha}
	return commitDir, nil
}

func (h *Server) HandleStatusEvent(g GithubHeaders, e github.StatusEvent) error {

	// maybe we want to move Dial in the parent function
	ctx := context.Background()
	client, err := datakit.Dial(ctx, h.proto, h.address)
	if err != nil {
		return err
	}
	defer client.Close(ctx)

	// create a new transaction in the DB
	tr, err := datakit.NewTransaction(ctx, client, h.branch, h.branch+"-"+g.GitHubDelivery)
	if err != nil {
		return err
	}

	// project the event in the the filesystem
	dir, err := h.CommitDir(e)
	if err != nil {
		return err
	}

	// the status name, e.g. 'continuous-integration/appveyor/pr'
	context := e.Context
	if context == nil {
		return fmt.Errorf("Status without a valid context")
	}
	dir = append(dir, strings.Split(*context, "/")...)

	if e.Description != nil {
		tr.Write(ctx, append(dir, "description"), *e.Description)
	}
	if e.State != nil {
		tr.Write(ctx, append(dir, "state"), *e.State)
	}
	if e.TargetURL != nil {
		tr.Write(ctx, append(dir, "target_url"), *e.TargetURL)
	}

	// commit the changes to the hook's branch
	err = tr.Commit(ctx, fmt.Sprintf("Status event: %s", g.GitHubDelivery))
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Fix serialisaton of GH events in the webhook<commit_after>package server

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/google/go-github/github"
	"golang.org/x/net/context"

	datakit "github.com/docker/datakit/api/go-datakit"
)

type GithubHeaders struct {
	GitHubEvent    string
	GitHubDelivery string
	HubSignature   string
}

func (h *Server) PRDir(e github.PullRequestEvent) ([]string, error) {
	n := e.Number
	if n == nil {
		return nil, fmt.Errorf("Invalid PR number")
	}

	if e.Repo.Owner.Login == nil {
		return nil, fmt.Errorf("Empty user")
	}
	user := *e.Repo.Owner.Login

	if e.Repo.Name == nil {
		return nil, fmt.Errorf("Empty repo")
	}
	repo := *e.Repo.Name

	h.logger.Debugf("user=%s, repo=%s", user, repo)
	prDir := []string{user, repo, "pr", strconv.Itoa(*n)}
	return prDir, nil
}

func (h *Server) HandlePullRequestEvent(g GithubHeaders, e github.PullRequestEvent) error {

	// do nothing if the pull request's head didn't change
	if e.Action == nil {
		return nil
	}
	h.logger.Debugf("action: %s", *e.Action)
	switch *e.Action {
	case "opened":
	case "closed":
	case "reopened":
	case "synchronize":
	default:
		return nil
	}

	// maybe we want to move Dial in the parent function
	ctx := context.Background()
	client, err := datakit.Dial(ctx, h.proto, h.address)
	if err != nil {
		return err
	}

	// create a new transaction in the DB
	tr, err := datakit.NewTransaction(ctx, client, h.branch, h.branch+"-"+g.GitHubDelivery)
	if err != nil {
		return err
	}

	dir, err := h.PRDir(e)
	if err != nil {
		return err
	}

	if *e.Action == "closed" {
		// we only store open PRs
		tr.Remove(ctx, dir)
	} else {
		// the PR's head has changed, so store the new head
		head := e.PullRequest.Head.SHA
		if head == nil {
			return fmt.Errorf("PR %d has an invalid head", *e.Number)
		}
		tr.Write(ctx, append(dir, "head"), *head)
		tr.Write(ctx, append(dir, "state"), "open")
		title := e.PullRequest.Title
		if title != nil {
			tr.Write(ctx, append(dir, "title"), *title)
		}
	}

	// commit the changes to the hook's branch
	err = tr.Commit(ctx, fmt.Sprintf("Pull request event: %s", g.GitHubDelivery))
	if err != nil {
		return err
	}

	client.Close(ctx)
	return nil
}

func (h *Server) CommitDir(e github.StatusEvent) ([]string, error) {

	if e.Repo.Owner.Login == nil {
		return nil, fmt.Errorf("Empty user")
	}
	user := *e.Repo.Owner.Login

	if e.Repo.Name == nil {
		return nil, fmt.Errorf("Empty repo")
	}
	repo := *e.Repo.Name

	if e.SHA == nil {
		return nil, fmt.Errorf("Empty SHA")
	}
	sha := *e.SHA

	h.logger.Debugf("user=%s, repo=%s", user, repo)
	commitDir := []string{user, repo, "commit", "status", sha}
	return commitDir, nil
}

func (h *Server) HandleStatusEvent(g GithubHeaders, e github.StatusEvent) error {

	// maybe we want to move Dial in the parent function
	ctx := context.Background()
	client, err := datakit.Dial(ctx, h.proto, h.address)
	if err != nil {
		return err
	}
	defer client.Close(ctx)

	// create a new transaction in the DB
	tr, err := datakit.NewTransaction(ctx, client, h.branch, h.branch+"-"+g.GitHubDelivery)
	if err != nil {
		return err
	}

	// project the event in the the filesystem
	dir, err := h.CommitDir(e)
	if err != nil {
		return err
	}

	// the status name, e.g. 'continuous-integration/appveyor/pr'
	context := e.Context
	if context == nil {
		return fmt.Errorf("Status without a valid context")
	}
	dir = append(dir, strings.Split(*context, "/")...)

	if e.Description != nil {
		tr.Write(ctx, append(dir, "description"), *e.Description)
	}
	if e.State != nil {
		tr.Write(ctx, append(dir, "state"), *e.State)
	}
	if e.TargetURL != nil {
		tr.Write(ctx, append(dir, "target_url"), *e.TargetURL)
	}

	// commit the changes to the hook's branch
	err = tr.Commit(ctx, fmt.Sprintf("Status event: %s", g.GitHubDelivery))
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package protobuf_filter

import (
        "fmt"
        "time"
        "github.com/mozilla-services/heka/message"
        . "github.com/mozilla-services/heka/pipeline"
        "code.google.com/p/go-uuid/uuid"
        "github.com/mozilla-services/heka/client"
        "sync"
)

type ProtobufFilter struct {
        *ProtobufFilterConfig
        batchChan chan []byte
        backChan  chan []byte
        msgLoopCount uint
}

type ProtobufFilterConfig struct {
        FlushInterval       uint32 `toml:"flush_interval"`
        FlushBytes          int    `toml:"flush_bytes"`
        ProtobufTag         string `toml:"protobuf_tag"`
        EncoderName         string `toml:"encoder"`
        Delimitter          string `toml:"delimitter"` // Delimitter used to append to end of each protobuf for splitting on when decoding later.
                                                       // Defaults to '\n'
}

func (f *ProtobufFilter) ConfigStruct() interface{} {
        return &ProtobufFilterConfig{
        FlushInterval: 1000,
        FlushBytes:    10,
        ProtobufTag:       "protobuf_filtered",
        Delimitter:     "\n",
        }
}

func (f *ProtobufFilter) Init(config interface{}) (err error) {
        f.ProtobufFilterConfig = config.(*ProtobufFilterConfig)
        f.batchChan = make(chan []byte)
        f.backChan = make(chan []byte, 2)

        if f.ProtobufTag == "" {
            return fmt.Errorf(`A protobuf_tag value must be specified for the ProtobufTag Field`)
        }

        if f.EncoderName == "" {
            return fmt.Errorf(`An encoder must be specified`)
        }

        return
}

func (f *ProtobufFilter) committer(fr FilterRunner, h PluginHelper, wg *sync.WaitGroup) {
        initBatch := make([]byte, 0, 10000)
        f.backChan <- initBatch
        var (
            tag string
            //ok bool
            outBatch []byte
        )
        tag = f.ProtobufTag

        for outBatch = range f.batchChan {
                pack := h.PipelinePack(f.msgLoopCount)
                if pack == nil {
                        fr.LogError(fmt.Errorf("exceeded MaxMsgLoops = %d",
                                h.PipelineConfig.Globals.MaxMsgLoops))
            break
                }
                tagField, _ := message.NewField("ProtobufTag", tag, "")
                pack.Message.AddField(tagField)
                pack.Message.SetUuid(uuid.NewRandom())
                pack.Message.SetPayload(string(outBatch))
                fr.Inject(pack)

                outBatch = outBatch[:0]
                f.backChan <- outBatch
        }
        wg.Done()
}

func (f *ProtobufFilter) receiver(fr FilterRunner, h PluginHelper, encoder Encoder, wg *sync.WaitGroup) {
        var (
                pack *PipelinePack
                ok bool
                e        error
        )
        ok = true
        delimitter := f.Delimitter
        outBatch := make([]byte, 0, 10000)
        outBytes := make([]byte, 0, 10000)
        ticker := time.Tick(time.Duration(f.FlushInterval) * time.Millisecond)
        inChan := fr.InChan()

        for ok {
                select {
                case pack, ok = <-inChan:
                        if !ok {
                                // Closed inChan => we're shutting down, flush data
                                if len(outBatch) > 0 {
                                        f.batchChan <- outBatch
                                }
                                close(f.batchChan)
                                break
                        }
                        f.msgLoopCount = pack.MsgLoopCount
                        encoder2 := client.NewProtobufEncoder(nil)
                        //if outBytes, e = encoder.Encode(pack); e != nil {
                        if e = encoder2.EncodeMessageStream(pack.Message, &outBytes);  e != nil {
                                fr.LogError(fmt.Errorf("Error encoding message: %s", e))
                        } else {
                            if len(outBytes) > 0 {
                                outBatch = append(outBatch, outBytes...)
                                outBatch = append(outBatch, delimitter...)

                                if len(outBatch) > f.FlushBytes {
                                        f.batchChan <- outBatch
                                        outBatch = <-f.backChan
                                }
                            }
                            outBytes = outBytes[:0]
                        }
                        pack.Recycle()
                case <-ticker:
                        if len(outBatch) > 0 {
                        outBatch = append(outBatch, delimitter...)
                        f.batchChan <- outBatch
                        outBatch = <-f.backChan
                        }
                }
        }

        wg.Done()
}

func (f *ProtobufFilter) Run(fr FilterRunner, h PluginHelper) (err error) {
        base_name := f.EncoderName
        full_name := fr.Name() + "-" + f.EncoderName
        encoder, ok := h.Encoder(base_name, full_name)
        if !ok {
            return fmt.Errorf("Encoder not found: %s", full_name)
        }

        var wg sync.WaitGroup
        wg.Add(2)
        go f.receiver(fr, h, encoder, &wg)
        go f.committer(fr, h, &wg)
        wg.Wait()

    return
}

func init() {
    RegisterPlugin("ProtobufFilter", func() interface{} {
        return new(ProtobufFilter)
    })
}
<commit_msg>re-fixing Globals to PipelineConfig.Globals call<commit_after>package protobuf_filter

import (
        "fmt"
        "time"
        "github.com/mozilla-services/heka/message"
        . "github.com/mozilla-services/heka/pipeline"
        "code.google.com/p/go-uuid/uuid"
        "github.com/mozilla-services/heka/client"
        "sync"
)

type ProtobufFilter struct {
        *ProtobufFilterConfig
        batchChan chan []byte
        backChan  chan []byte
        msgLoopCount uint
}

type ProtobufFilterConfig struct {
        FlushInterval       uint32 `toml:"flush_interval"`
        FlushBytes          int    `toml:"flush_bytes"`
        ProtobufTag         string `toml:"protobuf_tag"`
        EncoderName         string `toml:"encoder"`
        Delimitter          string `toml:"delimitter"` // Delimitter used to append to end of each protobuf for splitting on when decoding later.
                                                       // Defaults to '\n'
}

func (f *ProtobufFilter) ConfigStruct() interface{} {
        return &ProtobufFilterConfig{
        FlushInterval: 1000,
        FlushBytes:    10,
        ProtobufTag:       "protobuf_filtered",
        Delimitter:     "\n",
        }
}

func (f *ProtobufFilter) Init(config interface{}) (err error) {
        f.ProtobufFilterConfig = config.(*ProtobufFilterConfig)
        f.batchChan = make(chan []byte)
        f.backChan = make(chan []byte, 2)

        if f.ProtobufTag == "" {
            return fmt.Errorf(`A protobuf_tag value must be specified for the ProtobufTag Field`)
        }

        if f.EncoderName == "" {
            return fmt.Errorf(`An encoder must be specified`)
        }

        return
}

func (f *ProtobufFilter) committer(fr FilterRunner, h PluginHelper, wg *sync.WaitGroup) {
        initBatch := make([]byte, 0, 10000)
        f.backChan <- initBatch
        var (
            tag string
            //ok bool
            outBatch []byte
        )
        tag = f.ProtobufTag

        for outBatch = range f.batchChan {
                pack := h.PipelinePack(f.msgLoopCount)
                if pack == nil {
                        fr.LogError(fmt.Errorf("exceeded MaxMsgLoops = %d",
                                h.PipelineConfig().Globals.MaxMsgLoops))
            break
                }
                tagField, _ := message.NewField("ProtobufTag", tag, "")
                pack.Message.AddField(tagField)
                pack.Message.SetUuid(uuid.NewRandom())
                pack.Message.SetPayload(string(outBatch))
                fr.Inject(pack)

                outBatch = outBatch[:0]
                f.backChan <- outBatch
        }
        wg.Done()
}

func (f *ProtobufFilter) receiver(fr FilterRunner, h PluginHelper, encoder Encoder, wg *sync.WaitGroup) {
        var (
                pack *PipelinePack
                ok bool
                e        error
        )
        ok = true
        delimitter := f.Delimitter
        outBatch := make([]byte, 0, 10000)
        outBytes := make([]byte, 0, 10000)
        ticker := time.Tick(time.Duration(f.FlushInterval) * time.Millisecond)
        inChan := fr.InChan()

        for ok {
                select {
                case pack, ok = <-inChan:
                        if !ok {
                                // Closed inChan => we're shutting down, flush data
                                if len(outBatch) > 0 {
                                        f.batchChan <- outBatch
                                }
                                close(f.batchChan)
                                break
                        }
                        f.msgLoopCount = pack.MsgLoopCount
                        encoder2 := client.NewProtobufEncoder(nil)
                        //if outBytes, e = encoder.Encode(pack); e != nil {
                        if e = encoder2.EncodeMessageStream(pack.Message, &outBytes);  e != nil {
                                fr.LogError(fmt.Errorf("Error encoding message: %s", e))
                        } else {
                            if len(outBytes) > 0 {
                                outBatch = append(outBatch, outBytes...)
                                outBatch = append(outBatch, delimitter...)

                                if len(outBatch) > f.FlushBytes {
                                        f.batchChan <- outBatch
                                        outBatch = <-f.backChan
                                }
                            }
                            outBytes = outBytes[:0]
                        }
                        pack.Recycle()
                case <-ticker:
                        if len(outBatch) > 0 {
                        outBatch = append(outBatch, delimitter...)
                        f.batchChan <- outBatch
                        outBatch = <-f.backChan
                        }
                }
        }

        wg.Done()
}

func (f *ProtobufFilter) Run(fr FilterRunner, h PluginHelper) (err error) {
        base_name := f.EncoderName
        full_name := fr.Name() + "-" + f.EncoderName
        encoder, ok := h.Encoder(base_name, full_name)
        if !ok {
            return fmt.Errorf("Encoder not found: %s", full_name)
        }

        var wg sync.WaitGroup
        wg.Add(2)
        go f.receiver(fr, h, encoder, &wg)
        go f.committer(fr, h, &wg)
        wg.Wait()

    return
}

func init() {
    RegisterPlugin("ProtobufFilter", func() interface{} {
        return new(ProtobufFilter)
    })
}
<|endoftext|>


<commit_before>package protocol

import (
	"sync"

	log "github.com/sirupsen/logrus"

	"github.com/bytom/bytom/common"
	"github.com/bytom/bytom/errors"
	"github.com/bytom/bytom/protocol/bc"
	"github.com/bytom/bytom/protocol/state"
)

var (
	errPubKeyIsNotValidator     = errors.New("pub key is not in validators of target checkpoint")
	errVoteToGrowingCheckpoint  = errors.New("validator publish vote to growing checkpoint")
	errVoteToSameCheckpoint     = errors.New("source height and target height in verification is equals")
	errSameHeightInVerification = errors.New("validator publish two distinct votes for the same target height")
	errSpanHeightInVerification = errors.New("validator publish vote within the span of its other votes")
	errVoteToNonValidator       = errors.New("pubKey of vote is not validator")
	errGuarantyLessThanMinimum  = errors.New("guaranty less than minimum")
	errOverflow                 = errors.New("arithmetic overflow/underflow")
)

const minGuaranty = 1E14

// Casper is BFT based proof of stack consensus algorithm, it provides safety and liveness in theory,
// it's design mainly refers to https://github.com/ethereum/research/blob/master/papers/casper-basics/casper_basics.pdf
type Casper struct {
	mu               sync.RWMutex
	tree             *treeNode
	rollbackNotifyCh chan bc.Hash
	newEpochCh       chan bc.Hash
	store            Store
	// pubKey -> conflicting verifications
	evilValidators map[string][]*Verification
	// block hash -> previous checkpoint hash
	prevCheckpointCache *common.Cache
	// block hash + pubKey -> verification
	verificationCache *common.Cache
	// put the checkpoints which exist a majority supLink but the source checkpoint is not justified
	justifyingCheckpoints map[bc.Hash][]*state.Checkpoint
}

// NewCasper create a new instance of Casper
// argument checkpoints load the checkpoints from leveldb
// the first element of checkpoints must genesis checkpoint or the last finalized checkpoint in order to reduce memory space
// the others must be successors of first one
func NewCasper(store Store, checkpoints []*state.Checkpoint, rollbackNotifyCh chan bc.Hash) *Casper {
	if checkpoints[0].Height != 0 && checkpoints[0].Status != state.Finalized {
		log.Panic("first element of checkpoints must genesis or in finalized status")
	}

	casper := &Casper{
		tree:                  makeTree(checkpoints[0], checkpoints[1:]),
		rollbackNotifyCh:      rollbackNotifyCh,
		newEpochCh:            make(chan bc.Hash),
		store:                 store,
		evilValidators:        make(map[string][]*Verification),
		prevCheckpointCache:   common.NewCache(1024),
		verificationCache:     common.NewCache(1024),
		justifyingCheckpoints: make(map[bc.Hash][]*state.Checkpoint),
	}
	go casper.authVerificationLoop()
	return casper
}

// Best chain return the chain containing the justified checkpoint of the largest height
func (c *Casper) BestChain() (uint64, bc.Hash) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	return c.bestChain()
}

func (c *Casper) bestChain() (uint64, bc.Hash) {
	// root is init justified
	root := c.tree.checkpoint
	bestHeight, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, root.Height)
	return bestHeight, bestHash
}

// LastFinalized return the block height and block hash which is finalized ast last
func (c *Casper) LastFinalized() (uint64, bc.Hash) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	root := c.tree.checkpoint
	return root.Height, root.Hash
}

// Validators return the validators by specified block hash
// e.g. if the block num of epoch is 100, and the block height corresponding to the block hash is 130, then will return the voting results of height in 0~100
func (c *Casper) Validators(blockHash *bc.Hash) (map[string]*state.Validator, error) {
	checkpoint, err := c.prevCheckpoint(blockHash)
	if err != nil {
		return nil, err
	}

	return checkpoint.Validators(), nil
}

func (c *Casper) prevCheckpoint(blockHash *bc.Hash) (*state.Checkpoint, error) {
	hash, err := c.prevCheckpointHash(blockHash)
	if err != nil {
		return nil, err
	}

	return c.store.GetCheckpoint(hash)
}

func (c *Casper) prevCheckpointByPrevHash(prevBlockHash *bc.Hash) (*state.Checkpoint, error) {
	hash, err := c.prevCheckpointHashByPrevHash(prevBlockHash)
	if err != nil {
		return nil, err
	}

	return c.store.GetCheckpoint(hash)
}

// EvilValidator represent a validator who broadcast two distinct verification that violate the commandment
type EvilValidator struct {
	PubKey string
	V1     *Verification
	V2     *Verification
}

// EvilValidators return all evil validators
func (c *Casper) EvilValidators() []*EvilValidator {
	c.mu.RLock()
	defer c.mu.RUnlock()

	var validators []*EvilValidator
	for pubKey, verifications := range c.evilValidators {
		validators = append(validators, &EvilValidator{
			PubKey: pubKey,
			V1:     verifications[0],
			V2:     verifications[1],
		})
	}
	return validators
}

// justifiedHeight is the max justified height of checkpoint from node to root
func chainOfMaxJustifiedHeight(node *treeNode, justifiedHeight uint64) (uint64, bc.Hash, uint64) {
	checkpoint := node.checkpoint
	if checkpoint.Status == state.Justified {
		justifiedHeight = checkpoint.Height
	}

	bestHeight, bestHash, maxJustifiedHeight := checkpoint.Height, checkpoint.Hash, justifiedHeight
	for _, child := range node.children {
		if height, hash, justified := chainOfMaxJustifiedHeight(child, justifiedHeight); justified >= maxJustifiedHeight {
			bestHeight, bestHash, maxJustifiedHeight = height, hash, justified
		}
	}
	return bestHeight, bestHash, maxJustifiedHeight
}

func (c *Casper) prevCheckpointHash(blockHash *bc.Hash) (*bc.Hash, error) {
	if data, ok := c.prevCheckpointCache.Get(*blockHash); ok {
		return data.(*bc.Hash), nil
	}

	block, err := c.store.GetBlockHeader(blockHash)
	if err != nil {
		return nil, err
	}

	result, err := c.prevCheckpointHashByPrevHash(&block.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	c.prevCheckpointCache.Add(*blockHash, result)
	return result, nil
}

func (c *Casper) prevCheckpointHashByPrevHash(prevBlockHash *bc.Hash) (*bc.Hash, error) {
	prevHash := prevBlockHash
	for {
		if data, ok := c.prevCheckpointCache.Get(*prevHash); ok {
			c.prevCheckpointCache.Add(*prevBlockHash, data)
			return data.(*bc.Hash), nil
		}

		prevBlock, err := c.store.GetBlockHeader(prevHash)
		if err != nil {
			return nil, err
		}

		if prevBlock.Height%state.BlocksOfEpoch == 0 {
			c.prevCheckpointCache.Add(*prevBlockHash, prevHash)
			return prevHash, nil
		}

		prevHash = &prevBlock.PreviousBlockHash
	}
}
<commit_msg>fix prev checkpoint cache (#1943)<commit_after>package protocol

import (
	"sync"

	log "github.com/sirupsen/logrus"

	"github.com/bytom/bytom/common"
	"github.com/bytom/bytom/errors"
	"github.com/bytom/bytom/protocol/bc"
	"github.com/bytom/bytom/protocol/state"
)

var (
	errPubKeyIsNotValidator     = errors.New("pub key is not in validators of target checkpoint")
	errVoteToGrowingCheckpoint  = errors.New("validator publish vote to growing checkpoint")
	errVoteToSameCheckpoint     = errors.New("source height and target height in verification is equals")
	errSameHeightInVerification = errors.New("validator publish two distinct votes for the same target height")
	errSpanHeightInVerification = errors.New("validator publish vote within the span of its other votes")
	errVoteToNonValidator       = errors.New("pubKey of vote is not validator")
	errGuarantyLessThanMinimum  = errors.New("guaranty less than minimum")
	errOverflow                 = errors.New("arithmetic overflow/underflow")
)

const minGuaranty = 1E14

// Casper is BFT based proof of stack consensus algorithm, it provides safety and liveness in theory,
// it's design mainly refers to https://github.com/ethereum/research/blob/master/papers/casper-basics/casper_basics.pdf
type Casper struct {
	mu               sync.RWMutex
	tree             *treeNode
	rollbackNotifyCh chan bc.Hash
	newEpochCh       chan bc.Hash
	store            Store
	// pubKey -> conflicting verifications
	evilValidators map[string][]*Verification
	// block hash -> previous checkpoint hash
	prevCheckpointCache *common.Cache
	// block hash + pubKey -> verification
	verificationCache *common.Cache
	// put the checkpoints which exist a majority supLink but the source checkpoint is not justified
	justifyingCheckpoints map[bc.Hash][]*state.Checkpoint
}

// NewCasper create a new instance of Casper
// argument checkpoints load the checkpoints from leveldb
// the first element of checkpoints must genesis checkpoint or the last finalized checkpoint in order to reduce memory space
// the others must be successors of first one
func NewCasper(store Store, checkpoints []*state.Checkpoint, rollbackNotifyCh chan bc.Hash) *Casper {
	if checkpoints[0].Height != 0 && checkpoints[0].Status != state.Finalized {
		log.Panic("first element of checkpoints must genesis or in finalized status")
	}

	casper := &Casper{
		tree:                  makeTree(checkpoints[0], checkpoints[1:]),
		rollbackNotifyCh:      rollbackNotifyCh,
		newEpochCh:            make(chan bc.Hash),
		store:                 store,
		evilValidators:        make(map[string][]*Verification),
		prevCheckpointCache:   common.NewCache(1024),
		verificationCache:     common.NewCache(1024),
		justifyingCheckpoints: make(map[bc.Hash][]*state.Checkpoint),
	}
	go casper.authVerificationLoop()
	return casper
}

// Best chain return the chain containing the justified checkpoint of the largest height
func (c *Casper) BestChain() (uint64, bc.Hash) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	return c.bestChain()
}

func (c *Casper) bestChain() (uint64, bc.Hash) {
	// root is init justified
	root := c.tree.checkpoint
	bestHeight, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, root.Height)
	return bestHeight, bestHash
}

// LastFinalized return the block height and block hash which is finalized ast last
func (c *Casper) LastFinalized() (uint64, bc.Hash) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	root := c.tree.checkpoint
	return root.Height, root.Hash
}

// Validators return the validators by specified block hash
// e.g. if the block num of epoch is 100, and the block height corresponding to the block hash is 130, then will return the voting results of height in 0~100
func (c *Casper) Validators(blockHash *bc.Hash) (map[string]*state.Validator, error) {
	checkpoint, err := c.prevCheckpoint(blockHash)
	if err != nil {
		return nil, err
	}

	return checkpoint.Validators(), nil
}

func (c *Casper) prevCheckpoint(blockHash *bc.Hash) (*state.Checkpoint, error) {
	hash, err := c.prevCheckpointHash(blockHash)
	if err != nil {
		return nil, err
	}

	return c.store.GetCheckpoint(hash)
}

func (c *Casper) prevCheckpointByPrevHash(prevBlockHash *bc.Hash) (*state.Checkpoint, error) {
	hash, err := c.prevCheckpointHashByPrevHash(prevBlockHash)
	if err != nil {
		return nil, err
	}

	return c.store.GetCheckpoint(hash)
}

// EvilValidator represent a validator who broadcast two distinct verification that violate the commandment
type EvilValidator struct {
	PubKey string
	V1     *Verification
	V2     *Verification
}

// EvilValidators return all evil validators
func (c *Casper) EvilValidators() []*EvilValidator {
	c.mu.RLock()
	defer c.mu.RUnlock()

	var validators []*EvilValidator
	for pubKey, verifications := range c.evilValidators {
		validators = append(validators, &EvilValidator{
			PubKey: pubKey,
			V1:     verifications[0],
			V2:     verifications[1],
		})
	}
	return validators
}

// justifiedHeight is the max justified height of checkpoint from node to root
func chainOfMaxJustifiedHeight(node *treeNode, justifiedHeight uint64) (uint64, bc.Hash, uint64) {
	checkpoint := node.checkpoint
	if checkpoint.Status == state.Justified {
		justifiedHeight = checkpoint.Height
	}

	bestHeight, bestHash, maxJustifiedHeight := checkpoint.Height, checkpoint.Hash, justifiedHeight
	for _, child := range node.children {
		if height, hash, justified := chainOfMaxJustifiedHeight(child, justifiedHeight); justified >= maxJustifiedHeight {
			bestHeight, bestHash, maxJustifiedHeight = height, hash, justified
		}
	}
	return bestHeight, bestHash, maxJustifiedHeight
}

func (c *Casper) prevCheckpointHash(blockHash *bc.Hash) (*bc.Hash, error) {
	if data, ok := c.prevCheckpointCache.Get(*blockHash); ok {
		return data.(*bc.Hash), nil
	}

	block, err := c.store.GetBlockHeader(blockHash)
	if err != nil {
		return nil, err
	}

	result, err := c.prevCheckpointHashByPrevHash(&block.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	c.prevCheckpointCache.Add(*blockHash, result)
	return result, nil
}

func (c *Casper) prevCheckpointHashByPrevHash(prevBlockHash *bc.Hash) (*bc.Hash, error) {
	prevHash := prevBlockHash
	for {
		prevBlock, err := c.store.GetBlockHeader(prevHash)
		if err != nil {
			return nil, err
		}

		if prevBlock.Height%state.BlocksOfEpoch == 0 {
			c.prevCheckpointCache.Add(*prevBlockHash, prevHash)
			return prevHash, nil
		}

		if data, ok := c.prevCheckpointCache.Get(*prevHash); ok {
			c.prevCheckpointCache.Add(*prevBlockHash, data)
			return data.(*bc.Hash), nil
		}

		prevHash = &prevBlock.PreviousBlockHash
	}
}
<|endoftext|>