<commit_before>package client

import (
	"errors"
	"net/url"
	"reflect"
	"testing"
	"time"

	"github.com/grafana/loki/pkg/promtail/api"

	"github.com/cortexproject/cortex/pkg/util"
	"github.com/cortexproject/cortex/pkg/util/flagext"
	"github.com/grafana/loki/pkg/promtail/client/fake"
	"github.com/prometheus/common/model"
)

func TestNewMulti(t *testing.T) {
	_, err := NewMulti(util.Logger, []Config{}...)
	if err == nil {
		t.Fatal("expected err but got nil")
	}
	host1, _ := url.Parse("http://localhost:3100")
	host2, _ := url.Parse("https://grafana.com")
	expectedCfg1 := Config{BatchSize: 20, URL: flagext.URLValue{URL: host1}}
	expectedCfg2 := Config{BatchSize: 10, URL: flagext.URLValue{URL: host2}}

	clients, err := NewMulti(util.Logger, expectedCfg1, expectedCfg2)
	if err != nil {
		t.Fatalf("expected err: nil got:%v", err)
	}
	multi := clients.(MultiClient)
	if len(multi) != 2 {
		t.Fatalf("expected client: 2 got:%d", len(multi))
	}
	cfg1 := clients.(MultiClient)[0].(*client).cfg

	if !reflect.DeepEqual(cfg1, expectedCfg1) {
		t.Fatalf("expected cfg: %v got:%v", expectedCfg1, cfg1)
	}

	cfg2 := clients.(MultiClient)[1].(*client).cfg

	if !reflect.DeepEqual(cfg2, expectedCfg2) {
		t.Fatalf("expected cfg: %v got:%v", expectedCfg2, cfg2)
	}
}

func TestMultiClient_Stop(t *testing.T) {
	var stopped int

	stopping := func() {
		stopped++
	}
	fc := &fake.Client{OnStop: stopping}
	clients := []Client{fc, fc, fc, fc}
	m := MultiClient(clients)

	m.Stop()

	if stopped != len(clients) {
		t.Fatal("missing stop call")
	}
}

func TestMultiClient_Handle(t *testing.T) {

	var called int

	errorFn := api.EntryHandlerFunc(func(labels model.LabelSet, time time.Time, entry string) error { called++; return errors.New("") })
	okFn := api.EntryHandlerFunc(func(labels model.LabelSet, time time.Time, entry string) error { called++; return nil })

	errfc := &fake.Client{OnHandleEntry: errorFn}
	okfc := &fake.Client{OnHandleEntry: okFn}
	t.Run("some error", func(t *testing.T) {
		clients := []Client{okfc, errfc, okfc, errfc, errfc, okfc}
		m := MultiClient(clients)

		if err := m.Handle(nil, time.Now(), ""); err == nil {
			t.Fatal("expected err got nil")
		}

		if called != len(clients) {
			t.Fatal("missing handle call")
		}

	})
	t.Run("no error", func(t *testing.T) {
		called = 0
		clients := []Client{okfc, okfc, okfc, okfc, okfc, okfc}
		m := MultiClient(clients)

		if err := m.Handle(nil, time.Now(), ""); err != nil {
			t.Fatal("expected err to be nil")
		}

		if called != len(clients) {
			t.Fatal("missing handle call")
		}

	})

}
<commit_msg>Set default value for BatchWait as ticker does not accept 0<commit_after>package client

import (
	"errors"
	"net/url"
	"reflect"
	"testing"
	"time"

	"github.com/grafana/loki/pkg/promtail/api"

	"github.com/cortexproject/cortex/pkg/util"
	"github.com/cortexproject/cortex/pkg/util/flagext"
	"github.com/grafana/loki/pkg/promtail/client/fake"
	"github.com/prometheus/common/model"
)

func TestNewMulti(t *testing.T) {
	_, err := NewMulti(util.Logger, []Config{}...)
	if err == nil {
		t.Fatal("expected err but got nil")
	}
	host1, _ := url.Parse("http://localhost:3100")
	host2, _ := url.Parse("https://grafana.com")
	expectedCfg1 := Config{BatchSize: 20, BatchWait: 1 * time.Second, URL: flagext.URLValue{URL: host1}}
	expectedCfg2 := Config{BatchSize: 10, BatchWait: 1 * time.Second, URL: flagext.URLValue{URL: host2}}

	clients, err := NewMulti(util.Logger, expectedCfg1, expectedCfg2)
	if err != nil {
		t.Fatalf("expected err: nil got:%v", err)
	}
	multi := clients.(MultiClient)
	if len(multi) != 2 {
		t.Fatalf("expected client: 2 got:%d", len(multi))
	}
	cfg1 := clients.(MultiClient)[0].(*client).cfg

	if !reflect.DeepEqual(cfg1, expectedCfg1) {
		t.Fatalf("expected cfg: %v got:%v", expectedCfg1, cfg1)
	}

	cfg2 := clients.(MultiClient)[1].(*client).cfg

	if !reflect.DeepEqual(cfg2, expectedCfg2) {
		t.Fatalf("expected cfg: %v got:%v", expectedCfg2, cfg2)
	}
}

func TestMultiClient_Stop(t *testing.T) {
	var stopped int

	stopping := func() {
		stopped++
	}
	fc := &fake.Client{OnStop: stopping}
	clients := []Client{fc, fc, fc, fc}
	m := MultiClient(clients)

	m.Stop()

	if stopped != len(clients) {
		t.Fatal("missing stop call")
	}
}

func TestMultiClient_Handle(t *testing.T) {

	var called int

	errorFn := api.EntryHandlerFunc(func(labels model.LabelSet, time time.Time, entry string) error { called++; return errors.New("") })
	okFn := api.EntryHandlerFunc(func(labels model.LabelSet, time time.Time, entry string) error { called++; return nil })

	errfc := &fake.Client{OnHandleEntry: errorFn}
	okfc := &fake.Client{OnHandleEntry: okFn}
	t.Run("some error", func(t *testing.T) {
		clients := []Client{okfc, errfc, okfc, errfc, errfc, okfc}
		m := MultiClient(clients)

		if err := m.Handle(nil, time.Now(), ""); err == nil {
			t.Fatal("expected err got nil")
		}

		if called != len(clients) {
			t.Fatal("missing handle call")
		}

	})
	t.Run("no error", func(t *testing.T) {
		called = 0
		clients := []Client{okfc, okfc, okfc, okfc, okfc, okfc}
		m := MultiClient(clients)

		if err := m.Handle(nil, time.Now(), ""); err != nil {
			t.Fatal("expected err to be nil")
		}

		if called != len(clients) {
			t.Fatal("missing handle call")
		}

	})

}
<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"errors"
	"strings"
)

// synchronizable returns whether or not an entry kind is synchronizable.
func (k EntryKind) synchronizable() bool {
	return k == EntryKind_Directory ||
		k == EntryKind_File ||
		k == EntryKind_SymbolicLink
}

// EnsureValid ensures that Entry's invariants are respected. If synchronizable
// is true, then unsynchronizable content will be considered invalid.
func (e *Entry) EnsureValid(synchronizable bool) error {
	// A nil entry represents an absence of content and is therefore valid.
	if e == nil {
		return nil
	}

	// Otherwise validate based on kind.
	if e.Kind == EntryKind_Directory {
		// Ensure that no invalid fields are set.
		if e.Digest != nil {
			return errors.New("non-nil directory digest detected")
		} else if e.Executable {
			return errors.New("executable directory detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for directory")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for directory")
		}

		// Validate contents. Nil entries are not considered valid for contents.
		for name, entry := range e.Contents {
			if name == "" {
				return errors.New("empty content name detected")
			} else if name == "." || name == ".." {
				return errors.New("dot name detected")
			} else if strings.IndexByte(name, '/') != -1 {
				return errors.New("content name contains path separator")
			} else if entry == nil {
				return errors.New("nil content detected")
			} else if err := entry.EnsureValid(synchronizable); err != nil {
				return err
			}
		}
	} else if e.Kind == EntryKind_File {
		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil file content map detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for file")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for file")
		}

		// Ensure that the digest is non-empty.
		if len(e.Digest) == 0 {
			return errors.New("file with empty digest detected")
		}
	} else if e.Kind == EntryKind_SymbolicLink {
		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil symbolic link content map detected")
		} else if e.Digest != nil {
			return errors.New("non-nil symbolic link digest detected")
		} else if e.Executable {
			return errors.New("executable symbolic link detected")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for symbolic link")
		}

		// Ensure that the target is non-empty. We avoid any further validation
		// because there's none that we can reasonably perform.
		if e.Target == "" {
			return errors.New("symbolic link with empty target detected")
		}
	} else if e.Kind == EntryKind_Untracked {
		// Verify that unsynchronizable content is allowed.
		if synchronizable {
			return errors.New("untracked content is not synchronizable")
		}

		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil untracked content map detected")
		} else if e.Digest != nil {
			return errors.New("non-nil untracked content digest detected")
		} else if e.Executable {
			return errors.New("executable untracked content detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for untracked content")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for untracked content")
		}
	} else if e.Kind == EntryKind_Problematic {
		// Verify that unsynchronizable content is allowed.
		if synchronizable {
			return errors.New("problematic content is not synchronizable")
		}

		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil problematic content map detected")
		} else if e.Digest != nil {
			return errors.New("non-nil problematic content digest detected")
		} else if e.Executable {
			return errors.New("executable problematic content detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for problematic content")
		}

		// Ensure that the problem is non-empty.
		if e.Problem == "" {
			return errors.New("empty problem detected for problematic content")
		}
	} else {
		return errors.New("unknown entry kind detected")
	}

	// Success.
	return nil
}

