<commit_before>package hamt64

import (
	"fmt"
	"log"
	"strings"
)

// sparseTableInitCap constant sets the default capacity of a new
// sparseTable.
const sparseTableInitCap int = 16

type sparseTable struct {
	hashPath HashVal
	depth    uint
	nodeMap  Bitmap
	nodes    []nodeI
}

func (t *sparseTable) copy() tableI {
	var nt = new(sparseTable)
	nt.hashPath = t.hashPath
	nt.depth = t.depth
	nt.nodeMap = t.nodeMap
	nt.nodes = append(nt.nodes, t.nodes...)
	return nt
}

func (t *sparseTable) deepCopy() tableI {
	var nt = new(sparseTable)
	nt.hashPath = t.hashPath
	nt.depth = t.depth
	nt.nodeMap = t.nodeMap
	nt.nodes = make([]nodeI, len(t.nodes), cap(t.nodes))
	for i := 0; i < len(t.nodes); i++ {
		if table, isTable := t.nodes[i].(tableI); isTable {
			nt.nodes[i] = table.deepCopy()
		} else {
			//leafs are functional, so no need to copy
			//nils can be copied just fine; duh!
			nt.nodes[i] = t.nodes[i]
		}
	}
	return nt
}

func createRootSparseTable(lf leafI) tableI {
	var idx = lf.Hash().Index(0)

	var ct = new(sparseTable)
	//ct.hashPath = 0
	//ct.depth = 0
	//ct.nodeMap = uint64(1 << idx)
	ct.nodeMap.Set(idx)
	ct.nodes = make([]nodeI, 1, sparseTableInitCap)
	ct.nodes[0] = lf

	return ct
}

func createSparseTable(depth uint, leaf1 leafI, leaf2 *flatLeaf) tableI {
	if depth < 1 {
		log.Panic("createSparseTable(): depth < 1")
	}
	var hp1 = leaf1.Hash().HashPath(depth)
	var hp2 = leaf2.Hash().HashPath(depth)
	if hp1 != hp2 {
		log.Panic("createSparseTable(): hp1,%s != hp2,%s",
			hp1.HashPathString(depth), hp2.HashPathString(depth))
	}
	//for d := uint(0); d < depth; d++ {
	//	if leaf1.Hash().Index(d) != leaf2.Hash().Index(d) {
	//		log.Panicf("createSparseTable(): leaf1.Hash().Index(%d) != leaf2.Hash().Index(%d)", d, d)
	//	}
	//}

	var retTable = new(sparseTable)
	retTable.hashPath = leaf1.Hash().HashPath(depth)
	retTable.depth = depth
	//retTable.nodeMap = 0
	retTable.nodes = make([]nodeI, 0, sparseTableInitCap)

	var idx1 = leaf1.Hash().Index(depth)
	var idx2 = leaf2.Hash().Index(depth)
	if idx1 != idx2 {
		retTable.insert(idx1, leaf1)
		retTable.insert(idx2, leaf2)
	} else { //idx1 == idx2
		var node nodeI
		if depth == MaxDepth {
			node = newCollisionLeaf(append(leaf1.keyVals(), leaf2.keyVals()...))
		} else {
			node = createSparseTable(depth+1, leaf1, leaf2)
		}
		retTable.insert(idx1, node)
	}

	return retTable
}

// downgradeToSparseTable() converts fixedTable structs that have less than
// or equal to downgradeThreshold tableEntry's. One important thing we know is
// that none of the entries will collide with another.
//
// The ents []tableEntry slice is guaranteed to be in order from lowest idx to
// highest. tableI.entries() also adhears to this contract.
func downgradeToSparseTable(
	hashPath HashVal,
	depth uint,
	ents []tableEntry,
) *sparseTable {
	var nt = new(sparseTable)
	nt.hashPath = hashPath
	//nt.nodeMap = 0
	nt.nodes = make([]nodeI, len(ents), len(ents)+1)

	for i := 0; i < len(ents); i++ {
		var ent = ents[i]
		nt.nodeMap.Set(ent.idx)
		nt.nodes[i] = ent.node
	}

	return nt
}

// Hash returns an incomplete Hash of this table. Any levels past it's current
// depth should be zero.
func (t *sparseTable) Hash() HashVal {
	return t.hashPath
}

// String return a string representation of this table including the hashPath,
// depth, and number of entries.
func (t *sparseTable) String() string {
	return fmt.Sprintf("sparseTable{hashPath:%s, depth=%d, nentries()=%d}",
		t.hashPath, t.depth, t.nentries())
}

// LongString returns a string representation of this table and all the tables
// contained herein recursively.
func (t *sparseTable) LongString(indent string, depth uint) string {
	var strs = make([]string, 3+len(t.nodes))

	strs[0] = indent +
		fmt.Sprintf("sparseTable{hashPath=%s, depth=%d, nentries()=%d,",
			t.hashPath.HashPathString(depth), t.depth, t.nentries())

	strs[1] = indent + "\tnodeMap=" + t.nodeMap.String() + ","

	for i, n := range t.nodes {
		var idx = n.Hash().Index(depth)
		if t, isTable := n.(tableI); isTable {
			strs[2+i] = indent +
				fmt.Sprintf("\tt.nodes[%d]:\n%s",
					idx, t.LongString(indent+"\t", depth+1))
		} else {
			strs[2+i] = indent + fmt.Sprintf("\tt.nodes[%d]: %s", idx, n)
		}
	}

	strs[len(strs)-1] = indent + "}"

	return strings.Join(strs, "\n")
}

func (t *sparseTable) nentries() uint {
	return uint(len(t.nodes))
	//return t.nodeMap.Count(IndexLimit)
}

func (t *sparseTable) entries() []tableEntry {
	var n = t.nentries()
	var ents = make([]tableEntry, n)

	for idx, j := uint(0), uint(0); idx < IndexLimit; idx++ {
		if t.nodeMap.IsSet(idx) {
			ents[j] = tableEntry{idx, t.nodes[j]}
			j++
		}
	}

	return ents
}

func (t *sparseTable) get(idx uint) nodeI {
	if !t.nodeMap.IsSet(idx) {
		return nil
	}

	var j = t.nodeMap.Count(idx)

	return t.nodes[j]
}

func (t *sparseTable) insert(idx uint, n nodeI) {
	// THIS SHOULD BE A DEV ASSERT
	if t.nodeMap.IsSet(idx) {
		panic("t.insert(idx, n) where idx slot is NOT empty; this should be a replace")
	}

	var j = t.nodeMap.Count(idx)
	if j == uint(len(t.nodes)) {
		t.nodes = append(t.nodes, n)
	} else {
		t.nodes = append(t.nodes[:j], append([]nodeI{n}, t.nodes[j:]...)...)
	}
	t.nodeMap.Set(idx)
}

func (t *sparseTable) replace(idx uint, n nodeI) {
	// THIS SHOULD BE A DEV ASSERT
	if !t.nodeMap.IsSet(idx) {
		panic("t.replace(idx, n) where idx slot is empty; this should be an insert")
	}

	var j = t.nodeMap.Count(idx)
	t.nodes[j] = n
}

func (t *sparseTable) remove(idx uint) {
	// THIS SHOULD BE A DEV ASSERT
	if !t.nodeMap.IsSet(idx) {
		panic("t.remove(idx) where idx slot is already empty")
	}

	var j = t.nodeMap.Count(idx)
	if int(j) == len(t.nodes)-1 {
		t.nodes = t.nodes[:j]
	} else {
		t.nodes = append(t.nodes[:j], t.nodes[j+1:]...)
	}

	t.nodeMap.Unset(idx)
}
<commit_msg>rearanged sparseTale struct<commit_after>package hamt64

import (
	"fmt"
	"log"
	"strings"
)

// sparseTableInitCap constant sets the default capacity of a new
// sparseTable.
const sparseTableInitCap int = 16

// New sparseTable layout size == 48
type sparseTable struct {
	nodes    []nodeI // 24
	depth    uint    // 8; amd64 cpu
	nodeMap  Bitmap  // 8; uint32 x 2
	hashPath HashVal // 8
}

func (t *sparseTable) copy() tableI {
	var nt = new(sparseTable)
	nt.hashPath = t.hashPath
	nt.depth = t.depth
	nt.nodeMap = t.nodeMap
	nt.nodes = append(nt.nodes, t.nodes...)
	return nt
}

func (t *sparseTable) deepCopy() tableI {
	var nt = new(sparseTable)
	nt.hashPath = t.hashPath
	nt.depth = t.depth
	nt.nodeMap = t.nodeMap
	nt.nodes = make([]nodeI, len(t.nodes), cap(t.nodes))
	for i := 0; i < len(t.nodes); i++ {
		if table, isTable := t.nodes[i].(tableI); isTable {
			nt.nodes[i] = table.deepCopy()
		} else {
			//leafs are functional, so no need to copy
			//nils can be copied just fine; duh!
			nt.nodes[i] = t.nodes[i]
		}
	}
	return nt
}

func createRootSparseTable(lf leafI) tableI {
	var idx = lf.Hash().Index(0)

	var ct = new(sparseTable)
	//ct.hashPath = 0
	//ct.depth = 0
	//ct.nodeMap = uint64(1 << idx)
	ct.nodeMap.Set(idx)
	ct.nodes = make([]nodeI, 1, sparseTableInitCap)
	ct.nodes[0] = lf

	return ct
}

func createSparseTable(depth uint, leaf1 leafI, leaf2 *flatLeaf) tableI {
	if depth < 1 {
		log.Panic("createSparseTable(): depth < 1")
	}
	var hp1 = leaf1.Hash().HashPath(depth)
	var hp2 = leaf2.Hash().HashPath(depth)
	if hp1 != hp2 {
		log.Panic("createSparseTable(): hp1,%s != hp2,%s",
			hp1.HashPathString(depth), hp2.HashPathString(depth))
	}
	//for d := uint(0); d < depth; d++ {
	//	if leaf1.Hash().Index(d) != leaf2.Hash().Index(d) {
	//		log.Panicf("createSparseTable(): leaf1.Hash().Index(%d) != leaf2.Hash().Index(%d)", d, d)
	//	}
	//}

	var retTable = new(sparseTable)
	retTable.hashPath = leaf1.Hash().HashPath(depth)
	retTable.depth = depth
	//retTable.nodeMap = 0
	retTable.nodes = make([]nodeI, 0, sparseTableInitCap)

	var idx1 = leaf1.Hash().Index(depth)
	var idx2 = leaf2.Hash().Index(depth)
	if idx1 != idx2 {
		retTable.insert(idx1, leaf1)
		retTable.insert(idx2, leaf2)
	} else { //idx1 == idx2
		var node nodeI
		if depth == MaxDepth {
			node = newCollisionLeaf(append(leaf1.keyVals(), leaf2.keyVals()...))
		} else {
			node = createSparseTable(depth+1, leaf1, leaf2)
		}
		retTable.insert(idx1, node)
	}

	return retTable
}

// downgradeToSparseTable() converts fixedTable structs that have less than
// or equal to downgradeThreshold tableEntry's. One important thing we know is
// that none of the entries will collide with another.
//
// The ents []tableEntry slice is guaranteed to be in order from lowest idx to
// highest. tableI.entries() also adhears to this contract.
func downgradeToSparseTable(
	hashPath HashVal,
	depth uint,
	ents []tableEntry,
) *sparseTable {
	var nt = new(sparseTable)
	nt.hashPath = hashPath
	//nt.nodeMap = 0
	nt.nodes = make([]nodeI, len(ents), len(ents)+1)

	for i := 0; i < len(ents); i++ {
		var ent = ents[i]
		nt.nodeMap.Set(ent.idx)
		nt.nodes[i] = ent.node
	}

	return nt
}

// Hash returns an incomplete Hash of this table. Any levels past it's current
// depth should be zero.
func (t *sparseTable) Hash() HashVal {
	return t.hashPath
}

// String return a string representation of this table including the hashPath,
// depth, and number of entries.
func (t *sparseTable) String() string {
	return fmt.Sprintf("sparseTable{hashPath:%s, depth=%d, nentries()=%d}",
		t.hashPath, t.depth, t.nentries())
}

// LongString returns a string representation of this table and all the tables
// contained herein recursively.
func (t *sparseTable) LongString(indent string, depth uint) string {
	var strs = make([]string, 3+len(t.nodes))

	strs[0] = indent +
		fmt.Sprintf("sparseTable{hashPath=%s, depth=%d, nentries()=%d,",
			t.hashPath.HashPathString(depth), t.depth, t.nentries())

	strs[1] = indent + "\tnodeMap=" + t.nodeMap.String() + ","

	for i, n := range t.nodes {
		var idx = n.Hash().Index(depth)
		if t, isTable := n.(tableI); isTable {
			strs[2+i] = indent +
				fmt.Sprintf("\tt.nodes[%d]:\n%s",
					idx, t.LongString(indent+"\t", depth+1))
		} else {
			strs[2+i] = indent + fmt.Sprintf("\tt.nodes[%d]: %s", idx, n)
		}
	}

	strs[len(strs)-1] = indent + "}"

	return strings.Join(strs, "\n")
}

