tendermint/vm/stack.go
2015-03-18 23:23:56 -07:00

140 lines
2.5 KiB
Go

package vm
import (
"encoding/binary"
"errors"
"fmt"
)
var (
ErrDataStackOverflow = errors.New("DataStackOverflow")
ErrDataStackUnderflow = errors.New("DataStackUnderflow")
)
type Word [4]uint64
func Bytes2Uint64(bz []byte) uint64 {
return binary.LittleEndian.Uint64(bz)
}
func Uint642Bytes(dest []byte, i uint64) {
binary.LittleEndian.PutUint64(dest, i)
}
// Not goroutine safe
type Stack struct {
data []Word
ptr int
gas *uint64
err *error
}
func NewStack(capacity int, gas *uint64, err *error) *Stack {
return &Stack{
data: make([]Word, capacity),
ptr: 0,
gas: gas,
err: err,
}
}
func (st *Stack) Push(d Word) error {
if st.ptr == cap(st.data) {
return ErrDataStackOverflow
}
st.data[st.ptr] = d
st.ptr++
}
func (st *Stack) Push64(i uint64) error {
if st.ptr == cap(st.data) {
return ErrDataStackOverflow
}
st.data[st.ptr] = [4]uint64{i, 0, 0, 0}
st.ptr++
}
func (st *Stack) PushBytes(bz []byte) error {
if len(bz) != 32 {
panic("Invalid bytes size: expected 32")
}
if st.ptr == cap(st.data) {
return ErrDataStackOverflow
}
st.data[st.ptr] = [4]uint64{
Bytes2Uint64(bz[0:8]),
Bytes2Uint64(bz[8:16]),
Bytes2Uint64(bz[16:24]),
Bytes2Uint64(bz[24:32]),
}
st.ptr++
}
func (st *Stack) Pop() (Word, error) {
if st.ptr == 0 {
return Zero, ErrDataStackUnderflow
}
st.ptr--
return st.data[st.ptr], nil
}
func (st *Stack) Pop64() (uint64, error) {
if st.ptr == 0 {
return Zero, ErrDataStackUnderflow
}
st.ptr--
return st.data[st.ptr][0], nil
}
func (st *Stack) PopBytes() ([]byte, error) {
if st.ptr == 0 {
return Zero, ErrDataStackUnderflow
}
st.ptr--
res := make([]byte, 32)
copy(res[0:8], Uint642Bytes(st.data[st.ptr][0]))
copy(res[8:16], Uint642Bytes(st.data[st.ptr][1]))
copy(res[16:24], Uint642Bytes(st.data[st.ptr][2]))
copy(res[24:32], Uint642Bytes(st.data[st.ptr][3]))
return res, nil
}
func (st *Stack) Len() int {
return st.ptr
}
func (st *Stack) Swap(n int) error {
if st.ptr < n {
return ErrDataStackUnderflow
}
st.data[st.ptr-n], st.data[st.ptr-1] = st.data[st.ptr-1], st.data[st.ptr-n]
return nil
}
func (st *Stack) Dup(n int) {
st.Push(st.data[st.ptr-n])
}
func (st *Stack) Peek() Word {
return st.data[st.ptr-1]
}
func (st *Stack) Require(n int) error {
if st.ptr < n {
return ErrDataStackUnderflow
}
}
func (st *Stack) Print() {
fmt.Println("### stack ###")
if st.ptr > 0 {
for i, val := range st.data {
fmt.Printf("%-3d %v\n", i, val)
}
} else {
fmt.Println("-- empty --")
}
fmt.Println("#############")
}