<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package atomic

import (
	"bytes"
	"errors"

	"github.com/ava-labs/avalanchego/codec"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/linkeddb"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/hashing"
)

var (
	smallerValuePrefix = []byte{0}
	smallerIndexPrefix = []byte{1}
	largerValuePrefix  = []byte{2}
	largerIndexPrefix  = []byte{3}

	errDuplicatedOperation = errors.New("duplicated operation on provided value")
<<<<<<< HEAD
	errEmptyBatch          = errors.New("there are no transactions in this batch")
=======

	_ SharedMemory = &sharedMemory{}
>>>>>>> 9861943e76bdb1330452b070f1ada4dbd404c6ac
)

type SharedMemoryMethod int

const (
	Put SharedMemoryMethod = iota
	Remove
)

type Requests struct {
	RequestType SharedMemoryMethod
	UtxoIDs     [][]byte
	Elems       []*Element
}

type dbElement struct {
	// Present indicates the value was removed before existing.
	// If set to false, when this element is added to the shared memory, it will
	// be immediately removed.
	// If set to true, then this element will be removed normally when remove is
	// called.
	Present bool `serialize:"true"`

	// Value is the body of this element.
	Value []byte `serialize:"true"`

	// Traits are a collection of features that can be used to lookup this
	// element.
	Traits [][]byte `serialize:"true"`
}

// Element ...
type Element struct {
	Key    []byte
	Value  []byte
	Traits [][]byte
}

// SharedMemory ...
type SharedMemory interface {
	// Adds to the peer chain's side
	Put(peerChainID ids.ID, elems []*Element, batches ...database.Batch) error

	// Fetches from this chain's side
	Get(peerChainID ids.ID, keys [][]byte) (values [][]byte, err error)
	Indexed(
		peerChainID ids.ID,
		traits [][]byte,
		startTrait,
		startKey []byte,
		limit int,
	) (
		values [][]byte,
		lastTrait,
		lastKey []byte,
		err error,
	)
	Remove(peerChainID ids.ID, keys [][]byte, batches ...database.Batch) error

	RemoveAndPutMultiple(batchChainsAndInputs map[ids.ID][]*Requests, batches ...database.Batch) error
}

// sharedMemory provides the API for a blockchain to interact with shared memory
// of another blockchain
type sharedMemory struct {
	m           *Memory
	thisChainID ids.ID
}

func fetchValueAndIndexDB(smChainID []byte, peerChainID []byte, requestType SharedMemoryMethod, db database.Database) (database.Database, database.Database) {
	var valueDB, indexDB database.Database
	switch requestType {
	case Remove:
		if bytes.Compare(smChainID, peerChainID) == -1 {
			valueDB = prefixdb.New(smallerValuePrefix, db)
			indexDB = prefixdb.New(smallerIndexPrefix, db)
		} else {
			valueDB = prefixdb.New(largerValuePrefix, db)
			indexDB = prefixdb.New(largerIndexPrefix, db)
		}
	case Put:
		if bytes.Compare(smChainID, peerChainID) == -1 {
			valueDB = prefixdb.New(largerValuePrefix, db)
			indexDB = prefixdb.New(largerIndexPrefix, db)
		} else {
			valueDB = prefixdb.New(smallerValuePrefix, db)
			indexDB = prefixdb.New(smallerIndexPrefix, db)
		}
	default:
		panic("Illegal type")
	}

	return valueDB, indexDB
}

func (sm *sharedMemory) Put(peerChainID ids.ID, elems []*Element, batches ...database.Batch) error {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	vdb, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}

	s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Put, db)

	for _, elem := range elems {
		if err := s.SetValue(elem); err != nil {
			return err
		}
	}

	myBatch, err := vdb.CommitBatch()
	if err != nil {
		return err
	}
	return WriteAll(myBatch, batches...)
}

func (sm *sharedMemory) Get(peerChainID ids.ID, keys [][]byte) ([][]byte, error) {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	_, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}
	if bytes.Compare(sm.thisChainID[:], peerChainID[:]) == -1 {
		s.valueDB = prefixdb.New(smallerValuePrefix, db)
	} else {
		s.valueDB = prefixdb.New(largerValuePrefix, db)
	}

	values := make([][]byte, len(keys))
	for i, key := range keys {
		elem, err := s.Value(key)
		if err != nil {
			return nil, err
		}
		values[i] = elem.Value
	}
	return values, nil
}

func (sm *sharedMemory) Indexed(
	peerChainID ids.ID,
	traits [][]byte,
	startTrait,
	startKey []byte,
	limit int,
) ([][]byte, []byte, []byte, error) {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	_, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}
	if bytes.Compare(sm.thisChainID[:], peerChainID[:]) == -1 {
		s.valueDB = prefixdb.New(smallerValuePrefix, db)
		s.indexDB = prefixdb.New(smallerIndexPrefix, db)
	} else {
		s.valueDB = prefixdb.New(largerValuePrefix, db)
		s.indexDB = prefixdb.New(largerIndexPrefix, db)
	}

	keys, lastTrait, lastKey, err := s.getKeys(traits, startTrait, startKey, limit)
	if err != nil {
		return nil, nil, nil, err
	}

	values := make([][]byte, len(keys))
	for i, key := range keys {
		elem, err := s.Value(key)
		if err != nil {
			return nil, nil, nil, err
		}
		values[i] = elem.Value
	}
	return values, lastTrait, lastKey, nil
}

func (sm *sharedMemory) RemoveAndPutMultiple(batchChainsAndInputs map[ids.ID][]*Requests, batches ...database.Batch) error {

	if len(batchChainsAndInputs) == 0 {
		return errEmptyBatch
	}

	versionDBBatches := make([]database.Batch, 0, len(batchChainsAndInputs))
	sharedIDVersionDB := make(map[ids.ID]*versiondb.Database, len(batchChainsAndInputs))
	var vdb *versiondb.Database

	for peerChainID, atomicRequests := range batchChainsAndInputs {
		sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)

		var db database.Database

		if vdb == nil {
			vdb, db = sm.m.GetDatabase(sharedID)
			sharedIDVersionDB[sharedID] = vdb
			defer sm.m.ReleaseDatabase(sharedID)
		} else {
			db = sm.m.GetPrefixDBInstanceFromVdb(vdb, sharedID)
		}

		s := state{
			c: sm.m.codec,
		}

		for _, atomicRequest := range atomicRequests {
			switch atomicRequest.RequestType {
			case Remove:
				s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Remove, db)

				for _, key := range atomicRequest.UtxoIDs {
					if err := s.RemoveValue(key); err != nil {
						return err
					}
				}
			case Put:
				s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Put, db)

				for _, elem := range atomicRequest.Elems {
					if err := s.SetValue(elem); err != nil {
						return err
					}
				}
			default:
				panic("Illegal type")
			}
		}
	}

	for _, vdb := range sharedIDVersionDB {
		myBatch, err := vdb.CommitBatch()
		if err != nil {
			return err
		}

		versionDBBatches = append(versionDBBatches, myBatch)
	}

	baseBatch := versionDBBatches[0]

	if len(versionDBBatches) > 1 {
		batches = append(batches, versionDBBatches[1:]...)
	}

	return WriteAll(baseBatch, batches...)
}

func (sm *sharedMemory) Remove(peerChainID ids.ID, keys [][]byte, batches ...database.Batch) error {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	vdb, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}

	s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Remove, db)

	for _, key := range keys {
		if err := s.RemoveValue(key); err != nil {
			return err
		}
	}

	myBatch, err := vdb.CommitBatch()
	if err != nil {
		return err
	}
	return WriteAll(myBatch, batches...)
}

type state struct {
	c       codec.Manager
	valueDB database.Database
	indexDB database.Database
}

func (s *state) Value(key []byte) (*Element, error) {
	value, err := s.loadValue(key)
	if err != nil {
		return nil, err
	}

	if !value.Present {
		return nil, database.ErrNotFound
	}

	return &Element{
		Key:    key,
		Value:  value.Value,
		Traits: value.Traits,
	}, nil
}

func (s *state) SetValue(e *Element) error {
	value, err := s.loadValue(e.Key)
	if err == nil {
		// The key was already registered with the state.

		if !value.Present {
			// This was previously optimistically deleted from the database, so
			// it should be immediately removed.
			return s.valueDB.Delete(e.Key)
		}

		// This key was written twice, which is invalid
		return errDuplicatedOperation
	}
	if err != database.ErrNotFound {
		// An unexpected error occurred, so we should propagate that error
		return err
	}

	for _, trait := range e.Traits {
		traitDB := prefixdb.New(trait, s.indexDB)
		traitList := linkeddb.NewDefault(traitDB)
		if err := traitList.Put(e.Key, nil); err != nil {
			return err
		}
	}

	dbElem := dbElement{
		Present: true,
		Value:   e.Value,
		Traits:  e.Traits,
	}

	valueBytes, err := s.c.Marshal(codecVersion, &dbElem)
	if err != nil {
		return err
	}
	return s.valueDB.Put(e.Key, valueBytes)
}

func (s *state) RemoveValue(key []byte) error {
	value, err := s.loadValue(key)
	if err != nil {
		if err != database.ErrNotFound {
			// An unexpected error occurred, so we should propagate that error
			return err
		}

		// The value doesn't exist, so we should optimistically deleted it
		dbElem := dbElement{Present: false}
		valueBytes, err := s.c.Marshal(codecVersion, &dbElem)
		if err != nil {
			return err
		}
		return s.valueDB.Put(key, valueBytes)
	}

	// Don't allow the removal of something that was already removed.
	if !value.Present {
		return errDuplicatedOperation
	}

	for _, trait := range value.Traits {
		traitDB := prefixdb.New(trait, s.indexDB)
		traitList := linkeddb.NewDefault(traitDB)
		if err := traitList.Delete(key); err != nil {
			return err
		}
	}
	return s.valueDB.Delete(key)
}

func (s *state) loadValue(key []byte) (*dbElement, error) {
	valueBytes, err := s.valueDB.Get(key)
	if err != nil {
		return nil, err
	}

	// The key was in the database
	value := &dbElement{}
	_, err = s.c.Unmarshal(valueBytes, value)
	return value, err
}

func (s *state) getKeys(traits [][]byte, startTrait, startKey []byte, limit int) ([][]byte, []byte, []byte, error) {
	tracked := ids.Set{}
	keys := [][]byte(nil)
	lastTrait := startTrait
	lastKey := startKey
	utils.Sort2DBytes(traits)
	for _, trait := range traits {
		switch bytes.Compare(trait, startTrait) {
		case -1:
			continue
		case 1:
			startKey = nil
		}

		lastTrait = trait
		var err error
		lastKey, err = s.appendTraitKeys(&keys, &tracked, &limit, trait, startKey)
		if err != nil {
			return nil, nil, nil, err
		}

		if limit == 0 {
			break
		}
	}
	return keys, lastTrait, lastKey, nil
}

func (s *state) appendTraitKeys(keys *[][]byte, tracked *ids.Set, limit *int, trait, startKey []byte) ([]byte, error) {
	lastKey := startKey

	traitDB := prefixdb.New(trait, s.indexDB)
	traitList := linkeddb.NewDefault(traitDB)
	iter := traitList.NewIteratorWithStart(startKey)
	defer iter.Release()
	for iter.Next() && *limit > 0 {
		key := iter.Key()
		lastKey = key

		id := hashing.ComputeHash256Array(key)
		if tracked.Contains(id) {
			continue
		}

		tracked.Add(id)
		*keys = append(*keys, key)
		*limit--
	}
	return lastKey, iter.Error()
}
<commit_msg>chore: fix merge conflicts<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package atomic

import (
	"bytes"
	"errors"

	"github.com/ava-labs/avalanchego/codec"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/linkeddb"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/hashing"
)

var (
	smallerValuePrefix = []byte{0}
	smallerIndexPrefix = []byte{1}
	largerValuePrefix  = []byte{2}
	largerIndexPrefix  = []byte{3}

	errDuplicatedOperation = errors.New("duplicated operation on provided value")
	errEmptyBatch          = errors.New("there are no transactions in this batch")

	_ SharedMemory = &sharedMemory{}
)

type SharedMemoryMethod int

const (
	Put SharedMemoryMethod = iota
	Remove
)

type Requests struct {
	RequestType SharedMemoryMethod
	UtxoIDs     [][]byte
	Elems       []*Element
}

type dbElement struct {
	// Present indicates the value was removed before existing.
	// If set to false, when this element is added to the shared memory, it will
	// be immediately removed.
	// If set to true, then this element will be removed normally when remove is
	// called.
	Present bool `serialize:"true"`

	// Value is the body of this element.
	Value []byte `serialize:"true"`

	// Traits are a collection of features that can be used to lookup this
	// element.
	Traits [][]byte `serialize:"true"`
}

// Element ...
type Element struct {
	Key    []byte
	Value  []byte
	Traits [][]byte
}

// SharedMemory ...
type SharedMemory interface {
	// Adds to the peer chain's side
	Put(peerChainID ids.ID, elems []*Element, batches ...database.Batch) error

	// Fetches from this chain's side
	Get(peerChainID ids.ID, keys [][]byte) (values [][]byte, err error)
	Indexed(
		peerChainID ids.ID,
		traits [][]byte,
		startTrait,
		startKey []byte,
		limit int,
	) (
		values [][]byte,
		lastTrait,
		lastKey []byte,
		err error,
	)
	Remove(peerChainID ids.ID, keys [][]byte, batches ...database.Batch) error

	RemoveAndPutMultiple(batchChainsAndInputs map[ids.ID][]*Requests, batches ...database.Batch) error
}

// sharedMemory provides the API for a blockchain to interact with shared memory
// of another blockchain
type sharedMemory struct {
	m           *Memory
	thisChainID ids.ID
}