func (t *sparseTable) nentries() uint {
	return uint(len(t.nodes))
	//return t.nodeMap.Count(IndexLimit)
}

func (t *sparseTable) entries() []tableEntry {
	var n = t.nentries()
	var ents = make([]tableEntry, n)

	for idx, j := uint(0), uint(0); idx < IndexLimit; idx++ {
		if t.nodeMap.IsSet(idx) {
			ents[j] = tableEntry{idx, t.nodes[j]}
			j++
		}
	}

	return ents
}

func (t *sparseTable) get(idx uint) nodeI {
	if !t.nodeMap.IsSet(idx) {
		return nil
	}

	var j = t.nodeMap.Count(idx)

	return t.nodes[j]
}

func (t *sparseTable) insert(idx uint, n nodeI) {
	// THIS SHOULD BE A DEV ASSERT
	if t.nodeMap.IsSet(idx) {
		panic("t.insert(idx, n) where idx slot is NOT empty; this should be a replace")
	}

	var j = t.nodeMap.Count(idx)
	if j == uint(len(t.nodes)) {
		t.nodes = append(t.nodes, n)
	} else {
		t.nodes = append(t.nodes[:j], append([]nodeI{n}, t.nodes[j:]...)...)
	}
	t.nodeMap.Set(idx)
}

func (t *sparseTable) replace(idx uint, n nodeI) {
	// THIS SHOULD BE A DEV ASSERT
	if !t.nodeMap.IsSet(idx) {
		panic("t.replace(idx, n) where idx slot is empty; this should be an insert")
	}

	var j = t.nodeMap.Count(idx)
	t.nodes[j] = n
}

func (t *sparseTable) remove(idx uint) {
	// THIS SHOULD BE A DEV ASSERT
	if !t.nodeMap.IsSet(idx) {
		panic("t.remove(idx) where idx slot is already empty")
	}

	var j = t.nodeMap.Count(idx)
	if int(j) == len(t.nodes)-1 {
		t.nodes = t.nodes[:j]
	} else {
		t.nodes = append(t.nodes[:j], t.nodes[j+1:]...)
	}

	t.nodeMap.Unset(idx)
}
<|endoftext|>


<commit_before>package dora

import (
	"github.com/bnagy/gapstone"
	"github.com/fatih/color"
	w "github.com/williballenthin/Lancelot/workspace"
	"log"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}
func isBBEnd(insn gapstone.Instruction) bool {
	return w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) ||
		w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) ||
		w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) ||
		w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_IRET)
}

func GetNextInstructionPointer(emu *w.Emulator, sman *w.SnapshotManager) (w.VA, error) {
	var va w.VA
	e := sman.WithTempExcursion(func() error {
		e := emu.StepInto()
		if e != nil {
			return e
		}
		va = emu.GetInstructionPointer()
		return nil
	})
	return va, e
}
func IsConditionalJump(insn gapstone.Instruction) bool {
	if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && insn.Mnemonic != "jmp" {
		return true
	}
	if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && insn.Mnemonic == "jmp" {
		if insn.Mnemonic == "jmp" && insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
			// jmp 0x1000
			return false
		} else {
			// jmp eax
			return true
		}
	}
	return false
}

func GetJumpTargets(emu *w.Emulator, insn gapstone.Instruction) ([]w.VA, error) {
	var ret []w.VA

	if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && insn.Mnemonic == "jmp" {
		if insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
			// not a conditional jump???
			return ret, w.InvalidArgumentError
		}
		// jmp eax
		// don't know how to handle this case right now
		return ret, nil
	}

	// assume a two case situation
	falsePc := w.VA(uint64(emu.GetInstructionPointer()) + uint64(insn.Size))
	truePc := w.VA(insn.X86.Operands[0].Imm) // or .Mem???

	if truePc == 0 {
		// TODO
		panic("zero jump")
	}

	ret = append(ret, truePc, falsePc)
	return ret, nil
}

// dora the explora
type Dora struct {
	ws *w.Workspace
	ac ArtifactCollection
}

func New(ws *w.Workspace) (*Dora, error) {
	// TODO: get this from a real place
	ac, e := NewLoggingArtifactCollection()
	check(e)

	return &Dora{
		ws: ws,
		ac: ac,
	}, nil
}

type todoPath struct {
	state w.SnapshotManagerCookie
	va    w.VA
}

type FunctionExplorer struct {
	emu     *w.Emulator
	sman    *w.SnapshotManager
	todo    []todoPath
	hits    map[w.VA]bool
	startSp w.VA
	ac      ArtifactCollection
}

func (s *FunctionExplorer) pushState(va w.VA) error {
	here, e := s.sman.Push()
	check(e)
	s.todo = append(s.todo, todoPath{state: here, va: va})
	return nil
}

func (s *FunctionExplorer) popState() error {
	if len(s.todo) == 0 {
		// TODO: handle a real error
		panic("no paths to explore")
	}
	path := s.todo[len(s.todo)-1]
	s.todo = s.todo[1:]
	log.Printf("exploring path %s: va=0x%x", path.state, path.va)
	check(s.sman.RevertUntil(path.state))
	s.emu.SetInstructionPointer(path.va)
	return nil
}

func (s *FunctionExplorer) hasExploredThisAddressBefore() bool {
	_, ok := s.hits[s.emu.GetInstructionPointer()]
	return ok
}

func (s *FunctionExplorer) markThisAddressExplored() error {
	s.hits[s.emu.GetInstructionPointer()] = true
	return nil
}

func (s *FunctionExplorer) tracePc() error {
	str, _, e := s.emu.FormatAddress(s.emu.GetInstructionPointer())
	check(e)
	color.Set(color.FgHiBlack)
	log.Printf("ip:" + str)
	color.Unset()
	return nil
}

func (s *FunctionExplorer) handleRet(insn gapstone.Instruction) error {
	log.Printf("returning, done.")
	afterSp := s.emu.GetStackPointer()
	stackDelta := uint64(afterSp) - uint64(s.startSp)
	log.Printf("stack delta: 0x%x", stackDelta)
	return nil
}

func (s *FunctionExplorer) handleConditionalJump(insn gapstone.Instruction) error {
	targets, e := GetJumpTargets(s.emu, insn)
	check(e)
	if len(targets) < 2 {
		// TODO: by definition, a conditional jump should have at least two cases...
		panic("len(targets) < 2")
	}

	nextPc := targets[0]
	for _, target := range targets[1:] {
		log.Printf("other target: 0x%x", target)
		check(s.pushState(target))
	}
	s.emu.SetInstructionPointer(nextPc)
	return nil
}

func (s *FunctionExplorer) handleCall(insn gapstone.Instruction) error {
	// TODO: try to resolve imports before blindly emulating
	callPc, e := GetNextInstructionPointer(s.emu, s.sman)
	if e == nil {
		log.Printf("  call target: 0x%x", callPc)
	}
	s.ac.AddCallXref(CallCrossReference{s.emu.GetInstructionPointer(), callPc})

	nextPc := w.VA(uint64(s.emu.GetInstructionPointer()) + uint64(insn.Size))
	s.emu.SetInstructionPointer(nextPc)

	// TODO: need to detect calling convention, and in the case of stdcall,
	//   cleanup the stack

	return nil
}

func (s *FunctionExplorer) handleGeneralInstruction(insn gapstone.Instruction) error {
	return s.emu.StepOver()
}

// things yet to discover:
//   OK: final stack delta
//   TODO: arguments passed in registers
//     insn.cs_detail.regs_read/regs_write
//   TODO: arguments passed on stack
//   OK: all basic blocks
//   TODO: calling convention
//   TODO: no return functions
// TODO: ensure stack is set up with return pointer and some junk symbolic args
// TODO: track max hits
// this is going to be a pretty wild function :-(
func (dora *Dora) ExploreFunction(va w.VA) error {
	emu, e := dora.ws.GetEmulator()
	check(e)
	defer emu.Close()

	sman, e := w.NewSnapshotManager(emu)
	check(e)
	defer sman.Close()

	ex := &FunctionExplorer{
		emu:     emu,
		sman:    sman,
		todo:    []todoPath{},
		hits:    make(map[w.VA]bool),
		startSp: emu.GetStackPointer(),
		ac:      dora.ac,
	}

	bbStart := va
	emu.SetInstructionPointer(va)
	check(e)

	// TODO: how to disable these while on an excursion?
	rh, e := emu.HookMemRead(func(access int, addr w.VA, size int, value int64) {
		dora.ac.AddMemoryReadXref(MemoryReadCrossReference{emu.GetInstructionPointer(), addr})
	})
	check(e)
	defer rh.Close()

	wh, e := emu.HookMemWrite(func(access int, addr w.VA, size int, value int64) {
		dora.ac.AddMemoryWriteXref(MemoryWriteCrossReference{emu.GetInstructionPointer(), addr})
	})
	check(e)
	defer wh.Close()

	check(ex.pushState(va))

	// TODO: don't reach
	for len(ex.todo) > 0 {
		check(ex.popState())
		for {
			if ex.hasExploredThisAddressBefore() {
				log.Print("bad va")
				break
			}

			ex.tracePc()

			insn, e := ex.emu.GetCurrentInstruction()
			check(e)

			check(ex.markThisAddressExplored())

			if isBBEnd(insn) {
				e := dora.ac.AddBasicBlock(BasicBlock{Start: bbStart, End: emu.GetInstructionPointer()})
				check(e)
			}

			beforePc := ex.emu.GetInstructionPointer()
			if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) ||
				w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_IRET) {
				check(ex.handleRet(insn))
				break
			} else if IsConditionalJump(insn) {
				check(ex.handleConditionalJump(insn))
			} else if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) {
				check(ex.handleCall(insn))
			} else {
				e := ex.handleGeneralInstruction(insn)
				if e != nil {
					log.Printf("error: %s", e.Error())
					break
				}
				log.Print("ok")
			}

			// TODO: fetch stack read/write set for arg/variable detection
			// TODO: fetch reg read/write set for arg detection

			afterPc := emu.GetInstructionPointer()
			if isBBEnd(insn) {
				bbStart = emu.GetInstructionPointer()
				e := dora.ac.AddJumpXref(JumpCrossReference{beforePc, afterPc})
				check(e)
			}
		}
	}

	return nil
}
<commit_msg>dora: remove print statements<commit_after>package dora

import (
	"github.com/bnagy/gapstone"
	"github.com/fatih/color"
	w "github.com/williballenthin/Lancelot/workspace"
	"log"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}
func isBBEnd(insn gapstone.Instruction) bool {
	return w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) ||
		w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) ||
		w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) ||
		w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_IRET)
}

func GetNextInstructionPointer(emu *w.Emulator, sman *w.SnapshotManager) (w.VA, error) {
	var va w.VA
	e := sman.WithTempExcursion(func() error {
		e := emu.StepInto()
		if e != nil {
			return e
		}
		va = emu.GetInstructionPointer()
		return nil
	})
	return va, e
}
func IsConditionalJump(insn gapstone.Instruction) bool {
	if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && insn.Mnemonic != "jmp" {
		return true
	}
	if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && insn.Mnemonic == "jmp" {
		if insn.Mnemonic == "jmp" && insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
			// jmp 0x1000
			return false
		} else {
			// jmp eax
			return true
		}
	}
	return false
}

func GetJumpTargets(emu *w.Emulator, insn gapstone.Instruction) ([]w.VA, error) {
	var ret []w.VA

	if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && insn.Mnemonic == "jmp" {
		if insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
			// not a conditional jump???
			return ret, w.InvalidArgumentError
		}
		// jmp eax
		// don't know how to handle this case right now
		return ret, nil
	}

	// assume a two case situation
	falsePc := w.VA(uint64(emu.GetInstructionPointer()) + uint64(insn.Size))
	truePc := w.VA(insn.X86.Operands[0].Imm) // or .Mem???

	if truePc == 0 {
		// TODO
		panic("zero jump")
	}

	ret = append(ret, truePc, falsePc)
	return ret, nil
}

// dora the explora
type Dora struct {
	ws *w.Workspace
	ac ArtifactCollection
}

func New(ws *w.Workspace) (*Dora, error) {
	// TODO: get this from a real place
	ac, e := NewLoggingArtifactCollection()
	check(e)

	return &Dora{
		ws: ws,
		ac: ac,
	}, nil
}

type todoPath struct {
	state w.SnapshotManagerCookie
	va    w.VA
}