// entryVisitor is a callback type used for Entry.walk.
type entryVisitor func(path string, entry *Entry)

// walk performs a depth-first traversal of the entry, invoking the specified
// visitor on each element in the entry hierarchy. The path argument specifies
// the path at which the root entry should be treated as residing. If reverse is
// false, then each entry will be visited before its contents (i.e. a normal
// depth-first traversal), otherwise it will be visited after its contents (i.e.
// a reverse depth-first traversal).
func (e *Entry) walk(path string, visitor entryVisitor, reverse bool) {
	// If this is a normal walk, then visit the entry before its contents.
	if !reverse {
		visitor(path, e)
	}

	// If this entry is non-nil, then visit any child entries. We don't bother
	// checking if the entry is a directory since this is an internal method and
	// the caller is responsible for enforcing entry invariants (meaning that
	// only directories will have child entries).
	if e != nil {
		for name, child := range e.Contents {
			child.walk(pathJoin(path, name), visitor, reverse)
		}
	}

	// If this is a reverse walk, then visit the entry after its contents.
	if reverse {
		visitor(path, e)
	}
}

// Count returns the total number of entries within the entry hierarchy rooted
// at the entry, excluding nil and unsynchronizable entries.
func (e *Entry) Count() uint64 {
	// Nil entries represent an empty hierarchy.
	if e == nil {
		return 0
	}

	// Unsynchronizable entries can be excluded from the count because they
	// don't represent content that can or will be synchronized.
	if e.Kind == EntryKind_Untracked || e.Kind == EntryKind_Problematic {
		return 0
	}

	// Count ourselves.
	result := uint64(1)

	// Count any child entries. We don't bother checking if the entry is a
	// directory since the caller is responsible for enforcing entry invariants
	// (meaning that only directories will have child entries).
	for _, child := range e.Contents {
		// TODO: At the moment, we don't worry about overflow here. The
		// reason is that, in order to overflow uint64, we'd need a minimum
		// of 2**64 entries in the hierarchy. Even assuming that each entry
		// consumed only one byte of memory (and they consume at least an
		// order of magnitude more than that), we'd have to be on a system
		// with (at least) ~18.5 exabytes of memory. Additionally, Protocol
		// Buffers messages have even lower size limits that would prevent
		// such an Entry from being sent over the network. But we should
		// still fix this at some point.
		result += child.Count()
	}

	// Done.
	return result
}

// entryEqualWildcardProblemMatch controls whether or not wildcard problem
// matching is enabled for Entry.Equal. Ideally this would be a constant so that
// the compiler could optimize away the unused branch in Entry.Equal, but
// there's no "test" build tag that we can use to redefine constants for tests
// only. The Go developers seem adamant that no such flag should be added. We
// could define one manually, but modern CPUs will chew through this additional
// check quickly enough anyway, so it's not worth the trouble.
var entryEqualWildcardProblemMatch bool

// Equal performs an equivalence comparison between this entry and another. If
// deep is true, then the comparison is performed recursively, otherwise the
// comparison is only performed between entry properties at the top level and
// content maps are ignored.
func (e *Entry) Equal(other *Entry, deep bool) bool {
	// If the pointers are equal, then the entries are equal, both shallowly and
	// recursively. This includes the case where both pointers are nil, which
	// represents the absence of content. If only one pointer is nil, then they
	// can't possibly be equal.
	if e == other {
		return true
	} else if e == nil || other == nil {
		return false
	}

	// Compare all properties except for problem messages..
	propertiesEquivalent := e.Kind == other.Kind &&
		e.Executable == other.Executable &&
		bytes.Equal(e.Digest, other.Digest) &&
		e.Target == other.Target
	if !propertiesEquivalent {
		return false
	}

	// Compare problem messages according to whether or not wildcard problem
	// matching is enabled. We only enable this for tests, where we can't always
	// know the exact problem message ahead of time due to variations between
	// different operating systems. Wildcard matching means that if one or both
	// of the entries has a problem message of "*", it will be considered a
	// match for the other entry's problem message.
	if !entryEqualWildcardProblemMatch {
		if e.Problem != other.Problem {
			return false
		}
	} else {
		if e.Problem != "*" && other.Problem != "*" && e.Problem != other.Problem {
			return false
		}
	}

	// If a deep comparison wasn't requested, then we're done.
	if !deep {
		return true
	}

	// Compare entry contents.
	if len(e.Contents) != len(other.Contents) {
		return false
	}
	for name, child := range e.Contents {
		otherChild, ok := other.Contents[name]
		if !ok || !child.Equal(otherChild, true) {
			return false
		}
	}

	// Done.
	return true
}

// Copy creates a copy of the entry. If deep is true, then a deep copy of the
// entry is created, otherwise a "slim" copy is created, which is a shallow copy
// that excludes the content map. In general, entries are considered immutable
// (by convention) and should be copied by pointer. However, when creating
// derived entries (e.g. using Apply), a copy operation may be necessary to
// create a temporarily mutable entry that can be modified (until returned).
// That is the role of this method. Although exported for benchmarking, there
// should generally be no need for code outside of this package to use it,
// except perhaps to convert a full snapshot to a slim snapshot.
func (e *Entry) Copy(deep bool) *Entry {
	// If the entry is nil, then the copy is nil.
	if e == nil {
		return nil
	}

	// Create a slim copy.
	result := &Entry{
		Kind:       e.Kind,
		Executable: e.Executable,
		Digest:     e.Digest,
		Target:     e.Target,
		Problem:    e.Problem,
	}

	// If a deep copy wasn't requested, then we're done.
	if !deep {
		return result
	}

	// If the original entry doesn't have any contents, then return early to
	// avoid allocation of the content map.
	if len(e.Contents) == 0 {
		return result
	}

	// Copy the entry contents.
	result.Contents = make(map[string]*Entry, len(e.Contents))
	for name, child := range e.Contents {
		result.Contents[name] = child.Copy(true)
	}

	// Done.
	return result
}

// synchronizable returns the subtree of the entry hierarchy consisting of only
// synchronizable content. It is useful for constructing the new value of a
// change when attempting to propagate around unsychronizable content. It will
// return nil if the entry itself is unsynchronizable (which is technically the
// synchronizable subtree of the entry hierarchy in that case).
func (e *Entry) synchronizable() *Entry {
	// If the entry itself is nil, then the resulting subtree is nil.
	if e == nil {
		return nil
	}

	// If the entry itself consists of unsynchronizable content, then the
	// resulting subtree is nil.
	if e.Kind == EntryKind_Untracked || e.Kind == EntryKind_Problematic {
		return nil
	}

	// If the entry (which we know is synchronizable) is not a directory, then
	// we can just return the entry itself.
	if e.Kind != EntryKind_Directory {
		return e
	}

	// If the entry (which we know is a directory) doesn't have any contents,
	// then we can just return the entry itself.
	if len(e.Contents) == 0 {
		return e
	}

	// Create a slim copy of the entry. We only need to copy fields for
	// synchronizable entry types since we know this entry is synchronizable.
	result := &Entry{
		Kind:       e.Kind,
		Executable: e.Executable,
		Digest:     e.Digest,
		Target:     e.Target,
	}

	// Copy the entry contents. Some may not be synchronizable, in which case we
	// exclude them from the resulting map. We don't need to worry about them
	// already having been nil since nil entries aren't allowed in content maps.
	result.Contents = make(map[string]*Entry, len(e.Contents))
	for name, child := range e.Contents {
		if child = child.synchronizable(); child != nil {
			result.Contents[name] = child
		}
	}

	// Done.
	return result
}

// Problems generates a list of problems from the problematic entries contained
// within the entry hierarchy. The problems are returned in depth-first but
// non-deterministic order. Problem paths are computed assuming the entry
// represents the synchronization root.
func (e *Entry) Problems() []*Problem {
	// Create the result.
	var result []*Problem

	// Perform a walk to record problematic entries.
	e.walk("", func(path string, entry *Entry) {
		if entry != nil && entry.Kind == EntryKind_Problematic {
			result = append(result, &Problem{
				Path:  path,
				Error: entry.Problem,
			})
		}
	}, false)

	// Done.
	return result
}
<commit_msg>Expanded use of EntryKind.synchronizable and clarified behavior.<commit_after>package core

