<commit_before>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

import (
	"code.google.com/p/go.text/unicode/norm"
	"fmt"
	"github.com/demizer/go-elog"
	"github.com/demizer/go-spew/spew"
	"reflect"
)

var spd = spew.ConfigState{Indent: "\t", DisableMethods: true}

type systemMessageLevel int

const (
	levelInfo systemMessageLevel = iota
	levelWarning
	levelError
	levelSevere
)

var systemMessageLevels = [...]string{
	"INFO",
	"WARNING",
	"ERROR",
	"SEVERE",
}

func (s systemMessageLevel) String() string {
	return systemMessageLevels[s]
}

type parserMessage int

const (
	warningShortUnderline parserMessage = iota
	errorUnexpectedSectionTitle
	errorUnexpectedSectionTitleOrTransition
)

var parserErrors = [...]string{
	"warningShortUnderline",
	"errorUnexpectedSectionTitle",
	"errorUnexpectedSectionTitleOrTransition",
}

func (p parserMessage) String() string {
	return parserErrors[p]
}

func (p parserMessage) Message() (s string) {
	switch p {
	case warningShortUnderline:
		s = "Title underline too short."
	case errorUnexpectedSectionTitle:
		s = "Unexpected section title."
	case errorUnexpectedSectionTitleOrTransition:
		s = "Unexpected section title or transition."
	}
	return
}

func (p parserMessage) Level() (s systemMessageLevel) {
	switch p {
	case warningShortUnderline:
		s = levelWarning
	case errorUnexpectedSectionTitle:
		s = levelSevere
	case errorUnexpectedSectionTitleOrTransition:
		s = levelSevere
	}
	return
}

type sectionLevels []*SectionNode

func (s *sectionLevels) String() string {
	var out string
	for _, sec := range *s {
		out += fmt.Sprintf("level: %d, rune: %q, overline: %t, length: %d\n",
			sec.Level, sec.UnderLine.Rune, sec.OverLine != nil, sec.Length)
	}
	return out
}

// Returns nil if not found
func (s *sectionLevels) FindByRune(adornChar rune) *SectionNode {
	for _, sec := range *s {
		if sec.UnderLine.Rune == adornChar {
			return sec
		}
	}
	return nil
}

// If exists == true, a section node with the same text and underline has been found in
// sectionLevels, sec is the matching SectionNode. If exists == false, then the sec return value is
// the similarly leveled SectionNode. If exists == false and sec == nil, then the SectionNode added
// to sectionLevels is a new Node.
func (s *sectionLevels) Add(section *SectionNode) (exists bool, sec *SectionNode) {
	sec = s.FindByRune(section.UnderLine.Rune)
	if sec != nil {
		if sec.Text == section.Text {
			return true, sec
		} else if sec.Text != section.Text {
			section.Level = sec.Level
		}
	} else {
		section.Level = len(*s) + 1
	}
	exists = false
	*s = append(*s, section)
	return
}

func (s *sectionLevels) Level() int {
	return len(*s)
}

// Parse is the entry point for the reStructuredText parser.
func Parse(name, text string) (t *Tree, errors []error) {
	t = New(name)
	if !norm.NFC.IsNormalString(text) {
		text = norm.NFC.String(text)
	}
	t.text = text
	_, errors = t.Parse(text, t)
	return
}

func New(name string) *Tree {
	return &Tree{
		Name:          name,
		Nodes:         newList(),
		nodeTarget:    newList(),
		sectionLevels: new(sectionLevels),
		indentWidth:   indentWidth,
	}
}

const (
	zed         = 3
	indentWidth = 4 // Default indent width
)

type Tree struct {
	Name             string
	Nodes            *NodeList // The root node list
	nodeTarget       *NodeList // Used by the parser to add nodes to a target NodeList
	Errors           []error
	text             string
	lex              *lexer
	tokenBackupCount int
	peekCount        int
	token            [7]*item
	sectionLevels    *sectionLevels // Encountered section levels
	id               int            // The unique id of the node in the tree
	indentWidth      int
	indentLevel      int
}

// startParse initializes the parser, using the lexer.
func (t *Tree) startParse(lex *lexer) {
	t.lex = lex
}

func (t *Tree) Parse(text string, treeSet *Tree) (tree *Tree, errors []error) {
	log.Debugln("Start")
	t.startParse(lex(t.Name, text))
	t.text = text
	t.parse(treeSet)
	log.Debugln("End")
	return t, t.Errors
}

func (t *Tree) parse(tree *Tree) {
	log.Debugln("Start")

	t.nodeTarget = t.Nodes

	for t.peek(1).Type != itemEOF {
		var n Node

		token := t.next()
		log.Infof("\nParser got token: %#+v\n\n", token)

		switch token.Type {
		case itemSectionAdornment:
			n = t.section(token)
		case itemParagraph:
			n = newParagraph(token, &t.id)
		case itemSpace:
			n = t.indent(token)
			if n == nil {
				continue
			}
		case itemTitle, itemBlankLine:
			// itemTitle is consumed when evaluating itemSectionAdornment
			continue
		case itemEOF:
			goto exit
		default:
			panic(fmt.Errorf("%q Not implemented!", token.Type))
		}

		t.nodeTarget.append(n)
		switch n.NodeType() {
		case NodeSection, NodeBlockQuote:
			// Set the loop to append items to the NodeList of the new section
			t.nodeTarget = reflect.ValueOf(n).Elem().FieldByName("NodeList").Addr().Interface().(*NodeList)
		}
	}

exit:
	log.Debugln("End")
}

func (t *Tree) backup() *item {
	t.tokenBackupCount++
	// log.Debugln("t.tokenBackupCount:", t.peekCount)
	for i := len(t.token) - 1; i > 0; i-- {
		t.token[i] = t.token[i-1]
		t.token[i-1] = nil
	}
	// log.Debugf("\n##### backup() aftermath #####\n\n")
	// spd.Dump(t.token)
	return t.token[zed-t.tokenBackupCount]
}

func (t *Tree) peekBack(pos int) *item {
	return t.token[zed-pos]
}

func (t *Tree) peek(pos int) *item {
	// log.Debugln("t.peekCount:", t.peekCount, "Pos:", pos)
	if pos < 1 {
		panic("pos cannot be < 1")
	}
	var nItem *item
	for i := 0; i < pos; i++ {
		// log.Debugln("i:", i, "peekCount:", t.peekCount, "pos:", pos)
		if t.peekCount > i {
			nItem = t.token[zed+i]
			log.Debugf("Using %#+v\n", nItem)
			continue
		}
		log.Debugln(zed + t.peekCount + i)
		if t.token[zed+t.peekCount+i+1] == nil {
			t.peekCount++
			// log.Debugln("Getting next item")
			t.token[zed+t.peekCount+i] = t.lex.nextItem()
			nItem = t.token[zed+t.peekCount+i]
		} else {
			nItem = t.token[zed+t.peekCount+i]
		}
	}
	// log.Debugf("\n##### peek() aftermath #####\n\n")
	// spd.Dump(t.token)
	// log.Debugf("Returning: %#+v\n", nItem)
	return nItem
}

func (t *Tree) peekSkip(pos int, iSkip itemElement) *item {
	var nItem *item
outer:
	for i := 1; i <= pos; i++ {
		for {
			nItem = t.peek(i)
			if nItem.Type == iSkip {
				continue
			} else {
				break outer
			}
		}
	}
	return nItem
}

func (t *Tree) next() *item {
	// log.Debugln("t.peekCount:", t.peekCount)
	// skip shifts the pointers left in t.token, pos is the amount to shift
	skip := func(num int) {
		for i := num; i > 0; i-- {
			for x := 0; x < len(t.token)-1; x++ {
				t.token[x] = t.token[x+1]
				t.token[x+1] = nil
			}
		}
	}
	if t.peekCount > 0 {
		skip(t.peekCount)
	} else {
		skip(1)
		t.token[zed] = t.lex.nextItem()
	}
	t.tokenBackupCount, t.peekCount = 0, 0
	// log.Debugf("\n##### next() aftermath #####\n\n")
	// spd.Dump(t.token)
	return t.token[zed]
}

func (t *Tree) section(i *item) Node {
	log.Debugln("Start")
	var overAdorn, title, underAdorn *item
	var overline bool
	var sysMessage Node

	peekForward := t.peekSkip(1, itemSpace)
	if peekForward != nil && peekForward.Type == itemTitle {
		log.Debugln("FOUND SECTION WITH OVERLINE")
		if peekBack := t.peekBack(1); peekBack != nil && peekBack.Type == itemSpace {
			return t.systemMessage(errorUnexpectedSectionTitleOrTransition)
		}
		overAdorn = i
		t.next()
	loop:
		for {
			switch tTok := t.token[zed]; tTok.Type {
			case itemSpace:
				t.next()
			case itemTitle:
				title = tTok
				t.next()
				cur := t.token[zed]
				if cur != nil && cur.Type == itemSectionAdornment {
					continue
				}
				if pNext := t.peek(1); pNext != nil && pNext.Type != itemSectionAdornment {
					panic("Missing section underline!")
				}
			case itemSectionAdornment:
				underAdorn = tTok
				break loop
			}
		}
	} else {
		peekBack := t.peekBack(1)
		if peekBack != nil {
			if peekBack.Type == itemSpace {
				// Looking back past the white space
				if t.peekBack(2).Type == itemTitle {
					return t.systemMessage(errorUnexpectedSectionTitle)
				}
				return t.systemMessage(errorUnexpectedSectionTitleOrTransition)
			} else if peekBack.Type == itemTitle {
				if t.peekBack(2) != nil && t.peekBack(2).Type ==
					itemSectionAdornment {
					// The overline of the section
					overline = true
					overAdorn = peekBack
				}
			}
		}
		title = t.peekBack(1)
		underAdorn = i
	}

	// TODO: Change these into proper error messages!
	// Check adornment for proper syntax
	if underAdorn.Type == itemSpace {
		t.backup() // Put the parser back on the title
		return t.systemMessage(errorUnexpectedSectionTitle)
	} else if overline && title.Length != overAdorn.Length {
		panic("Section over line not equal to title length!")
	} else if overline && overAdorn.Text != underAdorn.Text {
		panic("Section title over line does not match section title under line.")
	}

	sec := newSection(title, overAdorn, underAdorn, &t.id)
	exists, eSec := t.sectionLevels.Add(sec)
	if exists && eSec != nil {
		panic(fmt.Errorf("SectionNode using Text \"%s\" and Rune '%s' was previously parsed!",
			sec.Text, string(sec.UnderLine.Rune)))
	} else if !exists && eSec != nil {
		// There is a matching level in sectionLevels
		t.nodeTarget = &(*t.sectionLevels)[sec.Level-2].NodeList
	}

	// System messages have to be applied after the section is created in order to preserve
	// a consecutive id number.
	if title.Length != underAdorn.Length {
		sysMessage = t.systemMessage(warningShortUnderline)
		sec.NodeList = append(sec.NodeList, sysMessage)
	}

	log.Debugln("End")
	return sec
}

func (t *Tree) systemMessage(err parserMessage) Node {
	var lbText string
	var lbTextLen int
	var backToken int

	s := newSystemMessage(&item{
		Type: itemSystemMessage,
		Line: t.token[zed].Line,
	},
		err.Level(), &t.id)

	msg := newParagraph(&item{
		Text:   err.Message(),
		Length: len(err.Message()),
	}, &t.id)

	log.Debugln("FOUND", err)

	switch err {
	case warningShortUnderline, errorUnexpectedSectionTitle:
		backToken = zed - 1
		if t.peekBack(1).Type == itemSpace {
			backToken = zed - 2
		}
		lbText = t.token[backToken].Text.(string) + "\n" + t.token[zed].Text.(string)
		lbTextLen = len(lbText) + 1
	case errorUnexpectedSectionTitleOrTransition:
		lbText = t.token[zed].Text.(string)
		lbTextLen = len(lbText)
	}

	lb := newLiteralBlock(&item{
		Type:   itemLiteralBlock,
		Text:   lbText,
		Length: lbTextLen, // Add one to account for the backslash
	}, &t.id)

	s.NodeList = append(s.NodeList, msg, lb)
	return s
}

func (t *Tree) indent(i *item) Node {
	level := i.Length / t.indentWidth
	if t.peekBack(1).Type == itemBlankLine {
		if t.indentLevel == level {
			// Append to the current blockquote NodeList
			return nil
		}
		t.indentLevel = level
		return newBlockQuote(&item{Type: itemBlockquote, Line: i.Line}, level, &t.id)
	}
	return nil
}
<commit_msg>Remove Tree.backup()<commit_after>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.

package parse

import (
	"code.google.com/p/go.text/unicode/norm"
	"fmt"
	"github.com/demizer/go-elog"
	"github.com/demizer/go-spew/spew"
	"reflect"
)

var spd = spew.ConfigState{Indent: "\t", DisableMethods: true}

type systemMessageLevel int

const (
	levelInfo systemMessageLevel = iota
	levelWarning
	levelError
	levelSevere
)

var systemMessageLevels = [...]string{
	"INFO",
	"WARNING",
	"ERROR",
	"SEVERE",
}

func (s systemMessageLevel) String() string {
	return systemMessageLevels[s]
}

type parserMessage int

const (
	warningShortUnderline parserMessage = iota
	errorUnexpectedSectionTitle
	errorUnexpectedSectionTitleOrTransition
)

var parserErrors = [...]string{
	"warningShortUnderline",
	"errorUnexpectedSectionTitle",
	"errorUnexpectedSectionTitleOrTransition",
}