type FunctionExplorer struct {
	emu     *w.Emulator
	sman    *w.SnapshotManager
	todo    []todoPath
	hits    map[w.VA]bool
	startSp w.VA
	ac      ArtifactCollection
}

func (s *FunctionExplorer) pushState(va w.VA) error {
	here, e := s.sman.Push()
	check(e)
	s.todo = append(s.todo, todoPath{state: here, va: va})
	return nil
}

func (s *FunctionExplorer) popState() error {
	if len(s.todo) == 0 {
		// TODO: handle a real error
		panic("no paths to explore")
	}
	path := s.todo[len(s.todo)-1]
	s.todo = s.todo[1:]
	log.Printf("exploring path %s: va=0x%x", path.state, path.va)
	check(s.sman.RevertUntil(path.state))
	s.emu.SetInstructionPointer(path.va)
	return nil
}

func (s *FunctionExplorer) hasExploredThisAddressBefore() bool {
	_, ok := s.hits[s.emu.GetInstructionPointer()]
	return ok
}

func (s *FunctionExplorer) markThisAddressExplored() error {
	s.hits[s.emu.GetInstructionPointer()] = true
	return nil
}

func (s *FunctionExplorer) tracePc() error {
	str, _, e := s.emu.FormatAddress(s.emu.GetInstructionPointer())
	check(e)
	color.Set(color.FgHiBlack)
	log.Printf("ip:" + str)
	color.Unset()
	return nil
}

func (s *FunctionExplorer) handleRet(insn gapstone.Instruction) error {
	log.Printf("returning, done.")
	afterSp := s.emu.GetStackPointer()
	stackDelta := uint64(afterSp) - uint64(s.startSp)
	log.Printf("stack delta: 0x%x", stackDelta)
	return nil
}

func (s *FunctionExplorer) handleConditionalJump(insn gapstone.Instruction) error {
	targets, e := GetJumpTargets(s.emu, insn)
	check(e)
	if len(targets) < 2 {
		// TODO: by definition, a conditional jump should have at least two cases...
		panic("len(targets) < 2")
	}

	nextPc := targets[0]
	for _, target := range targets[1:] {
		log.Printf("other target: 0x%x", target)
		check(s.pushState(target))
	}
	s.emu.SetInstructionPointer(nextPc)
	return nil
}

func (s *FunctionExplorer) handleCall(insn gapstone.Instruction) error {
	// TODO: try to resolve imports before blindly emulating
	callPc, e := GetNextInstructionPointer(s.emu, s.sman)
	if e == nil {
		log.Printf("  call target: 0x%x", callPc)
	}
	s.ac.AddCallXref(CallCrossReference{s.emu.GetInstructionPointer(), callPc})

	nextPc := w.VA(uint64(s.emu.GetInstructionPointer()) + uint64(insn.Size))
	s.emu.SetInstructionPointer(nextPc)

	// TODO: need to detect calling convention, and in the case of stdcall,
	//   cleanup the stack

	return nil
}

func (s *FunctionExplorer) handleGeneralInstruction(insn gapstone.Instruction) error {
	return s.emu.StepOver()
}

// things yet to discover:
//   OK: final stack delta
//   TODO: arguments passed in registers
//     insn.cs_detail.regs_read/regs_write
//   TODO: arguments passed on stack
//   OK: all basic blocks
//   TODO: calling convention
//   TODO: no return functions
// TODO: ensure stack is set up with return pointer and some junk symbolic args
// TODO: track max hits
// this is going to be a pretty wild function :-(
func (dora *Dora) ExploreFunction(va w.VA) error {
	emu, e := dora.ws.GetEmulator()
	check(e)
	defer emu.Close()

	sman, e := w.NewSnapshotManager(emu)
	check(e)
	defer sman.Close()

	ex := &FunctionExplorer{
		emu:     emu,
		sman:    sman,
		todo:    []todoPath{},
		hits:    make(map[w.VA]bool),
		startSp: emu.GetStackPointer(),
		ac:      dora.ac,
	}

	bbStart := va
	emu.SetInstructionPointer(va)
	check(e)

	// TODO: how to disable these while on an excursion?
	rh, e := emu.HookMemRead(func(access int, addr w.VA, size int, value int64) {
		// TODO: filter out stack references
		dora.ac.AddMemoryReadXref(MemoryReadCrossReference{emu.GetInstructionPointer(), addr})
	})
	check(e)
	defer rh.Close()

	wh, e := emu.HookMemWrite(func(access int, addr w.VA, size int, value int64) {
		// TODO: filter out stack references
		dora.ac.AddMemoryWriteXref(MemoryWriteCrossReference{emu.GetInstructionPointer(), addr})
	})
	check(e)
	defer wh.Close()

	check(ex.pushState(va))

	// TODO: don't reach
	for len(ex.todo) > 0 {
		check(ex.popState())
		for {
			if ex.hasExploredThisAddressBefore() {
				break
			}

			ex.tracePc()

			insn, e := ex.emu.GetCurrentInstruction()
			check(e)

			check(ex.markThisAddressExplored())

			if isBBEnd(insn) {
				e := dora.ac.AddBasicBlock(BasicBlock{Start: bbStart, End: emu.GetInstructionPointer()})
				check(e)
			}

			beforePc := ex.emu.GetInstructionPointer()
			if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) ||
				w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_IRET) {
				check(ex.handleRet(insn))
				break
			} else if IsConditionalJump(insn) {
				check(ex.handleConditionalJump(insn))
			} else if w.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) {
				check(ex.handleCall(insn))
			} else {
				e := ex.handleGeneralInstruction(insn)
				if e != nil {
					log.Printf("error: %s", e.Error())
					break
				}
			}

			// TODO: fetch stack read/write set for arg/variable detection
			// TODO: fetch reg read/write set for arg detection

			afterPc := emu.GetInstructionPointer()
			if isBBEnd(insn) {
				bbStart = emu.GetInstructionPointer()
				e := dora.ac.AddJumpXref(JumpCrossReference{beforePc, afterPc})
				check(e)
			}
		}
	}

	return nil
}
<|endoftext|>


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

package opengl

import (
	"fmt"
	"sync"

	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
)

type blendFactor int

const (
	glDstAlpha         blendFactor = 0x0304
	glDstColor         blendFactor = 0x0306
	glOne              blendFactor = 1
	glOneMinusDstAlpha blendFactor = 0x0305
	glOneMinusSrcAlpha blendFactor = 0x0303
	glSrcAlpha         blendFactor = 0x0302
	glZero             blendFactor = 0
)

type blendOperation int

const (
	glAdd blendOperation = 0x104
)

func convertBlendFactor(f graphicsdriver.BlendFactor) blendFactor {
	switch f {
	case graphicsdriver.BlendFactorZero:
		return glZero
	case graphicsdriver.BlendFactorOne:
		return glOne
	case graphicsdriver.BlendFactorSourceAlpha:
		return glSrcAlpha
	case graphicsdriver.BlendFactorDestinationAlpha:
		return glDstAlpha
	case graphicsdriver.BlendFactorOneMinusSourceAlpha:
		return glOneMinusSrcAlpha
	case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
		return glOneMinusDstAlpha
	case graphicsdriver.BlendFactorDestinationColor:
		return glDstColor
	default:
		panic(fmt.Sprintf("opengl: invalid blend factor %d", f))
	}
}

func convertBlendOperation(o graphicsdriver.BlendOperation) blendOperation {
	switch o {
	case graphicsdriver.BlendOperationAdd:
		return glAdd
	default:
		panic(fmt.Sprintf("opengl: invalid blend operation %d", o))
	}
}

type context struct {
	locationCache      *locationCache
	screenFramebuffer  framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
	lastFramebuffer    framebufferNative
	lastTexture        textureNative
	lastRenderbuffer   renderbufferNative
	lastViewportWidth  int
	lastViewportHeight int
	lastBlend          graphicsdriver.Blend
	maxTextureSize     int
	maxTextureSizeOnce sync.Once
	highp              bool
	highpOnce          sync.Once

	contextImpl
}

func (c *context) bindTexture(t textureNative) {
	if c.lastTexture.equal(t) {
		return
	}
	c.bindTextureImpl(t)
	c.lastTexture = t
}

func (c *context) bindRenderbuffer(r renderbufferNative) {
	if c.lastRenderbuffer.equal(r) {
		return
	}
	c.bindRenderbufferImpl(r)
	c.lastRenderbuffer = r
}

func (c *context) bindFramebuffer(f framebufferNative) {
	if c.lastFramebuffer.equal(f) {
		return
	}
	c.bindFramebufferImpl(f)
	c.lastFramebuffer = f
}

func (c *context) setViewport(f *framebuffer) {
	c.bindFramebuffer(f.native)
	if c.lastViewportWidth != f.width || c.lastViewportHeight != f.height {
		// On some environments, viewport size must be within the framebuffer size.
		// e.g. Edge (#71), Chrome on GPD Pocket (#420), macOS Mojave (#691).
		// Use the same size of the framebuffer here.
		c.setViewportImpl(f.width, f.height)

		// glViewport must be called at least at every frame on iOS.
		// As the screen framebuffer is the last render target, next SetViewport should be
		// the first call at a frame.
		if f.native.equal(c.screenFramebuffer) {
			c.lastViewportWidth = 0
			c.lastViewportHeight = 0
		} else {
			c.lastViewportWidth = f.width
			c.lastViewportHeight = f.height
		}
	}
}

func (c *context) getScreenFramebuffer() framebufferNative {
	return c.screenFramebuffer
}

func (c *context) getMaxTextureSize() int {
	c.maxTextureSizeOnce.Do(func() {
		c.maxTextureSize = c.maxTextureSizeImpl()
	})
	return c.maxTextureSize
}
<commit_msg>internal/grphicsdriver/opengl: bug fix: wrong consntant was used for blend operations<commit_after>// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package opengl

import (
	"fmt"
	"sync"

	"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
)

type blendFactor int

const (
	glDstAlpha         blendFactor = 0x0304
	glDstColor         blendFactor = 0x0306
	glOne              blendFactor = 1
	glOneMinusDstAlpha blendFactor = 0x0305
	glOneMinusSrcAlpha blendFactor = 0x0303
	glSrcAlpha         blendFactor = 0x0302
	glZero             blendFactor = 0
)

type blendOperation int

const (
	glFuncAdd blendOperation = 0x8006
)

func convertBlendFactor(f graphicsdriver.BlendFactor) blendFactor {
	switch f {
	case graphicsdriver.BlendFactorZero:
		return glZero
	case graphicsdriver.BlendFactorOne:
		return glOne
	case graphicsdriver.BlendFactorSourceAlpha:
		return glSrcAlpha
	case graphicsdriver.BlendFactorDestinationAlpha:
		return glDstAlpha
	case graphicsdriver.BlendFactorOneMinusSourceAlpha:
		return glOneMinusSrcAlpha
	case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
		return glOneMinusDstAlpha
	case graphicsdriver.BlendFactorDestinationColor:
		return glDstColor
	default:
		panic(fmt.Sprintf("opengl: invalid blend factor %d", f))
	}
}

func convertBlendOperation(o graphicsdriver.BlendOperation) blendOperation {
	switch o {
	case graphicsdriver.BlendOperationAdd:
		return glFuncAdd
	default:
		panic(fmt.Sprintf("opengl: invalid blend operation %d", o))
	}
}

type context struct {
	locationCache      *locationCache
	screenFramebuffer  framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
	lastFramebuffer    framebufferNative
	lastTexture        textureNative
	lastRenderbuffer   renderbufferNative
	lastViewportWidth  int
	lastViewportHeight int
	lastBlend          graphicsdriver.Blend
	maxTextureSize     int
	maxTextureSizeOnce sync.Once
	highp              bool
	highpOnce          sync.Once

	contextImpl
}

func (c *context) bindTexture(t textureNative) {
	if c.lastTexture.equal(t) {
		return
	}
	c.bindTextureImpl(t)
	c.lastTexture = t
}

func (c *context) bindRenderbuffer(r renderbufferNative) {
	if c.lastRenderbuffer.equal(r) {
		return
	}
	c.bindRenderbufferImpl(r)
	c.lastRenderbuffer = r
}

func (c *context) bindFramebuffer(f framebufferNative) {
	if c.lastFramebuffer.equal(f) {
		return
	}
	c.bindFramebufferImpl(f)
	c.lastFramebuffer = f
}