import (
	"bytes"
	"errors"
	"strings"
)

// synchronizable returns true if the entry kind is synchronizable and false if
// the entry kind is unsynchronizable.
func (k EntryKind) synchronizable() bool {
	return k == EntryKind_Directory ||
		k == EntryKind_File ||
		k == EntryKind_SymbolicLink
}

// EnsureValid ensures that Entry's invariants are respected. If synchronizable
// is true, then unsynchronizable content will be considered invalid.
func (e *Entry) EnsureValid(synchronizable bool) error {
	// A nil entry represents an absence of content and is therefore valid.
	if e == nil {
		return nil
	}

	// Otherwise validate based on kind.
	if e.Kind == EntryKind_Directory {
		// Ensure that no invalid fields are set.
		if e.Digest != nil {
			return errors.New("non-nil directory digest detected")
		} else if e.Executable {
			return errors.New("executable directory detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for directory")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for directory")
		}

		// Validate contents. Nil entries are not considered valid for contents.
		for name, entry := range e.Contents {
			if name == "" {
				return errors.New("empty content name detected")
			} else if name == "." || name == ".." {
				return errors.New("dot name detected")
			} else if strings.IndexByte(name, '/') != -1 {
				return errors.New("content name contains path separator")
			} else if entry == nil {
				return errors.New("nil content detected")
			} else if err := entry.EnsureValid(synchronizable); err != nil {
				return err
			}
		}
	} else if e.Kind == EntryKind_File {
		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil file content map detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for file")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for file")
		}

		// Ensure that the digest is non-empty.
		if len(e.Digest) == 0 {
			return errors.New("file with empty digest detected")
		}
	} else if e.Kind == EntryKind_SymbolicLink {
		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil symbolic link content map detected")
		} else if e.Digest != nil {
			return errors.New("non-nil symbolic link digest detected")
		} else if e.Executable {
			return errors.New("executable symbolic link detected")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for symbolic link")
		}

		// Ensure that the target is non-empty. We avoid any further validation
		// because there's none that we can reasonably perform.
		if e.Target == "" {
			return errors.New("symbolic link with empty target detected")
		}
	} else if e.Kind == EntryKind_Untracked {
		// Verify that unsynchronizable content is allowed.
		if synchronizable {
			return errors.New("untracked content is not synchronizable")
		}

		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil untracked content map detected")
		} else if e.Digest != nil {
			return errors.New("non-nil untracked content digest detected")
		} else if e.Executable {
			return errors.New("executable untracked content detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for untracked content")
		} else if e.Problem != "" {
			return errors.New("non-empty problem detected for untracked content")
		}
	} else if e.Kind == EntryKind_Problematic {
		// Verify that unsynchronizable content is allowed.
		if synchronizable {
			return errors.New("problematic content is not synchronizable")
		}

		// Ensure that no invalid fields are set.
		if e.Contents != nil {
			return errors.New("non-nil problematic content map detected")
		} else if e.Digest != nil {
			return errors.New("non-nil problematic content digest detected")
		} else if e.Executable {
			return errors.New("executable problematic content detected")
		} else if e.Target != "" {
			return errors.New("non-empty symbolic link target detected for problematic content")
		}

		// Ensure that the problem is non-empty.
		if e.Problem == "" {
			return errors.New("empty problem detected for problematic content")
		}
	} else {
		return errors.New("unknown entry kind detected")
	}

	// Success.
	return nil
}

// entryVisitor is a callback type used for Entry.walk.
type entryVisitor func(path string, entry *Entry)

// walk performs a depth-first traversal of the entry, invoking the specified
// visitor on each element in the entry hierarchy. The path argument specifies
// the path at which the root entry should be treated as residing. If reverse is
// false, then each entry will be visited before its contents (i.e. a normal
// depth-first traversal), otherwise it will be visited after its contents (i.e.
// a reverse depth-first traversal).
func (e *Entry) walk(path string, visitor entryVisitor, reverse bool) {
	// If this is a normal walk, then visit the entry before its contents.
	if !reverse {
		visitor(path, e)
	}

	// If this entry is non-nil, then visit any child entries. We don't bother
	// checking if the entry is a directory since this is an internal method and
	// the caller is responsible for enforcing entry invariants (meaning that
	// only directories will have child entries).
	if e != nil {
		for name, child := range e.Contents {
			child.walk(pathJoin(path, name), visitor, reverse)
		}
	}

	// If this is a reverse walk, then visit the entry after its contents.
	if reverse {
		visitor(path, e)
	}
}

// Count returns the total number of entries within the entry hierarchy rooted
// at the entry, excluding nil and unsynchronizable entries.
func (e *Entry) Count() uint64 {
	// Nil entries represent an empty hierarchy.
	if e == nil {
		return 0
	}

	// Unsynchronizable entries can be excluded from the count because they
	// don't represent content that can or will be synchronized.
	if !e.Kind.synchronizable() {
		return 0
	}

	// Count ourselves.
	result := uint64(1)

	// Count any child entries. We don't bother checking if the entry is a
	// directory since the caller is responsible for enforcing entry invariants
	// (meaning that only directories will have child entries).
	for _, child := range e.Contents {
		// TODO: At the moment, we don't worry about overflow here. The
		// reason is that, in order to overflow uint64, we'd need a minimum
		// of 2**64 entries in the hierarchy. Even assuming that each entry
		// consumed only one byte of memory (and they consume at least an
		// order of magnitude more than that), we'd have to be on a system
		// with (at least) ~18.5 exabytes of memory. Additionally, Protocol
		// Buffers messages have even lower size limits that would prevent
		// such an Entry from being sent over the network. But we should
		// still fix this at some point.
		result += child.Count()
	}

	// Done.
	return result
}

// entryEqualWildcardProblemMatch controls whether or not wildcard problem
// matching is enabled for Entry.Equal. Ideally this would be a constant so that
// the compiler could optimize away the unused branch in Entry.Equal, but
// there's no "test" build tag that we can use to redefine constants for tests
// only. The Go developers seem adamant that no such flag should be added. We
// could define one manually, but modern CPUs will chew through this additional
// check quickly enough anyway, so it's not worth the trouble.
var entryEqualWildcardProblemMatch bool

// Equal performs an equivalence comparison between this entry and another. If
// deep is true, then the comparison is performed recursively, otherwise the
// comparison is only performed between entry properties at the top level and
// content maps are ignored.
func (e *Entry) Equal(other *Entry, deep bool) bool {
	// If the pointers are equal, then the entries are equal, both shallowly and
	// recursively. This includes the case where both pointers are nil, which
	// represents the absence of content. If only one pointer is nil, then they
	// can't possibly be equal.
	if e == other {
		return true
	} else if e == nil || other == nil {
		return false
	}

	// Compare all properties except for problem messages..
	propertiesEquivalent := e.Kind == other.Kind &&
		e.Executable == other.Executable &&
		bytes.Equal(e.Digest, other.Digest) &&
		e.Target == other.Target
	if !propertiesEquivalent {
		return false
	}

	// Compare problem messages according to whether or not wildcard problem
	// matching is enabled. We only enable this for tests, where we can't always
	// know the exact problem message ahead of time due to variations between
	// different operating systems. Wildcard matching means that if one or both
	// of the entries has a problem message of "*", it will be considered a
	// match for the other entry's problem message.
	if !entryEqualWildcardProblemMatch {
		if e.Problem != other.Problem {
			return false
		}
	} else {
		if e.Problem != "*" && other.Problem != "*" && e.Problem != other.Problem {
			return false
		}
	}

	// If a deep comparison wasn't requested, then we're done.
	if !deep {
		return true
	}

	// Compare entry contents.
	if len(e.Contents) != len(other.Contents) {
		return false
	}
	for name, child := range e.Contents {
		otherChild, ok := other.Contents[name]
		if !ok || !child.Equal(otherChild, true) {
			return false
		}
	}

	// Done.
	return true
}

// Copy creates a copy of the entry. If deep is true, then a deep copy of the
// entry is created, otherwise a "slim" copy is created, which is a shallow copy
// that excludes the content map. In general, entries are considered immutable
// (by convention) and should be copied by pointer. However, when creating
// derived entries (e.g. using Apply), a copy operation may be necessary to
// create a temporarily mutable entry that can be modified (until returned).
// That is the role of this method. Although exported for benchmarking, there
// should generally be no need for code outside of this package to use it,
// except perhaps to convert a full snapshot to a slim snapshot.
func (e *Entry) Copy(deep bool) *Entry {
	// If the entry is nil, then the copy is nil.
	if e == nil {
		return nil
	}

	// Create a slim copy.
	result := &Entry{
		Kind:       e.Kind,
		Executable: e.Executable,
		Digest:     e.Digest,
		Target:     e.Target,
		Problem:    e.Problem,
	}

	// If a deep copy wasn't requested, then we're done.
	if !deep {
		return result
	}

	// If the original entry doesn't have any contents, then return early to
	// avoid allocation of the content map.
	if len(e.Contents) == 0 {
		return result
	}

	// Copy the entry contents.
	result.Contents = make(map[string]*Entry, len(e.Contents))
	for name, child := range e.Contents {
		result.Contents[name] = child.Copy(true)
	}

	// Done.
	return result
}