func fetchValueAndIndexDB(smChainID []byte, peerChainID []byte, requestType SharedMemoryMethod, db database.Database) (database.Database, database.Database) {
	var valueDB, indexDB database.Database
	switch requestType {
	case Remove:
		if bytes.Compare(smChainID, peerChainID) == -1 {
			valueDB = prefixdb.New(smallerValuePrefix, db)
			indexDB = prefixdb.New(smallerIndexPrefix, db)
		} else {
			valueDB = prefixdb.New(largerValuePrefix, db)
			indexDB = prefixdb.New(largerIndexPrefix, db)
		}
	case Put:
		if bytes.Compare(smChainID, peerChainID) == -1 {
			valueDB = prefixdb.New(largerValuePrefix, db)
			indexDB = prefixdb.New(largerIndexPrefix, db)
		} else {
			valueDB = prefixdb.New(smallerValuePrefix, db)
			indexDB = prefixdb.New(smallerIndexPrefix, db)
		}
	default:
		panic("Illegal type")
	}

	return valueDB, indexDB
}

func (sm *sharedMemory) Put(peerChainID ids.ID, elems []*Element, batches ...database.Batch) error {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	vdb, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}

	s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Put, db)

	for _, elem := range elems {
		if err := s.SetValue(elem); err != nil {
			return err
		}
	}

	myBatch, err := vdb.CommitBatch()
	if err != nil {
		return err
	}
	return WriteAll(myBatch, batches...)
}

func (sm *sharedMemory) Get(peerChainID ids.ID, keys [][]byte) ([][]byte, error) {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	_, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}
	if bytes.Compare(sm.thisChainID[:], peerChainID[:]) == -1 {
		s.valueDB = prefixdb.New(smallerValuePrefix, db)
	} else {
		s.valueDB = prefixdb.New(largerValuePrefix, db)
	}

	values := make([][]byte, len(keys))
	for i, key := range keys {
		elem, err := s.Value(key)
		if err != nil {
			return nil, err
		}
		values[i] = elem.Value
	}
	return values, nil
}

func (sm *sharedMemory) Indexed(
	peerChainID ids.ID,
	traits [][]byte,
	startTrait,
	startKey []byte,
	limit int,
) ([][]byte, []byte, []byte, error) {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	_, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}
	if bytes.Compare(sm.thisChainID[:], peerChainID[:]) == -1 {
		s.valueDB = prefixdb.New(smallerValuePrefix, db)
		s.indexDB = prefixdb.New(smallerIndexPrefix, db)
	} else {
		s.valueDB = prefixdb.New(largerValuePrefix, db)
		s.indexDB = prefixdb.New(largerIndexPrefix, db)
	}

	keys, lastTrait, lastKey, err := s.getKeys(traits, startTrait, startKey, limit)
	if err != nil {
		return nil, nil, nil, err
	}

	values := make([][]byte, len(keys))
	for i, key := range keys {
		elem, err := s.Value(key)
		if err != nil {
			return nil, nil, nil, err
		}
		values[i] = elem.Value
	}
	return values, lastTrait, lastKey, nil
}

func (sm *sharedMemory) RemoveAndPutMultiple(batchChainsAndInputs map[ids.ID][]*Requests, batches ...database.Batch) error {

	if len(batchChainsAndInputs) == 0 {
		return errEmptyBatch
	}

	versionDBBatches := make([]database.Batch, 0, len(batchChainsAndInputs))
	sharedIDVersionDB := make(map[ids.ID]*versiondb.Database, len(batchChainsAndInputs))
	var vdb *versiondb.Database

	for peerChainID, atomicRequests := range batchChainsAndInputs {
		sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)

		var db database.Database

		if vdb == nil {
			vdb, db = sm.m.GetDatabase(sharedID)
			sharedIDVersionDB[sharedID] = vdb
			defer sm.m.ReleaseDatabase(sharedID)
		} else {
			db = sm.m.GetPrefixDBInstanceFromVdb(vdb, sharedID)
		}

		s := state{
			c: sm.m.codec,
		}

		for _, atomicRequest := range atomicRequests {
			switch atomicRequest.RequestType {
			case Remove:
				s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Remove, db)

				for _, key := range atomicRequest.UtxoIDs {
					if err := s.RemoveValue(key); err != nil {
						return err
					}
				}
			case Put:
				s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Put, db)

				for _, elem := range atomicRequest.Elems {
					if err := s.SetValue(elem); err != nil {
						return err
					}
				}
			default:
				panic("Illegal type")
			}
		}
	}

	for _, vdb := range sharedIDVersionDB {
		myBatch, err := vdb.CommitBatch()
		if err != nil {
			return err
		}

		versionDBBatches = append(versionDBBatches, myBatch)
	}

	baseBatch := versionDBBatches[0]

	if len(versionDBBatches) > 1 {
		batches = append(batches, versionDBBatches[1:]...)
	}

	return WriteAll(baseBatch, batches...)
}

func (sm *sharedMemory) Remove(peerChainID ids.ID, keys [][]byte, batches ...database.Batch) error {
	sharedID := sm.m.sharedID(peerChainID, sm.thisChainID)
	vdb, db := sm.m.GetDatabase(sharedID)
	defer sm.m.ReleaseDatabase(sharedID)

	s := state{
		c: sm.m.codec,
	}

	s.valueDB, s.indexDB = fetchValueAndIndexDB(sm.thisChainID[:], peerChainID[:], Remove, db)

	for _, key := range keys {
		if err := s.RemoveValue(key); err != nil {
			return err
		}
	}

	myBatch, err := vdb.CommitBatch()
	if err != nil {
		return err
	}
	return WriteAll(myBatch, batches...)
}

type state struct {
	c       codec.Manager
	valueDB database.Database
	indexDB database.Database
}

func (s *state) Value(key []byte) (*Element, error) {
	value, err := s.loadValue(key)
	if err != nil {
		return nil, err
	}

	if !value.Present {
		return nil, database.ErrNotFound
	}

	return &Element{
		Key:    key,
		Value:  value.Value,
		Traits: value.Traits,
	}, nil
}

func (s *state) SetValue(e *Element) error {
	value, err := s.loadValue(e.Key)
	if err == nil {
		// The key was already registered with the state.

		if !value.Present {
			// This was previously optimistically deleted from the database, so
			// it should be immediately removed.
			return s.valueDB.Delete(e.Key)
		}

		// This key was written twice, which is invalid
		return errDuplicatedOperation
	}
	if err != database.ErrNotFound {
		// An unexpected error occurred, so we should propagate that error
		return err
	}

	for _, trait := range e.Traits {
		traitDB := prefixdb.New(trait, s.indexDB)
		traitList := linkeddb.NewDefault(traitDB)
		if err := traitList.Put(e.Key, nil); err != nil {
			return err
		}
	}

	dbElem := dbElement{
		Present: true,
		Value:   e.Value,
		Traits:  e.Traits,
	}

	valueBytes, err := s.c.Marshal(codecVersion, &dbElem)
	if err != nil {
		return err
	}
	return s.valueDB.Put(e.Key, valueBytes)
}

func (s *state) RemoveValue(key []byte) error {
	value, err := s.loadValue(key)
	if err != nil {
		if err != database.ErrNotFound {
			// An unexpected error occurred, so we should propagate that error
			return err
		}

		// The value doesn't exist, so we should optimistically deleted it
		dbElem := dbElement{Present: false}
		valueBytes, err := s.c.Marshal(codecVersion, &dbElem)
		if err != nil {
			return err
		}
		return s.valueDB.Put(key, valueBytes)
	}

	// Don't allow the removal of something that was already removed.
	if !value.Present {
		return errDuplicatedOperation
	}

	for _, trait := range value.Traits {
		traitDB := prefixdb.New(trait, s.indexDB)
		traitList := linkeddb.NewDefault(traitDB)
		if err := traitList.Delete(key); err != nil {
			return err
		}
	}
	return s.valueDB.Delete(key)
}

func (s *state) loadValue(key []byte) (*dbElement, error) {
	valueBytes, err := s.valueDB.Get(key)
	if err != nil {
		return nil, err
	}

	// The key was in the database
	value := &dbElement{}
	_, err = s.c.Unmarshal(valueBytes, value)
	return value, err
}

func (s *state) getKeys(traits [][]byte, startTrait, startKey []byte, limit int) ([][]byte, []byte, []byte, error) {
	tracked := ids.Set{}
	keys := [][]byte(nil)
	lastTrait := startTrait
	lastKey := startKey
	utils.Sort2DBytes(traits)
	for _, trait := range traits {
		switch bytes.Compare(trait, startTrait) {
		case -1:
			continue
		case 1:
			startKey = nil
		}

		lastTrait = trait
		var err error
		lastKey, err = s.appendTraitKeys(&keys, &tracked, &limit, trait, startKey)
		if err != nil {
			return nil, nil, nil, err
		}

		if limit == 0 {
			break
		}
	}
	return keys, lastTrait, lastKey, nil
}

func (s *state) appendTraitKeys(keys *[][]byte, tracked *ids.Set, limit *int, trait, startKey []byte) ([]byte, error) {
	lastKey := startKey

	traitDB := prefixdb.New(trait, s.indexDB)
	traitList := linkeddb.NewDefault(traitDB)
	iter := traitList.NewIteratorWithStart(startKey)
	defer iter.Release()
	for iter.Next() && *limit > 0 {
		key := iter.Key()
		lastKey = key

		id := hashing.ComputeHash256Array(key)
		if tracked.Contains(id) {
			continue
		}

		tracked.Add(id)
		*keys = append(*keys, key)
		*limit--
	}
	return lastKey, iter.Error()
}
<|endoftext|>


<commit_before>package scraper

import (
	"encoding/json"
	"errors"
	"strconv"
	"strings"
	"time"
)

type PersonaState int

const (
	PersonaStateOffline PersonaState = 0
	PersonaStateOnline  PersonaState = 1
	PersonaStateBusy    PersonaState = 2
	PersonaStateAway    PersonaState = 3
	PersonaStateSnooze  PersonaState = 4
	PersonaStateLTTrade PersonaState = 5
	PersonaStateLTPlay  PersonaState = 6
)

var PersonaStatesStr = [...]string{
	"Offline",
	"Online",
	"Busy",
	"Away",
	"Snooze",
	"Looking to Trade",
	"Looking to Play",
}

func (ps *PersonaState) String() string {
	return PersonaStatesStr[int(*ps)]
}

type PlayerInfo struct {
	// name
	Name        string
	Personaname string
	Realname    string

	// profile
	Profileurl        string
	Personastate      PersonaState
	Profilestate      int
	Commentpermission string

	// steam
	Steamid                  string
	Communityvisibilitystate int
	Visibility               string

	// time creaTed
	Timecreated int

	// location
	Loccountrycode string
	Locstatecode   string
	Loccityid      int

	// logoff
	Lastlogoff int

	// avatar
	Avatar       string
	Avatarfull   string
	Avatarmedium string
}

// get player "timecreated" as Time
func (p *PlayerInfo) GetTimeCreated() time.Time {
	return time.Unix(int64(p.Timecreated), 0)
}

// get player "lastlogoff" as Time
func (p *PlayerInfo) GetLogoffTime() time.Time {
	return time.Unix(int64(p.Lastlogoff), 0)
}

func tryParsingString(arg interface{}) string {
	res, ok := arg.(string)
	if !ok {
		return ""
	}
	return res
}

func tryParsingNumber(arg interface{}) (int, error) {
	json, ok := arg.(json.Number)
	if !ok {
		return 0, errors.New("Not a json number")
	}

	res, err := json.Int64()
	if err != nil {
		return 0, err
	}
	return int(res), nil
}

// parse player json
func (p *PlayerInfo) Parse(elem map[string]interface{}) error {
	p.Steamid = elem["steamid"].(string)
	p.Profileurl = elem["profileurl"].(string)

	// profile visibility
	visibility := elem["communityvisibilitystate"].(json.Number).String()
	var vState string

	switch {
	case visibility == "1":
		vState = "private"
	case visibility == "3":
		vState = "public"
	}

	// same as communityvisibilitystate
	// but as string (public or private)
	p.Visibility = vState

	playerV, vErr := strconv.Atoi(visibility)
	if vErr != nil {
		return vErr
	}

	// int as str
	p.Communityvisibilitystate = playerV

	// Logoff
	playerL, pErr := tryParsingNumber(elem["lastlogoff"])
	p.Lastlogoff = playerL
	if pErr != nil {
		p.Lastlogoff = -1
	}

	// if the account has a steam community profile set then this should be 1
	profileState, stErr := tryParsingNumber(elem["profilestate"])
	if stErr != nil {
		return stErr
	}

	p.Profilestate = profileState

	// variables that are only available when
	// the profile visibility is set to public
	if vState == "public" {
		p.Realname = tryParsingString(elem["realname"])

		// user is online, offline...
		playerState, pstErr := tryParsingNumber(elem["personastate"])
		if pstErr != nil {
			return pstErr
		}

		p.Personastate = PersonaState(playerState)

		// timecreated
		playerTC, tcErr := tryParsingNumber(elem["timecreated"])
		if tcErr != nil {
			return tcErr
		}

		p.Timecreated = playerTC

		// location
		p.Loccountrycode = tryParsingString(elem["loccountrycode"])
		p.Locstatecode = tryParsingString(elem["locstatecode"])

		cityId, cErr := tryParsingNumber(elem["loccityid"])
		if cErr != nil {
			cityId = 0
		}

		p.Loccityid = cityId
	}

	// avatar
	p.Avatar = elem["avatar"].(string)
	p.Avatarfull = elem["avatarfull"].(string)
	p.Avatarmedium = elem["avatarmedium"].(string)

	// name
	p.Personaname = elem["personaname"].(string)
	p.Name = p.Personaname // alias

	return nil
}

var steamApiKey string

func SetSteamApiKey(key string) {
	steamApiKey = key
}

func GetTF2Hours(steamid string) (int, error) {
	games, err := GetSteamGamesOwned(steamid)
	if err != nil {
		return 0, err
	}

	return GetTF2HoursFromGamesOwned(games), nil
}

func GetTF2HoursFromGamesOwned(res *map[string]string) int {
	hours, ok := (*res)["440"]

	if !ok {
		return 0
	}

	num, err := strconv.ParseInt(hours, 10, 32)
	if err != nil {
		return 0
	}

	return int(num) / 60
}