func (c *context) setViewport(f *framebuffer) {
	c.bindFramebuffer(f.native)
	if c.lastViewportWidth != f.width || c.lastViewportHeight != f.height {
		// On some environments, viewport size must be within the framebuffer size.
		// e.g. Edge (#71), Chrome on GPD Pocket (#420), macOS Mojave (#691).
		// Use the same size of the framebuffer here.
		c.setViewportImpl(f.width, f.height)

		// glViewport must be called at least at every frame on iOS.
		// As the screen framebuffer is the last render target, next SetViewport should be
		// the first call at a frame.
		if f.native.equal(c.screenFramebuffer) {
			c.lastViewportWidth = 0
			c.lastViewportHeight = 0
		} else {
			c.lastViewportWidth = f.width
			c.lastViewportHeight = f.height
		}
	}
}

func (c *context) getScreenFramebuffer() framebufferNative {
	return c.screenFramebuffer
}

func (c *context) getMaxTextureSize() int {
	c.maxTextureSizeOnce.Do(func() {
		c.maxTextureSize = c.maxTextureSizeImpl()
	})
	return c.maxTextureSize
}
<|endoftext|>


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

package opengl

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/web"
)

// arrayBufferLayoutPart is a part of an array buffer layout.
type arrayBufferLayoutPart struct {
	// TODO: This struct should belong to a program and know it.
	name string
	num  int
}

// arrayBufferLayout is an array buffer layout.
//
// An array buffer in OpenGL is a buffer representing vertices and
// is passed to a vertex shader.
type arrayBufferLayout struct {
	parts []arrayBufferLayoutPart
	total int
}

func (a *arrayBufferLayout) names() []string {
	ns := make([]string, len(a.parts))
	for i, p := range a.parts {
		ns[i] = p.name
	}
	return ns
}

// totalBytes returns the size in bytes for one element of the array buffer.
func (a *arrayBufferLayout) totalBytes() int {
	if a.total != 0 {
		return a.total
	}
	t := 0
	for _, p := range a.parts {
		t += float.SizeInBytes() * p.num
	}
	a.total = t
	return a.total
}

// newArrayBuffer creates OpenGL's buffer object for the array buffer.
func (a *arrayBufferLayout) newArrayBuffer(context *context) buffer {
	return context.newArrayBuffer(a.totalBytes() * graphics.IndicesNum)
}

// enable binds the array buffer the given program to use the array buffer.
func (a *arrayBufferLayout) enable(context *context, program program) {
	for i := range a.parts {
		context.enableVertexAttribArray(program, i)
	}
	total := a.totalBytes()
	offset := 0
	for i, p := range a.parts {
		context.vertexAttribPointer(program, i, p.num, float, total, offset)
		offset += float.SizeInBytes() * p.num
	}
}

// disable stops using the array buffer.
func (a *arrayBufferLayout) disable(context *context, program program) {
	// TODO: Disabling should be done in reversed order?
	for i := range a.parts {
		context.disableVertexAttribArray(program, i)
	}
}

// theArrayBufferLayout is the array buffer layout for Ebiten.
var theArrayBufferLayout = arrayBufferLayout{
	// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
	parts: []arrayBufferLayoutPart{
		{
			name: "vertex",
			num:  2,
		},
		{
			name: "tex",
			num:  2,
		},
		{
			name: "tex_region",
			num:  4,
		},
		{
			name: "color_scale",
			num:  4,
		},
	},
}

func init() {
	vertexFloatNum := theArrayBufferLayout.totalBytes() / float.SizeInBytes()
	if graphics.VertexFloatNum != vertexFloatNum {
		panic(fmt.Sprintf("vertex float num must be %d but %d", graphics.VertexFloatNum, vertexFloatNum))
	}
}

type programKey struct {
	useColorM bool
	filter    graphics.Filter
	address   graphics.Address
}

// openGLState is a state for
type openGLState struct {
	// arrayBuffer is OpenGL's array buffer (vertices data).
	arrayBuffer buffer

	// elementArrayBuffer is OpenGL's element array buffer (indices data).
	elementArrayBuffer buffer

	// programs is OpenGL's program for rendering a texture.
	programs map[programKey]program

	lastProgram                program
	lastViewportWidth          int
	lastViewportHeight         int
	lastColorMatrix            []float32
	lastColorMatrixTranslation []float32
	lastSourceWidth            int
	lastSourceHeight           int
	lastFilter                 *graphics.Filter
	lastAddress                *graphics.Address

	source      *Image
	destination *Image
}

var (
	zeroBuffer  buffer
	zeroProgram program
)

const (
	maxTriangles = graphics.IndicesNum / 3
	maxQuads     = maxTriangles / 2
)

// reset resets or initializes the OpenGL state.
func (s *openGLState) reset(context *context) error {
	if err := context.reset(); err != nil {
		return err
	}

	s.lastProgram = zeroProgram
	s.lastViewportWidth = 0
	s.lastViewportHeight = 0
	s.lastColorMatrix = nil
	s.lastColorMatrixTranslation = nil
	s.lastSourceWidth = 0
	s.lastSourceHeight = 0
	s.lastFilter = nil
	s.lastAddress = nil

	// When context lost happens, deleting programs or buffers is not necessary.
	// However, it is not assumed that reset is called only when context lost happens.
	// Let's delete them explicitly.
	if s.programs == nil {
		s.programs = map[programKey]program{}
	} else {
		for k, p := range s.programs {
			context.deleteProgram(p)
			delete(s.programs, k)
		}
	}

	// On browsers (at least Chrome), buffers are already detached from the context
	// and must not be deleted by DeleteBuffer.
	if !web.IsBrowser() {
		if s.arrayBuffer != zeroBuffer {
			context.deleteBuffer(s.arrayBuffer)
		}
		if s.elementArrayBuffer != zeroBuffer {
			context.deleteBuffer(s.elementArrayBuffer)
		}
	}

	shaderVertexModelviewNative, err := context.newShader(vertexShader, vertexShaderStr())
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.deleteShader(shaderVertexModelviewNative)

	for _, c := range []bool{false, true} {
		for _, a := range []graphics.Address{
			graphics.AddressClampToZero,
			graphics.AddressRepeat,
		} {
			for _, f := range []graphics.Filter{
				graphics.FilterNearest,
				graphics.FilterLinear,
				graphics.FilterScreen,
			} {
				shaderFragmentColorMatrixNative, err := context.newShader(fragmentShader, fragmentShaderStr(c, f, a))
				if err != nil {
					panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
				}
				defer context.deleteShader(shaderFragmentColorMatrixNative)

				program, err := context.newProgram([]shader{
					shaderVertexModelviewNative,
					shaderFragmentColorMatrixNative,
				}, theArrayBufferLayout.names())

				if err != nil {
					return err
				}

				s.programs[programKey{
					useColorM: c,
					filter:    f,
					address:   a,
				}] = program
			}
		}
	}

	s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)

	// Note that the indices passed to NewElementArrayBuffer is not under GC management
	// in opengl package due to unsafe-way.
	// See NewElementArrayBuffer in context_mobile.go.
	s.elementArrayBuffer = context.newElementArrayBuffer(graphics.IndicesNum * 2)

	return nil
}

// areSameFloat32Array returns a boolean indicating if a and b are deeply equal.
func areSameFloat32Array(a, b []float32) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

// useProgram uses the program (programTexture).
func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM, filter graphics.Filter, address graphics.Address) error {
	destination := d.state.destination
	if destination == nil {
		panic("destination image is not set")
	}
	source := d.state.source
	if source == nil {
		panic("source image is not set")
	}

	if err := destination.setViewport(); err != nil {
		return err
	}
	dstW := destination.width
	srcW, srcH := source.width, source.height

	d.context.blendFunc(mode)

	program := d.state.programs[programKey{
		useColorM: colorM != nil,
		filter:    filter,
		address:   address,
	}]
	if d.state.lastProgram != program {
		d.context.useProgram(program)
		if d.state.lastProgram != zeroProgram {
			theArrayBufferLayout.disable(&d.context, d.state.lastProgram)
		}
		theArrayBufferLayout.enable(&d.context, program)

		if d.state.lastProgram == zeroProgram {
			d.context.bindBuffer(arrayBuffer, d.state.arrayBuffer)
			d.context.bindBuffer(elementArrayBuffer, d.state.elementArrayBuffer)
			d.context.uniformInt(program, "texture", 0)
		}

		d.state.lastProgram = program
		d.state.lastViewportWidth = 0
		d.state.lastViewportHeight = 0
		d.state.lastColorMatrix = nil
		d.state.lastColorMatrixTranslation = nil
		d.state.lastSourceWidth = 0
		d.state.lastSourceHeight = 0
	}

	vw := destination.framebuffer.width
	vh := destination.framebuffer.height
	if d.state.lastViewportWidth != vw || d.state.lastViewportHeight != vh {
		d.context.uniformFloats(program, "viewport_size", []float32{float32(vw), float32(vh)})
		d.state.lastViewportWidth = vw
		d.state.lastViewportHeight = vh
	}

	if colorM != nil {
		esBody, esTranslate := colorM.UnsafeElements()
		if !areSameFloat32Array(d.state.lastColorMatrix, esBody) {
			d.context.uniformFloats(program, "color_matrix_body", esBody)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrix = esBody
		}
		if !areSameFloat32Array(d.state.lastColorMatrixTranslation, esTranslate) {
			d.context.uniformFloats(program, "color_matrix_translation", esTranslate)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrixTranslation = esTranslate
		}
	}

	sw := graphics.InternalImageSize(srcW)
	sh := graphics.InternalImageSize(srcH)

	if filter != graphics.FilterNearest {
		if d.state.lastSourceWidth != sw || d.state.lastSourceHeight != sh {
			d.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})
			d.state.lastSourceWidth = sw
			d.state.lastSourceHeight = sh
		}
	}

	if filter == graphics.FilterScreen {
		scale := float32(dstW) / float32(srcW)
		d.context.uniformFloat(program, "scale", scale)
	}

	// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
	// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
	d.context.bindTexture(source.textureNative)

	d.state.source = nil
	d.state.destination = nil
	return nil
}
<commit_msg>graphicsdriver/opengl: Remove unnecessary function calls<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package opengl

import (
	"fmt"

	"github.com/hajimehoshi/ebiten/internal/affine"
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/web"
)

// arrayBufferLayoutPart is a part of an array buffer layout.
type arrayBufferLayoutPart struct {
	// TODO: This struct should belong to a program and know it.
	name string
	num  int
}

// arrayBufferLayout is an array buffer layout.
//
// An array buffer in OpenGL is a buffer representing vertices and
// is passed to a vertex shader.
type arrayBufferLayout struct {
	parts []arrayBufferLayoutPart
	total int
}

func (a *arrayBufferLayout) names() []string {
	ns := make([]string, len(a.parts))
	for i, p := range a.parts {
		ns[i] = p.name
	}
	return ns
}

// totalBytes returns the size in bytes for one element of the array buffer.
func (a *arrayBufferLayout) totalBytes() int {
	if a.total != 0 {
		return a.total
	}
	t := 0
	for _, p := range a.parts {
		t += float.SizeInBytes() * p.num
	}
	a.total = t
	return a.total
}

// newArrayBuffer creates OpenGL's buffer object for the array buffer.
func (a *arrayBufferLayout) newArrayBuffer(context *context) buffer {
	return context.newArrayBuffer(a.totalBytes() * graphics.IndicesNum)
}

// enable binds the array buffer the given program to use the array buffer.
func (a *arrayBufferLayout) enable(context *context, program program) {
	for i := range a.parts {
		context.enableVertexAttribArray(program, i)
	}
	total := a.totalBytes()
	offset := 0
	for i, p := range a.parts {
		context.vertexAttribPointer(program, i, p.num, float, total, offset)
		offset += float.SizeInBytes() * p.num
	}
}

// disable stops using the array buffer.
func (a *arrayBufferLayout) disable(context *context, program program) {
	// TODO: Disabling should be done in reversed order?
	for i := range a.parts {
		context.disableVertexAttribArray(program, i)
	}
}

// theArrayBufferLayout is the array buffer layout for Ebiten.
var theArrayBufferLayout = arrayBufferLayout{
	// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
	parts: []arrayBufferLayoutPart{
		{
			name: "vertex",
			num:  2,
		},
		{
			name: "tex",
			num:  2,
		},
		{
			name: "tex_region",
			num:  4,
		},
		{
			name: "color_scale",
			num:  4,
		},
	},
}

func init() {
	vertexFloatNum := theArrayBufferLayout.totalBytes() / float.SizeInBytes()
	if graphics.VertexFloatNum != vertexFloatNum {
		panic(fmt.Sprintf("vertex float num must be %d but %d", graphics.VertexFloatNum, vertexFloatNum))
	}
}

type programKey struct {
	useColorM bool
	filter    graphics.Filter
	address   graphics.Address
}