// synchronizable returns the subtree of the entry hierarchy consisting of only
// synchronizable content. It is useful for constructing the new value of a
// change when attempting to propagate around unsychronizable content. It will
// return nil if the entry itself is unsynchronizable (which is technically the
// synchronizable subtree of the entry hierarchy in that case).
func (e *Entry) synchronizable() *Entry {
	// If the entry itself is nil, then the resulting subtree is nil.
	if e == nil {
		return nil
	}

	// If the entry itself consists of unsynchronizable content, then the
	// resulting subtree is nil.
	if !e.Kind.synchronizable() {
		return nil
	}

	// If the entry (which we know is synchronizable) is not a directory, then
	// we can just return the entry itself.
	if e.Kind != EntryKind_Directory {
		return e
	}

	// If the entry (which we know is a directory) doesn't have any contents,
	// then we can just return the entry itself.
	if len(e.Contents) == 0 {
		return e
	}

	// Create a slim copy of the entry. We only need to copy fields for
	// synchronizable entry types since we know this entry is synchronizable.
	result := &Entry{
		Kind:       e.Kind,
		Executable: e.Executable,
		Digest:     e.Digest,
		Target:     e.Target,
	}

	// Copy the entry contents. Some may not be synchronizable, in which case we
	// exclude them from the resulting map. We don't need to worry about them
	// already having been nil since nil entries aren't allowed in content maps.
	result.Contents = make(map[string]*Entry, len(e.Contents))
	for name, child := range e.Contents {
		if child = child.synchronizable(); child != nil {
			result.Contents[name] = child
		}
	}

	// Done.
	return result
}

// Problems generates a list of problems from the problematic entries contained
// within the entry hierarchy. The problems are returned in depth-first but
// non-deterministic order. Problem paths are computed assuming the entry
// represents the synchronization root.
func (e *Entry) Problems() []*Problem {
	// Create the result.
	var result []*Problem

	// Perform a walk to record problematic entries.
	e.walk("", func(path string, entry *Entry) {
		if entry != nil && entry.Kind == EntryKind_Problematic {
			result = append(result, &Problem{
				Path:  path,
				Error: entry.Problem,
			})
		}
	}, false)

	// Done.
	return result
}
<|endoftext|>


<commit_before>package starbind

import (
	"context"
	"fmt"
	"io/ioutil"
	"runtime"
	"strings"
	"sync"

	"go.starlark.net/resolve"
	"go.starlark.net/starlark"

	"github.com/go-delve/delve/service"
	"github.com/go-delve/delve/service/api"
)

//go:generate go run ../../../scripts/gen-starlark-bindings.go go ./starlark_mapping.go
//go:generate go run ../../../scripts/gen-starlark-bindings.go doc ../../../Documentation/cli/starlark.md

const (
	dlvCommandBuiltinName        = "dlv_command"
	readFileBuiltinName          = "read_file"
	writeFileBuiltinName         = "write_file"
	commandPrefix                = "command_"
	dlvContextName               = "dlv_context"
	curScopeBuiltinName          = "cur_scope"
	defaultLoadConfigBuiltinName = "default_load_config"
)

func init() {
	resolve.AllowNestedDef = true
	resolve.AllowLambda = true
	resolve.AllowFloat = true
	resolve.AllowSet = true
	resolve.AllowBitwise = true
	resolve.AllowRecursion = true
	resolve.AllowGlobalReassign = true
}

// Context is the context in which starlark scripts are evaluated.
// It contains methods to call API functions, command line commands, etc.
type Context interface {
	Client() service.Client
	RegisterCommand(name, helpMsg string, cmdfn func(args string) error)
	CallCommand(cmdstr string) error
	Scope() api.EvalScope
	LoadConfig() api.LoadConfig
}

// Env is the environment used to evaluate starlark scripts.
type Env struct {
	env       starlark.StringDict
	contextMu sync.Mutex
	cancelfn  context.CancelFunc

	ctx Context
}

// New creates a new starlark binding environment.
func New(ctx Context) *Env {
	env := &Env{}

	env.ctx = ctx

	env.env = env.starlarkPredeclare()
	env.env[dlvCommandBuiltinName] = starlark.NewBuiltin(dlvCommandBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		if err := isCancelled(thread); err != nil {
			return starlark.None, err
		}
		argstrs := make([]string, len(args))
		for i := range args {
			a, ok := args[i].(starlark.String)
			if !ok {
				return nil, fmt.Errorf("argument of dlv_command is not a string")
			}
			argstrs[i] = string(a)
		}
		err := env.ctx.CallCommand(strings.Join(argstrs, " "))
		if err != nil && strings.Contains(err.Error(), " has exited with status ") {
			return env.interfaceToStarlarkValue(err), nil
		}
		return starlark.None, decorateError(thread, err)
	})
	env.env[readFileBuiltinName] = starlark.NewBuiltin(readFileBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		if len(args) != 1 {
			return nil, decorateError(thread, fmt.Errorf("wrong number of arguments"))
		}
		path, ok := args[0].(starlark.String)
		if !ok {
			return nil, decorateError(thread, fmt.Errorf("argument of read_file was not a string"))
		}
		buf, err := ioutil.ReadFile(string(path))
		if err != nil {
			return nil, decorateError(thread, err)
		}
		return starlark.String(string(buf)), nil
	})
	env.env[writeFileBuiltinName] = starlark.NewBuiltin(writeFileBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		if len(args) != 2 {
			return nil, decorateError(thread, fmt.Errorf("wrong number of arguments"))
		}
		path, ok := args[0].(starlark.String)
		if !ok {
			return nil, decorateError(thread, fmt.Errorf("first argument of write_file was not a string"))
		}
		err := ioutil.WriteFile(string(path), []byte(args[1].String()), 0640)
		return starlark.None, decorateError(thread, err)
	})
	env.env[curScopeBuiltinName] = starlark.NewBuiltin(curScopeBuiltinName, func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		return env.interfaceToStarlarkValue(env.ctx.Scope()), nil
	})
	env.env[defaultLoadConfigBuiltinName] = starlark.NewBuiltin(defaultLoadConfigBuiltinName, func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		return env.interfaceToStarlarkValue(env.ctx.LoadConfig()), nil
	})
	return env
}

// Execute executes a script. Path is the name of the file to execute and
// source is the source code to execute.
// Source can be either a []byte, a string or a io.Reader. If source is nil
// Execute will execute the file specified by 'path'.
// After the file is executed if a function named mainFnName exists it will be called, passing args to it.
func (env *Env) Execute(path string, source interface{}, mainFnName string, args []interface{}) (starlark.Value, error) {
	defer func() {
		err := recover()
		if err == nil {
			return
		}
		fmt.Printf("panic executing starlark script: %v\n", err)
		for i := 0; ; i++ {
			pc, file, line, ok := runtime.Caller(i)
			if !ok {
				break
			}
			fname := "<unknown>"
			fn := runtime.FuncForPC(pc)
			if fn != nil {
				fname = fn.Name()
			}
			fmt.Printf("%s\n\tin %s:%d\n", fname, file, line)
		}
	}()

	thread := env.newThread()
	globals, err := starlark.ExecFile(thread, path, source, env.env)
	if err != nil {
		return starlark.None, err
	}

	err = env.exportGlobals(globals)
	if err != nil {
		return starlark.None, err
	}

	return env.callMain(thread, globals, mainFnName, args)
}

// exportGlobals saves globals with a name starting with a capital letter
// into the environment and creates commands from globals with a name
// starting with "command_"
func (env *Env) exportGlobals(globals starlark.StringDict) error {
	for name, val := range globals {
		switch {
		case strings.HasPrefix(name, commandPrefix):
			err := env.createCommand(name, val)
			if err != nil {
				return err
			}
		case name[0] >= 'A' && name[0] <= 'Z':
			env.env[name] = val
		}
	}
	return nil
}

// Cancel cancels the execution of a currently running script or function.
func (env *Env) Cancel() {
	if env == nil {
		return
	}
	env.contextMu.Lock()
	if env.cancelfn != nil {
		env.cancelfn()
		env.cancelfn = nil
	}
	env.contextMu.Unlock()
}

func (env *Env) newThread() *starlark.Thread {
	thread := &starlark.Thread{
		Print: func(_ *starlark.Thread, msg string) { fmt.Println(msg) },
	}
	env.contextMu.Lock()
	var ctx context.Context
	ctx, env.cancelfn = context.WithCancel(context.Background())
	env.contextMu.Unlock()
	thread.SetLocal(dlvContextName, ctx)
	return thread
}