func GetSteamGamesOwned(steamid string) (*map[string]string, error) {
	url := "http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=" +
		steamApiKey + "&steamid=" + steamid + "&include_played_free_games=1&format=json"

	response, err := getJsonFromUrl(url)

	if err != nil {
		return nil, err
	}

	res := make(map[string]string)

	ps, err := response.Get("response").Get("games").Array()
	if err != nil {
		return nil, err
	}

	for _, _elem := range ps {
		elem := _elem.(map[string]interface{})
		name := elem["appid"].(json.Number).String()
		value := elem["playtime_forever"].(json.Number).String()
		res[name] = value
	}

	return &res, nil
}

func GetTF2Stats(steamid string) (*map[string]string, error) {
	url := "http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=440&key=" +
		steamApiKey + "&steamid=" + steamid

	res := make(map[string]string)

	response, err := getJsonFromUrl(url)
	if err != nil {
		return nil, err
	}

	ps, err := response.Get("playerstats").Get("stats").Array()
	if err != nil {
		return nil, err
	}

	for _, _elem := range ps {
		elem := _elem.(map[string]interface{})
		name, _ := elem["name"].(string)
		value := elem["value"].(json.Number).String()
		res[name] = value
	}

	if err != nil {
		return nil, err
	}

	return &res, nil
}

// https://developer.valvesoftware.com/wiki/Steam_Web_API#GetPlayerSummaries_.28v0002.29
func GetPlayersInfo(steamids []string) (map[string]*PlayerInfo, error) {
	url := "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" +
		steamApiKey + "&steamids=" + strings.Join(steamids, ",")

	profiles := make(map[string]*PlayerInfo)

	response, err := getJsonFromUrl(url)
	if err != nil {
		return nil, err
	}

	ps, err := response.Get("response").Get("players").Array()
	if err != nil {
		return nil, err
	}

	for _, _elem := range ps {
		elem := _elem.(map[string]interface{})
		player := new(PlayerInfo)

		pErr := player.Parse(elem)

		if pErr != nil {
			return nil, pErr
		}

		profiles[player.Steamid] = player
	}

	return profiles, nil
}

func GetPlayerInfo(steamid string) (*PlayerInfo, error) {
	url := "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" +
		steamApiKey + "&steamids=" + steamid

	player := new(PlayerInfo)

	response, err := getJsonFromUrl(url)
	if err != nil {
		return nil, err
	}

	ps, err := response.Get("response").Get("players").Array()
	if err != nil {
		return nil, err
	}

	if len(ps) == 1 {
		elem := ps[0].(map[string]interface{})
		pErr := player.Parse(elem)

		if pErr != nil {
			return nil, pErr
		}
	}

	return player, nil
}
<commit_msg>Add ErrNoGamesOwned, use it.<commit_after>package scraper

import (
	"encoding/json"
	"errors"
	"strconv"
	"strings"
	"time"
)

type PersonaState int

const (
	PersonaStateOffline PersonaState = 0
	PersonaStateOnline  PersonaState = 1
	PersonaStateBusy    PersonaState = 2
	PersonaStateAway    PersonaState = 3
	PersonaStateSnooze  PersonaState = 4
	PersonaStateLTTrade PersonaState = 5
	PersonaStateLTPlay  PersonaState = 6
)

var PersonaStatesStr = [...]string{
	"Offline",
	"Online",
	"Busy",
	"Away",
	"Snooze",
	"Looking to Trade",
	"Looking to Play",
}

func (ps *PersonaState) String() string {
	return PersonaStatesStr[int(*ps)]
}

type PlayerInfo struct {
	// name
	Name        string
	Personaname string
	Realname    string

	// profile
	Profileurl        string
	Personastate      PersonaState
	Profilestate      int
	Commentpermission string

	// steam
	Steamid                  string
	Communityvisibilitystate int
	Visibility               string

	// time creaTed
	Timecreated int

	// location
	Loccountrycode string
	Locstatecode   string
	Loccityid      int

	// logoff
	Lastlogoff int

	// avatar
	Avatar       string
	Avatarfull   string
	Avatarmedium string
}

// get player "timecreated" as Time
func (p *PlayerInfo) GetTimeCreated() time.Time {
	return time.Unix(int64(p.Timecreated), 0)
}

// get player "lastlogoff" as Time
func (p *PlayerInfo) GetLogoffTime() time.Time {
	return time.Unix(int64(p.Lastlogoff), 0)
}

func tryParsingString(arg interface{}) string {
	res, ok := arg.(string)
	if !ok {
		return ""
	}
	return res
}

func tryParsingNumber(arg interface{}) (int, error) {
	json, ok := arg.(json.Number)
	if !ok {
		return 0, errors.New("Not a json number")
	}

	res, err := json.Int64()
	if err != nil {
		return 0, err
	}
	return int(res), nil
}

// parse player json
func (p *PlayerInfo) Parse(elem map[string]interface{}) error {
	p.Steamid = elem["steamid"].(string)
	p.Profileurl = elem["profileurl"].(string)

	// profile visibility
	visibility := elem["communityvisibilitystate"].(json.Number).String()
	var vState string

	switch {
	case visibility == "1":
		vState = "private"
	case visibility == "3":
		vState = "public"
	}

	// same as communityvisibilitystate
	// but as string (public or private)
	p.Visibility = vState

	playerV, vErr := strconv.Atoi(visibility)
	if vErr != nil {
		return vErr
	}

	// int as str
	p.Communityvisibilitystate = playerV

	// Logoff
	playerL, pErr := tryParsingNumber(elem["lastlogoff"])
	p.Lastlogoff = playerL
	if pErr != nil {
		p.Lastlogoff = -1
	}

	// if the account has a steam community profile set then this should be 1
	profileState, stErr := tryParsingNumber(elem["profilestate"])
	if stErr != nil {
		return stErr
	}

	p.Profilestate = profileState

	// variables that are only available when
	// the profile visibility is set to public
	if vState == "public" {
		p.Realname = tryParsingString(elem["realname"])

		// user is online, offline...
		playerState, pstErr := tryParsingNumber(elem["personastate"])
		if pstErr != nil {
			return pstErr
		}

		p.Personastate = PersonaState(playerState)

		// timecreated
		playerTC, tcErr := tryParsingNumber(elem["timecreated"])
		if tcErr != nil {
			return tcErr
		}

		p.Timecreated = playerTC

		// location
		p.Loccountrycode = tryParsingString(elem["loccountrycode"])
		p.Locstatecode = tryParsingString(elem["locstatecode"])

		cityId, cErr := tryParsingNumber(elem["loccityid"])
		if cErr != nil {
			cityId = 0
		}

		p.Loccityid = cityId
	}

	// avatar
	p.Avatar = elem["avatar"].(string)
	p.Avatarfull = elem["avatarfull"].(string)
	p.Avatarmedium = elem["avatarmedium"].(string)

	// name
	p.Personaname = elem["personaname"].(string)
	p.Name = p.Personaname // alias

	return nil
}

var steamApiKey string

var ErrNoGamesOwned = errors.New("No Steam game in player's profile.")

func SetSteamApiKey(key string) {
	steamApiKey = key
}

func GetTF2Hours(steamid string) (int, error) {
	games, err := GetSteamGamesOwned(steamid)
	if err != nil {
		if err == ErrNoGamesOwned {
			return 0, nil
		}
		return 0, err
	}

	return GetTF2HoursFromGamesOwned(games), nil
}

func GetTF2HoursFromGamesOwned(res *map[string]string) int {
	hours, ok := (*res)["440"]

	if !ok {
		return 0
	}

	num, err := strconv.ParseInt(hours, 10, 32)
	if err != nil {
		return 0
	}

	return int(num) / 60
}

func GetSteamGamesOwned(steamid string) (*map[string]string, error) {
	url := "http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=" +
		steamApiKey + "&steamid=" + steamid + "&include_played_free_games=true&format=json1"

	response, err := getJsonFromUrl(url)

	if err != nil {
		return nil, err
	}

	res := make(map[string]string)

	ps, err := response.Get("response").Get("games").Array()
	count, _ := response.Get("response").Get("game_count").Int()
	if err != nil && count == 0 {
		return nil, ErrNoGamesOwned
	}

	for _, _elem := range ps {
		elem := _elem.(map[string]interface{})
		name := elem["appid"].(json.Number).String()
		value := elem["playtime_forever"].(json.Number).String()
		res[name] = value
	}

	return &res, nil
}

func GetTF2Stats(steamid string) (*map[string]string, error) {
	url := "http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=440&key=" +
		steamApiKey + "&steamid=" + steamid

	res := make(map[string]string)

	response, err := getJsonFromUrl(url)
	if err != nil {
		return nil, err
	}

	ps, err := response.Get("playerstats").Get("stats").Array()
	if err != nil {
		return nil, err
	}

	for _, _elem := range ps {
		elem := _elem.(map[string]interface{})
		name, _ := elem["name"].(string)
		value := elem["value"].(json.Number).String()
		res[name] = value
	}

	if err != nil {
		return nil, err
	}

	return &res, nil
}

// https://developer.valvesoftware.com/wiki/Steam_Web_API#GetPlayerSummaries_.28v0002.29
func GetPlayersInfo(steamids []string) (map[string]*PlayerInfo, error) {
	url := "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" +
		steamApiKey + "&steamids=" + strings.Join(steamids, ",")

	profiles := make(map[string]*PlayerInfo)

	response, err := getJsonFromUrl(url)
	if err != nil {
		return nil, err
	}

	ps, err := response.Get("response").Get("players").Array()
	if err != nil {
		return nil, err
	}

	for _, _elem := range ps {
		elem := _elem.(map[string]interface{})
		player := new(PlayerInfo)

		pErr := player.Parse(elem)

		if pErr != nil {
			return nil, pErr
		}

		profiles[player.Steamid] = player
	}

	return profiles, nil
}

func GetPlayerInfo(steamid string) (*PlayerInfo, error) {
	url := "http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" +
		steamApiKey + "&steamids=" + steamid

	player := new(PlayerInfo)

	response, err := getJsonFromUrl(url)
	if err != nil {
		return nil, err
	}

	ps, err := response.Get("response").Get("players").Array()
	if err != nil {
		return nil, err
	}

	if len(ps) == 1 {
		elem := ps[0].(map[string]interface{})
		pErr := player.Parse(elem)

		if pErr != nil {
			return nil, pErr
		}
	}

	return player, nil
}
<|endoftext|>


<commit_before>package gtreap

import (
	"errors"
	"os"
)

// A persistent store holding collections of ordered keys & values.
// The persistence is append-only based on immutable, copy-on-write
// treaps.  This implementation is single-threaded, so users should
// serialize their accesses.
//
// TODO: use atomic.CAS and unsafe.Pointers for safe snapshot'ability.
// TODO: allow read-only snapshots.
//
type Store struct {
	coll map[string]*PTreap
	file *os.File
}

func NewStore(file *os.File) (*Store, error) {
	if file == nil { // Return a memory-only Store.
		return &Store{coll: make(map[string]*PTreap)}, nil
	}
	return nil, errors.New("not implemented yet")
}

func (s *Store) AddCollection(name string, compare KeyCompare) *PTreap {
	if s.coll[name] == nil {
		s.coll[name] = &PTreap{store: s, compare: compare}
	}
	return s.coll[name]
}

func (s *Store) GetCollection(name string) *PTreap {
	return s.coll[name]
}

func (s *Store) RemoveCollection(name string) {
	delete(s.coll, name)
}

// User-supplied key comparison func should return 0 if a == b,
// -1 if a < b, and +1 if a > b.
type KeyCompare func(a, b []byte) int

// A persisted treap.
type PTreap struct {
	store   *Store
	compare KeyCompare
	root    pnodeLoc
}

// A persisted node and its persistence location.
type pnodeLoc struct {
	loc  *ploc  // Can be nil if node is dirty (not yet persisted).
	node *pnode // Can be nil if node is not fetched into memory yet.
}

func (nloc *pnodeLoc) isEmpty() bool {
	return nloc == nil || (nloc.loc == nil && nloc.node == nil)
}

var empty = &pnodeLoc{}

// A persisted node.
type pnode struct {
	item        PItemLoc
	left, right pnodeLoc
}

// A persisted item.
type PItem struct {
	Key, Val []byte // Val may be nil if not fetched into memory yet.
	Priority int32
}

// A persisted item and its persistence location.
type PItemLoc struct {
	loc  *ploc  // Can be nil if item is dirty (not yet persisted).
	item *PItem // Can be nil if item is not fetched into memory yet.
}

// Offset/location of persisted range of bytes.
type ploc struct {
	offset int64  // Usable for os.Seek/ReadAt/WriteAt() at file offset 0.
	length uint32 // Number of bytes.
}

func (t *PTreap) Get(key []byte, withValue bool) (*PItem, error) {
	n, err := t.store.loadNodeLoc(&t.root)
	for {
		if err != nil || n.isEmpty() {
			break
		}
		i, err := t.store.loadMetaItemLoc(&n.node.item)
		if err != nil {
			return nil, err
		}
		if i == nil || i.item == nil || i.item.Key == nil {
			panic("no item after loadMetaItemLoc() in get()")
		}
		c := t.compare(key, i.item.Key)
		if c < 0 {
			n, err = t.store.loadNodeLoc(&n.node.left)
		} else if c > 0 {
			n, err = t.store.loadNodeLoc(&n.node.right)
		} else {
			if withValue {
				t.store.loadItemLoc(i)
			}
			return i.item, nil
		}
	}
	return nil, err
}

// Replace or insert an item of a given key.
func (t *PTreap) Upsert(item *PItem) error {
	r, err := t.store.union(t, &t.root,
		&pnodeLoc{node: &pnode{item: PItemLoc{item: &PItem{
			Key:      item.Key,
			Val:      item.Val,
			Priority: item.Priority,
		}}}})
	if err == nil {
		t.root = *r
	}
	return err
}

func (t *PTreap) Delete(key []byte) error {
	left, _, right, err := t.store.split(t, &t.root, key)
	if err == nil {
		r, err := t.store.join(left, right)
		if err == nil {
			t.root = *r
		}
	}
	return err
}

func (t *PTreap) Min(withValue bool) (*PItem, error) {
	return t.store.edge(t, withValue, func(n *pnode) *pnodeLoc { return &n.left })
}

func (t *PTreap) Max(withValue bool) (*PItem, error) {
	return t.store.edge(t, withValue, func(n *pnode) *pnodeLoc { return &n.right })
}