// openGLState is a state for
type openGLState struct {
	// arrayBuffer is OpenGL's array buffer (vertices data).
	arrayBuffer buffer

	// elementArrayBuffer is OpenGL's element array buffer (indices data).
	elementArrayBuffer buffer

	// programs is OpenGL's program for rendering a texture.
	programs map[programKey]program

	lastProgram                program
	lastViewportWidth          int
	lastViewportHeight         int
	lastColorMatrix            []float32
	lastColorMatrixTranslation []float32
	lastSourceWidth            int
	lastSourceHeight           int
	lastFilter                 *graphics.Filter
	lastAddress                *graphics.Address

	source      *Image
	destination *Image
}

var (
	zeroBuffer  buffer
	zeroProgram program
)

const (
	maxTriangles = graphics.IndicesNum / 3
	maxQuads     = maxTriangles / 2
)

// reset resets or initializes the OpenGL state.
func (s *openGLState) reset(context *context) error {
	if err := context.reset(); err != nil {
		return err
	}

	s.lastProgram = zeroProgram
	s.lastViewportWidth = 0
	s.lastViewportHeight = 0
	s.lastColorMatrix = nil
	s.lastColorMatrixTranslation = nil
	s.lastSourceWidth = 0
	s.lastSourceHeight = 0
	s.lastFilter = nil
	s.lastAddress = nil

	// When context lost happens, deleting programs or buffers is not necessary.
	// However, it is not assumed that reset is called only when context lost happens.
	// Let's delete them explicitly.
	if s.programs == nil {
		s.programs = map[programKey]program{}
	} else {
		for k, p := range s.programs {
			context.deleteProgram(p)
			delete(s.programs, k)
		}
	}

	// On browsers (at least Chrome), buffers are already detached from the context
	// and must not be deleted by DeleteBuffer.
	if !web.IsBrowser() {
		if s.arrayBuffer != zeroBuffer {
			context.deleteBuffer(s.arrayBuffer)
		}
		if s.elementArrayBuffer != zeroBuffer {
			context.deleteBuffer(s.elementArrayBuffer)
		}
	}

	shaderVertexModelviewNative, err := context.newShader(vertexShader, vertexShaderStr())
	if err != nil {
		panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
	}
	defer context.deleteShader(shaderVertexModelviewNative)

	for _, c := range []bool{false, true} {
		for _, a := range []graphics.Address{
			graphics.AddressClampToZero,
			graphics.AddressRepeat,
		} {
			for _, f := range []graphics.Filter{
				graphics.FilterNearest,
				graphics.FilterLinear,
				graphics.FilterScreen,
			} {
				shaderFragmentColorMatrixNative, err := context.newShader(fragmentShader, fragmentShaderStr(c, f, a))
				if err != nil {
					panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
				}
				defer context.deleteShader(shaderFragmentColorMatrixNative)

				program, err := context.newProgram([]shader{
					shaderVertexModelviewNative,
					shaderFragmentColorMatrixNative,
				}, theArrayBufferLayout.names())

				if err != nil {
					return err
				}

				s.programs[programKey{
					useColorM: c,
					filter:    f,
					address:   a,
				}] = program
			}
		}
	}

	s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)

	// Note that the indices passed to NewElementArrayBuffer is not under GC management
	// in opengl package due to unsafe-way.
	// See NewElementArrayBuffer in context_mobile.go.
	s.elementArrayBuffer = context.newElementArrayBuffer(graphics.IndicesNum * 2)

	return nil
}

// areSameFloat32Array returns a boolean indicating if a and b are deeply equal.
func areSameFloat32Array(a, b []float32) bool {
	if len(a) != len(b) {
		return false
	}
	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

// useProgram uses the program (programTexture).
func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM, filter graphics.Filter, address graphics.Address) error {
	destination := d.state.destination
	if destination == nil {
		panic("destination image is not set")
	}
	source := d.state.source
	if source == nil {
		panic("source image is not set")
	}

	if err := destination.setViewport(); err != nil {
		return err
	}
	dstW := destination.width
	srcW, srcH := source.width, source.height

	d.context.blendFunc(mode)

	program := d.state.programs[programKey{
		useColorM: colorM != nil,
		filter:    filter,
		address:   address,
	}]
	if d.state.lastProgram != program {
		d.context.useProgram(program)
		if d.state.lastProgram == zeroProgram {
			theArrayBufferLayout.enable(&d.context, program)
			d.context.bindBuffer(arrayBuffer, d.state.arrayBuffer)
			d.context.bindBuffer(elementArrayBuffer, d.state.elementArrayBuffer)
			d.context.uniformInt(program, "texture", 0)
		}

		d.state.lastProgram = program
		d.state.lastViewportWidth = 0
		d.state.lastViewportHeight = 0
		d.state.lastColorMatrix = nil
		d.state.lastColorMatrixTranslation = nil
		d.state.lastSourceWidth = 0
		d.state.lastSourceHeight = 0
	}

	vw := destination.framebuffer.width
	vh := destination.framebuffer.height
	if d.state.lastViewportWidth != vw || d.state.lastViewportHeight != vh {
		d.context.uniformFloats(program, "viewport_size", []float32{float32(vw), float32(vh)})
		d.state.lastViewportWidth = vw
		d.state.lastViewportHeight = vh
	}

	if colorM != nil {
		esBody, esTranslate := colorM.UnsafeElements()
		if !areSameFloat32Array(d.state.lastColorMatrix, esBody) {
			d.context.uniformFloats(program, "color_matrix_body", esBody)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrix = esBody
		}
		if !areSameFloat32Array(d.state.lastColorMatrixTranslation, esTranslate) {
			d.context.uniformFloats(program, "color_matrix_translation", esTranslate)
			// ColorM's elements are immutable. It's OK to hold the reference without copying.
			d.state.lastColorMatrixTranslation = esTranslate
		}
	}

	sw := graphics.InternalImageSize(srcW)
	sh := graphics.InternalImageSize(srcH)

	if filter != graphics.FilterNearest {
		if d.state.lastSourceWidth != sw || d.state.lastSourceHeight != sh {
			d.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})
			d.state.lastSourceWidth = sw
			d.state.lastSourceHeight = sh
		}
	}

	if filter == graphics.FilterScreen {
		scale := float32(dstW) / float32(srcW)
		d.context.uniformFloat(program, "scale", scale)
	}

	// We don't have to call gl.ActiveTexture here: GL_TEXTURE0 is the default active texture
	// See also: https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml
	d.context.bindTexture(source.textureNative)

	d.state.source = nil
	d.state.destination = nil
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Update<commit_after><|endoftext|>


<commit_before>package trie

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethutil"
)

type Db map[string][]byte

func (self Db) Get(k []byte) ([]byte, error) { return self[string(k)], nil }
func (self Db) Put(k, v []byte)              { self[string(k)] = v }

// Used for testing
func NewEmpty() *Trie {
	return New(nil, make(Db))
}

func TestEmptyTrie(t *testing.T) {
	trie := NewEmpty()
	res := trie.Hash()
	exp := crypto.Sha3(ethutil.Encode(""))
	if !bytes.Equal(res, exp) {
		t.Errorf("expected %x got %x", exp, res)
	}
}