func (p parserMessage) String() string {
	return parserErrors[p]
}

func (p parserMessage) Message() (s string) {
	switch p {
	case warningShortUnderline:
		s = "Title underline too short."
	case errorUnexpectedSectionTitle:
		s = "Unexpected section title."
	case errorUnexpectedSectionTitleOrTransition:
		s = "Unexpected section title or transition."
	}
	return
}

func (p parserMessage) Level() (s systemMessageLevel) {
	switch p {
	case warningShortUnderline:
		s = levelWarning
	case errorUnexpectedSectionTitle:
		s = levelSevere
	case errorUnexpectedSectionTitleOrTransition:
		s = levelSevere
	}
	return
}

type sectionLevels []*SectionNode

func (s *sectionLevels) String() string {
	var out string
	for _, sec := range *s {
		out += fmt.Sprintf("level: %d, rune: %q, overline: %t, length: %d\n",
			sec.Level, sec.UnderLine.Rune, sec.OverLine != nil, sec.Length)
	}
	return out
}

// Returns nil if not found
func (s *sectionLevels) FindByRune(adornChar rune) *SectionNode {
	for _, sec := range *s {
		if sec.UnderLine.Rune == adornChar {
			return sec
		}
	}
	return nil
}

// If exists == true, a section node with the same text and underline has been found in
// sectionLevels, sec is the matching SectionNode. If exists == false, then the sec return value is
// the similarly leveled SectionNode. If exists == false and sec == nil, then the SectionNode added
// to sectionLevels is a new Node.
func (s *sectionLevels) Add(section *SectionNode) (exists bool, sec *SectionNode) {
	sec = s.FindByRune(section.UnderLine.Rune)
	if sec != nil {
		if sec.Text == section.Text {
			return true, sec
		} else if sec.Text != section.Text {
			section.Level = sec.Level
		}
	} else {
		section.Level = len(*s) + 1
	}
	exists = false
	*s = append(*s, section)
	return
}

func (s *sectionLevels) Level() int {
	return len(*s)
}

// Parse is the entry point for the reStructuredText parser.
func Parse(name, text string) (t *Tree, errors []error) {
	t = New(name)
	if !norm.NFC.IsNormalString(text) {
		text = norm.NFC.String(text)
	}
	t.text = text
	_, errors = t.Parse(text, t)
	return
}

func New(name string) *Tree {
	return &Tree{
		Name:          name,
		Nodes:         newList(),
		nodeTarget:    newList(),
		sectionLevels: new(sectionLevels),
		indentWidth:   indentWidth,
	}
}

const (
	zed         = 3
	indentWidth = 4 // Default indent width
)

type Tree struct {
	Name             string
	Nodes            *NodeList // The root node list
	nodeTarget       *NodeList // Used by the parser to add nodes to a target NodeList
	Errors           []error
	text             string
	lex              *lexer
	tokenBackupCount int
	peekCount        int
	token            [7]*item
	sectionLevels    *sectionLevels // Encountered section levels
	id               int            // The unique id of the node in the tree
	indentWidth      int
	indentLevel      int
}

// startParse initializes the parser, using the lexer.
func (t *Tree) startParse(lex *lexer) {
	t.lex = lex
}

func (t *Tree) Parse(text string, treeSet *Tree) (tree *Tree, errors []error) {
	log.Debugln("Start")
	t.startParse(lex(t.Name, text))
	t.text = text
	t.parse(treeSet)
	log.Debugln("End")
	return t, t.Errors
}

func (t *Tree) parse(tree *Tree) {
	log.Debugln("Start")

	t.nodeTarget = t.Nodes

	for t.peek(1).Type != itemEOF {
		var n Node

		token := t.next()
		log.Infof("\nParser got token: %#+v\n\n", token)

		switch token.Type {
		case itemSectionAdornment:
			n = t.section(token)
		case itemParagraph:
			n = newParagraph(token, &t.id)
		case itemSpace:
			n = t.indent(token)
			if n == nil {
				continue
			}
		case itemTitle, itemBlankLine:
			// itemTitle is consumed when evaluating itemSectionAdornment
			continue
		case itemEOF:
			goto exit
		default:
			panic(fmt.Errorf("%q Not implemented!", token.Type))
		}

		t.nodeTarget.append(n)
		switch n.NodeType() {
		case NodeSection, NodeBlockQuote:
			// Set the loop to append items to the NodeList of the new section
			t.nodeTarget = reflect.ValueOf(n).Elem().FieldByName("NodeList").Addr().Interface().(*NodeList)
		}
	}

exit:
	log.Debugln("End")
}

func (t *Tree) peekBack(pos int) *item {
	return t.token[zed-pos]
}

func (t *Tree) peek(pos int) *item {
	// log.Debugln("t.peekCount:", t.peekCount, "Pos:", pos)
	if pos < 1 {
		panic("pos cannot be < 1")
	}
	var nItem *item
	for i := 0; i < pos; i++ {
		// log.Debugln("i:", i, "peekCount:", t.peekCount, "pos:", pos)
		if t.peekCount > i {
			nItem = t.token[zed+i]
			log.Debugf("Using %#+v\n", nItem)
			continue
		}
		log.Debugln(zed + t.peekCount + i)
		if t.token[zed+t.peekCount+i+1] == nil {
			t.peekCount++
			// log.Debugln("Getting next item")
			t.token[zed+t.peekCount+i] = t.lex.nextItem()
			nItem = t.token[zed+t.peekCount+i]
		} else {
			nItem = t.token[zed+t.peekCount+i]
		}
	}
	// log.Debugf("\n##### peek() aftermath #####\n\n")
	// spd.Dump(t.token)
	// log.Debugf("Returning: %#+v\n", nItem)
	return nItem
}

func (t *Tree) peekSkip(pos int, iSkip itemElement) *item {
	var nItem *item
outer:
	for i := 1; i <= pos; i++ {
		for {
			nItem = t.peek(i)
			if nItem.Type == iSkip {
				continue
			} else {
				break outer
			}
		}
	}
	return nItem
}

func (t *Tree) next() *item {
	// log.Debugln("t.peekCount:", t.peekCount)
	// skip shifts the pointers left in t.token, pos is the amount to shift
	skip := func(num int) {
		for i := num; i > 0; i-- {
			for x := 0; x < len(t.token)-1; x++ {
				t.token[x] = t.token[x+1]
				t.token[x+1] = nil
			}
		}
	}
	if t.peekCount > 0 {
		skip(t.peekCount)
	} else {
		skip(1)
		t.token[zed] = t.lex.nextItem()
	}
	t.tokenBackupCount, t.peekCount = 0, 0
	// log.Debugf("\n##### next() aftermath #####\n\n")
	// spd.Dump(t.token)
	return t.token[zed]
}

func (t *Tree) section(i *item) Node {
	log.Debugln("Start")
	var overAdorn, title, underAdorn *item
	var overline bool
	var sysMessage Node

	peekForward := t.peekSkip(1, itemSpace)
	if peekForward != nil && peekForward.Type == itemTitle {
		log.Debugln("FOUND SECTION WITH OVERLINE")
		if peekBack := t.peekBack(1); peekBack != nil && peekBack.Type == itemSpace {
			return t.systemMessage(errorUnexpectedSectionTitleOrTransition)
		}
		overAdorn = i
		t.next()
	loop:
		for {
			switch tTok := t.token[zed]; tTok.Type {
			case itemSpace:
				t.next()
			case itemTitle:
				title = tTok
				t.next()
				cur := t.token[zed]
				if cur != nil && cur.Type == itemSectionAdornment {
					continue
				}
				if pNext := t.peek(1); pNext != nil && pNext.Type != itemSectionAdornment {
					panic("Missing section underline!")
				}
			case itemSectionAdornment:
				underAdorn = tTok
				break loop
			}
		}
	} else {
		peekBack := t.peekBack(1)
		if peekBack != nil {
			if peekBack.Type == itemSpace {
				// Looking back past the white space
				if t.peekBack(2).Type == itemTitle {
					return t.systemMessage(errorUnexpectedSectionTitle)
				}
				return t.systemMessage(errorUnexpectedSectionTitleOrTransition)
			} else if peekBack.Type == itemTitle {
				if t.peekBack(2) != nil && t.peekBack(2).Type ==
					itemSectionAdornment {
					// The overline of the section
					overline = true
					overAdorn = peekBack
				}
			}
		}
		title = t.peekBack(1)
		underAdorn = i
	}

	// TODO: Change these into proper error messages!
	// Check adornment for proper syntax
	if underAdorn.Type == itemSpace {
		t.backup() // Put the parser back on the title
		return t.systemMessage(errorUnexpectedSectionTitle)
	} else if overline && title.Length != overAdorn.Length {
		panic("Section over line not equal to title length!")
	} else if overline && overAdorn.Text != underAdorn.Text {
		panic("Section title over line does not match section title under line.")
	}

	sec := newSection(title, overAdorn, underAdorn, &t.id)
	exists, eSec := t.sectionLevels.Add(sec)
	if exists && eSec != nil {
		panic(fmt.Errorf("SectionNode using Text \"%s\" and Rune '%s' was previously parsed!",
			sec.Text, string(sec.UnderLine.Rune)))
	} else if !exists && eSec != nil {
		// There is a matching level in sectionLevels
		t.nodeTarget = &(*t.sectionLevels)[sec.Level-2].NodeList
	}

	// System messages have to be applied after the section is created in order to preserve
	// a consecutive id number.
	if title.Length != underAdorn.Length {
		sysMessage = t.systemMessage(warningShortUnderline)
		sec.NodeList = append(sec.NodeList, sysMessage)
	}

	log.Debugln("End")
	return sec
}

func (t *Tree) systemMessage(err parserMessage) Node {
	var lbText string
	var lbTextLen int
	var backToken int

	s := newSystemMessage(&item{
		Type: itemSystemMessage,
		Line: t.token[zed].Line,
	},
		err.Level(), &t.id)

	msg := newParagraph(&item{
		Text:   err.Message(),
		Length: len(err.Message()),
	}, &t.id)

	log.Debugln("FOUND", err)

	switch err {
	case warningShortUnderline, errorUnexpectedSectionTitle:
		backToken = zed - 1
		if t.peekBack(1).Type == itemSpace {
			backToken = zed - 2
		}
		lbText = t.token[backToken].Text.(string) + "\n" + t.token[zed].Text.(string)
		lbTextLen = len(lbText) + 1
	case errorUnexpectedSectionTitleOrTransition:
		lbText = t.token[zed].Text.(string)
		lbTextLen = len(lbText)
	}

	lb := newLiteralBlock(&item{
		Type:   itemLiteralBlock,
		Text:   lbText,
		Length: lbTextLen, // Add one to account for the backslash
	}, &t.id)

	s.NodeList = append(s.NodeList, msg, lb)
	return s
}

func (t *Tree) indent(i *item) Node {
	level := i.Length / t.indentWidth
	if t.peekBack(1).Type == itemBlankLine {
		if t.indentLevel == level {
			// Append to the current blockquote NodeList
			return nil
		}
		t.indentLevel = level
		return newBlockQuote(&item{Type: itemBlockquote, Line: i.Line}, level, &t.id)
	}
	return nil
}
<|endoftext|>


<commit_before>package dicom

import (
	"os"
	"testing"
)

func TestConstructor(t *testing.T) {
	p, err := NewParser()
	if err != nil {
		t.Error(err)
	}

	if len(p.dictionary) == 0 {
		t.Error("Error constructing parser. Dictionary can not be of 0 length")
	}

}

func TestDictionaryOption(t *testing.T) {

	fh, err := os.Open("dicom.dic")
	defer fh.Close()
	if err != nil {
		t.Error(err)
	}

	p, err := NewParser(Dictionary(fh))
	if err != nil {
		t.Error(err)
	}

	if len(p.dictionary) == 0 {
		t.Error("Error constructing parser. Dictionary can not be of 0 length")
	}
}

func TestReadTag(t *testing.T) {

}

func TestReadDataElement(t *testing.T) {

}
<commit_msg>Added test for getTag function<commit_after>package dicom

import (
	"os"
	"testing"
)

func TestConstructor(t *testing.T) {
	p, err := NewParser()
	if err != nil {
		t.Error(err)
	}

	if len(p.dictionary) == 0 {
		t.Error("Error constructing parser. Dictionary can not be of 0 length")
	}

}

func TestDictionaryOption(t *testing.T) {

	fh, err := os.Open("dicom.dic")
	defer fh.Close()
	if err != nil {
		t.Error(err)
	}

	p, err := NewParser(Dictionary(fh))
	if err != nil {
		t.Error(err)
	}

	if len(p.dictionary) == 0 {
		t.Error("Error constructing parser. Dictionary can not be of 0 length")
	}
}

func TestGetTag(t *testing.T) {
	elem := &DicomElement{0x7FE0, 0x0010, "PixelData", "ox", 1, nil}

	if tag := elem.getTag(); tag != "(7FE0,0010)" {
		t.Errorf("Error creating tag. Incorrect value %s", tag)
	}

}

func TestReadTag(t *testing.T) {

}

func TestReadDataElement(t *testing.T) {

}
<|endoftext|>


<commit_before>package sjson_test

import (
	"encoding/json"
	"fmt"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/types"
	"github.com/vovkasm/go-sjson"
	"testing"
)

func TestDict(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "sjson")
}

func ExpectNoErr() func(err error) {
	f := func(err error) {
		ExpectWithOffset(1, err).To(Succeed())
	}
	return f
}

func ExpectErr(msgLike string) func(err error) {
	f := func(err error) {
		ExpectWithOffset(1, err).To(MatchError(MatchRegexp(msgLike)))
	}
	return f
}