type PItemVisitor func(i *PItem) bool

// Visit items greater-than-or-equal to the target.
func (t *PTreap) VisitAscend(target []byte, withValue bool, visitor PItemVisitor) error {
	_, err := t.store.visitAscendNode(t, &t.root, target, withValue, visitor)
	return err
}

func (o *Store) loadNodeLoc(nloc *pnodeLoc) (*pnodeLoc, error) {
	if nloc != nil && nloc.node == nil && nloc.loc != nil {
		// TODO.
	}
	return nloc, nil
}

func (o *Store) loadItemLoc(iloc *PItemLoc) (*PItemLoc, error) {
	if iloc != nil && iloc.item == nil && iloc.loc != nil {
		// TODO.
	}
	return iloc, nil
}

func (o *Store) loadMetaItemLoc(iloc *PItemLoc) (*PItemLoc, error) {
	if iloc != nil && iloc.item == nil && iloc.loc != nil {
		// TODO.
	}
	return iloc, nil
}

func (o *Store) union(t *PTreap, this *pnodeLoc, that *pnodeLoc) (*pnodeLoc, error) {
	thisNode, err := o.loadNodeLoc(this)
	if err != nil {
		return empty, err
	}
	thatNode, err := o.loadNodeLoc(that)
	if err != nil {
		return empty, err
	}

	if thisNode.isEmpty() {
		thisNode = empty
	}
	if thatNode.isEmpty() {
		thatNode = empty
	}

	if thisNode.isEmpty() {
		return that, nil
	}
	if thatNode.isEmpty() {
		return this, nil
	}

	thisItem, err := o.loadMetaItemLoc(&thisNode.node.item)
	if err != nil {
		return empty, err
	}
	thatItem, err := o.loadMetaItemLoc(&thatNode.node.item)
	if err != nil {
		return empty, err
	}

	if thisItem.item.Priority > thatItem.item.Priority {
		left, middle, right, err := o.split(t, that, thisItem.item.Key)
		if err != nil {
			return empty, err
		}
		if middle.isEmpty() {
			newLeft, err := o.union(t, &thisNode.node.left, left)
			if err != nil {
				return empty, err
			}
			newRight, err := o.union(t, &thisNode.node.right, right)
			if err != nil {
				return nil, err
			}
			return &pnodeLoc{node: &pnode{
				item:  *thisItem,
				left:  *newLeft,
				right: *newRight,
			}}, nil
		}

		newLeft, err := o.union(t, &thisNode.node.left, left)
		if err != nil {
			return empty, err
		}
		newRight, err := o.union(t, &thisNode.node.right, right)
		if err != nil {
			return empty, err
		}
		return &pnodeLoc{node: &pnode{
			item:  middle.node.item,
			left:  *newLeft,
			right: *newRight,
		}}, nil
	}

	// We don't use middle because the "that" node has precendence.
	left, _, right, err := o.split(t, this, thatItem.item.Key)
	if err != nil {
		return empty, err
	}
	newLeft, err := o.union(t, left, &thatNode.node.left)
	if err != nil {
		return empty, err
	}
	newRight, err := o.union(t, right, &thatNode.node.right)
	if err != nil {
		return empty, err
	}
	return &pnodeLoc{node: &pnode{
		item:  *thatItem,
		left:  *newLeft,
		right: *newRight,
	}}, nil
}

// Splits a treap into two treaps based on a split key "s".  The
// result is (left, middle, right), where left treap has keys < s,
// right treap has keys > s, and middle is either...
// * empty/nil - meaning key s was not in the original treap.
// * non-empty - returning the pnodeLoc that had item s.
func (o *Store) split(t *PTreap, n *pnodeLoc, s []byte) (
	*pnodeLoc, *pnodeLoc, *pnodeLoc, error) {
	nNode, err := o.loadNodeLoc(n)
	if err != nil || nNode.isEmpty() {
		return empty, empty, empty, err
	}
	nItem, err := o.loadMetaItemLoc(&nNode.node.item)
	if err != nil {
		return empty, empty, empty, err
	}

	c := t.compare(s, nItem.item.Key)
	if c == 0 {
		return &nNode.node.left, n, &nNode.node.right, nil
	}

	if c < 0 {
		left, middle, right, err := o.split(t, &nNode.node.left, s)
		if err != nil {
			return empty, empty, empty, err
		}
		return left, middle, &pnodeLoc{node: &pnode{
			item:  *nItem,
			left:  *right,
			right: nNode.node.right,
		}}, nil
	}

	left, middle, right, err := o.split(t, &nNode.node.right, s)
	if err != nil {
		return empty, empty, empty, err
	}
	return &pnodeLoc{node: &pnode{
		item:  *nItem,
		left:  nNode.node.left,
		right: *left,
	}}, middle, right, nil
}

// All the keys from this are < keys from that.
func (o *Store) join(this *pnodeLoc, that *pnodeLoc) (*pnodeLoc, error) {
	thisNode, err := o.loadNodeLoc(this)
	if err != nil {
		return empty, err
	}
	thatNode, err := o.loadNodeLoc(that)
	if err != nil {
		return empty, err
	}

	if thisNode.isEmpty() {
		thisNode = empty
	}
	if thatNode.isEmpty() {
		thatNode = empty
	}

	if thisNode.isEmpty() {
		return that, nil
	}
	if thatNode.isEmpty() {
		return this, nil
	}

	thisItem, err := o.loadMetaItemLoc(&thisNode.node.item)
	if err != nil {
		return empty, err
	}
	thatItem, err := o.loadMetaItemLoc(&thatNode.node.item)
	if err != nil {
		return empty, err
	}

	if thisItem.item.Priority > thatItem.item.Priority {
		newRight, err := o.join(&thisNode.node.right, that)
		if err != nil {
			return empty, err
		}
		return &pnodeLoc{node: &pnode{
			item:  *thisItem,
			left:  thisNode.node.left,
			right: *newRight,
		}}, nil
	}

	newLeft, err := o.join(this, &thatNode.node.left)
	if err != nil {
		return empty, err
	}
	return &pnodeLoc{node: &pnode{
		item:  *thatItem,
		left:  *newLeft,
		right: thatNode.node.right,
	}}, nil
}

func (o *Store) edge(t *PTreap, withValue bool, cfn func(*pnode) *pnodeLoc) (
	*PItem, error) {
	n, err := o.loadNodeLoc(&t.root)
	if err != nil || n.isEmpty() {
		return nil, err
	}
	for {
		child, err := o.loadNodeLoc(cfn(n.node))
		if err != nil {
			return nil, err
		}
		if child.isEmpty() {
			i, err := o.loadMetaItemLoc(&n.node.item)
			if err == nil {
				if withValue {
					i, err = o.loadItemLoc(i)
				}
				if err == nil {
					return i.item, nil
				}
			}
			return nil, err
		}
		n = child
	}
	return nil, nil
}

func (o *Store) visitAscendNode(t *PTreap, n *pnodeLoc, target []byte,
	withValue bool, visitor PItemVisitor) (bool, error) {
	nNode, err := o.loadNodeLoc(n)
	if err != nil {
		return false, err
	}
	if nNode.isEmpty() {
		return true, nil
	}
	nItem, err := o.loadMetaItemLoc(&nNode.node.item)
	if err != nil {
		return false, err
	}
	if t.compare(target, nItem.item.Key) <= 0 {
		keepGoing, err := o.visitAscendNode(t, &nNode.node.left, target, withValue, visitor)
		if err != nil || !keepGoing {
			return false, err
		}
		if withValue {
			nItem, err = o.loadItemLoc(nItem)
			if err != nil {
				return false, err
			}
		}
		if !visitor(nItem.item) {
			return false, nil
		}
	}
	return o.visitAscendNode(t, &nNode.node.right, target, withValue, visitor)
}
<commit_msg>Parametrized loadItemLoc() with 'withValue'.<commit_after>package gtreap

import (
	"errors"
	"os"
)

// A persistent store holding collections of ordered keys & values.
// The persistence is append-only based on immutable, copy-on-write
// treaps.  This implementation is single-threaded, so users should
// serialize their accesses.
//
// TODO: use atomic.CAS and unsafe.Pointers for safe snapshot'ability.
// TODO: allow read-only snapshots.
//
type Store struct {
	coll map[string]*PTreap
	file *os.File
}

func NewStore(file *os.File) (*Store, error) {
	if file == nil { // Return a memory-only Store.
		return &Store{coll: make(map[string]*PTreap)}, nil
	}
	return nil, errors.New("not implemented yet")
}

func (s *Store) AddCollection(name string, compare KeyCompare) *PTreap {
	if s.coll[name] == nil {
		s.coll[name] = &PTreap{store: s, compare: compare}
	}
	return s.coll[name]
}

func (s *Store) GetCollection(name string) *PTreap {
	return s.coll[name]
}

func (s *Store) RemoveCollection(name string) {
	delete(s.coll, name)
}

// User-supplied key comparison func should return 0 if a == b,
// -1 if a < b, and +1 if a > b.
type KeyCompare func(a, b []byte) int

// A persisted treap.
type PTreap struct {
	store   *Store
	compare KeyCompare
	root    pnodeLoc
}

// A persisted node and its persistence location.
type pnodeLoc struct {
	loc  *ploc  // Can be nil if node is dirty (not yet persisted).
	node *pnode // Can be nil if node is not fetched into memory yet.
}

func (nloc *pnodeLoc) isEmpty() bool {
	return nloc == nil || (nloc.loc == nil && nloc.node == nil)
}

var empty = &pnodeLoc{}

// A persisted node.
type pnode struct {
	item        PItemLoc
	left, right pnodeLoc
}

// A persisted item.
type PItem struct {
	Key, Val []byte // Val may be nil if not fetched into memory yet.
	Priority int32
}

// A persisted item and its persistence location.
type PItemLoc struct {
	loc  *ploc  // Can be nil if item is dirty (not yet persisted).
	item *PItem // Can be nil if item is not fetched into memory yet.
}

// Offset/location of persisted range of bytes.
type ploc struct {
	offset int64  // Usable for os.Seek/ReadAt/WriteAt() at file offset 0.
	length uint32 // Number of bytes.
}

func (t *PTreap) Get(key []byte, withValue bool) (*PItem, error) {
	n, err := t.store.loadNodeLoc(&t.root)
	for {
		if err != nil || n.isEmpty() {
			break
		}
		i, err := t.store.loadItemLoc(&n.node.item, false)
		if err != nil {
			return nil, err
		}
		if i == nil || i.item == nil || i.item.Key == nil {
			panic("no item after loadMetaItemLoc() in get()")
		}
		c := t.compare(key, i.item.Key)
		if c < 0 {
			n, err = t.store.loadNodeLoc(&n.node.left)
		} else if c > 0 {
			n, err = t.store.loadNodeLoc(&n.node.right)
		} else {
			if withValue {
				t.store.loadItemLoc(i, true)
			}
			return i.item, nil
		}
	}
	return nil, err
}

// Replace or insert an item of a given key.
func (t *PTreap) Upsert(item *PItem) error {
	r, err := t.store.union(t, &t.root,
		&pnodeLoc{node: &pnode{item: PItemLoc{item: &PItem{
			Key:      item.Key,
			Val:      item.Val,
			Priority: item.Priority,
		}}}})
	if err == nil {
		t.root = *r
	}
	return err
}

func (t *PTreap) Delete(key []byte) error {
	left, _, right, err := t.store.split(t, &t.root, key)
	if err == nil {
		r, err := t.store.join(left, right)
		if err == nil {
			t.root = *r
		}
	}
	return err
}

func (t *PTreap) Min(withValue bool) (*PItem, error) {
	return t.store.edge(t, withValue, func(n *pnode) *pnodeLoc { return &n.left })
}

func (t *PTreap) Max(withValue bool) (*PItem, error) {
	return t.store.edge(t, withValue, func(n *pnode) *pnodeLoc { return &n.right })
}

type PItemVisitor func(i *PItem) bool

// Visit items greater-than-or-equal to the target.
func (t *PTreap) VisitAscend(target []byte, withValue bool, visitor PItemVisitor) error {
	_, err := t.store.visitAscendNode(t, &t.root, target, withValue, visitor)
	return err
}

func (o *Store) loadNodeLoc(nloc *pnodeLoc) (*pnodeLoc, error) {
	if nloc != nil && nloc.node == nil && nloc.loc != nil {
		// TODO.
	}
	return nloc, nil
}

func (o *Store) loadItemLoc(iloc *PItemLoc, withValue bool) (*PItemLoc, error) {
	if iloc != nil && iloc.item == nil && iloc.loc != nil {
		// TODO.
	}
	return iloc, nil
}

func (o *Store) union(t *PTreap, this *pnodeLoc, that *pnodeLoc) (*pnodeLoc, error) {
	thisNode, err := o.loadNodeLoc(this)
	if err != nil {
		return empty, err
	}
	thatNode, err := o.loadNodeLoc(that)
	if err != nil {
		return empty, err
	}

	if thisNode.isEmpty() {
		thisNode = empty
	}
	if thatNode.isEmpty() {
		thatNode = empty
	}

	if thisNode.isEmpty() {
		return that, nil
	}
	if thatNode.isEmpty() {
		return this, nil
	}

	thisItem, err := o.loadItemLoc(&thisNode.node.item, false)
	if err != nil {
		return empty, err
	}
	thatItem, err := o.loadItemLoc(&thatNode.node.item, false)
	if err != nil {
		return empty, err
	}

	if thisItem.item.Priority > thatItem.item.Priority {
		left, middle, right, err := o.split(t, that, thisItem.item.Key)
		if err != nil {
			return empty, err
		}
		if middle.isEmpty() {
			newLeft, err := o.union(t, &thisNode.node.left, left)
			if err != nil {
				return empty, err
			}
			newRight, err := o.union(t, &thisNode.node.right, right)
			if err != nil {
				return nil, err
			}
			return &pnodeLoc{node: &pnode{
				item:  *thisItem,
				left:  *newLeft,
				right: *newRight,
			}}, nil
		}

		newLeft, err := o.union(t, &thisNode.node.left, left)
		if err != nil {
			return empty, err
		}
		newRight, err := o.union(t, &thisNode.node.right, right)
		if err != nil {
			return empty, err
		}
		return &pnodeLoc{node: &pnode{
			item:  middle.node.item,
			left:  *newLeft,
			right: *newRight,
		}}, nil
	}

	// We don't use middle because the "that" node has precendence.
	left, _, right, err := o.split(t, this, thatItem.item.Key)
	if err != nil {
		return empty, err
	}
	newLeft, err := o.union(t, left, &thatNode.node.left)
	if err != nil {
		return empty, err
	}
	newRight, err := o.union(t, right, &thatNode.node.right)
	if err != nil {
		return empty, err
	}
	return &pnodeLoc{node: &pnode{
		item:  *thatItem,
		left:  *newLeft,
		right: *newRight,
	}}, nil
}