func TestInsert(t *testing.T) {
	trie := NewEmpty()

	trie.UpdateString("doe", "reindeer")
	trie.UpdateString("dog", "puppy")
	trie.UpdateString("dogglesworth", "cat")

	exp := ethutil.Hex2Bytes("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3")
	root := trie.Hash()
	if !bytes.Equal(root, exp) {
		t.Errorf("exp %x got %x", exp, root)
	}

	trie = NewEmpty()
	trie.UpdateString("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

	exp = ethutil.Hex2Bytes("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
	root = trie.Hash()
	if !bytes.Equal(root, exp) {
		t.Errorf("exp %x got %x", exp, root)
	}
}

func TestGet(t *testing.T) {
	trie := NewEmpty()

	trie.UpdateString("doe", "reindeer")
	trie.UpdateString("dog", "puppy")
	trie.UpdateString("dogglesworth", "cat")

	res := trie.GetString("dog")
	if !bytes.Equal(res, []byte("puppy")) {
		t.Errorf("expected puppy got %x", res)
	}

	unknown := trie.GetString("unknown")
	if unknown != nil {
		t.Errorf("expected nil got %x", unknown)
	}
}

func TestDelete(t *testing.T) {
	trie := NewEmpty()

	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
	}
	for _, val := range vals {
		if val.v != "" {
			trie.UpdateString(val.k, val.v)
		} else {
			trie.DeleteString(val.k)
		}
	}

	hash := trie.Hash()
	exp := ethutil.Hex2Bytes("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
	if !bytes.Equal(hash, exp) {
		t.Errorf("expected %x got %x", exp, hash)
	}
}

func TestEmptyValues(t *testing.T) {
	trie := NewEmpty()

	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}

	hash := trie.Hash()
	exp := ethutil.Hex2Bytes("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
	if !bytes.Equal(hash, exp) {
		t.Errorf("expected %x got %x", exp, hash)
	}
}

func TestReplication(t *testing.T) {
	trie := NewEmpty()
	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
		{"somethingveryoddindeedthis is", "myothernodedata"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}
	trie.Commit()

	trie2 := New(trie.roothash, trie.cache.backend)
	if string(trie2.GetString("horse")) != "stallion" {
		t.Error("expected to have horse => stallion")
	}

	hash := trie2.Hash()
	exp := trie.Hash()
	if !bytes.Equal(hash, exp) {
		t.Errorf("root failure. expected %x got %x", exp, hash)
	}

}

func TestReset(t *testing.T) {
	trie := NewEmpty()
	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}
	trie.Commit()

	before := ethutil.CopyBytes(trie.roothash)
	trie.UpdateString("should", "revert")
	trie.Hash()
	// Should have no effect
	trie.Hash()
	trie.Hash()
	// ###

	trie.Reset()
	after := ethutil.CopyBytes(trie.roothash)

	if !bytes.Equal(before, after) {
		t.Errorf("expected roots to be equal. %x - %x", before, after)
	}
}

func TestParanoia(t *testing.T) {
	t.Skip()
	trie := NewEmpty()

	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
		{"somethingveryoddindeedthis is", "myothernodedata"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}
	trie.Commit()

	ok, t2 := ParanoiaCheck(trie, trie.cache.backend)
	if !ok {
		t.Errorf("trie paranoia check failed %x %x", trie.roothash, t2.roothash)
	}
}

// Not an actual test
func TestOutput(t *testing.T) {
	t.Skip()

	base := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	trie := NewEmpty()
	for i := 0; i < 50; i++ {
		trie.UpdateString(fmt.Sprintf("%s%d", base, i), "valueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
	}
	fmt.Println("############################## FULL ################################")
	fmt.Println(trie.root)

	trie.Commit()
	fmt.Println("############################## SMALL ################################")
	trie2 := New(trie.roothash, trie.cache.backend)
	trie2.GetString(base + "20")
	fmt.Println(trie2.root)
}

func BenchmarkGets(b *testing.B) {
	trie := NewEmpty()
	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
		{"somethingveryoddindeedthis is", "myothernodedata"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		trie.Get([]byte("horse"))
	}
}

func BenchmarkUpdate(b *testing.B) {
	trie := NewEmpty()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		trie.UpdateString(fmt.Sprintf("aaaaaaaaa%d", i), "value")
	}
	trie.Hash()
}

type kv struct {
	k, v []byte
	t    bool
}

func TestLargeData(t *testing.T) {
	trie := NewEmpty()
	vals := make(map[string]*kv)

	for i := byte(1); i < 255; i++ {
		value := &kv{ethutil.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
		value2 := &kv{ethutil.LeftPadBytes([]byte{10, i}, 32), []byte{i}, false}
		trie.Update(value.k, value.v)
		trie.Update(value2.k, value2.v)
		vals[string(value.k)] = value
		vals[string(value2.k)] = value2
		fmt.Println(value, "\n", value2)
	}

	it := trie.Iterator()
	for it.Next() {
		vals[string(it.Key)].t = true
	}

	var untouched []*kv
	for _, value := range vals {
		if !value.t {
			untouched = append(untouched, value)
		}
	}

	if len(untouched) > 0 {
		t.Errorf("Missed %d nodes", len(untouched))
		for _, value := range untouched {
			t.Error(value)
		}
	}
}
<commit_msg>Start at 0<commit_after>package trie

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethutil"
)

type Db map[string][]byte

func (self Db) Get(k []byte) ([]byte, error) { return self[string(k)], nil }
func (self Db) Put(k, v []byte)              { self[string(k)] = v }

// Used for testing
func NewEmpty() *Trie {
	return New(nil, make(Db))
}

func TestEmptyTrie(t *testing.T) {
	trie := NewEmpty()
	res := trie.Hash()
	exp := crypto.Sha3(ethutil.Encode(""))
	if !bytes.Equal(res, exp) {
		t.Errorf("expected %x got %x", exp, res)
	}
}

func TestInsert(t *testing.T) {
	trie := NewEmpty()

	trie.UpdateString("doe", "reindeer")
	trie.UpdateString("dog", "puppy")
	trie.UpdateString("dogglesworth", "cat")

	exp := ethutil.Hex2Bytes("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3")
	root := trie.Hash()
	if !bytes.Equal(root, exp) {
		t.Errorf("exp %x got %x", exp, root)
	}

	trie = NewEmpty()
	trie.UpdateString("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

	exp = ethutil.Hex2Bytes("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
	root = trie.Hash()
	if !bytes.Equal(root, exp) {
		t.Errorf("exp %x got %x", exp, root)
	}
}

func TestGet(t *testing.T) {
	trie := NewEmpty()

	trie.UpdateString("doe", "reindeer")
	trie.UpdateString("dog", "puppy")
	trie.UpdateString("dogglesworth", "cat")

	res := trie.GetString("dog")
	if !bytes.Equal(res, []byte("puppy")) {
		t.Errorf("expected puppy got %x", res)
	}

	unknown := trie.GetString("unknown")
	if unknown != nil {
		t.Errorf("expected nil got %x", unknown)
	}
}

func TestDelete(t *testing.T) {
	trie := NewEmpty()

	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
	}
	for _, val := range vals {
		if val.v != "" {
			trie.UpdateString(val.k, val.v)
		} else {
			trie.DeleteString(val.k)
		}
	}

	hash := trie.Hash()
	exp := ethutil.Hex2Bytes("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
	if !bytes.Equal(hash, exp) {
		t.Errorf("expected %x got %x", exp, hash)
	}
}

func TestEmptyValues(t *testing.T) {
	trie := NewEmpty()

	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}

	hash := trie.Hash()
	exp := ethutil.Hex2Bytes("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
	if !bytes.Equal(hash, exp) {
		t.Errorf("expected %x got %x", exp, hash)
	}
}

func TestReplication(t *testing.T) {
	trie := NewEmpty()
	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
		{"somethingveryoddindeedthis is", "myothernodedata"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}
	trie.Commit()

	trie2 := New(trie.roothash, trie.cache.backend)
	if string(trie2.GetString("horse")) != "stallion" {
		t.Error("expected to have horse => stallion")
	}

	hash := trie2.Hash()
	exp := trie.Hash()
	if !bytes.Equal(hash, exp) {
		t.Errorf("root failure. expected %x got %x", exp, hash)
	}

}

func TestReset(t *testing.T) {
	trie := NewEmpty()
	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}
	trie.Commit()

	before := ethutil.CopyBytes(trie.roothash)
	trie.UpdateString("should", "revert")
	trie.Hash()
	// Should have no effect
	trie.Hash()
	trie.Hash()
	// ###

	trie.Reset()
	after := ethutil.CopyBytes(trie.roothash)

	if !bytes.Equal(before, after) {
		t.Errorf("expected roots to be equal. %x - %x", before, after)
	}
}

func TestParanoia(t *testing.T) {
	t.Skip()
	trie := NewEmpty()

	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
		{"somethingveryoddindeedthis is", "myothernodedata"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}
	trie.Commit()

	ok, t2 := ParanoiaCheck(trie, trie.cache.backend)
	if !ok {
		t.Errorf("trie paranoia check failed %x %x", trie.roothash, t2.roothash)
	}
}

// Not an actual test
func TestOutput(t *testing.T) {
	t.Skip()

	base := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	trie := NewEmpty()
	for i := 0; i < 50; i++ {
		trie.UpdateString(fmt.Sprintf("%s%d", base, i), "valueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
	}
	fmt.Println("############################## FULL ################################")
	fmt.Println(trie.root)

	trie.Commit()
	fmt.Println("############################## SMALL ################################")
	trie2 := New(trie.roothash, trie.cache.backend)
	trie2.GetString(base + "20")
	fmt.Println(trie2.root)
}

func BenchmarkGets(b *testing.B) {
	trie := NewEmpty()
	vals := []struct{ k, v string }{
		{"do", "verb"},
		{"ether", "wookiedoo"},
		{"horse", "stallion"},
		{"shaman", "horse"},
		{"doge", "coin"},
		{"ether", ""},
		{"dog", "puppy"},
		{"shaman", ""},
		{"somethingveryoddindeedthis is", "myothernodedata"},
	}
	for _, val := range vals {
		trie.UpdateString(val.k, val.v)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		trie.Get([]byte("horse"))
	}
}

func BenchmarkUpdate(b *testing.B) {
	trie := NewEmpty()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		trie.UpdateString(fmt.Sprintf("aaaaaaaaa%d", i), "value")
	}
	trie.Hash()
}

type kv struct {
	k, v []byte
	t    bool
}

func TestLargeData(t *testing.T) {
	trie := NewEmpty()
	vals := make(map[string]*kv)

	for i := byte(0); i < 255; i++ {
		value := &kv{ethutil.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
		value2 := &kv{ethutil.LeftPadBytes([]byte{10, i}, 32), []byte{i}, false}
		trie.Update(value.k, value.v)
		trie.Update(value2.k, value2.v)
		vals[string(value.k)] = value
		vals[string(value2.k)] = value2
	}

	it := trie.Iterator()
	for it.Next() {
		vals[string(it.Key)].t = true
	}

	var untouched []*kv
	for _, value := range vals {
		if !value.t {
			untouched = append(untouched, value)
		}
	}

	if len(untouched) > 0 {
		t.Errorf("Missed %d nodes", len(untouched))
		for _, value := range untouched {
			t.Error(value)
		}
	}
}
<|endoftext|>


<commit_before>package helpers

import (
  "seeme/db"
  "errors"
)

func VerifyConnection(username string, connection string) (error) {
  connectionsMap, err := db.GetConnectionsMap(username)
  if err != nil {
    return errors.New("Connection Search Error!")
  }
  if connectionsMap[connection] {
    return nil
  }

  return errors.New("Connection Not Found")
}

func VerifyNewConnection(username string, connection string) (error) {
  userMap, err := db.GetConnectionsMap(connection)
  if err != nil {
    return errors.New("Connection Search Error!")
  }
  if userMap[username] {
    return errors.New("Connection Already Exists")
  }

  userMap, err = db.GetConnectionsMap(username)
  if err != nil {
    return errors.New("Connection Search Error!")
  }
  if userMap[connection] {
    return errors.New("Connection Already Exists")
  }

  return nil
}

<commit_msg>applied new GetConnectionsMap refactoring to VerifyNewConnections<commit_after>package helpers

import (
  "seeme/db"
  "errors"
)

func VerifyConnection(username string, connection string) (error) {
  connectionsMap, err := db.GetConnectionsMap(username)
  if err != nil {
    return errors.New("Connection Search Error!")
  }
  if connectionsMap[connection] {
    return nil
  }

  return errors.New("Connection Not Found")
}

func VerifyNewConnection(username string, connection string) (error) {
  connectionsMap, err := db.GetConnectionsMap(username)
  if err != nil {
    return errors.New("Connection Search Error!")
  }
  if connectionsMap[connection] {
    return errors.New("Connection Already Exists")
  }

  return nil
}

<|endoftext|>


<commit_before>// Copyright 2014 docker-cluster 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 cluster

import (
	"fmt"
	"github.com/fsouza/go-dockerclient"
	"net/http"
	"strings"
	"sync"
)

// RemoveImage removes an image from the nodes where this images exists, returning an
// error in case of failure. Will wait for the image to be removed from all nodes.
func (c *Cluster) RemoveImageWait(name string) error {
	return c.removeImage(name, true)
}

// RemoveImage removes an image from the nodes where this images exists, returning an
// error in case of failure. Will wait for the image to be removed only from one node,
// removal from the other nodes will happen in background.
func (c *Cluster) RemoveImage(name string) error {
	return c.removeImage(name, false)
}

func (c *Cluster) removeImage(name string, waitForAll bool) error {
	err := c.removeFromRegistry(name)
	if err != nil {
		return err
	}
	hosts, err := c.storage().RetrieveImage(name)
	if err != nil {
		return err
	}
	_, err = c.runOnNodes(func(n node) (interface{}, error) {
		return nil, n.RemoveImage(name)
	}, docker.ErrNoSuchImage, waitForAll, hosts...)
	if err == nil || err == docker.ErrNoSuchImage {
		otherErr := c.storage().RemoveImage(name)
		if otherErr != nil {
			return otherErr
		}
	}
	return err
}

func (c *Cluster) removeFromRegistry(imageId string) error {
	parts := strings.SplitN(imageId, "/", 3)
	if len(parts) < 3 {
		return nil
	}
	registryServer := parts[0]
	url := fmt.Sprintf("http://%s/v1/repositories/%s/tags", registryServer,
		strings.Join(parts[1:], "/"))
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = http.DefaultClient.Do(request)
	return err
}

// PullImage pulls an image from a remote registry server, returning an error
// in case of failure.
//
// It will pull all images in parallel, so users need to make sure that the
// given buffer is safe.
func (c *Cluster) PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration, nodes ...string) error {
	_, err := c.runOnNodes(func(n node) (interface{}, error) {
		key := opts.Repository
		c.storage().StoreImage(key, n.addr)
		return nil, n.PullImage(opts, auth)
	}, docker.ErrNoSuchImage, true, nodes...)
	return err
}

// PushImage pushes an image to a remote registry server, returning an error in
// case of failure.
func (c *Cluster) PushImage(opts docker.PushImageOptions, auth docker.AuthConfiguration) error {
	nodes, err := c.getNodesForImage(opts.Name)
	if err != nil {
		return err
	}
	for _, node := range nodes {
		return node.PushImage(opts, auth)
	}
	return nil
}

func (c *Cluster) ListImages(all bool) ([]docker.APIImages, error) {
	nodes, err := c.Nodes()
	if err != nil {
		return nil, err
	}
	resultChan := make(chan []docker.APIImages, len(nodes))
	errChan := make(chan error, len(nodes))
	var wg sync.WaitGroup
	for _, node := range nodes {
		wg.Add(1)
		go func(addr string) {
			defer wg.Done()
			client, err := c.getNodeByAddr(addr)
			if err != nil {
				errChan <- err
			}
			nodeImages, err := client.ListImages(all)
			if err != nil {
				errChan <- err
			}
			resultChan <- nodeImages
		}(node.Address)
	}
	wg.Wait()
	close(resultChan)
	select {
	case err := <-errChan:
		return nil, err
	default:
	}
	var allImages []docker.APIImages
	for images := range resultChan {
		allImages = append(allImages, images...)
	}
	return allImages, nil
}

func (c *Cluster) getNodesForImage(image string) ([]node, error) {
	var nodes []node
	hosts, err := c.storage().RetrieveImage(image)
	if err != nil {
		return nil, err
	}
	for _, host := range hosts {
		node, err := c.getNode(func(s Storage) (string, error) { return host, nil })
		if err != nil {
			return nil, err
		}
		nodes = append(nodes, node)
	}
	return nodes, err
}

// ImportImage imports an image from a url or stdin
func (c *Cluster) ImportImage(opts docker.ImportImageOptions) error {
	_, err := c.runOnNodes(func(n node) (interface{}, error) {
		return nil, n.ImportImage(opts)
	}, docker.ErrNoSuchImage, false)
	return err
}

//BuildImage build an image and push it to register
func (c *Cluster) BuildImage(buildOptions docker.BuildImageOptions) error {
	nodes, err := c.Nodes()
	if err != nil {
		return err
	}
	nodeAddress := nodes[0].Address
	node, err := c.getNode(func(Storage) (string, error) {
		return nodeAddress, nil
	})
	if err != nil {
		return err
	}
	err = node.BuildImage(buildOptions)
	if err != nil {
		return err
	}
	return c.storage().StoreImage(buildOptions.Name, nodeAddress)
}
<commit_msg>return nil when check if the image was removed from storage<commit_after>// Copyright 2014 docker-cluster 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 cluster

import (
	"fmt"
	"github.com/fsouza/go-dockerclient"
	"net/http"
	"strings"
	"sync"
)

// RemoveImage removes an image from the nodes where this images exists, returning an
// error in case of failure. Will wait for the image to be removed from all nodes.
func (c *Cluster) RemoveImageWait(name string) error {
	return c.removeImage(name, true)
}

// RemoveImage removes an image from the nodes where this images exists, returning an
// error in case of failure. Will wait for the image to be removed only from one node,
// removal from the other nodes will happen in background.
func (c *Cluster) RemoveImage(name string) error {
	return c.removeImage(name, false)
}

func (c *Cluster) removeImage(name string, waitForAll bool) error {
	err := c.removeFromRegistry(name)
	if err != nil {
		return err
	}
	hosts, err := c.storage().RetrieveImage(name)
	if err != nil {
		return err
	}
	_, err = c.runOnNodes(func(n node) (interface{}, error) {
		return nil, n.RemoveImage(name)
	}, docker.ErrNoSuchImage, waitForAll, hosts...)
	if err == nil || err == docker.ErrNoSuchImage {
		otherErr := c.storage().RemoveImage(name)
		if otherErr != nil {
			return otherErr
		}
		return nil
	}
	return err
}

func (c *Cluster) removeFromRegistry(imageId string) error {
	parts := strings.SplitN(imageId, "/", 3)
	if len(parts) < 3 {
		return nil
	}
	registryServer := parts[0]
	url := fmt.Sprintf("http://%s/v1/repositories/%s/tags", registryServer,
		strings.Join(parts[1:], "/"))
	request, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return err
	}
	_, err = http.DefaultClient.Do(request)
	return err
}

// PullImage pulls an image from a remote registry server, returning an error
// in case of failure.
//
// It will pull all images in parallel, so users need to make sure that the
// given buffer is safe.
func (c *Cluster) PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration, nodes ...string) error {
	_, err := c.runOnNodes(func(n node) (interface{}, error) {
		key := opts.Repository
		c.storage().StoreImage(key, n.addr)
		return nil, n.PullImage(opts, auth)
	}, docker.ErrNoSuchImage, true, nodes...)
	return err
}