func (env *Env) createCommand(name string, val starlark.Value) error {
	fnval, ok := val.(*starlark.Function)
	if !ok {
		return nil
	}

	name = name[len(commandPrefix):]

	helpMsg := fnval.Doc()
	if helpMsg == "" {
		helpMsg = "user defined"
	}

	if fnval.NumParams() == 1 {
		if p0, _ := fnval.Param(0); p0 == "args" {
			env.ctx.RegisterCommand(name, helpMsg, func(args string) error {
				_, err := starlark.Call(env.newThread(), fnval, starlark.Tuple{starlark.String(args)}, nil)
				return err
			})
			return nil
		}
	}

	env.ctx.RegisterCommand(name, helpMsg, func(args string) error {
		thread := env.newThread()
		argval, err := starlark.Eval(thread, "<input>", "("+args+")", env.env)
		if err != nil {
			return err
		}
		argtuple, ok := argval.(starlark.Tuple)
		if !ok {
			argtuple = starlark.Tuple{argval}
		}
		_, err = starlark.Call(thread, fnval, argtuple, nil)
		return err
	})
	return nil
}

// callMain calls the main function in globals, if one was defined.
func (env *Env) callMain(thread *starlark.Thread, globals starlark.StringDict, mainFnName string, args []interface{}) (starlark.Value, error) {
	if mainFnName == "" {
		return starlark.None, nil
	}
	mainval := globals[mainFnName]
	if mainval == nil {
		return starlark.None, nil
	}
	mainfn, ok := mainval.(*starlark.Function)
	if !ok {
		return starlark.None, fmt.Errorf("%s is not a function", mainFnName)
	}
	if mainfn.NumParams() != len(args) {
		return starlark.None, fmt.Errorf("wrong number of arguments for %s", mainFnName)
	}
	argtuple := make(starlark.Tuple, len(args))
	for i := range args {
		argtuple[i] = env.interfaceToStarlarkValue(args[i])
	}
	return starlark.Call(thread, mainfn, argtuple, nil)
}

type argument struct {
	name         string
	defaultValue defaultValue
}

type defaultValue uint8

const (
	defaultNone = iota
	defaultScope
	defaultLoadConfig
)

func isCancelled(thread *starlark.Thread) error {
	if ctx, ok := thread.Local(dlvContextName).(context.Context); ok {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}
	}
	return nil
}

func decorateError(thread *starlark.Thread, err error) error {
	if err == nil {
		return nil
	}
	pos := thread.Caller().Position()
	if pos.Col > 0 {
		return fmt.Errorf("%s:%d:%d: %v", pos.Filename(), pos.Line, pos.Col, err)
	}
	return fmt.Errorf("%s:%d: %v", pos.Filename(), pos.Line, err)
}
<commit_msg>pkg/terminal: Use new starlark CallFrame API<commit_after>package starbind

import (
	"context"
	"fmt"
	"io/ioutil"
	"runtime"
	"strings"
	"sync"

	"go.starlark.net/resolve"
	"go.starlark.net/starlark"

	"github.com/go-delve/delve/service"
	"github.com/go-delve/delve/service/api"
)

//go:generate go run ../../../scripts/gen-starlark-bindings.go go ./starlark_mapping.go
//go:generate go run ../../../scripts/gen-starlark-bindings.go doc ../../../Documentation/cli/starlark.md

const (
	dlvCommandBuiltinName        = "dlv_command"
	readFileBuiltinName          = "read_file"
	writeFileBuiltinName         = "write_file"
	commandPrefix                = "command_"
	dlvContextName               = "dlv_context"
	curScopeBuiltinName          = "cur_scope"
	defaultLoadConfigBuiltinName = "default_load_config"
)

func init() {
	resolve.AllowNestedDef = true
	resolve.AllowLambda = true
	resolve.AllowFloat = true
	resolve.AllowSet = true
	resolve.AllowBitwise = true
	resolve.AllowRecursion = true
	resolve.AllowGlobalReassign = true
}

// Context is the context in which starlark scripts are evaluated.
// It contains methods to call API functions, command line commands, etc.
type Context interface {
	Client() service.Client
	RegisterCommand(name, helpMsg string, cmdfn func(args string) error)
	CallCommand(cmdstr string) error
	Scope() api.EvalScope
	LoadConfig() api.LoadConfig
}

// Env is the environment used to evaluate starlark scripts.
type Env struct {
	env       starlark.StringDict
	contextMu sync.Mutex
	cancelfn  context.CancelFunc

	ctx Context
}

// New creates a new starlark binding environment.
func New(ctx Context) *Env {
	env := &Env{}

	env.ctx = ctx

	env.env = env.starlarkPredeclare()
	env.env[dlvCommandBuiltinName] = starlark.NewBuiltin(dlvCommandBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		if err := isCancelled(thread); err != nil {
			return starlark.None, err
		}
		argstrs := make([]string, len(args))
		for i := range args {
			a, ok := args[i].(starlark.String)
			if !ok {
				return nil, fmt.Errorf("argument of dlv_command is not a string")
			}
			argstrs[i] = string(a)
		}
		err := env.ctx.CallCommand(strings.Join(argstrs, " "))
		if err != nil && strings.Contains(err.Error(), " has exited with status ") {
			return env.interfaceToStarlarkValue(err), nil
		}
		return starlark.None, decorateError(thread, err)
	})
	env.env[readFileBuiltinName] = starlark.NewBuiltin(readFileBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		if len(args) != 1 {
			return nil, decorateError(thread, fmt.Errorf("wrong number of arguments"))
		}
		path, ok := args[0].(starlark.String)
		if !ok {
			return nil, decorateError(thread, fmt.Errorf("argument of read_file was not a string"))
		}
		buf, err := ioutil.ReadFile(string(path))
		if err != nil {
			return nil, decorateError(thread, err)
		}
		return starlark.String(string(buf)), nil
	})
	env.env[writeFileBuiltinName] = starlark.NewBuiltin(writeFileBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		if len(args) != 2 {
			return nil, decorateError(thread, fmt.Errorf("wrong number of arguments"))
		}
		path, ok := args[0].(starlark.String)
		if !ok {
			return nil, decorateError(thread, fmt.Errorf("first argument of write_file was not a string"))
		}
		err := ioutil.WriteFile(string(path), []byte(args[1].String()), 0640)
		return starlark.None, decorateError(thread, err)
	})
	env.env[curScopeBuiltinName] = starlark.NewBuiltin(curScopeBuiltinName, func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		return env.interfaceToStarlarkValue(env.ctx.Scope()), nil
	})
	env.env[defaultLoadConfigBuiltinName] = starlark.NewBuiltin(defaultLoadConfigBuiltinName, func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
		return env.interfaceToStarlarkValue(env.ctx.LoadConfig()), nil
	})
	return env
}

// Execute executes a script. Path is the name of the file to execute and
// source is the source code to execute.
// Source can be either a []byte, a string or a io.Reader. If source is nil
// Execute will execute the file specified by 'path'.
// After the file is executed if a function named mainFnName exists it will be called, passing args to it.
func (env *Env) Execute(path string, source interface{}, mainFnName string, args []interface{}) (starlark.Value, error) {
	defer func() {
		err := recover()
		if err == nil {
			return
		}
		fmt.Printf("panic executing starlark script: %v\n", err)
		for i := 0; ; i++ {
			pc, file, line, ok := runtime.Caller(i)
			if !ok {
				break
			}
			fname := "<unknown>"
			fn := runtime.FuncForPC(pc)
			if fn != nil {
				fname = fn.Name()
			}
			fmt.Printf("%s\n\tin %s:%d\n", fname, file, line)
		}
	}()

	thread := env.newThread()
	globals, err := starlark.ExecFile(thread, path, source, env.env)
	if err != nil {
		return starlark.None, err
	}

	err = env.exportGlobals(globals)
	if err != nil {
		return starlark.None, err
	}

	return env.callMain(thread, globals, mainFnName, args)
}

// exportGlobals saves globals with a name starting with a capital letter
// into the environment and creates commands from globals with a name
// starting with "command_"
func (env *Env) exportGlobals(globals starlark.StringDict) error {
	for name, val := range globals {
		switch {
		case strings.HasPrefix(name, commandPrefix):
			err := env.createCommand(name, val)
			if err != nil {
				return err
			}
		case name[0] >= 'A' && name[0] <= 'Z':
			env.env[name] = val
		}
	}
	return nil
}

// Cancel cancels the execution of a currently running script or function.
func (env *Env) Cancel() {
	if env == nil {
		return
	}
	env.contextMu.Lock()
	if env.cancelfn != nil {
		env.cancelfn()
		env.cancelfn = nil
	}
	env.contextMu.Unlock()
}