// Splits a treap into two treaps based on a split key "s".  The
// result is (left, middle, right), where left treap has keys < s,
// right treap has keys > s, and middle is either...
// * empty/nil - meaning key s was not in the original treap.
// * non-empty - returning the pnodeLoc that had item s.
func (o *Store) split(t *PTreap, n *pnodeLoc, s []byte) (
	*pnodeLoc, *pnodeLoc, *pnodeLoc, error) {
	nNode, err := o.loadNodeLoc(n)
	if err != nil || nNode.isEmpty() {
		return empty, empty, empty, err
	}
	nItem, err := o.loadItemLoc(&nNode.node.item, false)
	if err != nil {
		return empty, empty, empty, err
	}

	c := t.compare(s, nItem.item.Key)
	if c == 0 {
		return &nNode.node.left, n, &nNode.node.right, nil
	}

	if c < 0 {
		left, middle, right, err := o.split(t, &nNode.node.left, s)
		if err != nil {
			return empty, empty, empty, err
		}
		return left, middle, &pnodeLoc{node: &pnode{
			item:  *nItem,
			left:  *right,
			right: nNode.node.right,
		}}, nil
	}

	left, middle, right, err := o.split(t, &nNode.node.right, s)
	if err != nil {
		return empty, empty, empty, err
	}
	return &pnodeLoc{node: &pnode{
		item:  *nItem,
		left:  nNode.node.left,
		right: *left,
	}}, middle, right, nil
}

// All the keys from this are < keys from that.
func (o *Store) join(this *pnodeLoc, that *pnodeLoc) (*pnodeLoc, error) {
	thisNode, err := o.loadNodeLoc(this)
	if err != nil {
		return empty, err
	}
	thatNode, err := o.loadNodeLoc(that)
	if err != nil {
		return empty, err
	}

	if thisNode.isEmpty() {
		thisNode = empty
	}
	if thatNode.isEmpty() {
		thatNode = empty
	}

	if thisNode.isEmpty() {
		return that, nil
	}
	if thatNode.isEmpty() {
		return this, nil
	}

	thisItem, err := o.loadItemLoc(&thisNode.node.item, false)
	if err != nil {
		return empty, err
	}
	thatItem, err := o.loadItemLoc(&thatNode.node.item, false)
	if err != nil {
		return empty, err
	}

	if thisItem.item.Priority > thatItem.item.Priority {
		newRight, err := o.join(&thisNode.node.right, that)
		if err != nil {
			return empty, err
		}
		return &pnodeLoc{node: &pnode{
			item:  *thisItem,
			left:  thisNode.node.left,
			right: *newRight,
		}}, nil
	}

	newLeft, err := o.join(this, &thatNode.node.left)
	if err != nil {
		return empty, err
	}
	return &pnodeLoc{node: &pnode{
		item:  *thatItem,
		left:  *newLeft,
		right: thatNode.node.right,
	}}, nil
}

func (o *Store) edge(t *PTreap, withValue bool, cfn func(*pnode) *pnodeLoc) (
	*PItem, error) {
	n, err := o.loadNodeLoc(&t.root)
	if err != nil || n.isEmpty() {
		return nil, err
	}
	for {
		child, err := o.loadNodeLoc(cfn(n.node))
		if err != nil {
			return nil, err
		}
		if child.isEmpty() {
			i, err := o.loadItemLoc(&n.node.item, false)
			if err == nil {
				if withValue {
					i, err = o.loadItemLoc(i, true)
				}
				if err == nil {
					return i.item, nil
				}
			}
			return nil, err
		}
		n = child
	}
	return nil, nil
}

func (o *Store) visitAscendNode(t *PTreap, n *pnodeLoc, target []byte,
	withValue bool, visitor PItemVisitor) (bool, error) {
	nNode, err := o.loadNodeLoc(n)
	if err != nil {
		return false, err
	}
	if nNode.isEmpty() {
		return true, nil
	}
	nItem, err := o.loadItemLoc(&nNode.node.item, false)
	if err != nil {
		return false, err
	}
	if t.compare(target, nItem.item.Key) <= 0 {
		keepGoing, err := o.visitAscendNode(t, &nNode.node.left, target, withValue, visitor)
		if err != nil || !keepGoing {
			return false, err
		}
		if withValue {
			nItem, err = o.loadItemLoc(nItem, true)
			if err != nil {
				return false, err
			}
		}
		if !visitor(nItem.item) {
			return false, nil
		}
	}
	return o.visitAscendNode(t, &nNode.node.right, target, withValue, visitor)
}
<|endoftext|>


<commit_before>package hg

import (
	"os/exec"
	"fmt"
	"strings"
	"os"
	"encoding/json"
	"time"
)

type Repository struct {
	Path                string
	Branch              string
	IncludePaths        []string
	ExcludePaths        []string
	TagFilter           string
	SkipSslVerification bool
}

type CommitProperty struct {
	Name  string `json:"name"`
	Value string `json:"value"`
	Type  string `json:"type,omitempty"`
}

type HgChangeset struct {
	Rev       int `json:"rev"`
	Node      string `json:"node"`
	Branch    string `json:"branch"`
	Phase     string `json:"phase"`
	User      string `json:"user"`
	Date      []int64 `json:"date"`
	Desc      string `json:"desc"`
	Bookmarks []string `json:"bookmarks"`
	Tags      []string `json:"tags"`
	Parents   []string `json:"parents"`
}

func (self *Repository) CloneOrPull(sourceUri string) ([]byte, error) {
	if len(self.Path) == 0 {
		return []byte{}, fmt.Errorf("CloneOrPull: repository path must be set")
	}

	if len(self.Branch) == 0 {
		return []byte{}, fmt.Errorf("CloneOrPull: branch must be set")
	}

	dirInfo, errIfNotExists := os.Stat(self.Path)
	if errIfNotExists != nil || !dirInfo.IsDir() {
		return self.clone(sourceUri, insecure)
	} else {
		return self.pull(insecure)
	}
}

func (self *Repository) clone(sourceUri string, insecure bool) (output []byte, err error) {
	err = os.RemoveAll(self.Path)
	if err != nil {
		err = fmt.Errorf("clone: %s", err)
		return
	}

	_, output, err = self.run("clone", []string{
		"-q",
		"--branch", self.Branch,
		sourceUri,
		self.Path,
	})
	if err != nil {
		err = fmt.Errorf("Error cloning repository from %s: %s", sourceUri, err)
	}

	return
}

func (self *Repository) pull() (output []byte, err error) {
	_, output, err = self.run("pull", []string{
		"-q",
		"--cwd", self.Path,
	})
	if err != nil {
		err = fmt.Errorf("Error pulling changes from repository: %s", err)
		return
	}

	_, checkoutOutput, err := self.run("checkout", []string{
		"-q",
		"--cwd", self.Path,
		"--clean",
		"--rev", "tip",
	})
	output = append(output, checkoutOutput...)
	if err != nil {
		err = fmt.Errorf("Error updating working directory to tip: %s", err)
	}

	return
}

func (self *Repository) PullWithRebase(sourceUri string, branch string) (output []byte, err error) {
	_, output, err = self.run("pull", []string{
		"-q",
		"--cwd", self.Path,
		"--config", "extensions.rebase=",
		"--config", "paths.push-target=" + sourceUri,
		"--rebase",
		"--branch", branch,
		"push-target",
	})
	if err != nil {
		err = fmt.Errorf("Error pulling/rebasing from: %s: %s", sourceUri, err)
	}
	return
}

// Clones sourceUri into the repository and truncates all history after the given commit,
// making it the new tip. After truncating, we can add a tag commit at tip, and then push
// the whole known branch (... -> given commit -> tag commit == tip) to another repository.
func (self *Repository) CloneAtCommit(sourceUri string, commitId string) (output []byte, err error) {
	_, output, err = self.run("clone", []string{
		"-q",
		"--rev", commitId,
		sourceUri,
		self.Path,
	})
	if err != nil {
		err = fmt.Errorf("Error cloning repository %s@%s: %s", sourceUri, commitId, err)
	}

	return
}

// Makes the repository rebaseable. See `hg help phases`.
func (self *Repository) SetDraftPhase() (output []byte, err error) {
	_, output, err = self.run("phase", []string{
		"--cwd", self.Path,
		"--force",
		"--draft",
	})
	if err != nil {
		err = fmt.Errorf("Error setting repo phase to draft: %s", err)
	}

	return
}

func (self *Repository) Push(destUri string, branch string) (output []byte, err error) {
	_, output, err = self.run("push", []string{
		"--cwd", self.Path,
		"--config", "paths.push-target=" + destUri,
		"--branch", branch,
		"push-target",
	})
	if err != nil {
		err = fmt.Errorf("Error pushing to %s: %s", destUri, err)
	}

	return
}

// Tags a commit. Expects to be run only at tip!
func (self *Repository) Tag(tagValue string) (output []byte, err error) {
	_, output, err = self.run("tag", []string{
		"--cwd", self.Path,
		tagValue,
	})
	if err != nil {
		err = fmt.Errorf("Error tagging current commit: %s", err)
	}

	return
}

func (self *Repository) Delete() error {
	err := os.RemoveAll(self.Path)
	if err != nil {
		return fmt.Errorf("Error deleting repository: %s", err)
	}

	return nil
}

func (self *Repository) Checkout(commitId string) (output []byte, err error) {
	_, output, err = self.run("checkout", []string{
		"-q",
		"--cwd", self.Path,
		"--clean",
		"--rev", commitId,
	})
	if err != nil {
		err = fmt.Errorf("Error checking out %s: %s", commitId, err)
	}

	return
}

func (self *Repository) Purge() (output []byte, err error) {
	_, output, err = self.run("purge", []string{
		"--config", "extensions.purge=",
		"--cwd", self.Path,
		"--all",
	})
	if err != nil {
		err = fmt.Errorf("Error purging repository: %s", err)
	}

	return
}

func (self *Repository) GetLatestCommitId() (output string, err error) {
	include := self.makeIncludeQueryFragment()
	exclude := self.makeExcludeQueryFragment()
	tagFilter := self.maybeTagFilter()
	revSet := fmt.Sprintf("last((((%s) - (%s)) & %s) - desc('[ci skip]'))", include, exclude, tagFilter)

	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", revSet,
		"--template", "{node}",
	})
	output = string(outBytes)
	if err != nil {
		err = fmt.Errorf("Error getting latest commit id: %s", err)
	}

	return
}

func (self *Repository) GetCurrentCommitId() (output string, err error) {
	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", ".",
		"--template", "{node}",
	})
	output = string(outBytes)
	if err != nil {
		err = fmt.Errorf("Error getting current commit id: %s", err)
	}

	return
}

func (self *Repository) GetDescendantsOf(commitId string) ([]string, error) {
	include := self.makeIncludeQueryFragment()
	exclude := self.makeExcludeQueryFragment()
	tagFilter := self.maybeTagFilter()
	revSet := fmt.Sprintf("(descendants(%s) - %s) & %s & ((%s) - (%s)) - desc('[ci skip]')",
		commitId, commitId, tagFilter, include, exclude)

	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", revSet,
		"--template", "{node}\n",
	})

	output := string(outBytes)
	if err != nil {
		return []string{}, fmt.Errorf("Error getting descendant commits of %s: %s\n%s", commitId, err, output)
	}

	trimmed := strings.Trim(output, "\n\r ")
	if len(trimmed) == 0 {
		return []string{}, nil
	}

	commits := strings.Split(trimmed, "\n")
	return commits, nil
}

func (self *Repository) maybeTagFilter() string {
	if len(self.TagFilter) > 0 {
		return "tag('re:" + escapePath(self.TagFilter) + "')"
	} else {
		return "all()"
	}
}

func (self *Repository) Metadata(commitId string) (metadata []CommitProperty, err error) {
	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", commitId,
		"--template", "json",
	})
	if err != nil {
		err = fmt.Errorf("Error getting metadata for commit %s: %s\n%s", commitId, err, string(outBytes))
	}

	metadata, err = parseMetadata(outBytes)
	return
}

func parseHgTime(hgTime []int64) (parsedTime time.Time, err error) {
	if len(hgTime) != 2 {
		err = fmt.Errorf("parseHgTime: expected slice hgTime to have 2 elements")
	}
	utcEpoch := hgTime[0]
	offset := hgTime[1]

	utcTime := time.Unix(utcEpoch, 0)

	// for some reason, mercurial uses the inverse sign on the offset
	zone := time.FixedZone("internet time", -1 * int(offset))

	parsedTime = utcTime.In(zone)
	return
}

func timeToIso8601(timestamp time.Time) string {
	return timestamp.Format("2006-01-02 15:04:05 -0700")
}

func (commit *HgChangeset) toCommitProperties() (metadata []CommitProperty, err error) {
	timestamp, err := parseHgTime(commit.Date)
	if err != nil {
		return
	}

	metadata = append(metadata,
		CommitProperty{
			Name: "commit",
			Value: commit.Node,
		},
		CommitProperty{
			Name: "author",
			Value: commit.User,
		},
		CommitProperty{
			Name: "author_date",
			Value: timeToIso8601(timestamp),
			Type: "time",
		},
		CommitProperty{
			Name: "message",
			Value: commit.Desc,
			Type: "message",
		},
		CommitProperty{
			Name: "tags",
			Value: strings.Join(commit.Tags, ", "),
		},
	)

	return
}