// PushImage pushes an image to a remote registry server, returning an error in
// case of failure.
func (c *Cluster) PushImage(opts docker.PushImageOptions, auth docker.AuthConfiguration) error {
	nodes, err := c.getNodesForImage(opts.Name)
	if err != nil {
		return err
	}
	for _, node := range nodes {
		return node.PushImage(opts, auth)
	}
	return nil
}

func (c *Cluster) ListImages(all bool) ([]docker.APIImages, error) {
	nodes, err := c.Nodes()
	if err != nil {
		return nil, err
	}
	resultChan := make(chan []docker.APIImages, len(nodes))
	errChan := make(chan error, len(nodes))
	var wg sync.WaitGroup
	for _, node := range nodes {
		wg.Add(1)
		go func(addr string) {
			defer wg.Done()
			client, err := c.getNodeByAddr(addr)
			if err != nil {
				errChan <- err
			}
			nodeImages, err := client.ListImages(all)
			if err != nil {
				errChan <- err
			}
			resultChan <- nodeImages
		}(node.Address)
	}
	wg.Wait()
	close(resultChan)
	select {
	case err := <-errChan:
		return nil, err
	default:
	}
	var allImages []docker.APIImages
	for images := range resultChan {
		allImages = append(allImages, images...)
	}
	return allImages, nil
}

func (c *Cluster) getNodesForImage(image string) ([]node, error) {
	var nodes []node
	hosts, err := c.storage().RetrieveImage(image)
	if err != nil {
		return nil, err
	}
	for _, host := range hosts {
		node, err := c.getNode(func(s Storage) (string, error) { return host, nil })
		if err != nil {
			return nil, err
		}
		nodes = append(nodes, node)
	}
	return nodes, err
}

// ImportImage imports an image from a url or stdin
func (c *Cluster) ImportImage(opts docker.ImportImageOptions) error {
	_, err := c.runOnNodes(func(n node) (interface{}, error) {
		return nil, n.ImportImage(opts)
	}, docker.ErrNoSuchImage, false)
	return err
}

//BuildImage build an image and push it to register
func (c *Cluster) BuildImage(buildOptions docker.BuildImageOptions) error {
	nodes, err := c.Nodes()
	if err != nil {
		return err
	}
	nodeAddress := nodes[0].Address
	node, err := c.getNode(func(Storage) (string, error) {
		return nodeAddress, nil
	})
	if err != nil {
		return err
	}
	err = node.BuildImage(buildOptions)
	if err != nil {
		return err
	}
	return c.storage().StoreImage(buildOptions.Name, nodeAddress)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:              "apex",
	PersistentPreRun: pv.preRun,
	SilenceErrors:    true,
}

func init() {
	pf := rootCmd.PersistentFlags()

	rootCmd.AddCommand(deployCmd)
	rootCmd.AddCommand(deleteCmd)
	rootCmd.AddCommand(invokeCmd)
	rootCmd.AddCommand(rollbackCmd)
	rootCmd.AddCommand(logsCmd)
	rootCmd.AddCommand(metricsCmd)
	rootCmd.AddCommand(buildCmd)
	rootCmd.AddCommand(listCmd)
	rootCmd.AddCommand(docsCmd)
	rootCmd.AddCommand(upgradeCmd)
	rootCmd.AddCommand(versionCmd)

	pf.StringVarP(&pv.Chdir, "chdir", "C", "", "Working directory")
	pf.BoolVarP(&pv.DryRun, "dry-run", "D", false, "Perform a dry-run")
	pf.StringSliceVarP(&pv.Env, "env", "e", nil, "Environment variable")
	pf.StringVarP(&pv.LogLevel, "log-level", "l", "info", "Log severity level")
	pf.BoolVarP(&pv.Verbose, "verbose", "v", false, "Output verbose logs")
	pf.BoolVarP(&pv.Yes, "yes", "y", false, "Automatic yes to prompts")

	// Add a newline at the top of each help message
	rootCmd.SetHelpTemplate("\n" + rootCmd.HelpTemplate())
}
<commit_msg>remove old --verbose flag<commit_after>package main

import (
	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:              "apex",
	PersistentPreRun: pv.preRun,
	SilenceErrors:    true,
}

func init() {
	pf := rootCmd.PersistentFlags()

	rootCmd.AddCommand(deployCmd)
	rootCmd.AddCommand(deleteCmd)
	rootCmd.AddCommand(invokeCmd)
	rootCmd.AddCommand(rollbackCmd)
	rootCmd.AddCommand(logsCmd)
	rootCmd.AddCommand(metricsCmd)
	rootCmd.AddCommand(buildCmd)
	rootCmd.AddCommand(listCmd)
	rootCmd.AddCommand(docsCmd)
	rootCmd.AddCommand(upgradeCmd)
	rootCmd.AddCommand(versionCmd)

	pf.StringVarP(&pv.Chdir, "chdir", "C", "", "Working directory")
	pf.BoolVarP(&pv.DryRun, "dry-run", "D", false, "Perform a dry-run")
	pf.StringSliceVarP(&pv.Env, "env", "e", nil, "Environment variable")
	pf.StringVarP(&pv.LogLevel, "log-level", "l", "info", "Log severity level")
	pf.BoolVarP(&pv.Yes, "yes", "y", false, "Automatic yes to prompts")

	// Add a newline at the top of each help message
	rootCmd.SetHelpTemplate("\n" + rootCmd.HelpTemplate())
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"sort"
	"strings"
	"syscall"

	"github.com/ianremmler/clac"
	"golang.org/x/crypto/ssh/terminal"
	"robpike.io/ivy/value"
)

const usageStr = `usage:

Interactive:  clac [-i <input>]
Command line: clac [-x | -p <precision>] <input>

Command line mode requires input from arguments (without -i) and/or stdin.
`

var (
	// flags
	doInitStack      = false
	doHexOut         = false
	cliPrec     uint = 12

	trm         *terminal.Terminal
	oldTrmState *terminal.State
	lastErr     error
	cl          = clac.New()
	cmdList     = []string{}
)

var cmdMap = map[string]func() error{
	"neg":    cl.Neg,
	"n":      cl.Neg,
	"abs":    cl.Abs,
	"a":      cl.Abs,
	"inv":    cl.Inv,
	"i":      cl.Inv,
	"+":      cl.Add,
	"-":      cl.Sub,
	"*":      cl.Mul,
	"x":      cl.Mul,
	"/":      cl.Div,
	"div":    cl.IntDiv,
	"%":      cl.Mod,
	"exp":    cl.Exp,
	"^":      cl.Pow,
	"2^":     cl.Pow2,
	"10^":    cl.Pow10,
	"logn":   cl.LogN,
	"ln":     cl.Ln,
	"log":    cl.Log,
	"lg":     cl.Lg,
	"sqrt":   cl.Sqrt,
	"!":      cl.Factorial,
	"comb":   cl.Comb,
	"perm":   cl.Perm,
	"sin":    cl.Sin,
	"cos":    cl.Cos,
	"tan":    cl.Tan,
	"asin":   cl.Asin,
	"acos":   cl.Acos,
	"atan":   cl.Atan,
	"atan2":  cl.Atan2,
	"dtor":   cl.DegToRad,
	"rtod":   cl.RadToDeg,
	"rtop":   cl.RectToPolar,
	"ptor":   cl.PolarToRect,
	"floor":  cl.Floor,
	"ceil":   cl.Ceil,
	"trunc":  cl.Trunc,
	"and":    cl.And,
	"or":     cl.Or,
	"xor":    cl.Xor,
	"not":    cl.Not,
	"andn":   cl.AndN,
	"orn":    cl.OrN,
	"xorn":   cl.XorN,
	"sum":    cl.Sum,
	"avg":    cl.Avg,
	"drop":   cl.Drop,
	"k":      cl.Drop,
	"dropn":  cl.DropN,
	"dropr":  cl.DropR,
	"dup":    cl.Dup,
	"d":      cl.Dup,
	"dupn":   cl.DupN,
	"dupr":   cl.DupR,
	"pick":   cl.Pick,
	"p":      cl.Pick,
	"swap":   cl.Swap,
	"s":      cl.Swap,
	"depth":  cl.Depth,
	"min":    cl.Min,
	"max":    cl.Max,
	"minn":   cl.MinN,
	"maxn":   cl.MaxN,
	"rot":    cl.Rot,
	"r":      cl.Rot,
	"rotr":   cl.RotR,
	"unrot":  cl.Unrot,
	"unrotr": cl.UnrotR,
	"mag":    cl.Mag,
	"hyp":    cl.Hypot,
	"dot":    cl.Dot,
	"dot3":   cl.Dot3,
	"cross":  cl.Cross,
	"pi":     constant(clac.Pi),
	"e":      constant(clac.E),
	"phi":    constant(clac.Phi),
}

var interactiveCmdMap = map[string]func() error{
	"undo":  cl.Undo,
	"u":     cl.Undo,
	"redo":  cl.Redo,
	"r":     cl.Redo,
	"clear": cl.Clear,
	"reset": cl.Reset,
	"help":  help,
	"quit":  exit,
}

type term struct {
	io.Reader
	io.Writer
}

func init() {
	log.SetFlags(0)
	log.SetPrefix("clac: ")
	flag.BoolVar(&doHexOut, "x", doHexOut,
		"Command line mode: hexidecimal output")
	flag.UintVar(&cliPrec, "p", cliPrec,
		"Command line mode: output precision")
	flag.BoolVar(&doInitStack, "i", doInitStack,
		"Initialize with input from command line arguments")
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, usageStr)
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	if !processCmdLine() {
		printCmdLineStack(cl.Stack())
		os.Exit(0)
	}

	interactiveSetup()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)
	go func() {
		<-sigChan
		exit()
	}()

	repl()
}

func constant(v value.Value) func() error {
	return func() error { return cl.Push(v) }
}

func interactiveSetup() {
	if !terminal.IsTerminal(syscall.Stdin) {
		log.Fatalln("this doesn't look like an interactive terminal")
	}
	var err error
	oldTrmState, err = terminal.MakeRaw(syscall.Stdin)
	if err != nil {
		log.Fatalln(err)
	}
	trm = terminal.NewTerminal(term{os.Stdin, os.Stdout}, "")

	for cmd, fn := range interactiveCmdMap {
		cmdMap[cmd] = fn
	}
	for cmd := range cmdMap {
		cmdList = append(cmdList, cmd)
	}
	sort.Strings(cmdList)
}

func repl() {
	for {
		printStack(cl.Stack())
		input, err := trm.ReadLine()
		lastErr = nil
		if err == io.EOF {
			exit()
		}
		if err != nil {
			continue
		}
		processInput(input, true)
	}
}

func processCmdLine() bool {
	input := ""
	if stat, err := os.Stdin.Stat(); err == nil && stat.Mode()&os.ModeNamedPipe != 0 {
		if pipeInput, err := ioutil.ReadAll(os.Stdin); err == nil {
			input = string(pipeInput)
		}
	}
	if len(flag.Args()) > 0 {
		input += " " + strings.Join(flag.Args(), " ")
	}
	isInteractive := doInitStack || (input == "")
	if isInteractive {
		cl.EnableHistory(isInteractive)
	}
	processInput(string(input), false)
	return isInteractive
}

func printCmdLineStack(stack clac.Stack) {
	if doHexOut {
		clac.SetFormat("%#x")
	} else {
		clac.SetFormat(fmt.Sprintf("%%.%dg", cliPrec))
	}
	for i := range stack {
		val := stack[len(stack)-i-1]
		var err error
		if doHexOut {
			val, err = clac.Trunc(val)
		}
		if err != nil {
			fmt.Print("error")
		} else {
			fmt.Print(clac.Sprint(val))
		}
		if i < len(stack)-1 {
			fmt.Print(" ")
		}
	}
	fmt.Println()
}