func (env *Env) newThread() *starlark.Thread {
	thread := &starlark.Thread{
		Print: func(_ *starlark.Thread, msg string) { fmt.Println(msg) },
	}
	env.contextMu.Lock()
	var ctx context.Context
	ctx, env.cancelfn = context.WithCancel(context.Background())
	env.contextMu.Unlock()
	thread.SetLocal(dlvContextName, ctx)
	return thread
}

func (env *Env) createCommand(name string, val starlark.Value) error {
	fnval, ok := val.(*starlark.Function)
	if !ok {
		return nil
	}

	name = name[len(commandPrefix):]

	helpMsg := fnval.Doc()
	if helpMsg == "" {
		helpMsg = "user defined"
	}

	if fnval.NumParams() == 1 {
		if p0, _ := fnval.Param(0); p0 == "args" {
			env.ctx.RegisterCommand(name, helpMsg, func(args string) error {
				_, err := starlark.Call(env.newThread(), fnval, starlark.Tuple{starlark.String(args)}, nil)
				return err
			})
			return nil
		}
	}

	env.ctx.RegisterCommand(name, helpMsg, func(args string) error {
		thread := env.newThread()
		argval, err := starlark.Eval(thread, "<input>", "("+args+")", env.env)
		if err != nil {
			return err
		}
		argtuple, ok := argval.(starlark.Tuple)
		if !ok {
			argtuple = starlark.Tuple{argval}
		}
		_, err = starlark.Call(thread, fnval, argtuple, nil)
		return err
	})
	return nil
}

// callMain calls the main function in globals, if one was defined.
func (env *Env) callMain(thread *starlark.Thread, globals starlark.StringDict, mainFnName string, args []interface{}) (starlark.Value, error) {
	if mainFnName == "" {
		return starlark.None, nil
	}
	mainval := globals[mainFnName]
	if mainval == nil {
		return starlark.None, nil
	}
	mainfn, ok := mainval.(*starlark.Function)
	if !ok {
		return starlark.None, fmt.Errorf("%s is not a function", mainFnName)
	}
	if mainfn.NumParams() != len(args) {
		return starlark.None, fmt.Errorf("wrong number of arguments for %s", mainFnName)
	}
	argtuple := make(starlark.Tuple, len(args))
	for i := range args {
		argtuple[i] = env.interfaceToStarlarkValue(args[i])
	}
	return starlark.Call(thread, mainfn, argtuple, nil)
}

type argument struct {
	name         string
	defaultValue defaultValue
}

type defaultValue uint8

const (
	defaultNone = iota
	defaultScope
	defaultLoadConfig
)

func isCancelled(thread *starlark.Thread) error {
	if ctx, ok := thread.Local(dlvContextName).(context.Context); ok {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}
	}
	return nil
}

func decorateError(thread *starlark.Thread, err error) error {
	if err == nil {
		return nil
	}
	pos := thread.CallFrame(1).Pos
	if pos.Col > 0 {
		return fmt.Errorf("%s:%d:%d: %v", pos.Filename(), pos.Line, pos.Col, err)
	}
	return fmt.Errorf("%s:%d: %v", pos.Filename(), pos.Line, err)
}
<|endoftext|>


<commit_before>package agent

import (
	"encoding/json"
	"strings"
	"sync"

	"github.com/coreos/go-etcd/etcd"
)

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, _ := b.getCurrentState(name)
	go func() {
		if response != nil {
			for _, n := range response.Node.Nodes {
				if update := b.responseToUpdate(response, &n); update != nil {
					stream.ch <- update
				}
			}
		}
		stream.ch <- &ServiceUpdate{}
		for resp := range watch {
			if update := b.responseToUpdate(resp, resp.Node); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, node *etcd.Node) *ServiceUpdate {
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(node.Key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || "set" == resp.Action && (resp.PrevNode == nil || node.Value != resp.PrevNode.Value) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(node.Value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:    serviceName,
			Addr:    serviceAddr,
			Online:  true,
			Attrs:   serviceAttrs,
			Created: uint(node.CreatedIndex),
		}
	} else if "delete" == resp.Action || "expire" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.Get(servicePath(name, ""), false, true)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.Watch(servicePath(name, ""), 0, true, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJson, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	_, err = b.Client.Set(servicePath(name, addr), string(attrsJson), HeartbeatIntervalSecs+MissedHearbeatTTL)
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr), false)
	return err
}
<commit_msg>discoverd: Fix leader election<commit_after>package agent

import (
	"encoding/json"
	"strings"
	"sync"

	"github.com/coreos/go-etcd/etcd"
)

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, _ := b.getCurrentState(name)
	go func() {
		if response != nil {
			for _, n := range response.Node.Nodes {
				if update := b.responseToUpdate(response, &n); update != nil {
					stream.ch <- update
				}
			}
		}
		stream.ch <- &ServiceUpdate{}
		for resp := range watch {
			if update := b.responseToUpdate(resp, resp.Node); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, node *etcd.Node) *ServiceUpdate {
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(node.Key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || "set" == resp.Action && (resp.PrevNode == nil || node.Value != resp.PrevNode.Value) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(node.Value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:    serviceName,
			Addr:    serviceAddr,
			Online:  true,
			Attrs:   serviceAttrs,
			Created: uint(node.CreatedIndex),
		}
	} else if "delete" == resp.Action || "expire" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.Get(servicePath(name, ""), false, true)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.Watch(servicePath(name, ""), 0, true, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJSON, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	attrsString := string(attrsJSON)
	path := servicePath(name, addr)
	ttl := uint64(HeartbeatIntervalSecs + MissedHearbeatTTL)

	_, err = b.Client.Update(path, attrsString, ttl)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		// This is a workaround for etcd issue #407: https://github.com/coreos/etcd/issues/407
		// If we just do a Set and don't try to Update first, createdIndex will get incremented
		// on each heartbeat, breaking leader election.
		_, err = b.Client.Set(path, attrsString, ttl)
	}
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr), false)
	return err
}
<|endoftext|>


<commit_before>// aiffinfo is a command line tool to gather information about aiff/aifc files.
package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/mattetti/audio/aiff"
)

var pathToParse = flag.String("path", ".", "Where to find aiff files")
var fileToParse = flag.String("file", "", "The wav file to analyze (instead of a path)")
var logChunks = flag.Bool("v", false, "Should the parser log chunks (not SSND)")

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: \n")
		flag.PrintDefaults()
	}

	flag.Parse()

	if *fileToParse != "" {
		analyze(*fileToParse)
		return
	}
	if err := filepath.Walk(*pathToParse, walkFn); err != nil {
		log.Fatal(err)
	}
}

func walkFn(path string, fi os.FileInfo, err error) (e error) {
	if err != nil {
		log.Fatal(err)
	}
	if fi.IsDir() {
		filepath.Walk(path, walkFolder)
		return
	}
	if (!strings.HasSuffix(fi.Name(), ".aif") && !strings.HasSuffix(fi.Name(), ".aiff")) || fi.IsDir() {
		return
	}
	analyze(path)
	return nil
}

func walkFolder(path string, fi os.FileInfo, err error) (e error) {
	if (!strings.HasSuffix(fi.Name(), ".aif") && !strings.HasSuffix(fi.Name(), ".aiff")) || fi.IsDir() {
		return
	}
	analyze(path)
	return nil
}

func analyze(path string) {
	fmt.Println(path)
	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	if *logChunks {
		ch := make(chan *aiff.Chunk)
		c := aiff.NewParser(f, ch)
		go func() {
			if err := c.Parse(); err != nil {
				panic(err)
			}
		}()

		for chunk := range ch {
			id := string(chunk.ID[:])
			fmt.Println(id, chunk.Size)
			if id != "SSND" {
				buf := make([]byte, chunk.Size)
				chunk.ReadBE(buf)
				fmt.Print(hex.Dump(buf))
			}
			chunk.Done()
		}
		return
	}
	c := aiff.New(f)
	if err := c.Parse(); err != nil {
		log.Fatalf("Can't parse the headers of %s - %s\n", path, err)
	}
	f.Seek(0, 0)
	sampleRate, sampleSize, numChans, data := aiff.ReadFrames(f)
	fmt.Println("sampleRate", sampleRate)
	fmt.Println("sampleSize", sampleSize)
	fmt.Println("numChans", numChans)
	fmt.Printf("frames: %+v\n", data)
	fmt.Println(c)
}
<commit_msg>generate a naive waveform using aiffinfo<commit_after>// aiffinfo is a command line tool to gather information about aiff/aifc files.
package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/mattetti/audio/aiff"
)

const (
	// Height per channel.
	ImgHeight = 400
)

var pathToParse = flag.String("path", ".", "Where to find aiff files")
var fileToParse = flag.String("file", "", "The wav file to analyze (instead of a path)")
var logChunks = flag.Bool("v", false, "Should the parser log chunks (not SSND)")

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: \n")
		flag.PrintDefaults()
	}

	flag.Parse()

	if *fileToParse != "" {
		analyze(*fileToParse)
		return
	}
	if err := filepath.Walk(*pathToParse, walkFn); err != nil {
		log.Fatal(err)
	}
}