func parseMetadata(hgJsonOutput []byte) (metadata []CommitProperty, err error) {
	commits := []HgChangeset{}
	err = json.Unmarshal(hgJsonOutput, &commits)
	if err != nil {
		return
	}

	if len(commits) != 1 {
		err = fmt.Errorf("parseMetadata: expected 1 commit, found %d", len(commits))
		return
	}

	metadata, err = commits[0].toCommitProperties()
	return
}

func (self *Repository) run(command string, args []string) (cmd *exec.Cmd, output []byte, err error) {
	hgArgs := make([]string, 1, len(args) + 1)
	hgArgs[0] = command

	if self.SkipSslVerification && commandTakesInsecureOption(command) {
		hgArgs = append(hgArgs, "--insecure")
	}
	hgArgs = append(hgArgs, args...)

	cmd = exec.Command("hg", hgArgs...)

	output, err = cmd.CombinedOutput()
	return
}

func commandTakesInsecureOption(command string) bool {
	eligibleCommands := []string{
		"clone",
		"pull",
		"push",
	}
	for _, eligibleCommand := range (eligibleCommands) {
		if command == eligibleCommand {
			return true
		}
	}
	return false
}

func (self *Repository) makeIncludeQueryFragment() string {
	if len(self.IncludePaths) == 0 {
		return "all()"
	} else {
		return unionOfPaths(self.IncludePaths)
	}
}

func (self *Repository) makeExcludeQueryFragment() string {
	if len(self.ExcludePaths) == 0 {
		return "not all()"
	} else {
		return unionOfPaths(self.ExcludePaths)
	}
}

func unionOfPaths(paths []string) string {
	escapedPaths := make([]string, len(paths))
	for i, path := range (paths) {
		escapedPaths[i] = "file('re:" + escapePath(path) + "')"
	}
	return strings.Join(escapedPaths, "|")
}

func escapePath(path string) string {
	backslashesEscaped := strings.Replace(path, "\\", "\\\\\\\\", -1)
	quotesEscaped := strings.Replace(backslashesEscaped, "'", "\\'", -1)
	return quotesEscaped
}<commit_msg>hg: fix CloneOrPull<commit_after>package hg

import (
	"os/exec"
	"fmt"
	"strings"
	"os"
	"encoding/json"
	"time"
	"path"
)

type Repository struct {
	Path                string
	Branch              string
	IncludePaths        []string
	ExcludePaths        []string
	TagFilter           string
	SkipSslVerification bool
}

type CommitProperty struct {
	Name  string `json:"name"`
	Value string `json:"value"`
	Type  string `json:"type,omitempty"`
}

type HgChangeset struct {
	Rev       int `json:"rev"`
	Node      string `json:"node"`
	Branch    string `json:"branch"`
	Phase     string `json:"phase"`
	User      string `json:"user"`
	Date      []int64 `json:"date"`
	Desc      string `json:"desc"`
	Bookmarks []string `json:"bookmarks"`
	Tags      []string `json:"tags"`
	Parents   []string `json:"parents"`
}

func (self *Repository) CloneOrPull(sourceUri string) ([]byte, error) {
	if len(self.Path) == 0 {
		return []byte{}, fmt.Errorf("CloneOrPull: repository path must be set")
	}

	if len(self.Branch) == 0 {
		return []byte{}, fmt.Errorf("CloneOrPull: branch must be set")
	}

	dirInfo, errIfNotExists := os.Stat(path.Join(self.Path, ".hg"))
	if errIfNotExists != nil || !dirInfo.IsDir() {
		return self.clone(sourceUri)
	} else {
		return self.pull()
	}
}

func (self *Repository) clone(sourceUri string) (output []byte, err error) {
	_, output, err = self.run("clone", []string{
		"-q",
		"--branch", self.Branch,
		sourceUri,
		self.Path,
	})
	if err != nil {
		err = fmt.Errorf("Error cloning repository from %s: %s", sourceUri, err)
	}

	return
}

func (self *Repository) pull() (output []byte, err error) {
	_, output, err = self.run("pull", []string{
		"-q",
		"--cwd", self.Path,
	})
	if err != nil {
		err = fmt.Errorf("Error pulling changes from repository: %s", err)
		return
	}

	_, checkoutOutput, err := self.run("checkout", []string{
		"-q",
		"--cwd", self.Path,
		"--clean",
		"--rev", "tip",
	})
	output = append(output, checkoutOutput...)
	if err != nil {
		err = fmt.Errorf("Error updating working directory to tip: %s", err)
	}

	return
}

func (self *Repository) PullWithRebase(sourceUri string, branch string) (output []byte, err error) {
	_, output, err = self.run("pull", []string{
		"-q",
		"--cwd", self.Path,
		"--config", "extensions.rebase=",
		"--config", "paths.push-target=" + sourceUri,
		"--rebase",
		"--branch", branch,
		"push-target",
	})
	if err != nil {
		err = fmt.Errorf("Error pulling/rebasing from: %s: %s", sourceUri, err)
	}
	return
}

// Clones sourceUri into the repository and truncates all history after the given commit,
// making it the new tip. After truncating, we can add a tag commit at tip, and then push
// the whole known branch (... -> given commit -> tag commit == tip) to another repository.
func (self *Repository) CloneAtCommit(sourceUri string, commitId string) (output []byte, err error) {
	_, output, err = self.run("clone", []string{
		"-q",
		"--rev", commitId,
		sourceUri,
		self.Path,
	})
	if err != nil {
		err = fmt.Errorf("Error cloning repository %s@%s: %s", sourceUri, commitId, err)
	}

	return
}

// Makes the repository rebaseable. See `hg help phases`.
func (self *Repository) SetDraftPhase() (output []byte, err error) {
	_, output, err = self.run("phase", []string{
		"--cwd", self.Path,
		"--force",
		"--draft",
	})
	if err != nil {
		err = fmt.Errorf("Error setting repo phase to draft: %s", err)
	}

	return
}

func (self *Repository) Push(destUri string, branch string) (output []byte, err error) {
	_, output, err = self.run("push", []string{
		"--cwd", self.Path,
		"--config", "paths.push-target=" + destUri,
		"--branch", branch,
		"push-target",
	})
	if err != nil {
		err = fmt.Errorf("Error pushing to %s: %s", destUri, err)
	}

	return
}

// Tags a commit. Expects to be run only at tip!
func (self *Repository) Tag(tagValue string) (output []byte, err error) {
	_, output, err = self.run("tag", []string{
		"--cwd", self.Path,
		tagValue,
	})
	if err != nil {
		err = fmt.Errorf("Error tagging current commit: %s", err)
	}

	return
}

func (self *Repository) Delete() error {
	err := os.RemoveAll(self.Path)
	if err != nil {
		return fmt.Errorf("Error deleting repository: %s", err)
	}

	return nil
}

func (self *Repository) Checkout(commitId string) (output []byte, err error) {
	_, output, err = self.run("checkout", []string{
		"-q",
		"--cwd", self.Path,
		"--clean",
		"--rev", commitId,
	})
	if err != nil {
		err = fmt.Errorf("Error checking out %s: %s", commitId, err)
	}

	return
}

func (self *Repository) Purge() (output []byte, err error) {
	_, output, err = self.run("purge", []string{
		"--config", "extensions.purge=",
		"--cwd", self.Path,
		"--all",
	})
	if err != nil {
		err = fmt.Errorf("Error purging repository: %s", err)
	}

	return
}

func (self *Repository) GetLatestCommitId() (output string, err error) {
	include := self.makeIncludeQueryFragment()
	exclude := self.makeExcludeQueryFragment()
	tagFilter := self.maybeTagFilter()
	revSet := fmt.Sprintf("last((((%s) - (%s)) & %s) - desc('[ci skip]'))", include, exclude, tagFilter)

	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", revSet,
		"--template", "{node}",
	})
	output = string(outBytes)
	if err != nil {
		err = fmt.Errorf("Error getting latest commit id: %s", err)
	}

	return
}

func (self *Repository) GetCurrentCommitId() (output string, err error) {
	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", ".",
		"--template", "{node}",
	})
	output = string(outBytes)
	if err != nil {
		err = fmt.Errorf("Error getting current commit id: %s", err)
	}

	return
}

func (self *Repository) GetDescendantsOf(commitId string) ([]string, error) {
	include := self.makeIncludeQueryFragment()
	exclude := self.makeExcludeQueryFragment()
	tagFilter := self.maybeTagFilter()
	revSet := fmt.Sprintf("(descendants(%s) - %s) & %s & ((%s) - (%s)) - desc('[ci skip]')",
		commitId, commitId, tagFilter, include, exclude)

	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", revSet,
		"--template", "{node}\n",
	})

	output := string(outBytes)
	if err != nil {
		return []string{}, fmt.Errorf("Error getting descendant commits of %s: %s\n%s", commitId, err, output)
	}

	trimmed := strings.Trim(output, "\n\r ")
	if len(trimmed) == 0 {
		return []string{}, nil
	}

	commits := strings.Split(trimmed, "\n")
	return commits, nil
}

func (self *Repository) maybeTagFilter() string {
	if len(self.TagFilter) > 0 {
		return "tag('re:" + escapePath(self.TagFilter) + "')"
	} else {
		return "all()"
	}
}

func (self *Repository) Metadata(commitId string) (metadata []CommitProperty, err error) {
	_, outBytes, err := self.run("log", []string{
		"--cwd", self.Path,
		"--rev", commitId,
		"--template", "json",
	})
	if err != nil {
		err = fmt.Errorf("Error getting metadata for commit %s: %s\n%s", commitId, err, string(outBytes))
	}

	metadata, err = parseMetadata(outBytes)
	return
}

func parseHgTime(hgTime []int64) (parsedTime time.Time, err error) {
	if len(hgTime) != 2 {
		err = fmt.Errorf("parseHgTime: expected slice hgTime to have 2 elements")
	}
	utcEpoch := hgTime[0]
	offset := hgTime[1]

	utcTime := time.Unix(utcEpoch, 0)

	// for some reason, mercurial uses the inverse sign on the offset
	zone := time.FixedZone("internet time", -1 * int(offset))

	parsedTime = utcTime.In(zone)
	return
}

func timeToIso8601(timestamp time.Time) string {
	return timestamp.Format("2006-01-02 15:04:05 -0700")
}

func (commit *HgChangeset) toCommitProperties() (metadata []CommitProperty, err error) {
	timestamp, err := parseHgTime(commit.Date)
	if err != nil {
		return
	}

	metadata = append(metadata,
		CommitProperty{
			Name: "commit",
			Value: commit.Node,
		},
		CommitProperty{
			Name: "author",
			Value: commit.User,
		},
		CommitProperty{
			Name: "author_date",
			Value: timeToIso8601(timestamp),
			Type: "time",
		},
		CommitProperty{
			Name: "message",
			Value: commit.Desc,
			Type: "message",
		},
		CommitProperty{
			Name: "tags",
			Value: strings.Join(commit.Tags, ", "),
		},
	)

	return
}

func parseMetadata(hgJsonOutput []byte) (metadata []CommitProperty, err error) {
	commits := []HgChangeset{}
	err = json.Unmarshal(hgJsonOutput, &commits)
	if err != nil {
		return
	}

	if len(commits) != 1 {
		err = fmt.Errorf("parseMetadata: expected 1 commit, found %d", len(commits))
		return
	}

	metadata, err = commits[0].toCommitProperties()
	return
}

func (self *Repository) run(command string, args []string) (cmd *exec.Cmd, output []byte, err error) {
	hgArgs := make([]string, 1, len(args) + 1)
	hgArgs[0] = command

	if self.SkipSslVerification && commandTakesInsecureOption(command) {
		hgArgs = append(hgArgs, "--insecure")
	}
	hgArgs = append(hgArgs, args...)

	cmd = exec.Command("hg", hgArgs...)

	output, err = cmd.CombinedOutput()
	return
}

func commandTakesInsecureOption(command string) bool {
	eligibleCommands := []string{
		"clone",
		"pull",
		"push",
	}
	for _, eligibleCommand := range (eligibleCommands) {
		if command == eligibleCommand {
			return true
		}
	}
	return false
}

func (self *Repository) makeIncludeQueryFragment() string {
	if len(self.IncludePaths) == 0 {
		return "all()"
	} else {
		return unionOfPaths(self.IncludePaths)
	}
}

func (self *Repository) makeExcludeQueryFragment() string {
	if len(self.ExcludePaths) == 0 {
		return "not all()"
	} else {
		return unionOfPaths(self.ExcludePaths)
	}
}

func unionOfPaths(paths []string) string {
	escapedPaths := make([]string, len(paths))
	for i, path := range (paths) {
		escapedPaths[i] = "file('re:" + escapePath(path) + "')"
	}
	return strings.Join(escapedPaths, "|")
}

func escapePath(path string) string {
	backslashesEscaped := strings.Replace(path, "\\", "\\\\\\\\", -1)
	quotesEscaped := strings.Replace(backslashesEscaped, "'", "\\'", -1)
	return quotesEscaped
}<|endoftext|>


<commit_before>// Package convert resamples and converts audio data
package convert

import (
	"io"
	"math"
)

var cosTable = [65536]float64{}

func init() {
	for i := range cosTable {
		cosTable[i] = math.Cos(float64(i) * math.Pi / 2 / float64(len(cosTable)))
	}
}

func fastCos01(x float64) float64 {
	if x < 0 {
		x = -x
	}
	i := int(4 * float64(len(cosTable)) * x)
	if 4*len(cosTable) < i {
		i %= 4 * len(cosTable)
	}
	sign := 1
	switch {
	case i < len(cosTable):
	case i < len(cosTable)*2:
		i = len(cosTable)*2 - i
		sign = -1
	case i < len(cosTable)*3:
		i -= len(cosTable) * 2
		sign = -1
	default:
		i = len(cosTable)*4 - i
	}
	if i == len(cosTable) {
		return 0
	}
	return float64(sign) * cosTable[i]
}

func fastSin01(x float64) float64 {
	return fastCos01(x - 0.25)
}

func sinc01(x float64) float64 {
	if math.Abs(x) < 1e-8 {
		return 1
	}
	return fastSin01(x) / (x * 2 * math.Pi)
}