func ExpectSyntaxErr(msgLike string, offset int) func(err error) {
	return func(err error) {
		ExpectWithOffset(1, err).To(MatchError(MatchRegexp(msgLike)))
		syntaxErr, ok := err.(*sjson.SyntaxError)
		ExpectWithOffset(1, ok).To(BeTrue(), "error should implement *sjson.SyntaxError")
		ExpectWithOffset(1, syntaxErr.Offset).To(Equal(offset))
	}
}

var _ = Describe("parser", func() {
	table := []struct {
		Descr    string
		In       string
		Expect   types.GomegaMatcher
		ErrCheck func(err error)
	}{
		{"decode empty string with error", ``, BeNil(), ExpectSyntaxErr(`incorrect syntax`, 0)},
		{"can decode null value", `null`, BeNil(), ExpectNoErr()},
		{"decode incorrect value", `nula`, BeNil(), ExpectSyntaxErr(`'null' expected`, 0)},
		{"can decode false value", `false`, BeFalse(), ExpectNoErr()},
		{"can decode true value", `true`, BeTrue(), ExpectNoErr()},
		// numbers
		{"can decode numbers (simple)", `5`, Equal(5.0), ExpectNoErr()},
		{"can decode numbers (negative)", `-5`, Equal(-5.0), ExpectNoErr()},
		{"can decode numbers (exp)", `5e1`, Equal(50.0), ExpectNoErr()},
		{"can decode numbers (-333e+0)", `-333e+0`, Equal(-333.0), ExpectNoErr()},
		{"can decode numbers (fractional)", `2.5`, Equal(2.5), ExpectNoErr()},
		{"errors in numbers", `+0`, BeNil(), ExpectErr(`incorrect syntax`)},
		{"errors in numbers", `.2`, BeNil(), ExpectErr(`incorrect syntax`)},
		{"errors in numbers", `-0.`, Equal(0.0), ExpectErr(`incorrect number`)},
		{"errors in numbers", `-0e`, Equal(0.0), ExpectSyntaxErr(`incorrect number`, 3)},
		{"errors in numbers", `-e+1`, Equal(0.0), ExpectSyntaxErr(`incorrect number`, 1)},
		// strings
		{"can decode empty string", `""`, Equal(""), ExpectNoErr()},
		{"can decode simple string", `"abc"`, Equal("abc"), ExpectNoErr()},
		{"can decode unicode", `"ü"`, Equal("ü"), ExpectNoErr()},
		{"can decode escapes", `"\""`, Equal(`"`), ExpectNoErr()},
		{"can decode escapes2", `"\u00FC"`, Equal("\u00fc"), ExpectNoErr()},
		{"can decode escapes3", `"\u002F\u002f\//"`, Equal("////"), ExpectNoErr()},
		{"can decode escapes3", `"\u3042"`, Equal(`あ`), ExpectNoErr()},                          // Japanese "a"
		{"can decode escapes from extended range", `"\ud800\udd40"`, Equal("𐅀"), ExpectNoErr()}, // Greek Acrophonic Attic One Quarter
		{"errors in strings", `"ab`, Equal(""), ExpectSyntaxErr(`incorrect syntax`, 3)},
		{"errors in strings 2", `"ab\"cd`, Equal(""), ExpectSyntaxErr(`incorrect syntax`, 7)},
		// objects
		{"can decode empty object", `{}`, Equal(map[string]interface{}{}), ExpectNoErr()},
		{"can decode simple object", `{"key1":"val1"}`, Equal(map[string]interface{}{"key1": "val1"}), ExpectNoErr()},
		{"can decode simple object", `{"key1":"val1","key2":"val2"}`, Equal(map[string]interface{}{"key1": "val1", "key2": "val2"}), ExpectNoErr()},
		{"can decode simple object", ` { "key1" : 10 , "key2" : true } `, Equal(map[string]interface{}{"key1": 10.0, "key2": true}), ExpectNoErr()},
		{"can decode nested objects", `{"k1":{"kk1":10}}`, Equal(map[string]interface{}{"k1": map[string]interface{}{"kk1": 10.0}}), ExpectNoErr()},
		{"errors in objects", `{"k1:`, Equal(map[string]interface{}{}), ExpectSyntaxErr(`incorrect syntax`, 5)},
		// arrays
		{"can decode empty array", `[]`, Equal([]interface{}{}), ExpectNoErr()},
		{"can decode simple array", `[10,20]`, Equal([]interface{}{10.0, 20.0}), ExpectNoErr()},
		{"can decode long array", ` [ 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 ] `, Equal([]interface{}{10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}), ExpectNoErr()},
		{"errors in array", `[10,20`, Equal([]interface{}{10.0, 20.0}), ExpectSyntaxErr(`incomplete array`, 6)},
		{"errors in array", `[10,20[[[`, Equal([]interface{}{10.0, 20.0}), ExpectSyntaxErr(`incomplete array`, 6)},
	}
	for n, t := range table {
		n, t := n, t
		Context(fmt.Sprintf("test %d", n), func() {
			It(t.Descr, func() {
				res, err := sjson.Decode(t.In)
				t.ErrCheck(err)
				if t.Expect != nil {
					Expect(res).To(t.Expect)
				}
			})
		})
	}
	Context("real data test", func() {
		It("should produce equivalent json after reencoding", func() {
			res, err := sjson.Decode(sample)
			Expect(err).To(Succeed())
			enc, err := json.Marshal(res)
			Expect(err).To(Succeed())
			Expect(enc).To(MatchJSON(sample))
		})
		It("should correctly decode encoding/json test", func() {
			if codeJSON == nil {
				codeInit()
			}
			res, err := sjson.Decode(codeJSONStr)
			Expect(err).To(Succeed())
			enc, err := json.Marshal(res)
			Expect(err).To(Succeed())
			Expect(enc).To(MatchJSON(codeJSONStr))
		})
	})
})

func Example() {
	data := `{"name":"John","age":30}`
	obj, err := sjson.Decode(data)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Hi, %s!\n", obj.(map[string]interface{})["name"])
	// Output: Hi, John!
}
<commit_msg>improve code coverage<commit_after>package sjson_test

import (
	"encoding/json"
	"fmt"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/types"
	"github.com/vovkasm/go-sjson"
	"math"
	"testing"
)

func TestDict(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "sjson")
}

func ExpectNoErr() func(err error) {
	f := func(err error) {
		ExpectWithOffset(1, err).To(Succeed())
	}
	return f
}

func ExpectErr(msgLike string) func(err error) {
	f := func(err error) {
		ExpectWithOffset(1, err).To(MatchError(MatchRegexp(msgLike)))
	}
	return f
}

func ExpectSyntaxErr(msgLike string, offset int) func(err error) {
	return func(err error) {
		ExpectWithOffset(1, err).To(MatchError(MatchRegexp(msgLike)))
		syntaxErr, ok := err.(*sjson.SyntaxError)
		ExpectWithOffset(1, ok).To(BeTrue(), "error should implement *sjson.SyntaxError")
		ExpectWithOffset(1, syntaxErr.Offset).To(Equal(offset))
	}
}

var _ = Describe("parser", func() {
	table := []struct {
		Descr    string
		In       string
		Expect   types.GomegaMatcher
		ErrCheck func(err error)
	}{
		{"decode empty string with error", ``, BeNil(), ExpectSyntaxErr(`incorrect syntax`, 0)},
		{"can decode null value", `null`, BeNil(), ExpectNoErr()},
		{"decode incorrect value", `nula`, BeNil(), ExpectSyntaxErr(`'null' expected`, 0)},
		{"can decode false value", `false`, BeFalse(), ExpectNoErr()},
		{"can decode true value", `true`, BeTrue(), ExpectNoErr()},
		{"detect incorrect true", `traaa`, BeNil(), ExpectSyntaxErr(`'true' expected`, 0)},
		{"detect incorrect false", `faaaa`, BeNil(), ExpectSyntaxErr(`'false' expected`, 0)},
		// numbers
		{"can decode numbers (simple)", `5`, Equal(5.0), ExpectNoErr()},
		{"can decode numbers (negative)", `-5`, Equal(-5.0), ExpectNoErr()},
		{"can decode numbers (exp)", `5e1`, Equal(50.0), ExpectNoErr()},
		{"can decode numbers (-333e+0)", `-333e+0`, Equal(-333.0), ExpectNoErr()},
		{"can decode numbers (fractional)", `2.5`, Equal(2.5), ExpectNoErr()},
		{"errors in numbers", `+0`, BeNil(), ExpectErr(`incorrect syntax`)},
		{"errors in numbers", `.2`, BeNil(), ExpectErr(`incorrect syntax`)},
		{"errors in numbers", `-0.`, Equal(0.0), ExpectErr(`incorrect number`)},
		{"errors in numbers", `-0e`, Equal(0.0), ExpectSyntaxErr(`incorrect number`, 3)},
		{"errors in numbers", `-e+1`, Equal(0.0), ExpectSyntaxErr(`incorrect number`, 1)},
		{"parse flost error", `11222132131232132132132321.1e100000`, Equal(math.Inf(1)), ExpectErr(`value out of range`)},
		// strings
		{"can decode empty string", `""`, Equal(""), ExpectNoErr()},
		{"can decode simple string", `"abc"`, Equal("abc"), ExpectNoErr()},
		{"can decode unicode", `"ü"`, Equal("ü"), ExpectNoErr()},
		{"can decode escapes", `"\""`, Equal(`"`), ExpectNoErr()},
		{"can decode escapes2", `"\u00FC"`, Equal("\u00fc"), ExpectNoErr()},
		{"can decode escapes3", `"\u002F\u002f\//"`, Equal("////"), ExpectNoErr()},
		{"can decode escapes3", `"\u3042"`, Equal(`あ`), ExpectNoErr()},                          // Japanese "a"
		{"can decode escapes from extended range", `"\ud800\udd40"`, Equal("𐅀"), ExpectNoErr()}, // Greek Acrophonic Attic One Quarter
		{"errors in strings", `"ab`, Equal(""), ExpectSyntaxErr(`incorrect syntax`, 3)},
		{"errors in strings 2", `"ab\"cd`, Equal(""), ExpectSyntaxErr(`incorrect syntax`, 7)},
		{"many escapes", `"bbb\"\\\b\f\n\r\tあeee"`, Equal("bbb\"\\\b\f\n\r\tあeee"), ExpectNoErr()},
		{"many escapes 2", `"bあbb\"\\\b\f\n\r\teee"`, Equal("bあbb\"\\\b\f\n\r\teee"), ExpectNoErr()},
		{"invalid escapes", `"\a"`, Equal(``), ExpectErr("string contains invalid characters")},
		//TODO: do not allow control characters for fast paths :-(
		{"control characters invalid in strings", "\"\r\\\r\"", Equal(``), ExpectErr("string contains invalid characters")},
		// objects
		{"can decode empty object", `{}`, Equal(map[string]interface{}{}), ExpectNoErr()},
		{"can decode simple object", `{"key1":"val1"}`, Equal(map[string]interface{}{"key1": "val1"}), ExpectNoErr()},
		{"can decode simple object", `{"key1":"val1","key2":"val2"}`, Equal(map[string]interface{}{"key1": "val1", "key2": "val2"}), ExpectNoErr()},
		{"can decode simple object", ` { "key1" : 10 , "key2" : true } `, Equal(map[string]interface{}{"key1": 10.0, "key2": true}), ExpectNoErr()},
		{"can decode nested objects", `{"k1":{"kk1":10}}`, Equal(map[string]interface{}{"k1": map[string]interface{}{"kk1": 10.0}}), ExpectNoErr()},
		{"errors in objects", `{`, Equal(map[string]interface{}{}), ExpectSyntaxErr(`incorrect syntax`, 1)},
		{"errors in objects", `{"k1:`, Equal(map[string]interface{}{}), ExpectSyntaxErr(`incorrect syntax`, 5)},
		{"errors in objects", `{"k1"v`, Equal(map[string]interface{}{}), ExpectSyntaxErr(`incorrect syntax - expect ':' after object key`, 5)},
		{"errors in objects", `{"k1":"v1"hmm`, Equal(map[string]interface{}{"k1": "v1"}), ExpectSyntaxErr(`incorrect syntax - expect object key or incomplete object`, 10)},
		// arrays
		{"can decode empty array", `[]`, Equal([]interface{}{}), ExpectNoErr()},
		{"can decode simple array", `[10,20]`, Equal([]interface{}{10.0, 20.0}), ExpectNoErr()},
		{"can decode long array", ` [ 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 ] `, Equal([]interface{}{10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}), ExpectNoErr()},
		{"errors in array", `[10,20`, Equal([]interface{}{10.0, 20.0}), ExpectSyntaxErr(`incomplete array`, 6)},
		{"errors in array 2", `[10,20[[[`, Equal([]interface{}{10.0, 20.0}), ExpectSyntaxErr(`incomplete array`, 6)},
		{"error in first item", `[nuuu,ll]`, Equal([]interface{}{nil}), ExpectSyntaxErr(`'null' expected`, 1)},
		{"incomplete array after first item", `[true `, Equal([]interface{}{true}), ExpectSyntaxErr(`incomplete array`, 6)},
		{"error in second item", `[true, tru]`, Equal([]interface{}{true}), ExpectSyntaxErr(`'true' expected`, 7)},
		{"long incomplete array", `[1,2,3,4,5,6,7,8,9 `, Equal([]interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}), ExpectSyntaxErr(`incomplete array`, 19)},
		{"error in long incomplete array", `[1,2,3,4,5,6,7,8,tru] `, Equal([]interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}), ExpectSyntaxErr(`'true' expected`, 17)},
		// spaces
		{"skip various spaces", " \u000a\u000d\u0009true", BeTrue(), ExpectNoErr()},
		{"don't skip low chars as spaces", " \u0008true", BeNil(), ExpectSyntaxErr(`unrecognized token`, 1)},
	}
	for n, t := range table {
		n, t := n, t
		Context(fmt.Sprintf("test %d", n), func() {
			It(t.Descr, func() {
				res, err := sjson.Decode(t.In)
				t.ErrCheck(err)
				if t.Expect != nil {
					Expect(res).To(t.Expect)
				}
			})
		})
	}
	Context("real data test", func() {
		It("should produce equivalent json after reencoding", func() {
			res, err := sjson.Decode(sample)
			Expect(err).To(Succeed())
			enc, err := json.Marshal(res)
			Expect(err).To(Succeed())
			Expect(enc).To(MatchJSON(sample))
		})
		It("should correctly decode encoding/json test", func() {
			if codeJSON == nil {
				codeInit()
			}
			res, err := sjson.Decode(codeJSONStr)
			Expect(err).To(Succeed())
			enc, err := json.Marshal(res)
			Expect(err).To(Succeed())
			Expect(enc).To(MatchJSON(codeJSONStr))
		})
	})
})