func exit() error {
	terminal.Restore(syscall.Stdin, oldTrmState)
	fmt.Println()
	os.Exit(0)
	return nil
}

func help() error {
	clearScreen()
	for i := range cmdList {
		fmt.Printf("%-8s", cmdList[i])
		if (i+1)%5 == 0 {
			fmt.Println("\r")
		}
	}
	if len(cmdList)%5 != 0 {
		fmt.Println("\r")
	}
	fmt.Print("\n[Press any key to continue]")
	waitKey()
	return clac.ErrNoHistUpdate
}

func processInput(input string, isInteractive bool) {
	errorHandler := func(err error) { lastErr = err }
	if !isInteractive {
		errorHandler = func(err error) { log.Println(err) }
	}
	scanner := bufio.NewScanner(strings.NewReader(input))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		tok := scanner.Text()
		if num, err := clac.ParseNum(tok); err == nil {
			if err = cl.Exec(func() error { return cl.Push(num) }); err != nil {
				errorHandler(fmt.Errorf("push: %s", err))
			}
			continue
		}
		if cmd, ok := cmdMap[tok]; ok {
			if err := cl.Exec(cmd); err != nil {
				errorHandler(fmt.Errorf("%s: %s", tok, err))
			}
			continue
		}
		errorHandler(fmt.Errorf("%s: invalid input", tok))
	}
	if err := scanner.Err(); err != nil {
		errorHandler(err)
	}
}

func printStack(stack clac.Stack) {
	cols, rows, err := terminal.GetSize(syscall.Stdout)
	if err != nil {
		rows = len(stack) + 1
	}
	// ensure sane width
	if cols < 20 {
		cols = 20
	}
	clearScreen()

	dataCols := cols - 4
	hexCols := dataCols / 2
	floatCols := dataCols - hexCols
	floatFmt := fmt.Sprintf("%%%d.%dg", floatCols-1, floatCols-8)
	hexFmt := fmt.Sprintf("%%#%dx", hexCols-3)
	for i := rows - 3; i >= 0; i-- {
		line := fmt.Sprintf("%02d:", i)
		if i < len(stack) {
			clac.SetFormat(floatFmt)
			line += fmt.Sprintf(fmt.Sprintf(" %%%ds", floatCols), clac.Sprint(stack[i]))
			if val, err := clac.Trunc(stack[i]); err == nil {
				clac.SetFormat(hexFmt)
				hexStr := fmt.Sprintf(fmt.Sprintf(" %%%ds", hexCols-1), clac.Sprint(val))
				if len(hexStr) > hexCols {
					hexStr = hexStr[:hexCols-1] + "…"
				}
				line += hexStr
			}
		}
		fmt.Println(line + "\r")
	}
	if lastErr == nil {
		fmt.Println(strings.Repeat("-", cols))
	} else {
		fmt.Println("Error:", lastErr)
	}
	fmt.Print("\r")
}

func clearScreen() {
	fmt.Print("\033[2J\033[H")
}

func waitKey() {
	bufio.NewReader(os.Stdin).ReadByte()
}
<commit_msg>Removed conflicting alias.<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"sort"
	"strings"
	"syscall"

	"github.com/ianremmler/clac"
	"golang.org/x/crypto/ssh/terminal"
	"robpike.io/ivy/value"
)

const usageStr = `usage:

Interactive:  clac [-i <input>]
Command line: clac [-x | -p <precision>] <input>

Command line mode requires input from arguments (without -i) and/or stdin.
`

var (
	// flags
	doInitStack      = false
	doHexOut         = false
	cliPrec     uint = 12

	trm         *terminal.Terminal
	oldTrmState *terminal.State
	lastErr     error
	cl          = clac.New()
	cmdList     = []string{}
)

var cmdMap = map[string]func() error{
	"neg":    cl.Neg,
	"n":      cl.Neg,
	"abs":    cl.Abs,
	"a":      cl.Abs,
	"inv":    cl.Inv,
	"i":      cl.Inv,
	"+":      cl.Add,
	"-":      cl.Sub,
	"*":      cl.Mul,
	"x":      cl.Mul,
	"/":      cl.Div,
	"div":    cl.IntDiv,
	"%":      cl.Mod,
	"exp":    cl.Exp,
	"^":      cl.Pow,
	"2^":     cl.Pow2,
	"10^":    cl.Pow10,
	"logn":   cl.LogN,
	"ln":     cl.Ln,
	"log":    cl.Log,
	"lg":     cl.Lg,
	"sqrt":   cl.Sqrt,
	"!":      cl.Factorial,
	"comb":   cl.Comb,
	"perm":   cl.Perm,
	"sin":    cl.Sin,
	"cos":    cl.Cos,
	"tan":    cl.Tan,
	"asin":   cl.Asin,
	"acos":   cl.Acos,
	"atan":   cl.Atan,
	"atan2":  cl.Atan2,
	"dtor":   cl.DegToRad,
	"rtod":   cl.RadToDeg,
	"rtop":   cl.RectToPolar,
	"ptor":   cl.PolarToRect,
	"floor":  cl.Floor,
	"ceil":   cl.Ceil,
	"trunc":  cl.Trunc,
	"and":    cl.And,
	"or":     cl.Or,
	"xor":    cl.Xor,
	"not":    cl.Not,
	"andn":   cl.AndN,
	"orn":    cl.OrN,
	"xorn":   cl.XorN,
	"sum":    cl.Sum,
	"avg":    cl.Avg,
	"drop":   cl.Drop,
	"k":      cl.Drop,
	"dropn":  cl.DropN,
	"dropr":  cl.DropR,
	"dup":    cl.Dup,
	"d":      cl.Dup,
	"dupn":   cl.DupN,
	"dupr":   cl.DupR,
	"pick":   cl.Pick,
	"p":      cl.Pick,
	"swap":   cl.Swap,
	"s":      cl.Swap,
	"depth":  cl.Depth,
	"min":    cl.Min,
	"max":    cl.Max,
	"minn":   cl.MinN,
	"maxn":   cl.MaxN,
	"rot":    cl.Rot,
	"rotr":   cl.RotR,
	"unrot":  cl.Unrot,
	"unrotr": cl.UnrotR,
	"mag":    cl.Mag,
	"hyp":    cl.Hypot,
	"dot":    cl.Dot,
	"dot3":   cl.Dot3,
	"cross":  cl.Cross,
	"pi":     constant(clac.Pi),
	"e":      constant(clac.E),
	"phi":    constant(clac.Phi),
}

var interactiveCmdMap = map[string]func() error{
	"undo":  cl.Undo,
	"u":     cl.Undo,
	"redo":  cl.Redo,
	"r":     cl.Redo,
	"clear": cl.Clear,
	"reset": cl.Reset,
	"help":  help,
	"quit":  exit,
}

type term struct {
	io.Reader
	io.Writer
}

func init() {
	log.SetFlags(0)
	log.SetPrefix("clac: ")
	flag.BoolVar(&doHexOut, "x", doHexOut,
		"Command line mode: hexidecimal output")
	flag.UintVar(&cliPrec, "p", cliPrec,
		"Command line mode: output precision")
	flag.BoolVar(&doInitStack, "i", doInitStack,
		"Initialize with input from command line arguments")
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, usageStr)
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	if !processCmdLine() {
		printCmdLineStack(cl.Stack())
		os.Exit(0)
	}

	interactiveSetup()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)
	go func() {
		<-sigChan
		exit()
	}()

	repl()
}

func constant(v value.Value) func() error {
	return func() error { return cl.Push(v) }
}

func interactiveSetup() {
	if !terminal.IsTerminal(syscall.Stdin) {
		log.Fatalln("this doesn't look like an interactive terminal")
	}
	var err error
	oldTrmState, err = terminal.MakeRaw(syscall.Stdin)
	if err != nil {
		log.Fatalln(err)
	}
	trm = terminal.NewTerminal(term{os.Stdin, os.Stdout}, "")

	for cmd, fn := range interactiveCmdMap {
		cmdMap[cmd] = fn
	}
	for cmd := range cmdMap {
		cmdList = append(cmdList, cmd)
	}
	sort.Strings(cmdList)
}

func repl() {
	for {
		printStack(cl.Stack())
		input, err := trm.ReadLine()
		lastErr = nil
		if err == io.EOF {
			exit()
		}
		if err != nil {
			continue
		}
		processInput(input, true)
	}
}

func processCmdLine() bool {
	input := ""
	if stat, err := os.Stdin.Stat(); err == nil && stat.Mode()&os.ModeNamedPipe != 0 {
		if pipeInput, err := ioutil.ReadAll(os.Stdin); err == nil {
			input = string(pipeInput)
		}
	}
	if len(flag.Args()) > 0 {
		input += " " + strings.Join(flag.Args(), " ")
	}
	isInteractive := doInitStack || (input == "")
	if isInteractive {
		cl.EnableHistory(isInteractive)
	}
	processInput(string(input), false)
	return isInteractive
}

func printCmdLineStack(stack clac.Stack) {
	if doHexOut {
		clac.SetFormat("%#x")
	} else {
		clac.SetFormat(fmt.Sprintf("%%.%dg", cliPrec))
	}
	for i := range stack {
		val := stack[len(stack)-i-1]
		var err error
		if doHexOut {
			val, err = clac.Trunc(val)
		}
		if err != nil {
			fmt.Print("error")
		} else {
			fmt.Print(clac.Sprint(val))
		}
		if i < len(stack)-1 {
			fmt.Print(" ")
		}
	}
	fmt.Println()
}

func exit() error {
	terminal.Restore(syscall.Stdin, oldTrmState)
	fmt.Println()
	os.Exit(0)
	return nil
}

func help() error {
	clearScreen()
	for i := range cmdList {
		fmt.Printf("%-8s", cmdList[i])
		if (i+1)%5 == 0 {
			fmt.Println("\r")
		}
	}
	if len(cmdList)%5 != 0 {
		fmt.Println("\r")
	}
	fmt.Print("\n[Press any key to continue]")
	waitKey()
	return clac.ErrNoHistUpdate
}

func processInput(input string, isInteractive bool) {
	errorHandler := func(err error) { lastErr = err }
	if !isInteractive {
		errorHandler = func(err error) { log.Println(err) }
	}
	scanner := bufio.NewScanner(strings.NewReader(input))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		tok := scanner.Text()
		if num, err := clac.ParseNum(tok); err == nil {
			if err = cl.Exec(func() error { return cl.Push(num) }); err != nil {
				errorHandler(fmt.Errorf("push: %s", err))
			}
			continue
		}
		if cmd, ok := cmdMap[tok]; ok {
			if err := cl.Exec(cmd); err != nil {
				errorHandler(fmt.Errorf("%s: %s", tok, err))
			}
			continue
		}
		errorHandler(fmt.Errorf("%s: invalid input", tok))
	}
	if err := scanner.Err(); err != nil {
		errorHandler(err)
	}
}

func printStack(stack clac.Stack) {
	cols, rows, err := terminal.GetSize(syscall.Stdout)
	if err != nil {
		rows = len(stack) + 1
	}
	// ensure sane width
	if cols < 20 {
		cols = 20
	}
	clearScreen()

	dataCols := cols - 4
	hexCols := dataCols / 2
	floatCols := dataCols - hexCols
	floatFmt := fmt.Sprintf("%%%d.%dg", floatCols-1, floatCols-8)
	hexFmt := fmt.Sprintf("%%#%dx", hexCols-3)
	for i := rows - 3; i >= 0; i-- {
		line := fmt.Sprintf("%02d:", i)
		if i < len(stack) {
			clac.SetFormat(floatFmt)
			line += fmt.Sprintf(fmt.Sprintf(" %%%ds", floatCols), clac.Sprint(stack[i]))
			if val, err := clac.Trunc(stack[i]); err == nil {
				clac.SetFormat(hexFmt)
				hexStr := fmt.Sprintf(fmt.Sprintf(" %%%ds", hexCols-1), clac.Sprint(val))
				if len(hexStr) > hexCols {
					hexStr = hexStr[:hexCols-1] + "…"
				}
				line += hexStr
			}
		}
		fmt.Println(line + "\r")
	}
	if lastErr == nil {
		fmt.Println(strings.Repeat("-", cols))
	} else {
		fmt.Println("Error:", lastErr)
	}
	fmt.Print("\r")
}

func clearScreen() {
	fmt.Print("\033[2J\033[H")
}

func waitKey() {
	bufio.NewReader(os.Stdin).ReadByte()
}
<|endoftext|>