type Resampling struct {
	source       ReadSeekCloser
	size         int64
	from         int
	to           int
	pos          int64
	srcBlock     int64
	srcBufL      map[int64][]float64
	srcBufR      map[int64][]float64
	lruSrcBlocks []int64
}

func NewResampling(source ReadSeekCloser, size int64, from, to int) *Resampling {
	r := &Resampling{
		source:   source,
		size:     size,
		from:     from,
		to:       to,
		srcBlock: -1,
		srcBufL:  map[int64][]float64{},
		srcBufR:  map[int64][]float64{},
	}
	return r
}

func (r *Resampling) Length() int64 {
	s := int64(float64(r.size) * float64(r.to) / float64(r.from))
	return s / 4 * 4
}

func (r *Resampling) src(i int64) (float64, float64, error) {
	const resamplingBufferSize = 4096

	if i < 0 {
		return 0, 0, nil
	}
	if r.size/4 <= int64(i) {
		return 0, 0, nil
	}
	nextPos := int64(i) / resamplingBufferSize
	if _, ok := r.srcBufL[nextPos]; !ok {
		if r.srcBlock+1 != nextPos {
			if _, err := r.source.Seek(nextPos*resamplingBufferSize*4, io.SeekStart); err != nil {
				return 0, 0, err
			}
		}
		buf := make([]uint8, resamplingBufferSize*4)
		c := 0
		for c < len(buf) {
			n, err := r.source.Read(buf[c:])
			c += n
			if err != nil {
				if err == io.EOF {
					break
				}
				return 0, 0, err
			}
		}
		buf = buf[:c]
		sl := make([]float64, resamplingBufferSize)
		sr := make([]float64, resamplingBufferSize)
		for i := 0; i < len(buf)/4; i++ {
			sl[i] = float64(int16(buf[4*i])|(int16(buf[4*i+1])<<8)) / (1<<15 - 1)
			sr[i] = float64(int16(buf[4*i+2])|(int16(buf[4*i+3])<<8)) / (1<<15 - 1)
		}
		r.srcBlock = nextPos
		r.srcBufL[r.srcBlock] = sl
		r.srcBufR[r.srcBlock] = sr
		// To keep srcBufL/R not too big, let's remove the least used buffers.
		if len(r.lruSrcBlocks) >= 4 {
			p := r.lruSrcBlocks[0]
			delete(r.srcBufL, p)
			delete(r.srcBufR, p)
			r.lruSrcBlocks = r.lruSrcBlocks[1:]
		}
		r.lruSrcBlocks = append(r.lruSrcBlocks, r.srcBlock)
	} else {
		r.srcBlock = nextPos
		idx := -1
		for i, p := range r.lruSrcBlocks {
			if p == r.srcBlock {
				idx = i
				break
			}
		}
		if idx == -1 {
			panic("not reach")
		}
		r.lruSrcBlocks = append(r.lruSrcBlocks[:idx], r.lruSrcBlocks[idx+1:]...)
		r.lruSrcBlocks = append(r.lruSrcBlocks, r.srcBlock)
	}
	ii := i % resamplingBufferSize
	return r.srcBufL[r.srcBlock][ii], r.srcBufR[r.srcBlock][ii], nil
}

func (r *Resampling) at(t int64) (float64, float64, error) {
	windowSize := 8.0
	tInSrc := float64(t) * float64(r.from) / float64(r.to)
	startN := int64(tInSrc - windowSize)
	if startN < 0 {
		startN = 0
	}
	if r.size/4 <= startN {
		startN = r.size/4 - 1
	}
	endN := int64(tInSrc + windowSize)
	if r.size/4 <= endN {
		endN = r.size/4 - 1
	}
	lv := 0.0
	rv := 0.0
	for n := startN; n <= endN; n++ {
		srcL, srcR, err := r.src(n)
		if err != nil {
			return 0, 0, err
		}
		d := tInSrc - float64(n)
		w := 0.5 + 0.5*fastCos01(d/(windowSize*2+1))
		s := sinc01(d/2) * w
		lv += srcL * s
		rv += srcR * s
	}
	if lv < -1 {
		lv = -1
	}
	if lv > 1 {
		lv = 1
	}
	if rv < -1 {
		rv = -1
	}
	if rv > 1 {
		rv = 1
	}
	return lv, rv, nil
}

func (r *Resampling) Read(b []uint8) (int, error) {
	if r.pos == r.Length() {
		return 0, io.EOF
	}
	n := len(b) / 4 * 4
	if r.Length()-r.pos <= int64(n) {
		n = int(r.Length() - r.pos)
	}
	for i := 0; i < n/4; i++ {
		l, r, err := r.at(r.pos/4 + int64(i))
		if err != nil {
			return 0, err
		}
		l16 := int16(l * (1<<15 - 1))
		r16 := int16(r * (1<<15 - 1))
		b[4*i] = uint8(l16)
		b[4*i+1] = uint8(l16 >> 8)
		b[4*i+2] = uint8(r16)
		b[4*i+3] = uint8(r16 >> 8)
	}
	r.pos += int64(n)
	return n, nil
}

func (r *Resampling) Seek(offset int64, whence int) (int64, error) {
	switch whence {
	case io.SeekStart:
		r.pos = offset
	case io.SeekCurrent:
		r.pos += offset
	case io.SeekEnd:
		r.pos += r.Length() + offset
	}
	if r.pos < 0 {
		r.pos = 0
	}
	if r.Length() <= r.pos {
		r.pos = r.Length()
	}
	return r.pos, nil
}

func (r *Resampling) Close() error {
	return r.source.Close()
}

type Stereo16 struct {
	source ReadSeekCloser
	mono   bool
	eight  bool
}

func NewStereo16(source ReadSeekCloser, mono, eight bool) *Stereo16 {
	return &Stereo16{
		source: source,
		mono:   mono,
		eight:  eight,
	}
}

func (s *Stereo16) Read(b []uint8) (int, error) {
	l := len(b)
	if s.mono {
		l /= 2
	}
	if s.eight {
		l /= 2
	}
	buf := make([]uint8, l)
	n, err := s.source.Read(buf)
	if err != nil && err != io.EOF {
		return 0, err
	}
	switch {
	case s.mono && s.eight:
		for i := 0; i < n; i++ {
			v := int16(int(buf[i])*0x101 - (1 << 15))
			b[4*i] = uint8(v)
			b[4*i+1] = uint8(v >> 8)
			b[4*i+2] = uint8(v)
			b[4*i+3] = uint8(v >> 8)
		}
	case s.mono && !s.eight:
		for i := 0; i < n/2; i++ {
			b[4*i] = buf[2*i]
			b[4*i+1] = buf[2*i+1]
			b[4*i+2] = buf[2*i]
			b[4*i+3] = buf[2*i+1]
		}
	case !s.mono && s.eight:
		for i := 0; i < n/2; i++ {
			v0 := int16(int(buf[2*i])*0x101 - (1 << 15))
			v1 := int16(int(buf[2*i+1])*0x101 - (1 << 15))
			b[4*i] = uint8(v0)
			b[4*i+1] = uint8(v0 >> 8)
			b[4*i+2] = uint8(v1)
			b[4*i+3] = uint8(v1 >> 8)
		}
	}
	if s.mono {
		n *= 2
	}
	if s.eight {
		n *= 2
	}
	return n, err
}

func (s *Stereo16) Seek(offset int64, whence int) (int64, error) {
	if s.mono {
		offset /= 2
	}
	if s.eight {
		offset /= 2
	}
	return s.source.Seek(offset, whence)
}

func (s *Stereo16) Close() error {
	return s.source.Close()
}

//ReadSeekCloser is an io.ReadSeeker and an io.Closer
type ReadSeekCloser interface {
	io.ReadSeeker
	io.Closer
}
<commit_msg>fix unnecessary type conversions<commit_after>// Package convert resamples and converts audio data
package convert

import (
	"io"
	"math"
)

var cosTable = [65536]float64{}

func init() {
	for i := range cosTable {
		cosTable[i] = math.Cos(float64(i) * math.Pi / 2 / float64(len(cosTable)))
	}
}

func fastCos01(x float64) float64 {
	if x < 0 {
		x = -x
	}
	i := int(4 * float64(len(cosTable)) * x)
	if 4*len(cosTable) < i {
		i %= 4 * len(cosTable)
	}
	sign := 1
	switch {
	case i < len(cosTable):
	case i < len(cosTable)*2:
		i = len(cosTable)*2 - i
		sign = -1
	case i < len(cosTable)*3:
		i -= len(cosTable) * 2
		sign = -1
	default:
		i = len(cosTable)*4 - i
	}
	if i == len(cosTable) {
		return 0
	}
	return float64(sign) * cosTable[i]
}

func fastSin01(x float64) float64 {
	return fastCos01(x - 0.25)
}

func sinc01(x float64) float64 {
	if math.Abs(x) < 1e-8 {
		return 1
	}
	return fastSin01(x) / (x * 2 * math.Pi)
}

type Resampling struct {
	source       ReadSeekCloser
	size         int64
	from         int
	to           int
	pos          int64
	srcBlock     int64
	srcBufL      map[int64][]float64
	srcBufR      map[int64][]float64
	lruSrcBlocks []int64
}

func NewResampling(source ReadSeekCloser, size int64, from, to int) *Resampling {
	r := &Resampling{
		source:   source,
		size:     size,
		from:     from,
		to:       to,
		srcBlock: -1,
		srcBufL:  map[int64][]float64{},
		srcBufR:  map[int64][]float64{},
	}
	return r
}

func (r *Resampling) Length() int64 {
	s := int64(float64(r.size) * float64(r.to) / float64(r.from))
	return s / 4 * 4
}

func (r *Resampling) src(i int64) (float64, float64, error) {
	const resamplingBufferSize = 4096

	if i < 0 {
		return 0, 0, nil
	}
	if r.size/4 <= i {
		return 0, 0, nil
	}
	nextPos := i / resamplingBufferSize
	if _, ok := r.srcBufL[nextPos]; !ok {
		if r.srcBlock+1 != nextPos {
			if _, err := r.source.Seek(nextPos*resamplingBufferSize*4, io.SeekStart); err != nil {
				return 0, 0, err
			}
		}
		buf := make([]uint8, resamplingBufferSize*4)
		c := 0
		for c < len(buf) {
			n, err := r.source.Read(buf[c:])
			c += n
			if err != nil {
				if err == io.EOF {
					break
				}
				return 0, 0, err
			}
		}
		buf = buf[:c]
		sl := make([]float64, resamplingBufferSize)
		sr := make([]float64, resamplingBufferSize)
		for i := 0; i < len(buf)/4; i++ {
			sl[i] = float64(int16(buf[4*i])|(int16(buf[4*i+1])<<8)) / (1<<15 - 1)
			sr[i] = float64(int16(buf[4*i+2])|(int16(buf[4*i+3])<<8)) / (1<<15 - 1)
		}
		r.srcBlock = nextPos
		r.srcBufL[r.srcBlock] = sl
		r.srcBufR[r.srcBlock] = sr
		// To keep srcBufL/R not too big, let's remove the least used buffers.
		if len(r.lruSrcBlocks) >= 4 {
			p := r.lruSrcBlocks[0]
			delete(r.srcBufL, p)
			delete(r.srcBufR, p)
			r.lruSrcBlocks = r.lruSrcBlocks[1:]
		}
		r.lruSrcBlocks = append(r.lruSrcBlocks, r.srcBlock)
	} else {
		r.srcBlock = nextPos
		idx := -1
		for i, p := range r.lruSrcBlocks {
			if p == r.srcBlock {
				idx = i
				break
			}
		}
		if idx == -1 {
			panic("not reach")
		}
		r.lruSrcBlocks = append(r.lruSrcBlocks[:idx], r.lruSrcBlocks[idx+1:]...)
		r.lruSrcBlocks = append(r.lruSrcBlocks, r.srcBlock)
	}
	ii := i % resamplingBufferSize
	return r.srcBufL[r.srcBlock][ii], r.srcBufR[r.srcBlock][ii], nil
}

func (r *Resampling) at(t int64) (float64, float64, error) {
	windowSize := 8.0
	tInSrc := float64(t) * float64(r.from) / float64(r.to)
	startN := int64(tInSrc - windowSize)
	if startN < 0 {
		startN = 0
	}
	if r.size/4 <= startN {
		startN = r.size/4 - 1
	}
	endN := int64(tInSrc + windowSize)
	if r.size/4 <= endN {
		endN = r.size/4 - 1
	}
	lv := 0.0
	rv := 0.0
	for n := startN; n <= endN; n++ {
		srcL, srcR, err := r.src(n)
		if err != nil {
			return 0, 0, err
		}
		d := tInSrc - float64(n)
		w := 0.5 + 0.5*fastCos01(d/(windowSize*2+1))
		s := sinc01(d/2) * w
		lv += srcL * s
		rv += srcR * s
	}
	if lv < -1 {
		lv = -1
	}
	if lv > 1 {
		lv = 1
	}
	if rv < -1 {
		rv = -1
	}
	if rv > 1 {
		rv = 1
	}
	return lv, rv, nil
}

func (r *Resampling) Read(b []uint8) (int, error) {
	if r.pos == r.Length() {
		return 0, io.EOF
	}
	n := len(b) / 4 * 4
	if r.Length()-r.pos <= int64(n) {
		n = int(r.Length() - r.pos)
	}
	for i := 0; i < n/4; i++ {
		l, r, err := r.at(r.pos/4 + int64(i))
		if err != nil {
			return 0, err
		}
		l16 := int16(l * (1<<15 - 1))
		r16 := int16(r * (1<<15 - 1))
		b[4*i] = uint8(l16)
		b[4*i+1] = uint8(l16 >> 8)
		b[4*i+2] = uint8(r16)
		b[4*i+3] = uint8(r16 >> 8)
	}
	r.pos += int64(n)
	return n, nil
}

func (r *Resampling) Seek(offset int64, whence int) (int64, error) {
	switch whence {
	case io.SeekStart:
		r.pos = offset
	case io.SeekCurrent:
		r.pos += offset
	case io.SeekEnd:
		r.pos += r.Length() + offset
	}
	if r.pos < 0 {
		r.pos = 0
	}
	if r.Length() <= r.pos {
		r.pos = r.Length()
	}
	return r.pos, nil
}