func Example() {
	data := `{"name":"John","age":30}`
	obj, err := sjson.Decode(data)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Hi, %s!\n", obj.(map[string]interface{})["name"])
	// Output: Hi, John!
}
<|endoftext|>


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

package path

import (
	"container/heap"

	"github.com/gonum/graph"
	"github.com/gonum/graph/internal"
)

// Heuristic returns an estimate of the cost of travelling between two nodes.
type Heuristic func(x, y graph.Node) float64

// HeuristicCoster wraps the HeuristicCost method. A graph that implementing
// the interface provides a heuristic between any two given nodes.
type HeuristicCoster interface {
	HeuristicCost(x, y graph.Node) float64
}

// AStar returns the A*-shortest path from s to t in g using the heuristic h, and the
// cost of that path. The number of expanded nodes is also returned. This value may
// help with heuristic tuning.
//
// The path will be the shortest path if the heuristic is admissible. A heuristic is
// admissible if for any node, n, in the graph, the heuristic estimate of the cost of
// the path from n to t is less than or equal to the true cost of that path.
//
// If h is nil, AStar will use the g.HeuristicCost method if g implements HeuristicCoster,
// falling back to NullHeuristic otherwise. If the graph does not implement graph.Weighter,
// graph.UniformCost is used.
func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path []graph.Node, cost float64, expanded int) {
	var weight graph.WeightFunc
	if g, ok := g.(graph.Weighter); ok {
		weight = g.Weight
	} else {
		weight = graph.UniformCost
	}
	if h == nil {
		if g, ok := g.(HeuristicCoster); ok {
			h = g.HeuristicCost
		} else {
			h = NullHeuristic
		}
	}

	p := newShortestFrom(s, g.Nodes())
	tid := t.ID()

	visited := make(internal.IntSet)
	open := &aStarQueue{indexOf: make(map[int]int)}
	heap.Push(open, aStarNode{node: s, gscore: 0, fscore: h(s, t)})

	for open.Len() != 0 {
		u := heap.Pop(open).(aStarNode)
		uid := u.node.ID()
		i := p.indexOf[uid]
		expanded++

		if uid == tid {
			break
		}

		visited.Add(uid)
		for _, v := range g.From(u.node) {
			vid := v.ID()
			if visited.Has(vid) {
				continue
			}
			j := p.indexOf[vid]

			g := u.gscore + weight(g.Edge(u.node, v))
			if n, ok := open.node(vid); !ok {
				p.set(j, g, i)
				heap.Push(open, aStarNode{node: v, gscore: g, fscore: g + h(v, t)})
			} else if g < n.gscore {
				p.set(j, g, i)
				open.update(vid, g, g+h(v, t))
			}
		}
	}

	path, cost = p.To(t)
	return path, cost, expanded
}

// NullHeuristic is an admissible, consistent heuristic that will not speed up computation.
func NullHeuristic(_, _ graph.Node) float64 {
	return 0
}

// aStarNode adds A* accounting to a graph.Node.
type aStarNode struct {
	node   graph.Node
	gscore float64
	fscore float64
}

// aStarQueue is an A* priority queue.
type aStarQueue struct {
	indexOf map[int]int
	nodes   []aStarNode
}

func (q *aStarQueue) Less(i, j int) bool {
	return q.nodes[i].fscore < q.nodes[j].fscore
}

func (q *aStarQueue) Swap(i, j int) {
	q.indexOf[q.nodes[i].node.ID()] = j
	q.indexOf[q.nodes[j].node.ID()] = i
	q.nodes[i], q.nodes[j] = q.nodes[j], q.nodes[i]
}

func (q *aStarQueue) Len() int {
	return len(q.nodes)
}

func (q *aStarQueue) Push(x interface{}) {
	n := x.(aStarNode)
	q.nodes = append(q.nodes, n)
	q.indexOf[n.node.ID()] = len(q.nodes) - 1
}

func (q *aStarQueue) Pop() interface{} {
	n := q.nodes[len(q.nodes)-1]
	q.nodes = q.nodes[:len(q.nodes)-1]
	delete(q.indexOf, n.node.ID())
	return n
}

func (q *aStarQueue) update(id int, g, f float64) {
	i, ok := q.indexOf[id]
	if !ok {
		return
	}
	q.nodes[i].gscore = g
	q.nodes[i].fscore = f
	heap.Fix(q, i)
}

func (q *aStarQueue) node(id int) (aStarNode, bool) {
	loc, ok := q.indexOf[id]
	if ok {
		return q.nodes[loc], true
	}
	return aStarNode{}, false
}
<commit_msg>path: use order rather than arithmetic<commit_after>// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package path

import (
	"container/heap"

	"github.com/gonum/graph"
	"github.com/gonum/graph/internal"
)

// Heuristic returns an estimate of the cost of travelling between two nodes.
type Heuristic func(x, y graph.Node) float64

// HeuristicCoster wraps the HeuristicCost method. A graph that implementing
// the interface provides a heuristic between any two given nodes.
type HeuristicCoster interface {
	HeuristicCost(x, y graph.Node) float64
}

// AStar returns the A*-shortest path from s to t in g using the heuristic h, and the
// cost of that path. The number of expanded nodes is also returned. This value may
// help with heuristic tuning.
//
// The path will be the shortest path if the heuristic is admissible. A heuristic is
// admissible if for any node, n, in the graph, the heuristic estimate of the cost of
// the path from n to t is less than or equal to the true cost of that path.
//
// If h is nil, AStar will use the g.HeuristicCost method if g implements HeuristicCoster,
// falling back to NullHeuristic otherwise. If the graph does not implement graph.Weighter,
// graph.UniformCost is used.
func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path []graph.Node, cost float64, expanded int) {
	var weight graph.WeightFunc
	if g, ok := g.(graph.Weighter); ok {
		weight = g.Weight
	} else {
		weight = graph.UniformCost
	}
	if h == nil {
		if g, ok := g.(HeuristicCoster); ok {
			h = g.HeuristicCost
		} else {
			h = NullHeuristic
		}
	}

	p := newShortestFrom(s, g.Nodes())
	tid := t.ID()

	visited := make(internal.IntSet)
	open := &aStarQueue{indexOf: make(map[int]int)}
	heap.Push(open, aStarNode{node: s, gscore: 0, fscore: h(s, t)})

	for open.Len() != 0 {
		u := heap.Pop(open).(aStarNode)
		uid := u.node.ID()
		i := p.indexOf[uid]
		expanded++

		if uid == tid {
			break
		}

		visited.Add(uid)
		for _, v := range g.From(u.node) {
			vid := v.ID()
			if visited.Has(vid) {
				continue
			}
			j := p.indexOf[vid]

			g := u.gscore + weight(g.Edge(u.node, v))
			if n, ok := open.node(vid); !ok {
				p.set(j, g, i)
				heap.Push(open, aStarNode{node: v, gscore: g, fscore: g + h(v, t)})
			} else if g < n.gscore {
				p.set(j, g, i)
				open.update(vid, g, g+h(v, t))
			}
		}
	}

	path, cost = p.To(t)
	return path, cost, expanded
}

// NullHeuristic is an admissible, consistent heuristic that will not speed up computation.
func NullHeuristic(_, _ graph.Node) float64 {
	return 0
}

// aStarNode adds A* accounting to a graph.Node.
type aStarNode struct {
	node   graph.Node
	gscore float64
	fscore float64
}

// aStarQueue is an A* priority queue.
type aStarQueue struct {
	indexOf map[int]int
	nodes   []aStarNode
}

func (q *aStarQueue) Less(i, j int) bool {
	return q.nodes[i].fscore < q.nodes[j].fscore
}

func (q *aStarQueue) Swap(i, j int) {
	q.indexOf[q.nodes[i].node.ID()] = j
	q.indexOf[q.nodes[j].node.ID()] = i
	q.nodes[i], q.nodes[j] = q.nodes[j], q.nodes[i]
}

func (q *aStarQueue) Len() int {
	return len(q.nodes)
}

func (q *aStarQueue) Push(x interface{}) {
	n := x.(aStarNode)
	q.indexOf[n.node.ID()] = len(q.nodes)
	q.nodes = append(q.nodes, n)
}

func (q *aStarQueue) Pop() interface{} {
	n := q.nodes[len(q.nodes)-1]
	q.nodes = q.nodes[:len(q.nodes)-1]
	delete(q.indexOf, n.node.ID())
	return n
}

func (q *aStarQueue) update(id int, g, f float64) {
	i, ok := q.indexOf[id]
	if !ok {
		return
	}
	q.nodes[i].gscore = g
	q.nodes[i].fscore = f
	heap.Fix(q, i)
}

func (q *aStarQueue) node(id int) (aStarNode, bool) {
	loc, ok := q.indexOf[id]
	if ok {
		return q.nodes[loc], true
	}
	return aStarNode{}, false
}
<|endoftext|>


<commit_before>package neutrino

import (
	"container/heap"
	"github.com/davecgh/go-spew/spew"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
	"github.com/roasbeef/btcutil/gcs"
	"github.com/roasbeef/btcutil/gcs/builder"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"sync"
)

// GetUtxoRequest is a request to scan for OutPoint from the height StartHeight.
type GetUtxoRequest struct {
	OutPoint    *wire.OutPoint
	StartHeight uint32
	Result      func(*SpendReport, error)
}

// Interface exposes the necessary methods for interacting with the blockchain.
type Interface interface {
	GetBlockFromNetwork(chainhash.Hash, ...QueryOption) (*btcutil.Block, error)
	GetBlockHash(int64) (*chainhash.Hash, error)
	BestSnapshot() (*waddrmgr.BlockStamp, error)
	GetCFilter(blockHash chainhash.Hash, filterType wire.FilterType,
		options ...QueryOption) (*gcs.Filter, error)
}

// A PriorityQueue implements heap.Interface and holds GetUtxoRequests. The
// queue maintains that heap.Pop() will always return the GetUtxo request with
// the least starting height. This allows us to add new GetUtxo requests to
// an already running batch.
type PriorityQueue []*GetUtxoRequest

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
	// We want Pop to give us the least StartHeight.
	return pq[i].StartHeight < pq[j].StartHeight
}

func (pq PriorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

// Push is called by the heap.Interface implementation to add an element to the
// end of the backing store. The heap library will then maintain the heap
// invariant.
func (pq *PriorityQueue) Push(x interface{}) {
	item := x.(*GetUtxoRequest)
	*pq = append(*pq, item)
}

// Peek returns the least height element in the queue without removing it.
func (pq *PriorityQueue) Peek() *GetUtxoRequest {
	return (*pq)[0]
}

// Pop is called by the heap.Interface implementation to remove an element from
// the end of the backing store. The heap library will then maintain the heap
// invariant.
func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}

// IsEmpty returns true if the queue has no elements.
func (pq *PriorityQueue) IsEmpty() bool {
	return pq.Len() == 0
}

// UtxoScanner batches calls to GetUtxo so that a single scan can search for
// multiple outpoints. If a scan is in progress when a new element is added, we
// check whether it can safely be added to the current batch, if not it will be
// included in the next batch.
type UtxoScanner struct {
	pq      PriorityQueue
	stopped bool

	chainClient Interface

	nextBatch []*GetUtxoRequest

	cv *sync.Cond
}

// NewUtxoScanner creates a new instance of UtxoScanner using the given chain
// interface.
func NewUtxoScanner(chainClient Interface) UtxoScanner {
	return UtxoScanner{
		pq:          make(PriorityQueue, 0),
		cv:          sync.NewCond(&sync.Mutex{}),
		stopped:     false,
		chainClient: chainClient,
	}
}

// Start begins running scan batches.
func (s *UtxoScanner) Start() {
	go func() {
		for {
			s.cv.L.Lock()
			if s.stopped {
				s.cv.L.Unlock()
				return
			}

			// Re-queue previously skipped requests for next batch.
			for _, request := range s.nextBatch {
				heap.Push(&s.pq, request)
			}

			s.nextBatch = nil

			s.cv.L.Unlock()

			requests, err := s.runBatch()

			// If there was an error, then notify the currently outstanding
			// requests.
			if err != nil {
				for _, request := range requests {
					request.Result(nil, err)
				}
			}
		}
	}()
}