func walkFn(path string, fi os.FileInfo, err error) (e error) {
	if err != nil {
		log.Fatal(err)
	}
	if fi.IsDir() {
		filepath.Walk(path, walkFolder)
		return
	}
	if (!strings.HasSuffix(fi.Name(), ".aif") && !strings.HasSuffix(fi.Name(), ".aiff")) || fi.IsDir() {
		return
	}
	analyze(path)
	return nil
}

func walkFolder(path string, fi os.FileInfo, err error) (e error) {
	if (!strings.HasSuffix(fi.Name(), ".aif") && !strings.HasSuffix(fi.Name(), ".aiff")) || fi.IsDir() {
		return
	}
	analyze(path)
	return nil
}

func analyze(path string) {
	fmt.Println(path)
	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	if *logChunks {
		ch := make(chan *aiff.Chunk)
		c := aiff.NewParser(f, ch)
		go func() {
			if err := c.Parse(); err != nil {
				panic(err)
			}
		}()

		for chunk := range ch {
			id := string(chunk.ID[:])
			fmt.Println(id, chunk.Size)
			if id != "SSND" {
				buf := make([]byte, chunk.Size)
				chunk.ReadBE(buf)
				fmt.Print(hex.Dump(buf))
			}
			chunk.Done()
		}
		return
	}
	c := aiff.New(f)
	if err := c.Parse(); err != nil {
		log.Fatalf("Can't parse the headers of %s - %s\n", path, err)
	}
	f.Seek(0, 0)
	sampleRate, sampleSize, numChans, frames := aiff.ReadFrames(f)
	smpFile, err := os.Create("samples.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer smpFile.Close()

	imgFile, err := os.Create("waveform.png")
	if err != nil {
		log.Fatal(err)
	}
	defer imgFile.Close()

	fmt.Println("sampleRate", sampleRate)
	fmt.Println("sampleSize", sampleSize)
	fmt.Println("numChans", numChans)
	fmt.Printf("frames: %d\n", len(frames))
	fmt.Println(c)

	max := 0
	for _, f := range frames {
		for _, v := range f {
			if v > max {
				max = v
			} else if v*-1 > max {
				max = v * -1
			}
		}
	}

	img := image.NewRGBA(image.Rect(0, 0, len(frames), ImgHeight*int(numChans)))
	if err != nil {
		log.Fatal(err)
	}

	for i := 0; i < len(frames); i++ {
		for channel := 0; channel < int(numChans); channel++ {
			v := frames[i][channel]

			// drawing in the rectable, y=0 is the max, y=height-1 = is the minimun
			// y=height/2 is thw halfway point.
			if v > 0 {
				v = (frames[i][channel] * ImgHeight / 2) / max
				v = ImgHeight/2 - v
			} else {
				v = (abs(frames[i][channel]) * ImgHeight / 2) / max
				v = ImgHeight/2 + v
			}

			// max
			//img.Set(i, 0, color.RGBA{255, 0, 0, 255})
			// half
			img.Set(i, ImgHeight/2, color.RGBA{255, 255, 255, 127})
			// min
			//img.Set(i, ImgHeight-1, color.RGBA{255, 0, 0, 255})

			img.Set(i, v, color.Black)
			// 2nd point to make it thicker
			img.Set(i, v+1, color.Black)
			if channel == 0 {
				smpFile.Write([]byte(fmt.Sprintf("%d, ", v)))
			}
		}
	}

	png.Encode(imgFile, img)
}

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}
<|endoftext|>


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

// This package provides basic interfaces to I/O primitives.
// Its primary job is to wrap existing implementations of such primitives,
// such as those in package os, into shared public interfaces that
// abstract the functionality, plus some other related primitives.
package io

import "os"

// Error represents an unexpected I/O behavior.
type Error struct {
	os.ErrorString
}

// ErrShortWrite means that a write accepted fewer bytes than requested
// but failed to return an explicit error.
var ErrShortWrite os.Error = &Error{"short write"}

// ErrShortBuffer means that a read required a longer buffer than was provided.
var ErrShortBuffer os.Error = &Error{"short buffer"}

// ErrUnexpectedEOF means that os.EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"}

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
// Even if Read returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available rather than block waiting for more.
//
// At the end of the input stream, Read returns 0, os.EOF.
// Read may return a non-zero number of bytes with a non-nil err.
// In particular, a Read that exhausts the input may return n > 0, os.EOF.
type Reader interface {
	Read(p []byte) (n int, err os.Error)
}

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
type Writer interface {
	Write(p []byte) (n int, err os.Error)
}

// Closer is the interface that wraps the basic Close method.
type Closer interface {
	Close() os.Error
}

// Seeker is the interface that wraps the basic Seek method.
//
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
// relative to the end.  Seek returns the new offset and an Error, if
// any.
type Seeker interface {
	Seek(offset int64, whence int) (ret int64, err os.Error)
}

// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser is the interface that groups the basic Write and Close methods.
type WriteCloser interface {
	Writer
	Closer
}

// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}

// ReadSeeker is the interface that groups the basic Read and Seek methods.
type ReadSeeker interface {
	Reader
	Seeker
}

// WriteSeeker is the interface that groups the basic Write and Seek methods.
type WriteSeeker interface {
	Writer
	Seeker
}

// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods.
type ReadWriteSeeker interface {
	Reader
	Writer
	Seeker
}

// ReaderFrom is the interface that wraps the ReadFrom method.
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err os.Error)
}

// WriterTo is the interface that wraps the WriteTo method.
type WriterTo interface {
	WriteTo(w Writer) (n int64, err os.Error)
}

// ReaderAt is the interface that wraps the basic ReadAt method.
//
// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying data stream.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// Even if ReadAt returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, ReadAt blocks
// until either all the data is available or an error occurs.
//
// At the end of the input stream, ReadAt returns 0, os.EOF.
// ReadAt may return a non-zero number of bytes with a non-nil err.
// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF.
type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err os.Error)
}

// WriterAt is the interface that wraps the basic WriteAt method.
//
// WriteAt writes len(p) bytes from p to the underlying data stream
// at offset off.  It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// WriteAt must return a non-nil error if it returns n < len(p).
type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err os.Error)
}

// ByteReader is the interface that wraps the ReadByte method.
//
// ReadByte reads and returns the next byte from the input.
// If no byte is available, err will be set.
type ByteReader interface {
	ReadByte() (c byte, err os.Error)
}

// RuneReader is the interface that wraps the ReadRune method.
//
// ReadRune reads a single UTF-8 encoded Unicode character
// and returns the rune and its size in bytes. If no character is
// available, err will be set.
type RuneReader interface {
	ReadRune() (rune int, size int, err os.Error)
}

// WriteString writes the contents of the string s to w, which accepts an array of bytes.
func WriteString(w Writer, s string) (n int, err os.Error) {
	return w.Write([]byte(s))
}

// ReadAtLeast reads from r into buf until it has read at least min bytes.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	for n < min && err == nil {
		var nn int
		nn, err = r.Read(buf[n:])
		n += nn
	}
	if err == os.EOF {
		if n >= min {
			err = nil
		} else if n > 0 {
			err = ErrUnexpectedEOF
		}
	}
	return
}

// ReadFull reads exactly len(buf) bytes from r into buf.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
	return ReadAtLeast(r, buf, len(buf))
}

// Copyn copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids a buffer allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		written, err = rt.ReadFrom(LimitReader(src, n))
		if written < n && err == nil {
			// rt stopped early; must have been EOF.
			err = os.EOF
		}
		return
	}
	buf := make([]byte, 32*1024)
	for written < n {
		l := len(buf)
		if d := n - written; d < int64(l) {
			l = int(d)
		}
		nr, er := src.Read(buf[0:l])
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs.  It returns the number of bytes
// copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
// Otherwise, if src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
func Copy(dst Writer, src Reader) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids an allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// Similarly, if the reader has a WriteTo method, use it to do the copy.
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	buf := make([]byte, 32*1024)
	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er == os.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// LimitReader returns a Reader that reads from r
// but stops with os.EOF after n bytes.
func LimitReader(r Reader, n int64) Reader { return &limitedReader{r, n} }

type limitedReader struct {
	r Reader
	n int64
}

func (l *limitedReader) Read(p []byte) (n int, err os.Error) {
	if l.n <= 0 {
		return 0, os.EOF
	}
	if int64(len(p)) > l.n {
		p = p[0:l.n]
	}
	n, err = l.r.Read(p)
	l.n -= int64(n)
	return
}

// NewSectionReader returns a SectionReader that reads from r
// starting at offset off and stops with os.EOF after n bytes.
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

// SectionReader implements Read, Seek, and ReadAt on a section
// of an underlying ReaderAt.
type SectionReader struct {
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

func (s *SectionReader) Read(p []byte) (n int, err os.Error) {
	if s.off >= s.limit {
		return 0, os.EOF
	}
	if max := s.limit - s.off; int64(len(p)) > max {
		p = p[0:max]
	}
	n, err = s.r.ReadAt(p, s.off)
	s.off += int64(n)
	return
}

func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) {
	switch whence {
	default:
		return 0, os.EINVAL
	case 0:
		offset += s.base
	case 1:
		offset += s.off
	case 2:
		offset += s.limit
	}
	if offset < s.base || offset > s.limit {
		return 0, os.EINVAL
	}
	s.off = offset
	return offset - s.base, nil
}

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
	if off < 0 || off >= s.limit-s.base {
		return 0, os.EOF
	}
	off += s.base
	if max := s.limit - off; int64(len(p)) > max {
		p = p[0:max]
	}
	return s.r.ReadAt(p, off)
}

// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 { return s.limit - s.base }
<commit_msg>io: clarify that ReadAt shouldn't move the seek offset<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package provides basic interfaces to I/O primitives.
// Its primary job is to wrap existing implementations of such primitives,
// such as those in package os, into shared public interfaces that
// abstract the functionality, plus some other related primitives.
package io

import "os"

// Error represents an unexpected I/O behavior.
type Error struct {
	os.ErrorString
}

// ErrShortWrite means that a write accepted fewer bytes than requested
// but failed to return an explicit error.
var ErrShortWrite os.Error = &Error{"short write"}

// ErrShortBuffer means that a read required a longer buffer than was provided.
var ErrShortBuffer os.Error = &Error{"short buffer"}

// ErrUnexpectedEOF means that os.EOF was encountered in the
// middle of reading a fixed-size block or data structure.
var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"}

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
// Even if Read returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available rather than block waiting for more.
//
// At the end of the input stream, Read returns 0, os.EOF.
// Read may return a non-zero number of bytes with a non-nil err.
// In particular, a Read that exhausts the input may return n > 0, os.EOF.
type Reader interface {
	Read(p []byte) (n int, err os.Error)
}

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
type Writer interface {
	Write(p []byte) (n int, err os.Error)
}

// Closer is the interface that wraps the basic Close method.
type Closer interface {
	Close() os.Error
}

// Seeker is the interface that wraps the basic Seek method.
//
// Seek sets the offset for the next Read or Write to offset,
// interpreted according to whence: 0 means relative to the origin of
// the file, 1 means relative to the current offset, and 2 means
// relative to the end.  Seek returns the new offset and an Error, if
// any.
type Seeker interface {
	Seek(offset int64, whence int) (ret int64, err os.Error)
}

// ReadWriter is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser is the interface that groups the basic Read and Close methods.
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser is the interface that groups the basic Write and Close methods.
type WriteCloser interface {
	Writer
	Closer
}

// ReadWriteCloser is the interface that groups the basic Read, Write and Close methods.
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}

// ReadSeeker is the interface that groups the basic Read and Seek methods.
type ReadSeeker interface {
	Reader
	Seeker
}

// WriteSeeker is the interface that groups the basic Write and Seek methods.
type WriteSeeker interface {
	Writer
	Seeker
}

// ReadWriteSeeker is the interface that groups the basic Read, Write and Seek methods.
type ReadWriteSeeker interface {
	Reader
	Writer
	Seeker
}

// ReaderFrom is the interface that wraps the ReadFrom method.
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err os.Error)
}

// WriterTo is the interface that wraps the WriteTo method.
type WriterTo interface {
	WriteTo(w Writer) (n int64, err os.Error)
}

// ReaderAt is the interface that wraps the basic ReadAt method.
//
// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying data stream.  It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// Even if ReadAt returns n < len(p),
// it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, ReadAt blocks
// until either all the data is available or an error occurs.
//
// At the end of the input stream, ReadAt returns 0, os.EOF.
// ReadAt may return a non-zero number of bytes with a non-nil err.
// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF.
//
// If ReadAt is reading from an data stream with a seek offset,
// ReadAt should not affect nor be affected by the underlying
// seek offset.
type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err os.Error)
}

// WriterAt is the interface that wraps the basic WriteAt method.
//
// WriteAt writes len(p) bytes from p to the underlying data stream
// at offset off.  It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// WriteAt must return a non-nil error if it returns n < len(p).
type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err os.Error)
}

// ByteReader is the interface that wraps the ReadByte method.
//
// ReadByte reads and returns the next byte from the input.
// If no byte is available, err will be set.
type ByteReader interface {
	ReadByte() (c byte, err os.Error)
}

// RuneReader is the interface that wraps the ReadRune method.
//
// ReadRune reads a single UTF-8 encoded Unicode character
// and returns the rune and its size in bytes. If no character is
// available, err will be set.
type RuneReader interface {
	ReadRune() (rune int, size int, err os.Error)
}

// WriteString writes the contents of the string s to w, which accepts an array of bytes.
func WriteString(w Writer, s string) (n int, err os.Error) {
	return w.Write([]byte(s))
}

// ReadAtLeast reads from r into buf until it has read at least min bytes.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading fewer than min bytes,
// ReadAtLeast returns ErrUnexpectedEOF.
// If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer.
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err os.Error) {
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	for n < min && err == nil {
		var nn int
		nn, err = r.Read(buf[n:])
		n += nn
	}
	if err == os.EOF {
		if n >= min {
			err = nil
		} else if n > 0 {
			err = ErrUnexpectedEOF
		}
	}
	return
}

// ReadFull reads exactly len(buf) bytes from r into buf.
// It returns the number of bytes copied and an error if fewer bytes were read.
// The error is os.EOF only if no bytes were read.
// If an EOF happens after reading some but not all the bytes,
// ReadFull returns ErrUnexpectedEOF.
func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
	return ReadAtLeast(r, buf, len(buf))
}

// Copyn copies n bytes (or until an error) from src to dst.
// It returns the number of bytes copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids a buffer allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		written, err = rt.ReadFrom(LimitReader(src, n))
		if written < n && err == nil {
			// rt stopped early; must have been EOF.
			err = os.EOF
		}
		return
	}
	buf := make([]byte, 32*1024)
	for written < n {
		l := len(buf)
		if d := n - written; d < int64(l) {
			l = int(d)
		}
		nr, er := src.Read(buf[0:l])
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// Copy copies from src to dst until either EOF is reached
// on src or an error occurs.  It returns the number of bytes
// copied and the error, if any.
//
// If dst implements the ReaderFrom interface,
// the copy is implemented by calling dst.ReadFrom(src).
// Otherwise, if src implements the WriterTo interface,
// the copy is implemented by calling src.WriteTo(dst).
func Copy(dst Writer, src Reader) (written int64, err os.Error) {
	// If the writer has a ReadFrom method, use it to do the copy.
	// Avoids an allocation and a copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// Similarly, if the reader has a WriteTo method, use it to do the copy.
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	buf := make([]byte, 32*1024)
	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er == os.EOF {
			break
		}
		if er != nil {
			err = er
			break
		}
	}
	return written, err
}

// LimitReader returns a Reader that reads from r
// but stops with os.EOF after n bytes.
func LimitReader(r Reader, n int64) Reader { return &limitedReader{r, n} }

type limitedReader struct {
	r Reader
	n int64
}

func (l *limitedReader) Read(p []byte) (n int, err os.Error) {
	if l.n <= 0 {
		return 0, os.EOF
	}
	if int64(len(p)) > l.n {
		p = p[0:l.n]
	}
	n, err = l.r.Read(p)
	l.n -= int64(n)
	return
}

// NewSectionReader returns a SectionReader that reads from r
// starting at offset off and stops with os.EOF after n bytes.
func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

// SectionReader implements Read, Seek, and ReadAt on a section
// of an underlying ReaderAt.
type SectionReader struct {
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

func (s *SectionReader) Read(p []byte) (n int, err os.Error) {
	if s.off >= s.limit {
		return 0, os.EOF
	}
	if max := s.limit - s.off; int64(len(p)) > max {
		p = p[0:max]
	}
	n, err = s.r.ReadAt(p, s.off)
	s.off += int64(n)
	return
}

func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err os.Error) {
	switch whence {
	default:
		return 0, os.EINVAL
	case 0:
		offset += s.base
	case 1:
		offset += s.off
	case 2:
		offset += s.limit
	}
	if offset < s.base || offset > s.limit {
		return 0, os.EINVAL
	}
	s.off = offset
	return offset - s.base, nil
}

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err os.Error) {
	if off < 0 || off >= s.limit-s.base {
		return 0, os.EOF
	}
	off += s.base
	if max := s.limit - off; int64(len(p)) > max {
		p = p[0:max]
	}
	return s.r.ReadAt(p, off)
}

// Size returns the size of the section in bytes.
func (s *SectionReader) Size() int64 { return s.limit - s.base }
<|endoftext|>