func (r *Resampling) Close() error {
	return r.source.Close()
}

type Stereo16 struct {
	source ReadSeekCloser
	mono   bool
	eight  bool
}

func NewStereo16(source ReadSeekCloser, mono, eight bool) *Stereo16 {
	return &Stereo16{
		source: source,
		mono:   mono,
		eight:  eight,
	}
}

func (s *Stereo16) Read(b []uint8) (int, error) {
	l := len(b)
	if s.mono {
		l /= 2
	}
	if s.eight {
		l /= 2
	}
	buf := make([]uint8, l)
	n, err := s.source.Read(buf)
	if err != nil && err != io.EOF {
		return 0, err
	}
	switch {
	case s.mono && s.eight:
		for i := 0; i < n; i++ {
			v := int16(int(buf[i])*0x101 - (1 << 15))
			b[4*i] = uint8(v)
			b[4*i+1] = uint8(v >> 8)
			b[4*i+2] = uint8(v)
			b[4*i+3] = uint8(v >> 8)
		}
	case s.mono && !s.eight:
		for i := 0; i < n/2; i++ {
			b[4*i] = buf[2*i]
			b[4*i+1] = buf[2*i+1]
			b[4*i+2] = buf[2*i]
			b[4*i+3] = buf[2*i+1]
		}
	case !s.mono && s.eight:
		for i := 0; i < n/2; i++ {
			v0 := int16(int(buf[2*i])*0x101 - (1 << 15))
			v1 := int16(int(buf[2*i+1])*0x101 - (1 << 15))
			b[4*i] = uint8(v0)
			b[4*i+1] = uint8(v0 >> 8)
			b[4*i+2] = uint8(v1)
			b[4*i+3] = uint8(v1 >> 8)
		}
	}
	if s.mono {
		n *= 2
	}
	if s.eight {
		n *= 2
	}
	return n, err
}

func (s *Stereo16) Seek(offset int64, whence int) (int64, error) {
	if s.mono {
		offset /= 2
	}
	if s.eight {
		offset /= 2
	}
	return s.source.Seek(offset, whence)
}

func (s *Stereo16) Close() error {
	return s.source.Close()
}

//ReadSeekCloser is an io.ReadSeeker and an io.Closer
type ReadSeekCloser interface {
	io.ReadSeeker
	io.Closer
}
<|endoftext|>


<commit_before>package main
import (
	"log"
	"net/http"
	"errors"
	"regexp"
	"strings"
	"strconv"
)

type Response interface{}
type RouteHandler func(Request) Response

/*
	return NotFound("Missing file")
 */
func main() {
	s := NewSugoi()

	s.GET("/", func(req Request) Response {
		return "homepage"
	})

	s.GET("/hello/{name}", func(req Request) Response {
		name := req.GetAttribute("name")
		return "hello " + name
	})

	s.DELETE("/", func(req Request) Response {
		return nil
	})

	s.PUT("/", func(req Request) Response {
		return nil
	})

	s.POST("/", func(req Request) Response {
		return nil
	})

	s.OPTIONS("/", func(req Request) Response {
		return nil
	})

	s.PATCH("/", func(req Request) Response {
		return nil
	})

	s.Serve()
}

func NewSugoi() *SugoiServer {

	return &SugoiServer{
		handler: NewWrappedHandler(),
	}
}

type SugoiServer struct {
	handler 	*WrappedHandler
}

func (s *SugoiServer) add(method string, path string, fn RouteHandler) {
	s.handler.routes = append(s.handler.routes, CreateNewRoute(path, method, fn))
}

func (s *SugoiServer) GET(path string, fn RouteHandler) {
	s.add("get", path, fn)
}

func (s *SugoiServer) DELETE(path string, fn RouteHandler) {
	s.add("delete", path, fn)
}

func (s *SugoiServer) PUT(path string, fn RouteHandler) {
	s.add("put", path, fn)
}

func (s *SugoiServer) POST(path string, fn RouteHandler) {
	s.add("post", path, fn)
}

func (s *SugoiServer) OPTIONS(path string, fn RouteHandler) {
	s.add("options", path, fn)
}

func (s *SugoiServer) PATCH(path string, fn RouteHandler) {
	s.add("patch", path, fn)
}

func (s *SugoiServer) Serve() {
	log.Println("Starting Sugoi!")

	err := http.ListenAndServe(":8085", s.handler)
	if err != nil {
		log.Fatal(err)
	}
}

func CreateCompilableRoutePath(pre string) (*regexp.Regexp, error) {
	re, _ := regexp.Compile(`{[a-z]+}`)

	matches := re.FindAllStringSubmatch(pre, -1)

	pre = "^" + pre
	for _, b := range matches {
		origAttr := b[0]
		attr := strings.Replace(strings.Replace(origAttr, "{", "", -1), "}", "", -1)
		frag := `(?P<` + attr + `>\w+)`
		pre = strings.Replace(pre, origAttr, frag, -1)
	}
	pre += "$"
	re, err := regexp.Compile(pre)

	return re, err
}

func CreateNewRoute(path string, method string, fn RouteHandler) *Route {
	re, _ := CreateCompilableRoutePath(path)

	return &Route{
		path:    path,
		method:  method,
		handler: fn,
		regEx:   re,
	}
}

func NewWrappedHandler() (*WrappedHandler) {
	return &WrappedHandler{
		routes : []*Route{},
	}
}

var ERR_NO_MATCHING_ROUTE = errors.New("No matching route found")
var ERR_UNSUPPORTED_CONTENT_FORMAT = errors.New("Unsupported Content-Format")
var ERR_NO_MATCHING_METHOD = errors.New("No matching method")

func MatchesRoutePath(path string, re *regexp.Regexp) (bool, map[string]string) {
	matches := re.FindAllStringSubmatch(path, -1)
	attrs := make(map[string]string)
	if len(matches) > 0 {
		subExp := re.SubexpNames()
		for idx, exp := range subExp {
			attrs[exp] = matches[0][idx]
		}

		return true, attrs
	}

	return false, attrs
}

type Route struct {
	path 	string
	method 	string
	handler RouteHandler
	regEx 	*regexp.Regexp
}

func MatchingRoute(path string, method string, routes []*Route) (RouteHandler, map[string]string, error) {
	for _, route := range routes {
		match, attrs :=  MatchesRoutePath(path, route.regEx)
		if match {
			return route.handler, attrs, nil
		}
	}
	return nil, nil, ERR_NO_MATCHING_ROUTE
}

type WrappedHandler struct {
	routes 	[]*Route
}

func (wh *WrappedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fn, attrs, err := MatchingRoute(r.URL.Path, r.Method, wh.routes)

	if err != nil {
		if err == ERR_NO_MATCHING_ROUTE {
			http.NotFound(w, r)
			return
		}
	} else {
		req := NewRequestFromHttp(attrs)
		resp := fn(req)

		SendHttpResponse(resp, w)
		return
	}
}

func SendHttpResponse(response interface{}, w http.ResponseWriter) {
	log.Println("Sending Back Response..", response)
	ResponseHandler(response, w)
}

func ResponseHandler(response interface{}, w http.ResponseWriter) {

}

func NewRequestFromHttp(attrs map[string]string) Request {
	return Request{
		attrs: attrs,
	}
}

type Request struct {
	attrs 	map[string]string
	// Query
	// Cookies
	// Sessions
		// get attribute
		// set attribute
}

func (c *Request) GetAttributes() map[string]string {
	return c.attrs
}

func (c *Request) GetAttribute(o string) string {
	return c.attrs[o]
}

func (c *Request) GetAttributeAsInt(o string) int {
	attr := c.GetAttribute(o)
	i, _ := strconv.Atoi(attr)

	return i
}

// Functions
func Halt(msg string, code int) {
	// halt(int)
	// halt(msg)
	// halt(int, msg)
}

func HaltWithCode(code int) {

}

func HaltWithMessage(msg string) {

}

// Before every method
func BeforeAll(fn RouteHandler) {

}

func Before(pattern string, fn RouteHandler) {

}

// After every methods
func After(pattern string, fn RouteHandler) {

}

func AfterAll(fn RouteHandler) {

}

// Handling Exceptions
func Error(err error, fn RouteHandler) {

}<commit_msg>working copy<commit_after>package main
import (
	"log"
	"net/http"
	"errors"
	"regexp"
	"strings"
	"strconv"
)

type Response interface{}
type RouteHandler func(Request) Response

func main() {
	s := NewSugoi()

	s.GET("/", func(req Request) Response {
		return "homepage"
	})

	s.GET("/hello/{name}", func(req Request) Response {
		name := req.GetAttribute("name")
		return "hello " + name
	})

	s.DELETE("/", func(req Request) Response {
		return nil
	})

	s.PUT("/", func(req Request) Response {
		return nil
	})

	s.POST("/", func(req Request) Response {
		return nil
	})

	s.OPTIONS("/", func(req Request) Response {
		return nil
	})

	s.PATCH("/", func(req Request) Response {
		return nil
	})

	s.Serve()
}

func NewSugoi() *SugoiServer {

	return &SugoiServer{
		handler: NewWrappedHandler(),
	}
}

type SugoiServer struct {
	handler 	*WrappedHandler
}

func (s *SugoiServer) add(method string, path string, fn RouteHandler) {
	s.handler.routes = append(s.handler.routes, CreateNewRoute(path, method, fn))
}

func (s *SugoiServer) GET(path string, fn RouteHandler) {
	s.add("get", path, fn)
}

func (s *SugoiServer) DELETE(path string, fn RouteHandler) {
	s.add("delete", path, fn)
}

func (s *SugoiServer) PUT(path string, fn RouteHandler) {
	s.add("put", path, fn)
}

func (s *SugoiServer) POST(path string, fn RouteHandler) {
	s.add("post", path, fn)
}

func (s *SugoiServer) OPTIONS(path string, fn RouteHandler) {
	s.add("options", path, fn)
}

func (s *SugoiServer) PATCH(path string, fn RouteHandler) {
	s.add("patch", path, fn)
}

func (s *SugoiServer) Serve() {
	log.Println("Starting Sugoi!")

	err := http.ListenAndServe(":8085", s.handler)
	if err != nil {
		log.Fatal(err)
	}
}

func CreateCompilableRoutePath(pre string) (*regexp.Regexp, error) {
	re, _ := regexp.Compile(`{[a-z]+}`)

	matches := re.FindAllStringSubmatch(pre, -1)

	pre = "^" + pre
	for _, b := range matches {
		origAttr := b[0]
		attr := strings.Replace(strings.Replace(origAttr, "{", "", -1), "}", "", -1)
		frag := `(?P<` + attr + `>\w+)`
		pre = strings.Replace(pre, origAttr, frag, -1)
	}
	pre += "$"
	re, err := regexp.Compile(pre)

	return re, err
}

func CreateNewRoute(path string, method string, fn RouteHandler) *Route {
	re, _ := CreateCompilableRoutePath(path)

	return &Route{
		path:    path,
		method:  method,
		handler: fn,
		regEx:   re,
	}
}

func NewWrappedHandler() (*WrappedHandler) {
	return &WrappedHandler{
		routes : []*Route{},
	}
}

var ERR_NO_MATCHING_ROUTE = errors.New("No matching route found")
var ERR_UNSUPPORTED_CONTENT_FORMAT = errors.New("Unsupported Content-Format")
var ERR_NO_MATCHING_METHOD = errors.New("No matching method")

func MatchesRoutePath(path string, re *regexp.Regexp) (bool, map[string]string) {
	matches := re.FindAllStringSubmatch(path, -1)
	attrs := make(map[string]string)
	if len(matches) > 0 {
		subExp := re.SubexpNames()
		for idx, exp := range subExp {
			attrs[exp] = matches[0][idx]
		}

		return true, attrs
	}

	return false, attrs
}

type Route struct {
	path 	string
	method 	string
	handler RouteHandler
	regEx 	*regexp.Regexp
}

func MatchingRoute(path string, method string, routes []*Route) (RouteHandler, map[string]string, error) {
	for _, route := range routes {
		match, attrs :=  MatchesRoutePath(path, route.regEx)
		if match {
			return route.handler, attrs, nil
		}
	}
	return nil, nil, ERR_NO_MATCHING_ROUTE
}

type WrappedHandler struct {
	routes 	[]*Route
}

func (wh *WrappedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fn, attrs, err := MatchingRoute(r.URL.Path, r.Method, wh.routes)

	if err != nil {
		if err == ERR_NO_MATCHING_ROUTE {
			http.NotFound(w, r)
			return
		}
	} else {
		req := NewRequestFromHttp(attrs)
		resp := fn(req)

		SendHttpResponse(resp, w)
		return
	}
}

func SendHttpResponse(response interface{}, w http.ResponseWriter) {
	log.Println("Sending Back Response..", response)
	ResponseHandler(response, w)
}

func ResponseHandler(response interface{}, w http.ResponseWriter) {

}

func NewRequestFromHttp(attrs map[string]string) Request {
	return Request{
		attrs: attrs,
	}
}

type Request struct {
	attrs 	map[string]string
	// Query
	// Cookies
	// Sessions
		// get attribute
		// set attribute
}

func (c *Request) GetAttributes() map[string]string {
	return c.attrs
}

func (c *Request) GetAttribute(o string) string {
	return c.attrs[o]
}

func (c *Request) GetAttributeAsInt(o string) int {
	attr := c.GetAttribute(o)
	i, _ := strconv.Atoi(attr)

	return i
}

// Functions
func Halt(msg string, code int) {
	// halt(int)
	// halt(msg)
	// halt(int, msg)
}

func HaltWithCode(code int) {

}

func HaltWithMessage(msg string) {

}

// Before every method
func BeforeAll(fn RouteHandler) {

}

func Before(pattern string, fn RouteHandler) {

}

// After every methods
func After(pattern string, fn RouteHandler) {

}

func AfterAll(fn RouteHandler) {

}

// Handling Exceptions
func Error(err error, fn RouteHandler) {

}

// Return Content Types
	// text
	// object : implicit json
	// various error code objects
	// specific content type (with converters)
		// xml, json, plain text, binary etc
	// with html template<|endoftext|>