// getAtHeight returns all GetUtxo requests that have starting height of the
// given height.
func (s *UtxoScanner) getAtHeight(height uint32) []*GetUtxoRequest {
	s.cv.L.Lock()
	defer s.cv.L.Unlock()

	// Take any requests that are too old to go in this batch and keep them for
	// the next batch.
	for !s.pq.IsEmpty() && s.pq.Peek().StartHeight < height {
		item := heap.Pop(&s.pq).(*GetUtxoRequest)
		s.nextBatch = append(s.nextBatch, item)
	}

	var requests []*GetUtxoRequest
	for !s.pq.IsEmpty() && s.pq.Peek().StartHeight == height {
		item := heap.Pop(&s.pq).(*GetUtxoRequest)
		requests = append(requests, item)
	}

	return requests
}

// CheckTransactions finds any transactions in the block that spend the given
// outpoints.
func (s *UtxoScanner) CheckTransactions(block *wire.MsgBlock, height uint32,
	outpoints map[wire.OutPoint]struct{}) (map[wire.OutPoint]SpendReport,
	error) {
	spends := make(map[wire.OutPoint]SpendReport)

	// If we've spent the output in this block, return an
	// error stating that the output is spent.
	for _, tx := range block.Transactions {
		// Check each input to see if this transaction spends one of our
		// watched outpoints.
		for i, ti := range tx.TxIn {
			if _, ok := outpoints[ti.PreviousOutPoint]; ok {
				log.Debugf("Transaction %s spends outpoint %s", tx.TxHash(),
					ti.PreviousOutPoint)
				spends[ti.PreviousOutPoint] = SpendReport{
					SpendingTx:         tx,
					SpendingInputIndex: uint32(i),
					SpendingTxHeight:   height,
				}
			}
		}
	}

	return spends, nil
}

// filterMatches checks whether any of the filterEntries match for the given
// block.
func (s *UtxoScanner) filterMatches(hash chainhash.Hash,
	filterEntries [][]byte) (bool, error) {
	filter, err := s.chainClient.GetCFilter(hash,
		wire.GCSFilterRegular)
	if err != nil {
		return false, err
	}

	if filter != nil {
		filterKey := builder.DeriveKey(&hash)
		return filter.MatchAny(filterKey, filterEntries)
	}

	return false, nil
}

func buildFilterEntries(requests []*GetUtxoRequest) ([][]byte,
	map[wire.OutPoint]struct{}) {
	var filterEntries [][]byte
	outpoints := make(map[wire.OutPoint]struct{})

	for _, request := range requests {
		op := *request.OutPoint
		outpoints[op] = struct{}{}
		filterEntries = append(filterEntries, builder.OutPointToFilterEntry(op))
	}

	return filterEntries, outpoints
}

// runBatch runs a single batch. If there was an error, then return the
// outstanding requests.
func (s *UtxoScanner) runBatch() ([]*GetUtxoRequest, error) {
	var requests []*GetUtxoRequest

	// Take the request with the lowest block height so we can begin the scan
	// from there.
	req := s.peek()

	// Check if we were interrupted while waiting.
	if req == nil {
		return nil, nil
	}

	//log.Debugf("Running batch, looking for %d outpoints", s.Len())

	startHeight := req.StartHeight
	best, err := s.chainClient.BestSnapshot()
	if err != nil {
		return requests, err
	}

	// While scanning through the blockchain, take note of the transactions that
	// create the outpoints. If the outpoint isn't spent then return this
	// transaction.
	initialTx := make(map[wire.OutPoint]*SpendReport)

	filterEntries, outpoints := buildFilterEntries(requests)

	// Scan forward through the blockchain and look for any transactions that
	// might spend the given UTXOs.
	for height := startHeight; height <= uint32(best.Height); height++ {
		// If there are any new requests that can safely be added to this batch,
		// then try and fetch them.
		reqs := s.getAtHeight(height)
		if len(reqs) > 0 {
			for _, req := range reqs {
				log.Debugf("Adding %s (%d) to watchlist", req.OutPoint.String(),
					req.StartHeight)
				requests = append(requests, req)
				outpoints[*req.OutPoint] = struct{}{}
				filterEntries = append(filterEntries,
					builder.OutPointToFilterEntry(*req.OutPoint))
			}
		}

		hash, err := s.chainClient.GetBlockHash(int64(height))
		if err != nil {
			return requests, err
		}

		// If an outpoint is created in this block, then fetch it regardless.
		// Otherwise check to see if the filter matches any of our watched
		// outpoints.
		fetch := len(reqs) > 0
		if !fetch {
			match, err := s.filterMatches(*hash, filterEntries)
			if err != nil {
				return requests, err
			}
			fetch = match
		}

		if fetch {
			log.Debugf("Fetching block at height %d (%s)", height,
				hash.String())

			// FIXME(simon): Find out why this takes three minutes.
			// Fetch the block from the network.
			block, err := s.chainClient.GetBlockFromNetwork(*hash)
			if err != nil {
				return requests, err
			}

			log.Debugf("Got block %d (%s)", height, hash.String())

			if fetch {
				for _, request := range requests {
					if request.StartHeight == height {
						tx := findTransaction(block.MsgBlock(),
							request.OutPoint)
						// Grab the tx that created this output.
						initialTx[*request.OutPoint] = tx

						log.Debugf("Block %d creates output %s", height,
							request.OutPoint.String())
					}
				}
			}

			spends, err := s.CheckTransactions(block.MsgBlock(), height,
				outpoints)
			if err != nil {
				return requests, err
			}

			for outPoint, spend := range spends {
				log.Debugf("Outpoint %s is spent in tx %s", outPoint.String(),
					spew.Sprint(spend))

				// Find the request this spend relates to.
				var filteredRequests []*GetUtxoRequest
				for _, request := range requests {
					if *request.OutPoint == outPoint {
						request.Result(&spend, nil)
					} else {
						filteredRequests = append(filteredRequests, request)
					}
				}
				requests = filteredRequests

				// Remove the filter from filterEntries.
				filterEntries, outpoints = buildFilterEntries(requests)
			}
		}
	}

	log.Debugf("Finished batch, %d unspent outpoints", len(requests))

	for _, request := range requests {
		tx, ok := initialTx[*request.OutPoint]
		if ok {
			request.Result(tx, nil)
		} else {
			// A nil SpendReport indicates the output was not found.
			request.Result(nil, nil)
		}
	}

	return nil, nil
}

// Stop any in-progress scan.
func (s *UtxoScanner) Stop() {
	s.cv.L.Lock()
	defer s.cv.L.Unlock()

	s.stopped = true
}

// Returns the GetUtxoRequest with the lowest block height. If no elements are
// available, then block until one is added.
func (s *UtxoScanner) peek() *GetUtxoRequest {
	s.cv.L.Lock()
	defer s.cv.L.Unlock()

	// Block until the queue is no longer empty.
	for !s.stopped && s.pq.IsEmpty() {
		s.cv.Wait()
	}

	// We return nil only in the case that we've been interrupted, so callers
	// can use this to determine that the UtxoScanner is shutting down.
	if s.stopped {
		return nil
	}

	return s.pq.Peek()
}

// Enqueue takes a GetUtxoRequest and adds it to the next applicable batch.
func (s *UtxoScanner) Enqueue(req *GetUtxoRequest) {
	log.Debugf("Enqueuing request for %s with start height %d",
		req.OutPoint.String(), req.StartHeight)
	s.cv.L.Lock()

	// Insert the request into the queue and signal any threads that might be
	// waiting for new elements.
	heap.Push(&s.pq, req)

	s.cv.L.Unlock()
	s.cv.Signal()
}

// findTransaction returns a SpendReport for the UTXO, or nil if it does not
// exist in this block.
func findTransaction(block *wire.MsgBlock,
	point *wire.OutPoint) *SpendReport {
	for _, tx := range block.Transactions {
		if tx.TxHash() == point.Hash {
			outputs := tx.TxOut
			return &SpendReport{
				Output: outputs[point.Index],
			}
		}
	}

	log.Errorf("Failed to find tx %s", point.Hash.String())

	return nil
}
<commit_msg>utxoscanner: Remove commented code<commit_after>package neutrino

import (
	"container/heap"
	"github.com/davecgh/go-spew/spew"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
	"github.com/roasbeef/btcutil/gcs"
	"github.com/roasbeef/btcutil/gcs/builder"
	"github.com/roasbeef/btcwallet/waddrmgr"
	"sync"
)

// GetUtxoRequest is a request to scan for OutPoint from the height StartHeight.
type GetUtxoRequest struct {
	OutPoint    *wire.OutPoint
	StartHeight uint32
	Result      func(*SpendReport, error)
}

// Interface exposes the necessary methods for interacting with the blockchain.
type Interface interface {
	GetBlockFromNetwork(chainhash.Hash, ...QueryOption) (*btcutil.Block, error)
	GetBlockHash(int64) (*chainhash.Hash, error)
	BestSnapshot() (*waddrmgr.BlockStamp, error)
	GetCFilter(blockHash chainhash.Hash, filterType wire.FilterType,
		options ...QueryOption) (*gcs.Filter, error)
}

// A PriorityQueue implements heap.Interface and holds GetUtxoRequests. The
// queue maintains that heap.Pop() will always return the GetUtxo request with
// the least starting height. This allows us to add new GetUtxo requests to
// an already running batch.
type PriorityQueue []*GetUtxoRequest

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
	// We want Pop to give us the least StartHeight.
	return pq[i].StartHeight < pq[j].StartHeight
}

func (pq PriorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

// Push is called by the heap.Interface implementation to add an element to the
// end of the backing store. The heap library will then maintain the heap
// invariant.
func (pq *PriorityQueue) Push(x interface{}) {
	item := x.(*GetUtxoRequest)
	*pq = append(*pq, item)
}

// Peek returns the least height element in the queue without removing it.
func (pq *PriorityQueue) Peek() *GetUtxoRequest {
	return (*pq)[0]
}

// Pop is called by the heap.Interface implementation to remove an element from
// the end of the backing store. The heap library will then maintain the heap
// invariant.
func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}

// IsEmpty returns true if the queue has no elements.
func (pq *PriorityQueue) IsEmpty() bool {
	return pq.Len() == 0
}

// UtxoScanner batches calls to GetUtxo so that a single scan can search for
// multiple outpoints. If a scan is in progress when a new element is added, we
// check whether it can safely be added to the current batch, if not it will be
// included in the next batch.
type UtxoScanner struct {
	pq      PriorityQueue
	stopped bool

	chainClient Interface

	nextBatch []*GetUtxoRequest

	cv *sync.Cond
}

// NewUtxoScanner creates a new instance of UtxoScanner using the given chain
// interface.
func NewUtxoScanner(chainClient Interface) UtxoScanner {
	return UtxoScanner{
		pq:          make(PriorityQueue, 0),
		cv:          sync.NewCond(&sync.Mutex{}),
		stopped:     false,
		chainClient: chainClient,
	}
}

// Start begins running scan batches.
func (s *UtxoScanner) Start() {
	go func() {
		for {
			s.cv.L.Lock()
			if s.stopped {
				s.cv.L.Unlock()
				return
			}

			// Re-queue previously skipped requests for next batch.
			for _, request := range s.nextBatch {
				heap.Push(&s.pq, request)
			}

			s.nextBatch = nil

			s.cv.L.Unlock()

			requests, err := s.runBatch()

			// If there was an error, then notify the currently outstanding
			// requests.
			if err != nil {
				for _, request := range requests {
					request.Result(nil, err)
				}
			}
		}
	}()
}

// getAtHeight returns all GetUtxo requests that have starting height of the
// given height.
func (s *UtxoScanner) getAtHeight(height uint32) []*GetUtxoRequest {
	s.cv.L.Lock()
	defer s.cv.L.Unlock()

	// Take any requests that are too old to go in this batch and keep them for
	// the next batch.
	for !s.pq.IsEmpty() && s.pq.Peek().StartHeight < height {
		item := heap.Pop(&s.pq).(*GetUtxoRequest)
		s.nextBatch = append(s.nextBatch, item)
	}

	var requests []*GetUtxoRequest
	for !s.pq.IsEmpty() && s.pq.Peek().StartHeight == height {
		item := heap.Pop(&s.pq).(*GetUtxoRequest)
		requests = append(requests, item)
	}

	return requests
}

// CheckTransactions finds any transactions in the block that spend the given
// outpoints.
func (s *UtxoScanner) CheckTransactions(block *wire.MsgBlock, height uint32,
	outpoints map[wire.OutPoint]struct{}) (map[wire.OutPoint]SpendReport,
	error) {
	spends := make(map[wire.OutPoint]SpendReport)

	// If we've spent the output in this block, return an
	// error stating that the output is spent.
	for _, tx := range block.Transactions {
		// Check each input to see if this transaction spends one of our
		// watched outpoints.
		for i, ti := range tx.TxIn {
			if _, ok := outpoints[ti.PreviousOutPoint]; ok {
				log.Debugf("Transaction %s spends outpoint %s", tx.TxHash(),
					ti.PreviousOutPoint)
				spends[ti.PreviousOutPoint] = SpendReport{
					SpendingTx:         tx,
					SpendingInputIndex: uint32(i),
					SpendingTxHeight:   height,
				}
			}
		}
	}

	return spends, nil
}

// filterMatches checks whether any of the filterEntries match for the given
// block.
func (s *UtxoScanner) filterMatches(hash chainhash.Hash,
	filterEntries [][]byte) (bool, error) {
	filter, err := s.chainClient.GetCFilter(hash,
		wire.GCSFilterRegular)
	if err != nil {
		return false, err
	}

	if filter != nil {
		filterKey := builder.DeriveKey(&hash)
		return filter.MatchAny(filterKey, filterEntries)
	}

	return false, nil
}

func buildFilterEntries(requests []*GetUtxoRequest) ([][]byte,
	map[wire.OutPoint]struct{}) {
	var filterEntries [][]byte
	outpoints := make(map[wire.OutPoint]struct{})

	for _, request := range requests {
		op := *request.OutPoint
		outpoints[op] = struct{}{}
		filterEntries = append(filterEntries, builder.OutPointToFilterEntry(op))
	}

	return filterEntries, outpoints
}

// runBatch runs a single batch. If there was an error, then return the
// outstanding requests.
func (s *UtxoScanner) runBatch() ([]*GetUtxoRequest, error) {
	var requests []*GetUtxoRequest

	// Take the request with the lowest block height so we can begin the scan
	// from there.
	req := s.peek()

	// Check if we were interrupted while waiting.
	if req == nil {
		return nil, nil
	}

	startHeight := req.StartHeight
	best, err := s.chainClient.BestSnapshot()
	if err != nil {
		return requests, err
	}

	// While scanning through the blockchain, take note of the transactions that
	// create the outpoints. If the outpoint isn't spent then return this
	// transaction.
	initialTx := make(map[wire.OutPoint]*SpendReport)

	filterEntries, outpoints := buildFilterEntries(requests)

	// Scan forward through the blockchain and look for any transactions that
	// might spend the given UTXOs.
	for height := startHeight; height <= uint32(best.Height); height++ {
		// If there are any new requests that can safely be added to this batch,
		// then try and fetch them.
		reqs := s.getAtHeight(height)
		if len(reqs) > 0 {
			for _, req := range reqs {
				log.Debugf("Adding %s (%d) to watchlist", req.OutPoint.String(),
					req.StartHeight)
				requests = append(requests, req)
				outpoints[*req.OutPoint] = struct{}{}
				filterEntries = append(filterEntries,
					builder.OutPointToFilterEntry(*req.OutPoint))
			}
		}

		hash, err := s.chainClient.GetBlockHash(int64(height))
		if err != nil {
			return requests, err
		}

		// If an outpoint is created in this block, then fetch it regardless.
		// Otherwise check to see if the filter matches any of our watched
		// outpoints.
		fetch := len(reqs) > 0
		if !fetch {
			match, err := s.filterMatches(*hash, filterEntries)
			if err != nil {
				return requests, err
			}
			fetch = match
		}

		if fetch {
			log.Debugf("Fetching block at height %d (%s)", height,
				hash.String())

			// FIXME(simon): Find out why this takes three minutes.
			// Fetch the block from the network.
			block, err := s.chainClient.GetBlockFromNetwork(*hash)
			if err != nil {
				return requests, err
			}

			log.Debugf("Got block %d (%s)", height, hash.String())

			if fetch {
				for _, request := range requests {
					if request.StartHeight == height {
						tx := findTransaction(block.MsgBlock(),
							request.OutPoint)
						// Grab the tx that created this output.
						initialTx[*request.OutPoint] = tx

						log.Debugf("Block %d creates output %s", height,
							request.OutPoint.String())
					}
				}
			}

			spends, err := s.CheckTransactions(block.MsgBlock(), height,
				outpoints)
			if err != nil {
				return requests, err
			}

			for outPoint, spend := range spends {
				log.Debugf("Outpoint %s is spent in tx %s", outPoint.String(),
					spew.Sprint(spend))

				// Find the request this spend relates to.
				var filteredRequests []*GetUtxoRequest
				for _, request := range requests {
					if *request.OutPoint == outPoint {
						request.Result(&spend, nil)
					} else {
						filteredRequests = append(filteredRequests, request)
					}
				}
				requests = filteredRequests

				// Remove the filter from filterEntries.
				filterEntries, outpoints = buildFilterEntries(requests)
			}
		}
	}

	log.Debugf("Finished batch, %d unspent outpoints", len(requests))

	for _, request := range requests {
		tx, ok := initialTx[*request.OutPoint]
		if ok {
			request.Result(tx, nil)
		} else {
			// A nil SpendReport indicates the output was not found.
			request.Result(nil, nil)
		}
	}

	return nil, nil
}

// Stop any in-progress scan.
func (s *UtxoScanner) Stop() {
	s.cv.L.Lock()
	defer s.cv.L.Unlock()

	s.stopped = true
}

// Returns the GetUtxoRequest with the lowest block height. If no elements are
// available, then block until one is added.
func (s *UtxoScanner) peek() *GetUtxoRequest {
	s.cv.L.Lock()
	defer s.cv.L.Unlock()

	// Block until the queue is no longer empty.
	for !s.stopped && s.pq.IsEmpty() {
		s.cv.Wait()
	}

	// We return nil only in the case that we've been interrupted, so callers
	// can use this to determine that the UtxoScanner is shutting down.
	if s.stopped {
		return nil
	}

	return s.pq.Peek()
}

// Enqueue takes a GetUtxoRequest and adds it to the next applicable batch.
func (s *UtxoScanner) Enqueue(req *GetUtxoRequest) {
	log.Debugf("Enqueuing request for %s with start height %d",
		req.OutPoint.String(), req.StartHeight)
	s.cv.L.Lock()

	// Insert the request into the queue and signal any threads that might be
	// waiting for new elements.
	heap.Push(&s.pq, req)

	s.cv.L.Unlock()
	s.cv.Signal()
}

// findTransaction returns a SpendReport for the UTXO, or nil if it does not
// exist in this block.
func findTransaction(block *wire.MsgBlock,
	point *wire.OutPoint) *SpendReport {
	for _, tx := range block.Transactions {
		if tx.TxHash() == point.Hash {
			outputs := tx.TxOut
			return &SpendReport{
				Output: outputs[point.Index],
			}
		}
	}

	log.Errorf("Failed to find tx %s", point.Hash.String())

	return nil
}
<|endoftext|>


<commit_before>package gocube

// A Phase1Axis represents the y-axis corner orientations, ZZ edge orientations,
// and the permutation of the E slice.
type Phase1Axis struct {
	CornerOrientations int
	EdgeOrientations   int
	SlicePerm          int
}

// A Phase1Cube is an efficient way to represent the parts of a cube which
// matter for the first phase of Kociemba's algorithm.
// The FB edge orientation can be used for both Y and X phase-1 goals, and the
// UD edge orientation can be used for the Z phase-1 goal. Thus, no RL edge
// orientations are needed.
type Phase1Cube struct {
	XCornerOrientation int
	YCornerOrientation int
	ZCornerOrientation int

	FBEdgeOrientation int
	UDEdgeOrientation int

	ESlicePermutation int
	SSlicePermutation int
	MSlicePermutation int
}

// Move applies a move on a Phase1Cube using a moves table.
func (p *Phase1Cube) Move(m Move, table *Phase1Moves) {
	// TODO: apply the move to each axis of the represented data.
}

// Phase1Moves is a table containing the necessary data to efficiently perform
// moves on a Phase1Cube.
// Note that only one move table is needed for all 3 axes (i.e. all three
// phase-1 goals). Thus, the move tables apply directly to the Y-oriented
// phase-1 goal. Moves much be translated for the X-oriented and Z-oriented
// goals.
type Phase1Moves struct {
	ESliceMoves [495][18]int
	EOMoves [2048][18]int
	COMoves [2048][18]int
}

// NewPhase1Moves generates tables for applying phase-1 moves.
func NewPhase1Moves() *Phase1Moves {
	res := &Phase1Moves{}

	// TODO: generate the E-slice moves
	// TODO: generate the CO moves

	// Generate the EO cases and do moves on them.
	for i := 0; i < 2048; i++ {
		// Generate a CubieEdges object for this EO case.
		edges := SolvedCubieEdges()
		parity := false
		for x := 0; x < 11; x++ {
			if (i & (1 << x)) != 0 {
				parity = !parity
				edges[x].Flip = true
			}
		}
		edges[11].Flip = parity

		// Apply each move and encode the result.
		for m := 0; m < 18; m++ {
			aCase := edges
			aCase.Move(Move(m))
			res.EOMoves[i][m] = encodeEO(&aCase)
		}
	}

	return res
}

func encodeEO(c *CubieEdges) int {
	res := 0
	for i := 0; i < 11; i++ {
		if (*c)[i].Flip {
			res |= (1 << i)
		}
	}
	return res
}
<commit_msg>started working on decodeCO for CO cases<commit_after>package gocube

// A Phase1Axis represents the y-axis corner orientations, ZZ edge orientations,
// and the permutation of the E slice.
type Phase1Axis struct {
	CornerOrientations int
	EdgeOrientations   int
	SlicePerm          int
}

// A Phase1Cube is an efficient way to represent the parts of a cube which
// matter for the first phase of Kociemba's algorithm.
// The FB edge orientation can be used for both Y and X phase-1 goals, and the
// UD edge orientation can be used for the Z phase-1 goal. Thus, no RL edge
// orientations are needed.
type Phase1Cube struct {
	XCornerOrientation int
	YCornerOrientation int
	ZCornerOrientation int

	FBEdgeOrientation int
	UDEdgeOrientation int

	ESlicePermutation int
	SSlicePermutation int
	MSlicePermutation int
}

// Move applies a move on a Phase1Cube using a moves table.
func (p *Phase1Cube) Move(m Move, table *Phase1Moves) {
	// TODO: apply the move to each axis of the represented data.
}

// Phase1Moves is a table containing the necessary data to efficiently perform
// moves on a Phase1Cube.
// Note that only one move table is needed for all 3 axes (i.e. all three
// phase-1 goals). Thus, the move tables apply directly to the Y-oriented
// phase-1 goal. Moves much be translated for the X-oriented and Z-oriented
// goals.
type Phase1Moves struct {
	ESliceMoves [495][18]int
	EOMoves [2048][18]int
	COMoves [2187][18]int
}

// NewPhase1Moves generates tables for applying phase-1 moves.
func NewPhase1Moves() *Phase1Moves {
	res := &Phase1Moves{}

	// TODO: generate the E-slice moves
	// TODO: generate the CO moves

	// Generate the EO cases and do moves on them.
	for i := 0; i < 2048; i++ {
		edges := decodeEO(i)
		for m := 0; m < 18; m++ {
			aCase := edges
			aCase.Move(Move(m))
			res.EOMoves[i][m] = encodeEO(&aCase)
		}
	}

	return res
}

func decodeCO(co int) CubieCorners {
	corners := SolvedCubieCorners()
	scaler := 1
	for x := 0; x < 7; x++ {
		corners[x].Orientation = (co/scaler) % 3
		scaler *= 3
	}

	// TODO: compute the flip of the last corner

	return corners
}

func decodeEO(eo int) CubieEdges {
	edges := SolvedCubieEdges()
	parity := false
	for x := 0; x < 11; x++ {
		if (i & (1 << x)) != 0 {
			parity = !parity
			edges[x].Flip = true
		}
	}
	edges[11].Flip = parity
	return edges
}

func encodeEO(c *CubieEdges) int {
	res := 0
	for i := 0; i < 11; i++ {
		if (*c)[i].Flip {
			res |= (1 << i)
		}
	}
	return res
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
)
import "github.com/mozillazg/go-pinyin"

func main() {
	heteronym := flag.Bool("e", false, "启用多音字模式")
	flag.Parse()
	hans := flag.Args()
	args := pinyin.Args{Style: pinyin.TONE}

	if len(hans) == 0 {
		fmt.Println("请至少输入一个汉字: pinyin hans [hans ...]")
		os.Exit(1)
	}
	if *heteronym {
		args.Heteronym = true
	}
	pys := pinyin.Pinyin(strings.Join(hans, ""), args)
	for _, s := range pys {
		fmt.Print(strings.Join(s, ","), " ")
	}
	if len(pys) > 0 {
		fmt.Println()
	}
}
<commit_msg>fix cli tool<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
)
import "github.com/mozillazg/go-pinyin"

func main() {
	heteronym := flag.Bool("e", false, "启用多音字模式")
	flag.Parse()
	hans := flag.Args()
	args := pinyin.NewArgs()
	args.Style = pinyin.TONE

	if len(hans) == 0 {
		fmt.Println("请至少输入一个汉字: pinyin hans [hans ...]")
		os.Exit(1)
	}
	if *heteronym {
		args.Heteronym = true
	}
	pys := pinyin.Pinyin(strings.Join(hans, ""), args)
	for _, s := range pys {
		fmt.Print(strings.Join(s, ","), " ")
	}
	if len(pys) > 0 {
		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/lucapette/deluminator/pkg/app"
	"goji.io"
	"goji.io/pat"
)

func debugHandler(inner http.Handler) http.Handler {
	mw := func(w http.ResponseWriter, r *http.Request) {
		buf := bytes.NewBuffer(make([]byte, 0))
		reader := io.TeeReader(r.Body, buf)

		b, err := ioutil.ReadAll(reader)
		if err != nil {
			log.Fatal(err)
		}

		entry := log.WithFields(log.Fields{
			"method": r.Method,
			"body":   string(b),
		})

		for k, v := range r.Header {
			entry = entry.WithField(k, v)
		}

		entry.Info("incoming request")

		r.Body = ioutil.NopCloser(buf)
		inner.ServeHTTP(w, r)
	}
	return http.HandlerFunc(mw)
}

func logHandler(inner http.Handler) http.Handler {
	mw := func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		proxyWriter := wrapWriter(w)

		inner.ServeHTTP(proxyWriter, r)

		log.WithFields(log.Fields{
			"spent":  time.Now().Sub(start),
			"path":   r.URL.Path,
			"method": r.Method,
			"status": proxyWriter.status(),
		}).Info("request completed")
	}
	return http.HandlerFunc(mw)
}

func homeHandler(w http.ResponseWriter, request *http.Request) {
	asset, err := Asset("assets/index.html")

	if err != nil {
		log.Println(err)
	}

	w.Header().Set("Content-Type", "text/html")
	_, err = w.Write(asset)

	if err != nil {
		log.Println(err)
	}
}

func assetsHandler(w http.ResponseWriter, r *http.Request) {
	kind := pat.Param(r, "kind")
	name := pat.Param(r, "name")

	asset, err := Asset(strings.Join([]string{"assets", kind, name}, "/"))

	if err != nil {
		log.Println(err)
	}

	w.Header().Set("Content-Type", "text/"+kind)
	_, err = w.Write(asset)

	if err != nil {
		log.Println(err)
	}
}

func Start(app *app.App) {
	router := goji.NewMux()

	if app.Opts.Debug {
		router.Use(debugHandler)
	}

	router.Use(logHandler)

	router.HandleFunc(pat.Get("/"), homeHandler)
	router.HandleFunc(pat.Post("/graphql"), GraphQLHandler(app))
	router.HandleFunc(pat.Get("/assets/:kind/:name"), assetsHandler)

	go func() {
		err := http.ListenAndServe(":"+strconv.Itoa(app.Opts.Port), router)
		if err != nil {
			log.Println("Cant start server:", err)
			os.Exit(1)
		}
	}()
}
<commit_msg>Better logging level<commit_after>package api

import (
	"bytes"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/lucapette/deluminator/pkg/app"
	"goji.io"
	"goji.io/pat"
)

func debugHandler(inner http.Handler) http.Handler {
	mw := func(w http.ResponseWriter, r *http.Request) {
		buf := bytes.NewBuffer(make([]byte, 0))
		reader := io.TeeReader(r.Body, buf)

		b, err := ioutil.ReadAll(reader)
		if err != nil {
			log.Fatal(err)
		}

		entry := log.WithFields(log.Fields{
			"method": r.Method,
			"body":   string(b),
		})

		for k, v := range r.Header {
			entry = entry.WithField(k, v)
		}

		entry.Info("incoming request")

		r.Body = ioutil.NopCloser(buf)
		inner.ServeHTTP(w, r)
	}
	return http.HandlerFunc(mw)
}

func logHandler(inner http.Handler) http.Handler {
	mw := func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		proxyWriter := wrapWriter(w)

		inner.ServeHTTP(proxyWriter, r)

		log.WithFields(log.Fields{
			"spent":  time.Now().Sub(start),
			"path":   r.URL.Path,
			"method": r.Method,
			"status": proxyWriter.status(),
		}).Info("request completed")
	}
	return http.HandlerFunc(mw)
}

func homeHandler(w http.ResponseWriter, request *http.Request) {
	asset, err := Asset("assets/index.html")

	if err != nil {
		log.Println(err)
	}

	w.Header().Set("Content-Type", "text/html")
	_, err = w.Write(asset)

	if err != nil {
		log.Println(err)
	}
}

func assetsHandler(w http.ResponseWriter, r *http.Request) {
	kind := pat.Param(r, "kind")
	name := pat.Param(r, "name")

	asset, err := Asset(strings.Join([]string{"assets", kind, name}, "/"))

	if err != nil {
		log.Println(err)
	}

	w.Header().Set("Content-Type", "text/"+kind)
	_, err = w.Write(asset)

	if err != nil {
		log.Println(err)
	}
}

func Start(app *app.App) {
	router := goji.NewMux()

	if app.Opts.Debug {
		router.Use(debugHandler)
	}

	router.Use(logHandler)

	router.HandleFunc(pat.Get("/"), homeHandler)
	router.HandleFunc(pat.Post("/graphql"), GraphQLHandler(app))
	router.HandleFunc(pat.Get("/assets/:kind/:name"), assetsHandler)

	go func() {
		err := http.ListenAndServe(":"+strconv.Itoa(app.Opts.Port), router)
		if err != nil {
			log.Fatal("Cant start server:", err)
		}
	}()
}
<|endoftext|>


<commit_before>package env

import (
	"bytes"
	"fmt"
	"github.com/go-sql-driver/mysql"
	"gopkg.in/inconshreveable/log15.v2"
	golog "log"
	"os"
	"strconv"
	"time"
)

const (
	logBufferSize  = 256
	timeFormat     = "2006-01-02T15:04:05-0700"
	floatFormat    = 'f'
	floatPrecision = 3
	errorKey       = "ERROR"
)

// logging prefixes for different log levels
// see <http://0pointer.de/public/systemd-man/sd-daemon.html>
const (
	sdCrit    = "<2>"
	sdErr     = "<3>"
	sdWarning = "<4>"
	sdInfo    = "<6>"
	sdDebug   = "<7>"
)

// Log is the default logger. It has to be initialized through
var Log log15.Logger

func init() {
	// adjust the logging environment and set the default log15.Logger for further
	// use.
	//
	// We follow the new-style daemons approach
	// see <http://0pointer.de/public/systemd-man/daemon.html#New-Style%20Daemons>
	Log = log15.New()
	Log.SetHandler(log15.StreamHandler(os.Stderr, DaemonFormat()))
	golog.SetOutput(logBridge{Log})
	err := mysql.SetLogger(mysqlLog{})
	if err != nil {
		Log.Crit("error setting up mysql log", log15.Ctx{"err": err})
	}
}

// logBridge acts as a Writer for the log pkg
// It will log to log15
type logBridge struct {
	log log15.Logger
}

// logBridge Writer implementation
// will log all log pkg messages as log15.Info messages
func (l logBridge) Write(msg []byte) (int, error) {
	l.log.Info("log pkg message", log15.Ctx{"message": string(msg)})
	return len(msg), nil
}

func logLevel(buf *bytes.Buffer, lvl log15.Lvl) {
	switch lvl {
	case log15.LvlCrit:
		buf.WriteString(sdCrit)
	case log15.LvlError:
		buf.WriteString(sdErr)
	case log15.LvlWarn:
		buf.WriteString(sdWarning)
	case log15.LvlInfo:
		buf.WriteString(sdInfo)
	case log15.LvlDebug:
		buf.WriteString(sdDebug)
	}
}

func logRecord(buf *bytes.Buffer, ctx []interface{}) {
	for i := 0; i < len(ctx); i += 2 {
		if i != 0 {
			buf.WriteByte(' ')
		}
		k, ok := ctx[i].(string)
		v := logValue(ctx[i+1])
		if !ok {
			k, v = errorKey, logValue(k)
		}

		fmt.Fprintf(buf, "%s=%s", k, v)
	}
	buf.WriteByte('\n')
}

func logValue(value interface{}) string {
	if value == nil {
		return "nil"
	}

	switch v := value.(type) {
	case time.Time:
		return v.Format(timeFormat)
	case error:
		return v.Error()
	case fmt.Stringer:
		return escapeString(v.String())
	case bool:
		return strconv.FormatBool(v)
	case float32:
		return strconv.FormatFloat(float64(v), floatFormat, floatPrecision, 64)
	case float64:
		return strconv.FormatFloat(v, floatFormat, floatPrecision, 64)
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		return fmt.Sprintf("%d", v)
	case string:
		return escapeString(v)
	default:
		return escapeString(fmt.Sprintf("%+v", v))
	}
}

type mysqlLog struct{}

func (m mysqlLog) Print(v ...interface{}) {
	Log.Warn("mysql log", log15.Ctx{"mysqlLog": v})
}
<commit_msg>implement requirement: errors should be escaped<commit_after>package env

import (
	"bytes"
	"fmt"
	"github.com/go-sql-driver/mysql"
	"gopkg.in/inconshreveable/log15.v2"
	golog "log"
	"os"
	"strconv"
	"time"
)

const (
	logBufferSize  = 256
	timeFormat     = "2006-01-02T15:04:05-0700"
	floatFormat    = 'f'
	floatPrecision = 3
	errorKey       = "ERROR"
)

// logging prefixes for different log levels
// see <http://0pointer.de/public/systemd-man/sd-daemon.html>
const (
	sdCrit    = "<2>"
	sdErr     = "<3>"
	sdWarning = "<4>"
	sdInfo    = "<6>"
	sdDebug   = "<7>"
)

// Log is the default logger. It has to be initialized through
var Log log15.Logger

func init() {
	// adjust the logging environment and set the default log15.Logger for further
	// use.
	//
	// We follow the new-style daemons approach
	// see <http://0pointer.de/public/systemd-man/daemon.html#New-Style%20Daemons>
	Log = log15.New()
	Log.SetHandler(log15.StreamHandler(os.Stderr, DaemonFormat()))
	golog.SetOutput(logBridge{Log})
	err := mysql.SetLogger(mysqlLog{})
	if err != nil {
		Log.Crit("error setting up mysql log", log15.Ctx{"err": err})
	}
}

// logBridge acts as a Writer for the log pkg
// It will log to log15
type logBridge struct {
	log log15.Logger
}

// logBridge Writer implementation
// will log all log pkg messages as log15.Info messages
func (l logBridge) Write(msg []byte) (int, error) {
	l.log.Info("log pkg message", log15.Ctx{"message": string(msg)})
	return len(msg), nil
}

func logLevel(buf *bytes.Buffer, lvl log15.Lvl) {
	switch lvl {
	case log15.LvlCrit:
		buf.WriteString(sdCrit)
	case log15.LvlError:
		buf.WriteString(sdErr)
	case log15.LvlWarn:
		buf.WriteString(sdWarning)
	case log15.LvlInfo:
		buf.WriteString(sdInfo)
	case log15.LvlDebug:
		buf.WriteString(sdDebug)
	}
}

func logRecord(buf *bytes.Buffer, ctx []interface{}) {
	for i := 0; i < len(ctx); i += 2 {
		if i != 0 {
			buf.WriteByte(' ')
		}
		k, ok := ctx[i].(string)
		v := logValue(ctx[i+1])
		if !ok {
			k, v = errorKey, logValue(k)
		}

		fmt.Fprintf(buf, "%s=%s", k, v)
	}
	buf.WriteByte('\n')
}

func logValue(value interface{}) string {
	if value == nil {
		return "nil"
	}

	switch v := value.(type) {
	case time.Time:
		return v.Format(timeFormat)
	case error:
		return escapeString(v.Error())
	case fmt.Stringer:
		return escapeString(v.String())
	case bool:
		return strconv.FormatBool(v)
	case float32:
		return strconv.FormatFloat(float64(v), floatFormat, floatPrecision, 64)
	case float64:
		return strconv.FormatFloat(v, floatFormat, floatPrecision, 64)
	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
		return fmt.Sprintf("%d", v)
	case string:
		return escapeString(v)
	default:
		return escapeString(fmt.Sprintf("%+v", v))
	}
}

type mysqlLog struct{}

func (m mysqlLog) Print(v ...interface{}) {
	Log.Warn("mysql log", log15.Ctx{"mysqlLog": v})
}
<|endoftext|>


<commit_before>package mux

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/client"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
)

// Mux will manage all connections and subscriptions. Will check if subscriptions
// limit is reached and spawn new connection when that happens. It will also listen
// to all incomming client messages and reconnect client with all its subscriptions
// in case of a failure
type Mux struct {
	cid           int
	dms           int
	publicChan    chan msg.Msg
	publicClients map[int]*client.Client
	privateChan   chan msg.Msg
	closeChan     chan bool
	privateClient *client.Client
	mtx           *sync.RWMutex
	Err           error
	transform     bool
	apikey        string
	apisec        string
	subInfo       map[int64]event.Info
	authenticated bool
	publicURL     string
	authURL       string
	online        bool
	subsRateLimit int
}

// api rate limit is 20 calls per minute. 1x3s, 20x1min
const (
	rateLimitDuration  = 3 * time.Second
	rateLimitQueueSize = 20
)

// New returns pointer to instance of mux
func New() *Mux {
	return &Mux{
		publicChan:    make(chan msg.Msg),
		privateChan:   make(chan msg.Msg),
		closeChan:     make(chan bool),
		publicClients: make(map[int]*client.Client),
		mtx:           &sync.RWMutex{},
		subInfo:       map[int64]event.Info{},
		publicURL:     "wss://api-pub.bitfinex.com/ws/2",
		authURL:       "wss://api.bitfinex.com/ws/2",
	}
}

// TransformRaw enables data transformation and mapping to appropriate
// models before sending it to consumer
func (m *Mux) TransformRaw() *Mux {
	m.transform = true
	return m
}

// WithAPIKEY accepts and persists api key
func (m *Mux) WithAPIKEY(key string) *Mux {
	m.apikey = key
	return m
}

// WithDeadManSwitch - when socket is closed, cancel all account orders
func (m *Mux) WithDeadManSwitch() *Mux {
	m.dms = 4
	return m
}

// WithAPISEC accepts and persists api sec
func (m *Mux) WithAPISEC(sec string) *Mux {
	m.apisec = sec
	return m
}

// WithPublicURL accepts and persists public api url
func (m *Mux) WithPublicURL(url string) *Mux {
	m.publicURL = url
	return m
}

// WithAuthURL accepts and persists auth api url
func (m *Mux) WithAuthURL(url string) *Mux {
	m.authURL = url
	return m
}

func (m *Mux) IsConnected() bool {
	return m.online
}

func (m *Mux) Close() bool {
	m.closeChan <- true
	return true
}

// Subscribe - given the details in form of event.Subscribe,
// queues the subscriptions for eventual submission
func (m *Mux) Subscribe(sub event.Subscribe) *Mux {
	if m.Err != nil {
		return m
	}

	// if limit is reached, wait 1 second and recuresively
	// call Subscribe again with same subscription details
	if m.subsRateLimit == rateLimitQueueSize {
		time.Sleep(1 * time.Second)
		return m.Subscribe(sub)
	}

	m.mtx.RLock()
	defer m.mtx.RUnlock()
	if m.publicClients[m.cid].SubAdded(sub) {
		return m
	}

	if err := m.publicClients[m.cid].Subscribe(sub); err != nil {
		return m
	}

	if limitReached := m.publicClients[m.cid].SubsLimitReached(); limitReached {
		log.Printf("subs limit is reached on cid: %d, spawning new conn\n", m.cid)
		m.addPublicClient()
	}

	m.subsRateLimit++
	return m
}

// Start creates initial clients for accepting connections
func (m *Mux) Start() *Mux {
	if m.hasAPIKeys() && m.privateClient == nil {
		m.addPrivateClient()
	}

	m.watchRateLimit()
	return m.addPublicClient()
}

// Listen accepts a callback func that will get called each time mux
// receives a message from any of its clients/subscriptions. It
// should be called last, after all setup calls are made
func (m *Mux) Listen(cb func(interface{}, error)) error {
	if m.Err != nil {
		return m.Err
	}

	m.online = true
	for {
		select {
		case ms, ok := <-m.publicChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("conn:%d has failed | err:%s | reconnecting", ms.CID, ms.Err))
				m.resetPublicClient(ms.CID)
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.recordEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				raw, pld, chID, _, err := ms.PreprocessRaw()
				if err != nil {
					cb(nil, err)
					continue
				}

				inf, ok := m.subInfo[chID]
				if !ok {
					cb(nil, fmt.Errorf("unrecognized chanId:%d", chID))
					continue
				}
				cb(ms.ProcessPublic(raw, pld, chID, inf))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case ms, ok := <-m.privateChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("err: %s | reconnecting", ms.Err))
				m.resetPrivateClient()
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.recordEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				raw, pld, chID, msgType, err := ms.PreprocessRaw()
				if err != nil {
					cb(nil, err)
					continue
				}
				cb(ms.ProcessPrivate(raw, pld, chID, msgType))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case <-m.closeChan:
			m.mtx.Lock()
			defer m.mtx.Unlock()

			for _, v := range m.publicClients {
				if v == nil {
					continue
				}
				if err := v.Close(); err != nil {
					log.Printf("failed closing public client: %s\n", err)
				}
			}

			if m.privateClient != nil {
				if err := m.privateClient.Close(); err != nil {
					log.Printf("failed closing private client: %s\n", err)
				}
			}

			m.online = false
			return nil
		}
	}
}

// Send meant for authenticated input, takes payload in form of interface
// and calls client with it
func (m *Mux) Send(pld interface{}) error {
	if !m.authenticated || m.privateClient == nil {
		return errors.New("not authorized")
	}
	return m.privateClient.Send(pld)
}

func (m *Mux) hasAPIKeys() bool {
	return len(m.apikey) != 0 && len(m.apisec) != 0
}

func (m *Mux) recordEvent(i event.Info, err error) (event.Info, error) {
	switch i.Event {
	case "subscribed":
		m.subInfo[i.ChanID] = i
	case "auth":
		if i.Status == "OK" {
			m.subInfo[i.ChanID] = i
			m.authenticated = true
		}
	}
	// add more cases if/when needed
	return i, err
}

func (m *Mux) resetPublicClient(cid int) {
	// pull old client subscriptions
	subs := m.publicClients[cid].GetAllSubs()
	// add fresh client
	m.addPublicClient()
	// resubscribe old events
	for _, sub := range subs {
		log.Printf("resubscribing: %+v\n", sub)
		m.Subscribe(sub)
	}
	// remove old, closed channel from the list
	delete(m.publicClients, cid)
}

func (m *Mux) resetPrivateClient() {
	m.authenticated = false
	m.privateClient = nil
	m.addPrivateClient()
}

func (m *Mux) addPublicClient() *Mux {
	// adding new client so making sure we increment cid
	m.cid++
	// create new public client and pass error to mux if any
	c, err := client.
		New().
		WithID(m.cid).
		WithSubsLimit(30).
		Public(m.publicURL)
	if err != nil {
		m.Err = err
		return m
	}
	// add new client to list for later reference
	m.publicClients[m.cid] = c
	// start listening for incoming client messages
	go c.Read(m.publicChan)
	return m
}

func (m *Mux) addPrivateClient() *Mux {
	// create new private client and pass error to mux if any
	c, err := client.New().Private(m.apikey, m.apisec, m.authURL, m.dms)
	if err != nil {
		m.Err = err
		return m
	}

	m.privateClient = c
	go c.Read(m.privateChan)
	return m
}

// watchRateLimit will run once every rateLimitDuration
// and free up the queue
func (m *Mux) watchRateLimit() {
	go func() {
		for {
			if m.subsRateLimit > 0 {
				m.subsRateLimit--
			}

			time.Sleep(rateLimitDuration)
		}
	}()
}
<commit_msg>chenge in comment and error handling<commit_after>package mux

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/client"
	"github.com/bitfinexcom/bitfinex-api-go/pkg/mux/msg"
)

// Mux will manage all connections and subscriptions. Will check if subscriptions
// limit is reached and spawn new connection when that happens. It will also listen
// to all incomming client messages and reconnect client with all its subscriptions
// in case of a failure
type Mux struct {
	cid           int
	dms           int
	publicChan    chan msg.Msg
	publicClients map[int]*client.Client
	privateChan   chan msg.Msg
	closeChan     chan bool
	privateClient *client.Client
	mtx           *sync.RWMutex
	Err           error
	transform     bool
	apikey        string
	apisec        string
	subInfo       map[int64]event.Info
	authenticated bool
	publicURL     string
	authURL       string
	online        bool
	subsRateLimit int
}

// api rate limit is 20 calls per minute. 1x3s, 20x1min
const (
	rateLimitDuration  = 3 * time.Second
	rateLimitQueueSize = 20
)

// New returns pointer to instance of mux
func New() *Mux {
	return &Mux{
		publicChan:    make(chan msg.Msg),
		privateChan:   make(chan msg.Msg),
		closeChan:     make(chan bool),
		publicClients: make(map[int]*client.Client),
		mtx:           &sync.RWMutex{},
		subInfo:       map[int64]event.Info{},
		publicURL:     "wss://api-pub.bitfinex.com/ws/2",
		authURL:       "wss://api.bitfinex.com/ws/2",
	}
}

// TransformRaw enables data transformation and mapping to appropriate
// models before sending it to consumer
func (m *Mux) TransformRaw() *Mux {
	m.transform = true
	return m
}

// WithAPIKEY accepts and persists api key
func (m *Mux) WithAPIKEY(key string) *Mux {
	m.apikey = key
	return m
}

// WithDeadManSwitch - when socket is closed, cancel all account orders
func (m *Mux) WithDeadManSwitch() *Mux {
	m.dms = 4
	return m
}

// WithAPISEC accepts and persists api sec
func (m *Mux) WithAPISEC(sec string) *Mux {
	m.apisec = sec
	return m
}

// WithPublicURL accepts and persists public api url
func (m *Mux) WithPublicURL(url string) *Mux {
	m.publicURL = url
	return m
}

// WithAuthURL accepts and persists auth api url
func (m *Mux) WithAuthURL(url string) *Mux {
	m.authURL = url
	return m
}

func (m *Mux) IsConnected() bool {
	return m.online
}

func (m *Mux) Close() bool {
	m.closeChan <- true
	return true
}

// Subscribe - given the details in form of event.Subscribe, subscribes client to public
// channels. If rate limit is reached, calls itself recursively after 1s with same params
func (m *Mux) Subscribe(sub event.Subscribe) *Mux {
	if m.Err != nil {
		return m
	}

	// if limit is reached, wait 1 second and recuresively
	// call Subscribe again with same subscription details
	if m.subsRateLimit == rateLimitQueueSize {
		time.Sleep(1 * time.Second)
		return m.Subscribe(sub)
	}

	m.mtx.RLock()
	defer m.mtx.RUnlock()
	if m.publicClients[m.cid].SubAdded(sub) {
		return m
	}

	if m.Err = m.publicClients[m.cid].Subscribe(sub); m.Err != nil {
		return m
	}

	if limitReached := m.publicClients[m.cid].SubsLimitReached(); limitReached {
		log.Printf("subs limit is reached on cid: %d, spawning new conn\n", m.cid)
		m.addPublicClient()
	}

	m.subsRateLimit++
	return m
}

// Start creates initial clients for accepting connections
func (m *Mux) Start() *Mux {
	if m.hasAPIKeys() && m.privateClient == nil {
		m.addPrivateClient()
	}

	m.watchRateLimit()
	return m.addPublicClient()
}

// Listen accepts a callback func that will get called each time mux
// receives a message from any of its clients/subscriptions. It
// should be called last, after all setup calls are made
func (m *Mux) Listen(cb func(interface{}, error)) error {
	if m.Err != nil {
		return m.Err
	}

	m.online = true
	for {
		select {
		case ms, ok := <-m.publicChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("conn:%d has failed | err:%s | reconnecting", ms.CID, ms.Err))
				m.resetPublicClient(ms.CID)
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.recordEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				raw, pld, chID, _, err := ms.PreprocessRaw()
				if err != nil {
					cb(nil, err)
					continue
				}

				inf, ok := m.subInfo[chID]
				if !ok {
					cb(nil, fmt.Errorf("unrecognized chanId:%d", chID))
					continue
				}
				cb(ms.ProcessPublic(raw, pld, chID, inf))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case ms, ok := <-m.privateChan:
			if !ok {
				return errors.New("channel has closed unexpectedly")
			}
			if ms.Err != nil {
				cb(nil, fmt.Errorf("err: %s | reconnecting", ms.Err))
				m.resetPrivateClient()
				continue
			}
			// return raw payload data if transform is off
			if !m.transform {
				cb(ms.Data, nil)
				continue
			}
			// handle event type message
			if ms.IsEvent() {
				cb(m.recordEvent(ms.ProcessEvent()))
				continue
			}
			// handle data type message
			if ms.IsRaw() {
				raw, pld, chID, msgType, err := ms.PreprocessRaw()
				if err != nil {
					cb(nil, err)
					continue
				}
				cb(ms.ProcessPrivate(raw, pld, chID, msgType))
				continue
			}
			cb(nil, fmt.Errorf("unrecognized msg signature: %s", ms.Data))
		case <-m.closeChan:
			m.mtx.Lock()
			defer m.mtx.Unlock()

			for _, v := range m.publicClients {
				if v == nil {
					continue
				}
				if err := v.Close(); err != nil {
					log.Printf("failed closing public client: %s\n", err)
				}
			}

			if m.privateClient != nil {
				if err := m.privateClient.Close(); err != nil {
					log.Printf("failed closing private client: %s\n", err)
				}
			}

			m.online = false
			return nil
		}
	}
}

// Send meant for authenticated input, takes payload in form of interface
// and calls client with it
func (m *Mux) Send(pld interface{}) error {
	if !m.authenticated || m.privateClient == nil {
		return errors.New("not authorized")
	}
	return m.privateClient.Send(pld)
}

func (m *Mux) hasAPIKeys() bool {
	return len(m.apikey) != 0 && len(m.apisec) != 0
}

func (m *Mux) recordEvent(i event.Info, err error) (event.Info, error) {
	switch i.Event {
	case "subscribed":
		m.subInfo[i.ChanID] = i
	case "auth":
		if i.Status == "OK" {
			m.subInfo[i.ChanID] = i
			m.authenticated = true
		}
	}
	// add more cases if/when needed
	return i, err
}

func (m *Mux) resetPublicClient(cid int) {
	// pull old client subscriptions
	subs := m.publicClients[cid].GetAllSubs()
	// add fresh client
	m.addPublicClient()
	// resubscribe old events
	for _, sub := range subs {
		log.Printf("resubscribing: %+v\n", sub)
		m.Subscribe(sub)
	}
	// remove old, closed channel from the list
	delete(m.publicClients, cid)
}

func (m *Mux) resetPrivateClient() {
	m.authenticated = false
	m.privateClient = nil
	m.addPrivateClient()
}

func (m *Mux) addPublicClient() *Mux {
	// adding new client so making sure we increment cid
	m.cid++
	// create new public client and pass error to mux if any
	c, err := client.
		New().
		WithID(m.cid).
		WithSubsLimit(30).
		Public(m.publicURL)
	if err != nil {
		m.Err = err
		return m
	}
	// add new client to list for later reference
	m.publicClients[m.cid] = c
	// start listening for incoming client messages
	go c.Read(m.publicChan)
	return m
}

func (m *Mux) addPrivateClient() *Mux {
	// create new private client and pass error to mux if any
	c, err := client.New().Private(m.apikey, m.apisec, m.authURL, m.dms)
	if err != nil {
		m.Err = err
		return m
	}

	m.privateClient = c
	go c.Read(m.privateChan)
	return m
}

// watchRateLimit will run once every rateLimitDuration
// and free up the queue
func (m *Mux) watchRateLimit() {
	go func() {
		for {
			if m.subsRateLimit > 0 {
				m.subsRateLimit--
			}

			time.Sleep(rateLimitDuration)
		}
	}()
}
<|endoftext|>