<commit_before>// Copyright 2019 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.

package cache

import (
	"context"
	"crypto/sha1"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"html/template"
	"io/ioutil"
	"os"
	"reflect"
	"sort"
	"strconv"
	"sync/atomic"
	"time"

	"golang.org/x/tools/internal/event"
	"golang.org/x/tools/internal/gocommand"
	"golang.org/x/tools/internal/lsp/debug/tag"
	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/memoize"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

func New(ctx context.Context, options func(*source.Options)) *Cache {
	index := atomic.AddInt64(&cacheIndex, 1)
	c := &Cache{
		id:      strconv.FormatInt(index, 10),
		fset:    token.NewFileSet(),
		options: options,
	}
	return c
}

type Cache struct {
	id      string
	fset    *token.FileSet
	options func(*source.Options)

	store memoize.Store
}

type fileKey struct {
	uri     span.URI
	modTime time.Time
}

type fileHandle struct {
	uri span.URI
	memoize.NoCopy
	bytes []byte
	hash  string
	err   error
}

func (c *Cache) getFile(ctx context.Context, uri span.URI) (*fileHandle, error) {
	var modTime time.Time
	if fi, err := os.Stat(uri.Filename()); err == nil {
		modTime = fi.ModTime()
	}

	key := fileKey{
		uri:     uri,
		modTime: modTime,
	}
	h := c.store.Bind(key, func(ctx context.Context) interface{} {
		return readFile(ctx, uri, modTime)
	})
	v, err := h.Get(ctx)
	if err != nil {
		return nil, err
	}
	return v.(*fileHandle), nil
}

// ioLimit limits the number of parallel file reads per process.
var ioLimit = make(chan struct{}, 128)

func readFile(ctx context.Context, uri span.URI, origTime time.Time) *fileHandle {
	ctx, done := event.Start(ctx, "cache.getFile", tag.File.Of(uri.Filename()))
	_ = ctx
	defer done()

	ioLimit <- struct{}{}
	defer func() { <-ioLimit }()

	var modTime time.Time
	if fi, err := os.Stat(uri.Filename()); err == nil {
		modTime = fi.ModTime()
	}

	if modTime != origTime {
		return &fileHandle{err: errors.Errorf("%s: file has been modified", uri.Filename())}
	}
	data, err := ioutil.ReadFile(uri.Filename())
	if err != nil {
		return &fileHandle{err: err}
	}
	return &fileHandle{
		uri:   uri,
		bytes: data,
		hash:  hashContents(data),
	}
}

func (c *Cache) NewSession(ctx context.Context) *Session {
	index := atomic.AddInt64(&sessionIndex, 1)
	s := &Session{
		cache:       c,
		id:          strconv.FormatInt(index, 10),
		options:     source.DefaultOptions(),
		overlays:    make(map[span.URI]*overlay),
		gocmdRunner: &gocommand.Runner{},
	}
	event.Log(ctx, "New session", KeyCreateSession.Of(s))
	return s
}

func (c *Cache) FileSet() *token.FileSet {
	return c.fset
}

func (h *fileHandle) URI() span.URI {
	return h.uri
}

func (h *fileHandle) Kind() source.FileKind {
	return source.DetectLanguage("", h.uri.Filename())
}

func (h *fileHandle) Version() float64 {
	return 0
}

func (h *fileHandle) Identity() source.FileIdentity {
	return source.FileIdentity{
		URI:        h.uri,
		Identifier: h.hash,
		Kind:       h.Kind(),
	}
}

func (h *fileHandle) Read() ([]byte, error) {
	return h.bytes, h.err
}

func hashContents(contents []byte) string {
	// TODO: consider whether sha1 is the best choice here
	// This hash is used for internal identity detection only
	return fmt.Sprintf("%x", sha1.Sum(contents))
}

var cacheIndex, sessionIndex, viewIndex int64

func (c *Cache) ID() string                     { return c.id }
func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() }

type packageStat struct {
	id        packageID
	mode      source.ParseMode
	file      int64
	ast       int64
	types     int64
	typesInfo int64
	total     int64
}

func (c *Cache) PackageStats(withNames bool) template.HTML {
	var packageStats []packageStat
	c.store.DebugOnlyIterate(func(k, v interface{}) {
		switch k.(type) {
		case packageHandleKey:
			v := v.(*packageData)
			if v.pkg == nil {
				break
			}
			var typsCost, typInfoCost int64
			if v.pkg.types != nil {
				typsCost = typesCost(v.pkg.types.Scope())
			}
			if v.pkg.typesInfo != nil {
				typInfoCost = typesInfoCost(v.pkg.typesInfo)
			}
			stat := packageStat{
				id:        v.pkg.id,
				mode:      v.pkg.mode,
				types:     typsCost,
				typesInfo: typInfoCost,
			}
			for _, f := range v.pkg.compiledGoFiles {
				fvi := f.handle.Cached()
				if fvi == nil {
					continue
				}
				fv := fvi.(*parseGoData)
				stat.file += int64(len(fv.src))
				stat.ast += astCost(fv.ast)
			}
			stat.total = stat.file + stat.ast + stat.types + stat.typesInfo
			packageStats = append(packageStats, stat)
		}
	})
	var totalCost int64
	for _, stat := range packageStats {
		totalCost += stat.total
	}
	sort.Slice(packageStats, func(i, j int) bool {
		return packageStats[i].total > packageStats[j].total
	})
	html := "

Name	total = file + ast + types + types info
\n"
	human := func(n int64) string {
		return fmt.Sprintf("%.2f", float64(n)/(1024*1024))
	}
	var printedCost int64
	for _, stat := range packageStats {
		name := stat.id
		if !withNames {
			name = "-"
		}
		html += fmt.Sprintf("
%v (%v)	%v = %v + %v + %v + %v
\n", name, stat.mode,
			human(stat.total), human(stat.file), human(stat.ast), human(stat.types), human(stat.typesInfo))
		printedCost += stat.total
		if float64(printedCost) > float64(totalCost)*.9 {
			break
		}
	}
	html += "
\n"
	return template.HTML(html)
}

func astCost(f *ast.File) int64 {
	var count int64
	ast.Inspect(f, func(n ast.Node) bool {
		count += 32 // nodes are pretty small.
		return true
	})
	return count
}

func typesCost(scope *types.Scope) int64 {
	cost := 64 + int64(scope.Len())*128 // types.object looks pretty big
	for i := 0; i < scope.NumChildren(); i++ {
		cost += typesCost(scope.Child(i))
	}
	return cost
}

func typesInfoCost(info *types.Info) int64 {
	// Most of these refer to existing objects, with the exception of InitOrder, Selections, and Types.
	cost := 24*len(info.Defs) +
		32*len(info.Implicits) +
		256*len(info.InitOrder) + // these are big, but there aren't many of them.
		32*len(info.Scopes) +
		128*len(info.Selections) + // wild guess
		128*len(info.Types) + // wild guess
		32*len(info.Uses)
	return int64(cost)
}
<commit_msg>internal/lsp: handle nil pointer in PackageStats<commit_after>// Copyright 2019 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.

package cache

import (
	"context"
	"crypto/sha1"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"html/template"
	"io/ioutil"
	"os"
	"reflect"
	"sort"
	"strconv"
	"sync/atomic"
	"time"

	"golang.org/x/tools/internal/event"
	"golang.org/x/tools/internal/gocommand"
	"golang.org/x/tools/internal/lsp/debug/tag"
	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/memoize"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

func New(ctx context.Context, options func(*source.Options)) *Cache {
	index := atomic.AddInt64(&cacheIndex, 1)
	c := &Cache{
		id:      strconv.FormatInt(index, 10),
		fset:    token.NewFileSet(),
		options: options,
	}
	return c
}

type Cache struct {
	id      string
	fset    *token.FileSet
	options func(*source.Options)

	store memoize.Store
}

type fileKey struct {
	uri     span.URI
	modTime time.Time
}

type fileHandle struct {
	uri span.URI
	memoize.NoCopy
	bytes []byte
	hash  string
	err   error
}

func (c *Cache) getFile(ctx context.Context, uri span.URI) (*fileHandle, error) {
	var modTime time.Time
	if fi, err := os.Stat(uri.Filename()); err == nil {
		modTime = fi.ModTime()
	}

	key := fileKey{
		uri:     uri,
		modTime: modTime,
	}
	h := c.store.Bind(key, func(ctx context.Context) interface{} {
		return readFile(ctx, uri, modTime)
	})
	v, err := h.Get(ctx)
	if err != nil {
		return nil, err
	}
	return v.(*fileHandle), nil
}

// ioLimit limits the number of parallel file reads per process.
var ioLimit = make(chan struct{}, 128)

func readFile(ctx context.Context, uri span.URI, origTime time.Time) *fileHandle {
	ctx, done := event.Start(ctx, "cache.getFile", tag.File.Of(uri.Filename()))
	_ = ctx
	defer done()

	ioLimit <- struct{}{}
	defer func() { <-ioLimit }()

	var modTime time.Time
	if fi, err := os.Stat(uri.Filename()); err == nil {
		modTime = fi.ModTime()
	}

	if modTime != origTime {
		return &fileHandle{err: errors.Errorf("%s: file has been modified", uri.Filename())}
	}
	data, err := ioutil.ReadFile(uri.Filename())
	if err != nil {
		return &fileHandle{err: err}
	}
	return &fileHandle{
		uri:   uri,
		bytes: data,
		hash:  hashContents(data),
	}
}

func (c *Cache) NewSession(ctx context.Context) *Session {
	index := atomic.AddInt64(&sessionIndex, 1)
	s := &Session{
		cache:       c,
		id:          strconv.FormatInt(index, 10),
		options:     source.DefaultOptions(),
		overlays:    make(map[span.URI]*overlay),
		gocmdRunner: &gocommand.Runner{},
	}
	event.Log(ctx, "New session", KeyCreateSession.Of(s))
	return s
}

func (c *Cache) FileSet() *token.FileSet {
	return c.fset
}

func (h *fileHandle) URI() span.URI {
	return h.uri
}

func (h *fileHandle) Kind() source.FileKind {
	return source.DetectLanguage("", h.uri.Filename())
}

func (h *fileHandle) Version() float64 {
	return 0
}

func (h *fileHandle) Identity() source.FileIdentity {
	return source.FileIdentity{
		URI:        h.uri,
		Identifier: h.hash,
		Kind:       h.Kind(),
	}
}

func (h *fileHandle) Read() ([]byte, error) {
	return h.bytes, h.err
}

func hashContents(contents []byte) string {
	// TODO: consider whether sha1 is the best choice here
	// This hash is used for internal identity detection only
	return fmt.Sprintf("%x", sha1.Sum(contents))
}

var cacheIndex, sessionIndex, viewIndex int64

func (c *Cache) ID() string                     { return c.id }
func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() }

type packageStat struct {
	id        packageID
	mode      source.ParseMode
	file      int64
	ast       int64
	types     int64
	typesInfo int64
	total     int64
}

func (c *Cache) PackageStats(withNames bool) template.HTML {
	var packageStats []packageStat
	c.store.DebugOnlyIterate(func(k, v interface{}) {
		switch k.(type) {
		case packageHandleKey:
			v := v.(*packageData)
			if v.pkg == nil {
				break
			}
			var typsCost, typInfoCost int64
			if v.pkg.types != nil {
				typsCost = typesCost(v.pkg.types.Scope())
			}
			if v.pkg.typesInfo != nil {
				typInfoCost = typesInfoCost(v.pkg.typesInfo)
			}
			stat := packageStat{
				id:        v.pkg.id,
				mode:      v.pkg.mode,
				types:     typsCost,
				typesInfo: typInfoCost,
			}
			for _, f := range v.pkg.compiledGoFiles {
				fvi := f.handle.Cached()
				if fvi == nil {
					continue
				}
				fv := fvi.(*parseGoData)
				stat.file += int64(len(fv.src))
				stat.ast += astCost(fv.ast)
			}
			stat.total = stat.file + stat.ast + stat.types + stat.typesInfo
			packageStats = append(packageStats, stat)
		}
	})
	var totalCost int64
	for _, stat := range packageStats {
		totalCost += stat.total
	}
	sort.Slice(packageStats, func(i, j int) bool {
		return packageStats[i].total > packageStats[j].total
	})
	html := "

Name	total = file + ast + types + types info
\n"
	human := func(n int64) string {
		return fmt.Sprintf("%.2f", float64(n)/(1024*1024))
	}
	var printedCost int64
	for _, stat := range packageStats {
		name := stat.id
		if !withNames {
			name = "-"
		}
		html += fmt.Sprintf("
%v (%v)	%v = %v + %v + %v + %v
\n", name, stat.mode,
			human(stat.total), human(stat.file), human(stat.ast), human(stat.types), human(stat.typesInfo))
		printedCost += stat.total
		if float64(printedCost) > float64(totalCost)*.9 {
			break
		}
	}
	html += "
\n"
	return template.HTML(html)
}

func astCost(f *ast.File) int64 {
	if f == nil {
		return 0
	}
	var count int64
	ast.Inspect(f, func(n ast.Node) bool {
		count += 32 // nodes are pretty small.
		return true
	})
	return count
}

func typesCost(scope *types.Scope) int64 {
	cost := 64 + int64(scope.Len())*128 // types.object looks pretty big
	for i := 0; i < scope.NumChildren(); i++ {
		cost += typesCost(scope.Child(i))
	}
	return cost
}

func typesInfoCost(info *types.Info) int64 {
	// Most of these refer to existing objects, with the exception of InitOrder, Selections, and Types.
	cost := 24*len(info.Defs) +
		32*len(info.Implicits) +
		256*len(info.InitOrder) + // these are big, but there aren't many of them.
		32*len(info.Scopes) +
		128*len(info.Selections) + // wild guess
		128*len(info.Types) + // wild guess
		32*len(info.Uses)
	return int64(cost)
}
<|endoftext|>


<commit_before>package torrent

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"io/ioutil"
	"os"

	"code.google.com/p/bencode-go"

	"github.com/cenkalti/rain/internal/shared"
)

type Torrent struct {
	Info         Info       `bencode:"info"`
	Announce     string     `bencode:"announce"`
	AnnounceList [][]string `bencode:"announce-list"`
	CreationDate int64      `bencode:"creation date"`
	Comment      string     `bencode:"comment"`
	CreatedBy    string     `bencode:"created by"`
	Encoding     string     `bencode:"encoding"`
}

type Info struct {
	PieceLength uint32 `bencode:"piece length"`
	Pieces      string `bencode:"pieces"`
	Private     byte   `bencode:"private"`
	Name        string `bencode:"name"`
	// Single File Mode
	Length int64  `bencode:"length"`
	Md5sum string `bencode:"md5sum"`
	// Multiple File mode
	Files []fileDict `bencode:"files"`

	// These fields do not exist in torrent file.
	// They are calculated when a Torrent is created with New.
	Hash        shared.InfoHash
	TotalLength int64
	NumPieces   uint32
}

type fileDict struct {
	Length int64    `bencode:"length"`
	Path   []string `bencode:"path"`
	Md5sum string   `bencode:"md5sum"`
}

func New(path string) (*Torrent, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	data, err := ioutil.ReadAll(file)
	file.Close()
	if err != nil {
		return nil, err
	}

	reader := bytes.NewReader(data)

	decoded, err := bencode.Decode(reader)
	if err != nil {
		return nil, err
	}

	torrentMap, ok := decoded.(map[string]interface{})
	if !ok {
		return nil, errors.New("invalid torrent file")
	}

	infoMap, ok := torrentMap["info"]
	if !ok {
		return nil, errors.New("invalid torrent file")
	}

	t := new(Torrent)

	// Unmarshal bencoded bytes into the struct
	reader.Seek(0, os.SEEK_SET)
	err = bencode.Unmarshal(reader, t)
	if err != nil {
		return nil, err
	}

	// Calculate InfoHash
	hash := sha1.New()
	bencode.Marshal(hash, infoMap)
	copy(t.Info.Hash[:], hash.Sum(nil))

	// Calculate TotalLength
	if !t.Info.MultiFile() {
		t.Info.TotalLength = t.Info.Length
	} else {
		for _, f := range t.Info.Files {
			t.Info.TotalLength += f.Length
		}
	}

	// Calculate NumPieces
	t.Info.NumPieces = uint32(len(t.Info.Pieces)) / sha1.Size

	return t, nil
}

func (i *Info) HashOfPiece(index uint32) [sha1.Size]byte {
	if index < 0 || index >= i.NumPieces {
		panic("piece index out of range")
	}
	var hash [sha1.Size]byte
	start := index * sha1.Size
	end := start + sha1.Size
	copy(hash[:], []byte(i.Pieces[start:end]))
	return hash
}

// GetFiles returns the files in torrent as a slice, even if there is a single file.
func (i *Info) GetFiles() []fileDict {
	if i.MultiFile() {
		return i.Files
	} else {
		return []fileDict{fileDict{i.Length, []string{i.Name}, i.Md5sum}}
	}
}

// MultiFile returns true if the torrent contains more than one file.
func (i *Info) MultiFile() bool {
	return len(i.Files) != 0
}
<commit_msg>fix torrent<commit_after>package torrent

import (
	"bytes"
	"crypto/sha1"
	"errors"
	"io/ioutil"
	"os"

	"code.google.com/p/bencode-go"

	"github.com/cenkalti/rain/internal/shared"
)

type Torrent struct {
	Info         Info       `bencode:"info"`
	Announce     string     `bencode:"announce"`
	AnnounceList [][]string `bencode:"announce-list"`
	CreationDate int64      `bencode:"creation date"`
	Comment      string     `bencode:"comment"`
	CreatedBy    string     `bencode:"created by"`
	Encoding     string     `bencode:"encoding"`
}

type Info struct {
	PieceLength uint32 `bencode:"piece length"`
	Pieces      string `bencode:"pieces"`
	Private     byte   `bencode:"private"`
	Name        string `bencode:"name"`
	// Single File Mode
	Length int64  `bencode:"length"`
	Md5sum string `bencode:"md5sum"`
	// Multiple File mode
	Files []fileDict `bencode:"files"`

	// These fields do not exist in torrent file.
	// They are calculated when a Torrent is created with New.
	Hash        shared.InfoHash
	TotalLength int64
	NumPieces   uint32
}

type fileDict struct {
	Length int64    `bencode:"length"`
	Path   []string `bencode:"path"`
	Md5sum string   `bencode:"md5sum"`
}

func New(path string) (*Torrent, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	data, err := ioutil.ReadAll(file)
	file.Close()
	if err != nil {
		return nil, err
	}

	reader := bytes.NewReader(data)

	decoded, err := bencode.Decode(reader)
	if err != nil {
		return nil, err
	}

	torrentMap, ok := decoded.(map[string]interface{})
	if !ok {
		return nil, errors.New("invalid torrent file")
	}

	infoMap, ok := torrentMap["info"]
	if !ok {
		return nil, errors.New("invalid torrent file")
	}

	t := new(Torrent)

	// Unmarshal bencoded bytes into the struct
	reader.Seek(0, os.SEEK_SET)
	err = bencode.Unmarshal(reader, t)
	if err != nil {
		return nil, err
	}

	// Calculate InfoHash
	hash := sha1.New()
	bencode.Marshal(hash, infoMap)
	copy(t.Info.Hash[:], hash.Sum(nil))

	// Calculate TotalLength
	if !t.Info.MultiFile() {
		t.Info.TotalLength = t.Info.Length
	} else {
		for _, f := range t.Info.Files {
			t.Info.TotalLength += f.Length
		}
	}

	// Calculate NumPieces
	t.Info.NumPieces = uint32(len(t.Info.Pieces)) / sha1.Size

	return t, nil
}

func (i *Info) HashOfPiece(index uint32) [sha1.Size]byte {
	if index >= i.NumPieces {
		panic("piece index out of range")
	}
	var hash [sha1.Size]byte
	start := index * sha1.Size
	end := start + sha1.Size
	copy(hash[:], []byte(i.Pieces[start:end]))
	return hash
}

// GetFiles returns the files in torrent as a slice, even if there is a single file.
func (i *Info) GetFiles() []fileDict {
	if i.MultiFile() {
		return i.Files
	} else {
		return []fileDict{fileDict{i.Length, []string{i.Name}, i.Md5sum}}
	}
}

// MultiFile returns true if the torrent contains more than one file.
func (i *Info) MultiFile() bool {
	return len(i.Files) != 0
}
<|endoftext|>


<commit_before>package version

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"text/tabwriter"

	"github.com/blang/semver"
	"github.com/google/renameio"
	"github.com/pkg/errors"

	"github.com/cri-o/cri-o/utils"
)

// Variables injected during build-time
var (
	version      string // Version is the version of the build.
	gitCommit    string // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string // state of git tree, either "clean" or "dirty"
	buildDate    string // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)

type Info struct {
	Version      string `json:"version,omitempty"`
	GitCommit    string `json:"gitCommit,omitempty"`
	GitTreeState string `json:"gitTreeState,omitempty"`
	BuildDate    string `json:"buildDate,omitempty"`
	GoVersion    string `json:"goVersion,omitempty"`
	Compiler     string `json:"compiler,omitempty"`
	Platform     string `json:"platform,omitempty"`
	Linkmode     string `json:"linkmode,omitempty"`
}

// ShouldCrioWipe opens the version file, and parses it and the version string
// If there is a parsing error, then crio should wipe, and the error is returned.
// if parsing is successful, it compares the major and minor versions
// and returns whether the major and minor versions are the same.
// If they differ, then crio should wipe.
func ShouldCrioWipe(versionFileName string) (bool, error) {
	return shouldCrioWipe(versionFileName, version)
}

// shouldCrioWipe is an internal function for testing purposes
func shouldCrioWipe(versionFileName, versionString string) (bool, error) {
	f, err := os.Open(versionFileName)
	if err != nil {
		return true, errors.Errorf("version file %s not found: %v. Triggering wipe", versionFileName, err)
	}
	r := bufio.NewReader(f)
	versionBytes, err := ioutil.ReadAll(r)
	if err != nil {
		return true, errors.Errorf("reading version file %s failed: %v. Triggering wipe", versionFileName, err)
	}

	// parse the version that was laid down by a previous invocation of crio
	var oldVersion semver.Version
	if err := oldVersion.UnmarshalJSON(versionBytes); err != nil {
		return true, errors.Errorf("version file %s malformatted: %v. Triggering wipe", versionFileName, err)
	}

	// parse the version of the current binary
	newVersion, err := parseVersionConstant(versionString, "")
	if err != nil {
		return true, errors.Errorf("version constant %s malformatted: %v. Triggering wipe", versionString, err)
	}

	// in every case that the minor and major version are out of sync,
	// we want to preform a {down,up}grade. The common case here is newVersion > oldVersion,
	// but even in the opposite case, images are out of date and could be wiped
	return newVersion.Major != oldVersion.Major || newVersion.Minor != oldVersion.Minor, nil
}

// WriteVersionFile writes the version information to a given file
// file is the location of the old version file
// gitCommit is the current git commit version. It will be added to the file
// to aid in debugging, but will not be used to compare versions
func WriteVersionFile(file string) error {
	return writeVersionFile(file, gitCommit, version)
}

// writeVersionFile is an internal function for testing purposes
func writeVersionFile(file, gitCommit, version string) error {
	current, err := parseVersionConstant(version, gitCommit)
	// Sanity check-this should never happen
	if err != nil {
		return err
	}
	j, err := current.MarshalJSON()
	// Sanity check-this should never happen
	if err != nil {
		return err
	}

	// Create the top level directory if it doesn't exist
	if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
		return err
	}

	return renameio.WriteFile(file, j, 0644)
}

// parseVersionConstant parses the Version variable above
// a const crioVersion would be kept, but golang doesn't support
// const structs. We will instead spend some runtime on CRI-O startup
// Because the version string doesn't keep track of the git commit,
// but it could be useful for debugging, we pass it in here
// If our version constant is properly formatted, this should never error
func parseVersionConstant(versionString, gitCommit string) (*semver.Version, error) {
	v, err := semver.Make(versionString)
	if err != nil {
		return nil, err
	}
	if gitCommit != "" {
		gitBuild, err := semver.NewBuildVersion(strings.Trim(gitCommit, "\""))
		// If gitCommit is empty, silently error, as it's helpful, but not needed.
		if err == nil {
			v.Build = append(v.Build, gitBuild)
		}
	}
	return &v, nil
}

func Get() *Info {
	return &Info{
		Version:      version,
		GitCommit:    gitCommit,
		GitTreeState: gitTreeState,
		BuildDate:    buildDate,
		GoVersion:    runtime.Version(),
		Compiler:     runtime.Compiler,
		Platform:     fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
		Linkmode:     getLinkmode(),
	}
}

// String returns the string representation of the version info
func (i *Info) String() string {
	b := strings.Builder{}
	w := tabwriter.NewWriter(&b, 0, 0, 2, ' ', 0)

	v := reflect.ValueOf(*i)
	t := v.Type()
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		value := v.FieldByName(field.Name).String()
		fmt.Fprintf(w, "%s:\t%s", field.Name, value)
		if i+1 < t.NumField() {
			fmt.Fprintf(w, "\n")
		}
	}

	w.Flush()
	return b.String()
}

func getLinkmode() string {
	output, err := utils.ExecCmd("ldd", os.Args[0])
	if err != nil {
		return fmt.Sprintf("unknown: %v", err)
	}

	if strings.Contains(output, "not a dynamic executable") {
		return "static"
	}

	return "dynamic"
}

// JSONString returns the JSON representation of the version info
func (i *Info) JSONString() (string, error) {
	b, err := json.MarshalIndent(i, "", "  ")
	if err != nil {
		return "", err
	}
	return string(b), nil
}
<commit_msg>Use absolute path to binary when retrieving linkmode<commit_after>package version

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"text/tabwriter"

	"github.com/blang/semver"
	"github.com/google/renameio"
	"github.com/pkg/errors"

	"github.com/cri-o/cri-o/utils"
)

// Variables injected during build-time
var (
	version      string // Version is the version of the build.
	gitCommit    string // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string // state of git tree, either "clean" or "dirty"
	buildDate    string // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)

type Info struct {
	Version      string `json:"version,omitempty"`
	GitCommit    string `json:"gitCommit,omitempty"`
	GitTreeState string `json:"gitTreeState,omitempty"`
	BuildDate    string `json:"buildDate,omitempty"`
	GoVersion    string `json:"goVersion,omitempty"`
	Compiler     string `json:"compiler,omitempty"`
	Platform     string `json:"platform,omitempty"`
	Linkmode     string `json:"linkmode,omitempty"`
}

// ShouldCrioWipe opens the version file, and parses it and the version string
// If there is a parsing error, then crio should wipe, and the error is returned.
// if parsing is successful, it compares the major and minor versions
// and returns whether the major and minor versions are the same.
// If they differ, then crio should wipe.
func ShouldCrioWipe(versionFileName string) (bool, error) {
	return shouldCrioWipe(versionFileName, version)
}

// shouldCrioWipe is an internal function for testing purposes
func shouldCrioWipe(versionFileName, versionString string) (bool, error) {
	f, err := os.Open(versionFileName)
	if err != nil {
		return true, errors.Errorf("version file %s not found: %v. Triggering wipe", versionFileName, err)
	}
	r := bufio.NewReader(f)
	versionBytes, err := ioutil.ReadAll(r)
	if err != nil {
		return true, errors.Errorf("reading version file %s failed: %v. Triggering wipe", versionFileName, err)
	}

	// parse the version that was laid down by a previous invocation of crio
	var oldVersion semver.Version
	if err := oldVersion.UnmarshalJSON(versionBytes); err != nil {
		return true, errors.Errorf("version file %s malformatted: %v. Triggering wipe", versionFileName, err)
	}

	// parse the version of the current binary
	newVersion, err := parseVersionConstant(versionString, "")
	if err != nil {
		return true, errors.Errorf("version constant %s malformatted: %v. Triggering wipe", versionString, err)
	}

	// in every case that the minor and major version are out of sync,
	// we want to preform a {down,up}grade. The common case here is newVersion > oldVersion,
	// but even in the opposite case, images are out of date and could be wiped
	return newVersion.Major != oldVersion.Major || newVersion.Minor != oldVersion.Minor, nil
}

// WriteVersionFile writes the version information to a given file
// file is the location of the old version file
// gitCommit is the current git commit version. It will be added to the file
// to aid in debugging, but will not be used to compare versions
func WriteVersionFile(file string) error {
	return writeVersionFile(file, gitCommit, version)
}

// writeVersionFile is an internal function for testing purposes
func writeVersionFile(file, gitCommit, version string) error {
	current, err := parseVersionConstant(version, gitCommit)
	// Sanity check-this should never happen
	if err != nil {
		return err
	}
	j, err := current.MarshalJSON()
	// Sanity check-this should never happen
	if err != nil {
		return err
	}

	// Create the top level directory if it doesn't exist
	if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
		return err
	}

	return renameio.WriteFile(file, j, 0644)
}

// parseVersionConstant parses the Version variable above
// a const crioVersion would be kept, but golang doesn't support
// const structs. We will instead spend some runtime on CRI-O startup
// Because the version string doesn't keep track of the git commit,
// but it could be useful for debugging, we pass it in here
// If our version constant is properly formatted, this should never error
func parseVersionConstant(versionString, gitCommit string) (*semver.Version, error) {
	v, err := semver.Make(versionString)
	if err != nil {
		return nil, err
	}
	if gitCommit != "" {
		gitBuild, err := semver.NewBuildVersion(strings.Trim(gitCommit, "\""))
		// If gitCommit is empty, silently error, as it's helpful, but not needed.
		if err == nil {
			v.Build = append(v.Build, gitBuild)
		}
	}
	return &v, nil
}

func Get() *Info {
	return &Info{
		Version:      version,
		GitCommit:    gitCommit,
		GitTreeState: gitTreeState,
		BuildDate:    buildDate,
		GoVersion:    runtime.Version(),
		Compiler:     runtime.Compiler,
		Platform:     fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
		Linkmode:     getLinkmode(),
	}
}

// String returns the string representation of the version info
func (i *Info) String() string {
	b := strings.Builder{}
	w := tabwriter.NewWriter(&b, 0, 0, 2, ' ', 0)

	v := reflect.ValueOf(*i)
	t := v.Type()
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		value := v.FieldByName(field.Name).String()
		fmt.Fprintf(w, "%s:\t%s", field.Name, value)
		if i+1 < t.NumField() {
			fmt.Fprintf(w, "\n")
		}
	}

	w.Flush()
	return b.String()
}

func getLinkmode() string {
	abspath, err := filepath.Abs(os.Args[0])
	if err != nil {
		return fmt.Sprintf("unknown: %v", err)
	}

	output, err := utils.ExecCmd("ldd", abspath)
	if err != nil {
		return fmt.Sprintf("unknown: %v", err)
	}

	if strings.Contains(output, "not a dynamic executable") {
		return "static"
	}

	return "dynamic"
}

// JSONString returns the JSON representation of the version info
func (i *Info) JSONString() (string, error) {
	b, err := json.MarshalIndent(i, "", "  ")
	if err != nil {
		return "", err
	}
	return string(b), nil
}
<|endoftext|>


<commit_before>package moneybird

import (
	"testing"
	"time"
)

// Because Moneybird schedules a background job when you create a new invoice, this test will fail when running too soon after a previous run.
func TestInvoiceGatewayListAndDelete(t *testing.T) {
	invoices, err := testClient.Invoice().List()
	if err != nil {
		t.Error(err)
	}

	for _, invoice := range invoices {
		err := testClient.Invoice().Delete(invoice)
		if err != nil {
			// let's ignore this error for now... (see func doc)
			if err.Error() == "Sales invoice cannot be destroyed" {
				continue
			}

			t.Error(err)
		}
	}

}

func TestInvoiceGatewayCRUD(t *testing.T) {
	var err error
	// create contact
	contact := &Contact{
		Email:     "johndoe@email.com",
		FirstName: "John",
		LastName:  "Doe",
	}
	contact, err = testClient.Contact().Create(contact)
	if err != nil {
		t.Fatalf("ContactGateway.Create: %s", err)
	}

	// delete contact (deferred)
	defer func() {
		err = testClient.Contact().Delete(contact)
		if err != nil {
			t.Errorf("ContactGateway.Delete: %s", err)
		}
	}()

	gateway := testClient.Invoice()
	// create invoice
	invoice := &Invoice{
		ContactID:   contact.ID,
		InvoiceDate: time.Now().Format("2006-01-02"),
		Details: []*InvoiceDetails{
			&InvoiceDetails{
				Amount:      "1",
				Price:       "10.00",
				Description: "Test Service",
			},
		},
	}
	invoice, err = gateway.Create(invoice)
	if err != nil {
		t.Fatalf("InvoiceGateway.Create: %s", err) // abandon test if invoice creation fails
	}

	// update invoice
	invoice.Reference = "my-reference"
	invoice, err = gateway.Update(invoice)
	if err != nil {
		t.Errorf("InvoiceGateway.Update: %s", err)
	}

	if invoice.Reference != "my-reference" {
		t.Error("InvoiceGateway.Update: reference was not properly updated")
	}

	// get invoice
	invoice, err = gateway.Get(invoice.ID)
	if err != nil {
		t.Errorf("InvoiceGateway.Get: %s", err)
	}

	if invoice.Contact.ID != contact.ID {
		t.Errorf("InvoiceGateway.Get: invoice contact ID does not match, got %#v", invoice.Contact.ID)
	}

	//  create invoice sending (send invoice)
	err = testClient.InvoiceSending().Create(invoice, &InvoiceSending{
		DeliveryMethod: "Manual",
	})
	if err != nil {
		t.Fatalf("InvoiceSendingGateway.Create: %s", err)
	}

	// create invoice payment (mark invoice as paid)
	err = testClient.InvoicePayment().Create(invoice, &InvoicePayment{
		Price:       invoice.TotalUnpaid,
		PriceBase:   invoice.TotalUnpaid,
		PaymentDate: time.Now().Format("2006-01-02"),
	})
	if err != nil {
		t.Fatalf("InvoicePaymentGateway.Create: %s ", err)
	}

	// create invoice note
	note, err := testClient.InvoiceNote().Create(invoice, &InvoiceNote{
		Note: "my note",
	})
	if err != nil {
		t.Fatalf("InvoiceNoteGateway.Create: %s", err)
	}

	if note.Note != "my note" {
		t.Errorf("InvoiceNoteGateway.Create: note does not match input string. Got %#v", note.Note)
	}

	// delete invoice note
	err = testClient.InvoiceNote().Delete(invoice, note)
	if err != nil {
		t.Errorf("InvoiceNoteGateway.Delete: %s", err)
	}

}
<commit_msg>fix failing test<commit_after>package moneybird

import (
	"testing"
	"time"
)

// Because Moneybird schedules a background job when you create a new invoice, this test will fail when running too soon after a previous run.
func TestInvoiceGatewayListAndDelete(t *testing.T) {
	invoices, err := testClient.Invoice().List()
	if err != nil {
		t.Error(err)
	}

	for _, invoice := range invoices {
		err := testClient.Invoice().Delete(invoice)
		if err != nil {
			// let's ignore this error for now... (see func doc)
			if err.Error() == "moneybird: Sales invoice cannot be destroyed" {
				continue
			}

			t.Error(err)
		}
	}

}

func TestInvoiceGatewayCRUD(t *testing.T) {
	var err error
	// create contact
	contact := &Contact{
		Email:     "johndoe@email.com",
		FirstName: "John",
		LastName:  "Doe",
	}
	contact, err = testClient.Contact().Create(contact)
	if err != nil {
		t.Fatalf("ContactGateway.Create: %s", err)
	}

	// delete contact (deferred)
	defer func() {
		err = testClient.Contact().Delete(contact)
		if err != nil {
			t.Errorf("ContactGateway.Delete: %s", err)
		}
	}()

	gateway := testClient.Invoice()
	// create invoice
	invoice := &Invoice{
		ContactID:   contact.ID,
		InvoiceDate: time.Now().Format("2006-01-02"),
		Details: []*InvoiceDetails{
			&InvoiceDetails{
				Amount:      "1",
				Price:       "10.00",
				Description: "Test Service",
			},
		},
	}
	invoice, err = gateway.Create(invoice)
	if err != nil {
		t.Fatalf("InvoiceGateway.Create: %s", err) // abandon test if invoice creation fails
	}

	// update invoice
	invoice.Reference = "my-reference"
	invoice, err = gateway.Update(invoice)
	if err != nil {
		t.Errorf("InvoiceGateway.Update: %s", err)
	}

	if invoice.Reference != "my-reference" {
		t.Error("InvoiceGateway.Update: reference was not properly updated")
	}

	// get invoice
	invoice, err = gateway.Get(invoice.ID)
	if err != nil {
		t.Errorf("InvoiceGateway.Get: %s", err)
	}

	if invoice.Contact.ID != contact.ID {
		t.Errorf("InvoiceGateway.Get: invoice contact ID does not match, got %#v", invoice.Contact.ID)
	}

	//  create invoice sending (send invoice)
	err = testClient.InvoiceSending().Create(invoice, &InvoiceSending{
		DeliveryMethod: "Manual",
	})
	if err != nil {
		t.Fatalf("InvoiceSendingGateway.Create: %s", err)
	}

	// create invoice payment (mark invoice as paid)
	err = testClient.InvoicePayment().Create(invoice, &InvoicePayment{
		Price:       invoice.TotalUnpaid,
		PriceBase:   invoice.TotalUnpaid,
		PaymentDate: time.Now().Format("2006-01-02"),
	})
	if err != nil {
		t.Fatalf("InvoicePaymentGateway.Create: %s ", err)
	}

	// create invoice note
	note, err := testClient.InvoiceNote().Create(invoice, &InvoiceNote{
		Note: "my note",
	})
	if err != nil {
		t.Fatalf("InvoiceNoteGateway.Create: %s", err)
	}

	if note.Note != "my note" {
		t.Errorf("InvoiceNoteGateway.Create: note does not match input string. Got %#v", note.Note)
	}

	// delete invoice note
	err = testClient.InvoiceNote().Delete(invoice, note)
	if err != nil {
		t.Errorf("InvoiceNoteGateway.Delete: %s", err)
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"github.com/coreos/pkg/capnslog"
)

type Executor interface {
	StartExecuteCommand(actionName string, command string, arg ...string) (*exec.Cmd, error)
	ExecuteCommand(actionName string, command string, arg ...string) error
	ExecuteCommandWithOutput(actionName string, command string, arg ...string) (string, error)
	ExecuteCommandWithCombinedOutput(actionName string, command string, arg ...string) (string, error)
	ExecuteCommandWithOutputFile(actionName, command, outfileArg string, arg ...string) (string, error)
	ExecuteStat(name string) (os.FileInfo, error)
}

type CommandExecutor struct {
}

// Start a process and return immediately
func (*CommandExecutor) StartExecuteCommand(actionName string, command string, arg ...string) (*exec.Cmd, error) {
	cmd, stdout, stderr, err := startCommand(command, arg...)
	if err != nil {
		return cmd, createCommandError(err, actionName)
	}

	go logOutput(actionName, stdout, stderr)

	return cmd, nil
}

// Start a process and wait for its completion
func (*CommandExecutor) ExecuteCommand(actionName string, command string, arg ...string) error {
	cmd, stdout, stderr, err := startCommand(command, arg...)
	if err != nil {
		return createCommandError(err, actionName)
	}

	logOutput(actionName, stdout, stderr)

	if err := cmd.Wait(); err != nil {
		return createCommandError(err, actionName)
	}

	return nil
}

func (*CommandExecutor) ExecuteCommandWithOutput(actionName string, command string, arg ...string) (string, error) {
	logCommand(command, arg...)
	cmd := exec.Command(command, arg...)
	return runCommandWithOutput(actionName, cmd, false)
}

func (*CommandExecutor) ExecuteCommandWithCombinedOutput(actionName string, command string, arg ...string) (string, error) {
	logCommand(command, arg...)
	cmd := exec.Command(command, arg...)
	return runCommandWithOutput(actionName, cmd, true)
}

func (*CommandExecutor) ExecuteCommandWithOutputFile(actionName, command, outfileArg string, arg ...string) (string, error) {

	// create a temporary file to serve as the output file for the command to be run and ensure
	// it is cleaned up after this function is done
	outFile, err := ioutil.TempFile("", "")
	if err != nil {
		return "", fmt.Errorf("failed to open output file: %+v", err)
	}
	defer outFile.Close()
	defer os.Remove(outFile.Name())

	// append the output file argument to the list or args
	arg = append(arg, outfileArg, outFile.Name())

	logCommand(command, arg...)
	cmd := exec.Command(command, arg...)
	cmdOut, err := runCommandWithOutput(actionName, cmd, false)
	if err != nil {
		return cmdOut, err
	}

	// if there was anything that went to stdout/stderr then log it
	if cmdOut != "" {
		logger.Info(cmdOut)
	}

	// read the entire output file and return that to the caller
	fileOut, err := ioutil.ReadAll(outFile)
	return string(fileOut), err
}

func startCommand(command string, arg ...string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
	logCommand(command, arg...)

	cmd := exec.Command(command, arg...)
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()

	err := cmd.Start()

	return cmd, stdout, stderr, err
}

func (*CommandExecutor) ExecuteStat(name string) (os.FileInfo, error) {
	return os.Stat(name)
}

func logOutput(name string, stdout, stderr io.ReadCloser) {
	if stdout == nil || stderr == nil {
		logger.Warningf("failed to collect stdout and stderr")
		return
	}

	// The child processes should appropriately be outputting at the desired global level.  Therefore,
	// we always log at INFO level here, so that log statements from child procs at higher levels
	// (e.g., WARNING) will still be displayed.  We are relying on the child procs to output appropriately.
	childLogger := capnslog.NewPackageLogger("github.com/rook/rook", name)
	if !childLogger.LevelAt(capnslog.INFO) {
		rl, err := capnslog.GetRepoLogger("github.com/rook/rook")
		if err == nil {
			rl.SetLogLevel(map[string]capnslog.LogLevel{name: capnslog.INFO})
		}
	}

	// read command's stdout line by line and write it to the log
	in := bufio.NewScanner(io.MultiReader(stdout, stderr))
	lastLine := ""
	for in.Scan() {
		lastLine = in.Text()
		childLogger.Infof(lastLine)
	}
}

func runCommandWithOutput(actionName string, cmd *exec.Cmd, combinedOutput bool) (string, error) {
	var output []byte
	var err error

	if combinedOutput {
		output, err = cmd.CombinedOutput()
	} else {
		output, err = cmd.Output()
	}

	out := strings.TrimSpace(string(output))

	if err != nil {
		return out, createCommandError(err, actionName)
	}

	return out, nil
}

func logCommand(command string, arg ...string) {
	logger.Infof("Running command: %s %s", command, strings.Join(arg, " "))
}
<commit_msg>log all output of commands executed with an output file<commit_after>/*
Copyright 2016 The Rook Authors. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"github.com/coreos/pkg/capnslog"
)

type Executor interface {
	StartExecuteCommand(actionName string, command string, arg ...string) (*exec.Cmd, error)
	ExecuteCommand(actionName string, command string, arg ...string) error
	ExecuteCommandWithOutput(actionName string, command string, arg ...string) (string, error)
	ExecuteCommandWithCombinedOutput(actionName string, command string, arg ...string) (string, error)
	ExecuteCommandWithOutputFile(actionName, command, outfileArg string, arg ...string) (string, error)
	ExecuteStat(name string) (os.FileInfo, error)
}

type CommandExecutor struct {
}

// Start a process and return immediately
func (*CommandExecutor) StartExecuteCommand(actionName string, command string, arg ...string) (*exec.Cmd, error) {
	cmd, stdout, stderr, err := startCommand(command, arg...)
	if err != nil {
		return cmd, createCommandError(err, actionName)
	}

	go logOutput(actionName, stdout, stderr)

	return cmd, nil
}

// Start a process and wait for its completion
func (*CommandExecutor) ExecuteCommand(actionName string, command string, arg ...string) error {
	cmd, stdout, stderr, err := startCommand(command, arg...)
	if err != nil {
		return createCommandError(err, actionName)
	}

	logOutput(actionName, stdout, stderr)

	if err := cmd.Wait(); err != nil {
		return createCommandError(err, actionName)
	}

	return nil
}

func (*CommandExecutor) ExecuteCommandWithOutput(actionName string, command string, arg ...string) (string, error) {
	logCommand(command, arg...)
	cmd := exec.Command(command, arg...)
	return runCommandWithOutput(actionName, cmd, false)
}

func (*CommandExecutor) ExecuteCommandWithCombinedOutput(actionName string, command string, arg ...string) (string, error) {
	logCommand(command, arg...)
	cmd := exec.Command(command, arg...)
	return runCommandWithOutput(actionName, cmd, true)
}

func (*CommandExecutor) ExecuteCommandWithOutputFile(actionName, command, outfileArg string, arg ...string) (string, error) {

	// create a temporary file to serve as the output file for the command to be run and ensure
	// it is cleaned up after this function is done
	outFile, err := ioutil.TempFile("", "")
	if err != nil {
		return "", fmt.Errorf("failed to open output file: %+v", err)
	}
	defer outFile.Close()
	defer os.Remove(outFile.Name())

	// append the output file argument to the list or args
	arg = append(arg, outfileArg, outFile.Name())

	logCommand(command, arg...)
	cmd := exec.Command(command, arg...)
	cmdOut, err := cmd.CombinedOutput()
	// if there was anything that went to stdout/stderr then log it, even before we return an error
	if string(cmdOut) != "" {
		logger.Infof(string(cmdOut))
	}
	if err != nil {
		return string(cmdOut), err
	}

	// read the entire output file and return that to the caller
	fileOut, err := ioutil.ReadAll(outFile)
	return string(fileOut), err
}

func startCommand(command string, arg ...string) (*exec.Cmd, io.ReadCloser, io.ReadCloser, error) {
	logCommand(command, arg...)

	cmd := exec.Command(command, arg...)
	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()

	err := cmd.Start()

	return cmd, stdout, stderr, err
}

func (*CommandExecutor) ExecuteStat(name string) (os.FileInfo, error) {
	return os.Stat(name)
}

func logOutput(name string, stdout, stderr io.ReadCloser) {
	if stdout == nil || stderr == nil {
		logger.Warningf("failed to collect stdout and stderr")
		return
	}

	// The child processes should appropriately be outputting at the desired global level.  Therefore,
	// we always log at INFO level here, so that log statements from child procs at higher levels
	// (e.g., WARNING) will still be displayed.  We are relying on the child procs to output appropriately.
	childLogger := capnslog.NewPackageLogger("github.com/rook/rook", name)
	if !childLogger.LevelAt(capnslog.INFO) {
		rl, err := capnslog.GetRepoLogger("github.com/rook/rook")
		if err == nil {
			rl.SetLogLevel(map[string]capnslog.LogLevel{name: capnslog.INFO})
		}
	}

	// read command's stdout line by line and write it to the log
	in := bufio.NewScanner(io.MultiReader(stdout, stderr))
	lastLine := ""
	for in.Scan() {
		lastLine = in.Text()
		childLogger.Infof(lastLine)
	}
}

func runCommandWithOutput(actionName string, cmd *exec.Cmd, combinedOutput bool) (string, error) {
	var output []byte
	var err error

	if combinedOutput {
		output, err = cmd.CombinedOutput()
	} else {
		output, err = cmd.Output()
	}

	out := strings.TrimSpace(string(output))

	if err != nil {
		return out, createCommandError(err, actionName)
	}

	return out, nil
}

func logCommand(command string, arg ...string) {
	logger.Infof("Running command: %s %s", command, strings.Join(arg, " "))
}
<|endoftext|>


<commit_before>package glog

import (
	"fmt"
	"io"
	"strings"

	"github.com/golang/glog"
)

// Logger is a simple interface that is roughly equivalent to glog.
type Logger interface {
	Is(level int) bool
	V(level int) Logger
	Infof(format string, args ...interface{})
}

// ToFile creates a logger that will log any items at level or below to file, and defer
// any other output to glog (no matter what the level is.)
func ToFile(w io.Writer, level int) Logger {
	return file{w, level}
}

var (
	// None implements the Logger interface but does nothing with the log output
	None Logger = discard{}
	// Log implements the Logger interface for Glog
	Log Logger = glogger{}
)

// discard is a Logger that outputs nothing.
type discard struct{}

func (discard) Is(level int) bool                { return false }
func (discard) V(level int) Logger               { return None }
func (discard) Infof(_ string, _ ...interface{}) {}

// glogger outputs log messages to glog
type glogger struct{}

func (glogger) Is(level int) bool {
	return bool(glog.V(glog.Level(level)))
}

func (glogger) V(level int) Logger {
	return gverbose{glog.V(glog.Level(level))}
}

func (glogger) Infof(format string, args ...interface{}) {
	glog.Infof(format, args)
}

// gverbose handles glog.V(x) calls
type gverbose struct {
	glog.Verbose
}

func (gverbose) Is(level int) bool {
	return bool(glog.V(glog.Level(level)))
}

func (gverbose) V(level int) Logger {
	if glog.V(glog.Level(level)) {
		return Log
	}
	return None
}

func (g gverbose) Infof(format string, args ...interface{}) {
	g.Verbose.Infof(format, args)
}

// file logs the provided messages at level or below to the writer, or delegates
// to glog.
type file struct {
	w     io.Writer
	level int
}

func (f file) Is(level int) bool {
	return level <= f.level
}

func (f file) V(level int) Logger {
	// only log things that glog allows
	if !glog.V(glog.Level(level)) {
		return None
	}
	// send anything above our level to glog
	if level > f.level {
		return Log
	}
	return f
}

func (f file) Infof(format string, args ...interface{}) {
	fmt.Fprintf(f.w, format, args...)
	if !strings.HasSuffix(format, "\n") {
		fmt.Fprintln(f.w)
	}
}
<commit_msg>Fix  passing of the arguments in our Logger implementation.<commit_after>package glog

import (
	"fmt"
	"io"
	"strings"

	"github.com/golang/glog"
)

// Logger is a simple interface that is roughly equivalent to glog.
type Logger interface {
	Is(level int) bool
	V(level int) Logger
	Infof(format string, args ...interface{})
}

// ToFile creates a logger that will log any items at level or below to file, and defer
// any other output to glog (no matter what the level is.)
func ToFile(w io.Writer, level int) Logger {
	return file{w, level}
}

var (
	// None implements the Logger interface but does nothing with the log output
	None Logger = discard{}
	// Log implements the Logger interface for Glog
	Log Logger = glogger{}
)

// discard is a Logger that outputs nothing.
type discard struct{}

func (discard) Is(level int) bool                { return false }
func (discard) V(level int) Logger               { return None }
func (discard) Infof(_ string, _ ...interface{}) {}

// glogger outputs log messages to glog
type glogger struct{}

func (glogger) Is(level int) bool {
	return bool(glog.V(glog.Level(level)))
}

func (glogger) V(level int) Logger {
	return gverbose{glog.V(glog.Level(level))}
}

func (glogger) Infof(format string, args ...interface{}) {
	glog.Infof(format, args...)
}

// gverbose handles glog.V(x) calls
type gverbose struct {
	glog.Verbose
}

func (gverbose) Is(level int) bool {
	return bool(glog.V(glog.Level(level)))
}

func (gverbose) V(level int) Logger {
	if glog.V(glog.Level(level)) {
		return Log
	}
	return None
}

func (g gverbose) Infof(format string, args ...interface{}) {
	g.Verbose.Infof(format, args...)
}

// file logs the provided messages at level or below to the writer, or delegates
// to glog.
type file struct {
	w     io.Writer
	level int
}

func (f file) Is(level int) bool {
	return level <= f.level
}

func (f file) V(level int) Logger {
	// only log things that glog allows
	if !glog.V(glog.Level(level)) {
		return None
	}
	// send anything above our level to glog
	if level > f.level {
		return Log
	}
	return f
}

func (f file) Infof(format string, args ...interface{}) {
	fmt.Fprintf(f.w, format, args...)
	if !strings.HasSuffix(format, "\n") {
		fmt.Fprintln(f.w)
	}
}
<|endoftext|>


<commit_before>package log2

import "github.com/astaxie/beego/logs"
import "fmt"

var (
	logger = &Logger{logs.GetBeeLogger()}
)

// Logger 基于BeegoLog.
type Logger struct {
	*logs.BeeLogger
}

// GetLogger 返回默认的自定义Logger
func GetLogger() *Logger {
	return logger
}

// Output = Info
func (l *Logger) Output(calldepth int, s string) error {
	l.Info(s)
	return nil
}

// Errorf logs a message at error level.
func Errorf(f string, v ...interface{}) {
	logger.Error(f, v)
}

// Error logs a message at error level.
func Error(v ...interface{}) {
	logger.Error(fmt.Sprint(v))
}

// Warnf compatibility alias for Warning()
func Warnf(f string, v ...interface{}) {
	logger.Warn(f, v...)
}

// Warn compatibility alias for Warning()
func Warn(v ...interface{}) {
	logger.Warn(fmt.Sprint(v))
}

// Noticef logs a message at notice level.
func Noticef(f string, v ...interface{}) {
	logger.Notice(f, v...)
}

// Notice logs a message at notice level.
func Notice(v ...interface{}) {
	logger.Notice(fmt.Sprint(v))
}

// Infof compatibility alias for Info()
func Infof(f string, v ...interface{}) {
	logger.Info(f, v...)
}

// Info compatibility alias for Info()
func Info(v ...interface{}) {
	logger.Info(fmt.Sprint(v))
}

// Debugf logs a message at debug level.
func Debugf(f string, v ...interface{}) {
	logger.Debug(f, v...)
}

// Debug logs a message at debug level.
func Debug(v ...interface{}) {
	logs.Debug(fmt.Sprint(v))
}
<commit_msg>log2 优化<commit_after>package log2

import (
	"fmt"
	"os"

	"github.com/astaxie/beego/logs"
)

var (
	logger = &Logger{logs.GetBeeLogger()}
)

// Logger 基于BeegoLog.
type Logger struct {
	*logs.BeeLogger
}

// GetLogger 返回默认的自定义Logger
func GetLogger() *Logger {
	return logger
}

// Output = Info
func (l *Logger) Output(calldepth int, s string) error {
	l.Info(s)
	return nil
}

// Errorf logs a message at error level.
func Errorf(f string, v ...interface{}) {
	logger.Error(f, v)
}

// Error logs a message at error level.
func Error(v ...interface{}) {
	logger.Error(fmt.Sprint(v))
}

// Warnf compatibility alias for Warning()
func Warnf(f string, v ...interface{}) {
	logger.Warn(f, v...)
}

// Warn compatibility alias for Warning()
func Warn(v ...interface{}) {
	logger.Warn(fmt.Sprint(v))
}

// Noticef logs a message at notice level.
func Noticef(f string, v ...interface{}) {
	logger.Notice(f, v...)
}

// Notice logs a message at notice level.
func Notice(v ...interface{}) {
	logger.Notice(fmt.Sprint(v))
}

// Infof compatibility alias for Info()
func Infof(f string, v ...interface{}) {
	logger.Info(f, v...)
}

// Info compatibility alias for Info()
func Info(v ...interface{}) {
	logger.Info(fmt.Sprint(v))
}

// Debugf logs a message at debug level.
func Debugf(f string, v ...interface{}) {
	logger.Debug(f, v...)
}

// Debug logs a message at debug level.
func Debug(v ...interface{}) {
	logger.Debug(fmt.Sprint(v))
}

// Fatalln Print Log with Error()  followed by a call to os.Exit(1).
func Fatalln(v ...interface{}) {
	logger.Error(fmt.Sprint(v))
	os.Exit(1)
}

// Fatalf Print Log with Error()  followed by a call to os.Exit(1).
func Fatalf(f string, v ...interface{}) {
	logger.Error(f, v...)
	os.Exit(1)
}

func init() {
	// logs.SetLogFuncCall(true)
	logs.SetLogFuncCallDepth(4)
}
<|endoftext|>


<commit_before>package pkgcheck

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/visualfc/gotools/pkg/pkgutil"

	"github.com/visualfc/gotools/pkg/gomod"

	"github.com/visualfc/gotools/pkg/command"
)

var Command = &command.Command{
	Run:       runCheck,
	UsageLine: "pkgcheck [-pkg | -name] -w .",
	Short:     "pkg check utils",
	Long:      "check pkg mod or vendor path",
}

var (
	flagCheckPkg  string
	flagCheckDir  string = "."
	flagCheckName bool
)

func init() {
	Command.Flag.StringVar(&flagCheckPkg, "pkg", "", "check pkg name")
	Command.Flag.BoolVar(&flagCheckName, "name", false, "check module name")
	Command.Flag.StringVar(&flagCheckDir, "w", "", "work path")
}

func runCheck(cmd *command.Command, args []string) error {
	if flagCheckPkg == "" && !flagCheckName {
		cmd.Usage()
		return os.ErrInvalid
	}
	if flagCheckDir == "" || flagCheckDir == "." {
		flagCheckDir, _ = os.Getwd()
	}
	modList := gomod.LooupModList(flagCheckDir)
	if flagCheckName {
		if modList != nil {
			fmt.Println(modList.Module.Path)
		} else {
			_, fname := filepath.Split(flagCheckDir)
			fmt.Println(fname)
		}
		return nil
	}
	// check mod, check vendor
	if modList != nil {
		m, path, _ := modList.LookupModule(flagCheckPkg)
		if m != nil {
			fmt.Printf("%s,mod\n", path)
			return nil
		}
	} else {
		pkg := pkgutil.ImportDir(flagCheckDir)
		if pkg != nil {
			found, _ := pkgutil.VendoredImportPath(pkg, flagCheckPkg)
			if found != "" && found != flagCheckPkg {
				fmt.Printf("%s,vendor\n", found)
				return nil
			}
		}
	}
	fmt.Printf("%s,pkg\n", flagCheckPkg)
	return nil
}
<commit_msg>update pkg check<commit_after>package pkgcheck

import (
	"fmt"
	"go/build"
	"os"
	"path/filepath"

	"github.com/visualfc/fastmod"
	"github.com/visualfc/gotools/pkg/command"
	"github.com/visualfc/gotools/pkg/pkgutil"
)

var Command = &command.Command{
	Run:       runCheck,
	UsageLine: "pkgcheck [-pkg | -name] -w .",
	Short:     "pkg check utils",
	Long:      "check pkg mod or vendor path",
}

var (
	flagCheckPkg  string
	flagCheckDir  string = "."
	flagCheckName bool
)

func init() {
	Command.Flag.StringVar(&flagCheckPkg, "pkg", "", "check pkg name")
	Command.Flag.BoolVar(&flagCheckName, "name", false, "check module name")
	Command.Flag.StringVar(&flagCheckDir, "w", "", "work path")
}

func runCheck(cmd *command.Command, args []string) error {
	if flagCheckPkg == "" && !flagCheckName {
		cmd.Usage()
		return os.ErrInvalid
	}
	if flagCheckDir == "" || flagCheckDir == "." {
		flagCheckDir, _ = os.Getwd()
	}
	mods := fastmod.NewModuleList(&build.Default)
	mod, _ := mods.LoadModule(flagCheckDir)
	if flagCheckName {
		if mod != nil {
			fmt.Println(mod.Path())
		} else {
			_, fname := filepath.Split(flagCheckDir)
			fmt.Println(fname)
		}
		return nil
	}
	// check mod, check vendor
	if mod != nil {
		_, dir, _ := mod.Lookup(flagCheckPkg)
		if dir != "" {
			fmt.Printf("%s,mod\n", dir)
			return nil
		}
	} else {
		pkg := pkgutil.ImportDir(flagCheckDir)
		if pkg != nil {
			found, _ := pkgutil.VendoredImportPath(pkg, flagCheckPkg)
			if found != "" && found != flagCheckPkg {
				fmt.Printf("%s,vendor\n", found)
				return nil
			}
		}
	}
	fmt.Printf("%s,pkg\n", flagCheckPkg)
	return nil
}
<|endoftext|>


<commit_before>package file_storage

import (
	"github.com/photoshelf/photoshelf-storage/domain/model"
	"io/ioutil"
	"os"
	"path"
)

type FileStorage struct {
	baseDir string
}

func NewFileStorage(baseDir string) *FileStorage {
	return &FileStorage{baseDir}
}

func (storage *FileStorage) Save(photo model.Photo) (*model.Identifier, error) {
	data := photo.Image()
	id := photo.Id()
	if photo.IsNew() {
		id = *model.NewIdentifier(data)
	}

	dst, err := os.Create(path.Join(storage.baseDir, id.Value()))
	if err != nil {
		return nil, err
	}
	defer dst.Close()

	if _, err := dst.Write(data); err != nil {
		return nil, err
	}
	return &id, nil
}

func (storage *FileStorage) Read(id model.Identifier) (*model.Photo, error) {
	file, err := os.Open(path.Join(storage.baseDir, id.Value()))
	if err != nil {
		return nil, err
	}
	data, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, err
	}

	return model.PhotoOf(id, data), nil
}

func (storage *FileStorage) Delete(id model.Identifier) error {
	if err := os.Remove(path.Join(storage.baseDir, id.Value())); err != nil {
		return err
	}
	return nil
}
<commit_msg>Use ioutil for reduce code line<commit_after>package file_storage

import (
	"github.com/photoshelf/photoshelf-storage/domain/model"
	"io/ioutil"
	"os"
	"path"
)

type FileStorage struct {
	baseDir string
}

func NewFileStorage(baseDir string) *FileStorage {
	return &FileStorage{baseDir}
}

func (storage *FileStorage) Save(photo model.Photo) (*model.Identifier, error) {
	data := photo.Image()
	id := photo.Id()
	if photo.IsNew() {
		id = *model.NewIdentifier(data)
	}

	filename := path.Join(storage.baseDir, id.Value())
	ioutil.WriteFile(filename, data, 0600)

	return &id, nil
}

func (storage *FileStorage) Read(id model.Identifier) (*model.Photo, error) {
	filename := path.Join(storage.baseDir, id.Value())
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}

	return model.PhotoOf(id, data), nil
}

func (storage *FileStorage) Delete(id model.Identifier) error {
	if err := os.Remove(path.Join(storage.baseDir, id.Value())); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package plotter

import (
	"code.google.com/p/plotinum/plot"
)

// Points implements the Plotter interface, drawing
// a set of points.
type Points struct {
	XYs

	// LineStyle is the style of the line connecting
	// the points.  If the line width is non-positive
	// then no line is drawn.
	plot.LineStyle

	// GlyphStyle is the style of the glyphs drawn
	// at each point.  If GlyphStyle.Shape is nil
	// then no glyphs are drawn.
	plot.GlyphStyle
}

// NewLine returns a Points that uses the
// default line style and does not draw
// glyphs.
func NewLine(xys XYer) *Points {
	return &Points{
		XYs:       CopyXYs(xys),
		LineStyle: DefaultLineStyle,
	}
}

// NewLinePoints returns a Points that uses
// both the default line and glyph styles.
func NewLinePoints(xys XYer) *Points {
	return &Points{
		XYs:        CopyXYs(xys),
		LineStyle:  DefaultLineStyle,
		GlyphStyle: DefaultGlyphStyle,
	}
}

// NewScatter returns a Points that uses the
// default glyph style and does not draw a line.
func NewScatter(xys XYer) *Points {
	return &Points{
		XYs:        CopyXYs(xys),
		GlyphStyle: DefaultGlyphStyle,
	}
}

// Plot draws the Points, implementing the plot.Plotter
// interface.
func (pts *Points) Plot(da plot.DrawArea, plt *plot.Plot) {
	trX, trY := plt.Transforms(&da)
	ps := make([]plot.Point, len(pts.XYs))
	for i, p := range pts.XYs {
		ps[i].X = trX(p.X)
		ps[i].Y = trY(p.Y)
	}
	if pts.LineStyle.Width > 0 {
		da.StrokeLines(pts.LineStyle, da.ClipLinesXY(ps)...)
	}
	if pts.GlyphStyle.Shape != nil {
		for _, p := range ps {
			da.DrawGlyph(pts.GlyphStyle, p)
		}
	}
}

// DataRange returns the minimum and maximum
// x and y values, implementing the plot.DataRanger
// interface.
func (pts *Points) DataRange() (xmin, xmax, ymin, ymax float64) {
	return XYRange(pts)
}

// GlyphBoxes returns a slice of plot.GlyphBoxes.
// If the GlyphStyle.Radius is positive then there
// is a plot.GlyphBox for each glyph, otherwise
// the returned slice is empty.  This implements the
// plot.GlyphBoxer interface.
func (pts *Points) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
	if pts.GlyphStyle.Shape != nil {
		return []plot.GlyphBox{}
	}
	bs := make([]plot.GlyphBox, len(pts.XYs))
	for i, p := range pts.XYs {
		bs[i].X = plt.X.Norm(p.X)
		bs[i].Y = plt.Y.Norm(p.Y)
		bs[i].Rect = pts.GlyphStyle.Rect()
	}
	return bs
}

// Thumbnail the thumbnail for the Points,
// implementing the plot.Thumbnailer interface.
func (pts *Points) Thumbnail(da *plot.DrawArea) {
	if pts.LineStyle.Width > 0 {
		y := da.Center().Y
		da.StrokeLine2(pts.LineStyle, da.Min.X, y, da.Max().X, y)
	}
	da.DrawGlyph(pts.GlyphStyle, da.Center())
}
<commit_msg>In the Points plotter, use Color to determine drawability. If LineStyle.Color == nil or if GlyphStyle.Color == nil then don't draw the line or glyphs respectively.<commit_after>// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package plotter

import (
	"code.google.com/p/plotinum/plot"
)

// Points implements the Plotter interface, drawing
// a set of points.
type Points struct {
	XYs

	// LineStyle is the style of the line connecting
	// the points.  If the color is nil then no line is
	// drawn.
	plot.LineStyle

	// GlyphStyle is the style of the glyphs drawn
	// at each point.  If Shape or Color are nil
	// then no glyphs are drawn.
	plot.GlyphStyle
}

// NewLine returns a Points that uses the
// default line style and does not draw
// glyphs.
func NewLine(xys XYer) *Points {
	return &Points{
		XYs:       CopyXYs(xys),
		LineStyle: DefaultLineStyle,
	}
}

// NewLinePoints returns a Points that uses
// both the default line and glyph styles.
func NewLinePoints(xys XYer) *Points {
	return &Points{
		XYs:        CopyXYs(xys),
		LineStyle:  DefaultLineStyle,
		GlyphStyle: DefaultGlyphStyle,
	}
}

// NewScatter returns a Points that uses the
// default glyph style and does not draw a line.
func NewScatter(xys XYer) *Points {
	return &Points{
		XYs:        CopyXYs(xys),
		GlyphStyle: DefaultGlyphStyle,
	}
}

// Plot draws the Points, implementing the plot.Plotter
// interface.
func (pts *Points) Plot(da plot.DrawArea, plt *plot.Plot) {
	trX, trY := plt.Transforms(&da)
	ps := make([]plot.Point, len(pts.XYs))
	for i, p := range pts.XYs {
		ps[i].X = trX(p.X)
		ps[i].Y = trY(p.Y)
	}
	if pts.LineStyle.Color != nil {
		da.StrokeLines(pts.LineStyle, da.ClipLinesXY(ps)...)
	}
	if pts.GlyphStyle.Shape != nil && pts.GlyphStyle.Color != nil {
		for _, p := range ps {
			da.DrawGlyph(pts.GlyphStyle, p)
		}
	}
}

// DataRange returns the minimum and maximum
// x and y values, implementing the plot.DataRanger
// interface.
func (pts *Points) DataRange() (xmin, xmax, ymin, ymax float64) {
	return XYRange(pts)
}

// GlyphBoxes returns a slice of plot.GlyphBoxes.
// If the GlyphStyle.Shape is non-nil then there
// is a plot.GlyphBox for each glyph, otherwise
// the returned slice is empty.  This implements the
// plot.GlyphBoxer interface.
func (pts *Points) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox {
	if pts.GlyphStyle.Shape != nil && pts.GlyphStyle.Color != nil {
		return []plot.GlyphBox{}
	}
	bs := make([]plot.GlyphBox, len(pts.XYs))
	for i, p := range pts.XYs {
		bs[i].X = plt.X.Norm(p.X)
		bs[i].Y = plt.Y.Norm(p.Y)
		bs[i].Rect = pts.GlyphStyle.Rect()
	}
	return bs
}

// Thumbnail the thumbnail for the Points,
// implementing the plot.Thumbnailer interface.
func (pts *Points) Thumbnail(da *plot.DrawArea) {
	if pts.LineStyle.Color != nil {
		y := da.Center().Y
		da.StrokeLine2(pts.LineStyle, da.Min.X, y, da.Max().X, y)
	}
	if pts.GlyphStyle.Shape != nil && pts.GlyphStyle.Color != nil {
		da.DrawGlyph(pts.GlyphStyle, da.Center())
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016-2019 Wei Shen <shenwei356@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"runtime"
	"sort"
	"strings"

	combinations "github.com/mxschmitt/golang-combinations"
	"github.com/shenwei356/natsort"
	"github.com/shenwei356/xopen"
	"github.com/spf13/cobra"
)

// combCmd represents the comb command
var combCmd = &cobra.Command{
	Use:   "comb",
	Short: "compute combinations of items at every row",
	Long: `compute combinations of items at every row

`,
	Run: func(cmd *cobra.Command, args []string) {
		config := getConfigs(cmd)
		files := getFileList(args)
		runtime.GOMAXPROCS(config.NumCPUs)

		sortItems := getFlagBool(cmd, "sort")
		sortItemsNatSort := getFlagBool(cmd, "nat-sort")
		number := getFlagNonNegativeInt(cmd, "number")
		ignoreCase := getFlagBool(cmd, "ignore-case")

		outfh, err := xopen.Wopen(config.OutFile)
		checkError(err)
		defer outfh.Close()

		writer := csv.NewWriter(outfh)
		if config.OutTabs || config.Tabs {
			writer.Comma = '\t'
		} else {
			writer.Comma = config.OutDelimiter
		}

		var fh *xopen.Reader
		var text string
		var reader *csv.Reader
		var line int
		var item string
		var _items, items []string
		var combs [][]string
		var comb []string

		for _, file := range files {
			fh, err = xopen.Ropen(file)
			if err != nil {
				checkError(fmt.Errorf("reading file %s: %s", file, err))
			}

			scanner := bufio.NewScanner(fh)

			line = 0
			for scanner.Scan() {
				line++
				if !config.NoHeaderRow && line == 1 {
					continue
				}

				text = strings.TrimSpace(scanner.Text())
				if ignoreCase {
					text = strings.ToLower(text)
				}

				reader = csv.NewReader(strings.NewReader(text))
				if config.Tabs {
					reader.Comma = '\t'
				} else {
					reader.Comma = config.Delimiter
				}

				reader.Comment = config.CommentChar
				for {
					_items, err = reader.Read()
					if err != nil {
						checkError(fmt.Errorf("[line %d] failed parsing: %s", line, text))
					}
					break
				}
				items = make([]string, 0, len(_items))
				for _, item = range _items {
					if item == "" {
						continue
					}
					items = append(items, item)
				}

				if len(items) == 0 {
					continue
				}

				combs = combinations.Combinations(items, number)
				for _, comb = range combs {
					if sortItems {
						sort.Strings(comb)
					} else if sortItemsNatSort {
						natsort.Sort(comb)
					}
					writer.Write(comb)
				}

			}
			checkError(scanner.Err())
		}
		writer.Flush()
		checkError(writer.Error())
	},
}

func init() {
	RootCmd.AddCommand(combCmd)

	combCmd.Flags().BoolP("sort", "s", false, `sort items in a combination`)
	combCmd.Flags().BoolP("nat-sort", "S", false, `sort items in natural order`)
	combCmd.Flags().IntP("number", "n", 2, `number of items in a combination, 0 for no limit, i.e., return all combinations`)
	combCmd.Flags().BoolP("ignore-case", "i", false, "ignore-case")
}
<commit_msg>csvtk comb: add help message<commit_after>// Copyright © 2016-2019 Wei Shen <shenwei356@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"runtime"
	"sort"
	"strings"

	combinations "github.com/mxschmitt/golang-combinations"
	"github.com/shenwei356/natsort"
	"github.com/shenwei356/xopen"
	"github.com/spf13/cobra"
)

// combCmd represents the comb command
var combCmd = &cobra.Command{
	Use:   "comb",
	Short: "compute combinations of items at every row",
	Long: `compute combinations of items at every row

`,
	Run: func(cmd *cobra.Command, args []string) {
		config := getConfigs(cmd)
		files := getFileList(args)
		runtime.GOMAXPROCS(config.NumCPUs)

		sortItems := getFlagBool(cmd, "sort")
		sortItemsNatSort := getFlagBool(cmd, "nat-sort")
		number := getFlagNonNegativeInt(cmd, "number")
		ignoreCase := getFlagBool(cmd, "ignore-case")

		outfh, err := xopen.Wopen(config.OutFile)
		checkError(err)
		defer outfh.Close()

		writer := csv.NewWriter(outfh)
		if config.OutTabs || config.Tabs {
			writer.Comma = '\t'
		} else {
			writer.Comma = config.OutDelimiter
		}

		var fh *xopen.Reader
		var text string
		var reader *csv.Reader
		var line int
		var item string
		var _items, items []string
		var combs [][]string
		var comb []string
		var n int

		for _, file := range files {
			fh, err = xopen.Ropen(file)
			if err != nil {
				checkError(fmt.Errorf("reading file %s: %s", file, err))
			}

			scanner := bufio.NewScanner(fh)

			line = 0
			n = 0
			for scanner.Scan() {
				line++
				if !config.NoHeaderRow && line == 1 {
					continue
				}
				n++

				text = strings.TrimSpace(scanner.Text())
				if ignoreCase {
					text = strings.ToLower(text)
				}

				reader = csv.NewReader(strings.NewReader(text))
				if config.Tabs {
					reader.Comma = '\t'
				} else {
					reader.Comma = config.Delimiter
				}

				reader.Comment = config.CommentChar
				for {
					_items, err = reader.Read()
					if err != nil {
						checkError(fmt.Errorf("[line %d] failed parsing: %s", line, text))
					}
					break
				}
				items = make([]string, 0, len(_items))
				for _, item = range _items {
					if item == "" {
						continue
					}
					items = append(items, item)
				}

				if len(items) == 0 {
					continue
				}

				combs = combinations.Combinations(items, number)
				for _, comb = range combs {
					if sortItems {
						sort.Strings(comb)
					} else if sortItemsNatSort {
						natsort.Sort(comb)
					}
					writer.Write(comb)
				}

			}
			checkError(scanner.Err())
			if n == 0 {
				log.Warning("no input? or only one row? you may need switch on '-H' for single-line input")
			}
		}
		writer.Flush()
		checkError(writer.Error())
	},
}

func init() {
	RootCmd.AddCommand(combCmd)

	combCmd.Flags().BoolP("sort", "s", false, `sort items in a combination`)
	combCmd.Flags().BoolP("nat-sort", "S", false, `sort items in natural order`)
	combCmd.Flags().IntP("number", "n", 2, `number of items in a combination, 0 for no limit, i.e., return all combinations`)
	combCmd.Flags().BoolP("ignore-case", "i", false, "ignore-case")
}
<|endoftext|>


<commit_before>package core

type GeneralRegs [16]uint32

type Registers struct {
	R   GeneralRegs
	Psr uint32
}

/* Special registers in r13-15 */
const (
	LR = 13
	SP = 14
	PC = 15
)

func (regs *Registers) Lr() uint32 {
	return regs.R[LR]
}

func (regs *Registers) Sp() uint32 {
	return regs.R[SP]
}

func (regs *Registers) Pc() uint32 {
	return regs.R[PC]
}
<commit_msg>Register helper methods do not need a reference to the object<commit_after>package core

type GeneralRegs [16]uint32

type Registers struct {
	R   GeneralRegs
	Psr uint32
}

/* Special registers in r13-15 */
const (
	LR = 13
	SP = 14
	PC = 15
)

func (regs Registers) Lr() uint32 {
	return regs.R[LR]
}

func (regs Registers) Sp() uint32 {
	return regs.R[SP]
}

func (regs Registers) Pc() uint32 {
	return regs.R[PC]
}
<|endoftext|>


<commit_before>package klash

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type ArgumentParser struct {
	Parser  *ParamParser
	Args    []string
	OutArgs []string
	Idx     int
	Stop    bool
	Stopped bool
}

func NewArgumentParser(parser *ParamParser, args []string, stop bool) *ArgumentParser {
	return &ArgumentParser{
		parser,
		args,
		make([]string, 0, len(args)),
		0,
		stop,
		false,
	}
}

func (ap *ArgumentParser) Terminated() bool {
	return ap.Idx >= len(ap.Args)
}

func (ap *ArgumentParser) extractVal(stringval string, value *reflect.Value) error {
	switch value.Kind() {
	case reflect.String:
		value.Set(reflect.ValueOf(stringval))
	case reflect.Int:
		val, err := strconv.ParseInt(stringval, 0, 0)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(int(val)))
	case reflect.Uint:
		val, err := strconv.ParseUint(stringval, 0, 0)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(uint(val)))
	case reflect.Float32:
		val, err := strconv.ParseFloat(stringval, 32)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(float32(val)))
	case reflect.Float64:
		val, err := strconv.ParseFloat(stringval, 64)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(float64(val)))
	default:
		return fmt.Errorf("klash: Invalid type %s",
			value.Kind(),
		)
	}
	return nil
}

func (ap *ArgumentParser) ParseOne() error {
	arg := ap.Args[ap.Idx]

	if ap.Stopped || arg[0] != '-' {
		ap.OutArgs = append(ap.OutArgs, arg)
		if ap.Stop {
			ap.Stopped = true
		}
		ap.Idx++
		return nil
	}

	arg = strings.ToLower(arg)

	for len(arg) > 0 && arg[0] == '-' {
		arg = arg[1:]
	}

	if param, ok := ap.Parser.Params[arg]; ok {
		if param.Value.Kind() == reflect.Bool {
			param.Value.Set(reflect.ValueOf(true))
		} else {
			ap.Idx++
			stringval := ap.Args[ap.Idx]

			if param.Value.Kind() == reflect.Slice {
				value := reflect.New(param.Value.Type().Elem()).Elem()
				if err := ap.extractVal(stringval, &value); err != nil {
					return err
				}
				param.Value.Set(reflect.Append(param.Value, value))
			} else if err := ap.extractVal(stringval, &param.Value); err != nil {
				return err
			}
		}
	} else {
		return fmt.Errorf("klash: Invalid flag: %s", arg)
	}

	ap.Idx++
	return nil
}
<commit_msg>Handle argument assignation + fix bug on empty values, closes #2<commit_after>package klash

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type ArgumentParser struct {
	Parser  *ParamParser
	Args    []string
	OutArgs []string
	Idx     int
	Stop    bool
	Stopped bool
}

func NewArgumentParser(parser *ParamParser, args []string, stop bool) *ArgumentParser {
	return &ArgumentParser{
		parser,
		args,
		make([]string, 0, len(args)),
		0,
		stop,
		false,
	}
}

func (ap *ArgumentParser) Terminated() bool {
	return ap.Idx >= len(ap.Args)
}

func (ap *ArgumentParser) extractVal(stringval string, value *reflect.Value) error {
	switch value.Kind() {
	case reflect.String:
		value.Set(reflect.ValueOf(stringval))
	case reflect.Int:
		val, err := strconv.ParseInt(stringval, 0, 0)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(int(val)))
	case reflect.Uint:
		val, err := strconv.ParseUint(stringval, 0, 0)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(uint(val)))
	case reflect.Float32:
		val, err := strconv.ParseFloat(stringval, 32)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(float32(val)))
	case reflect.Float64:
		val, err := strconv.ParseFloat(stringval, 64)
		if err != nil {
			return err
		}
		value.Set(reflect.ValueOf(float64(val)))
	default:
		return fmt.Errorf("klash: Invalid type %s",
			value.Kind(),
		)
	}
	return nil
}

func (ap *ArgumentParser) ParseOne() error {
	arg := ap.Args[ap.Idx]
	var stringval string

	if ap.Stopped || arg[0] != '-' {
		ap.OutArgs = append(ap.OutArgs, arg)
		if ap.Stop {
			ap.Stopped = true
		}
		ap.Idx++
		return nil
	}

	for len(arg) > 0 && arg[0] == '-' {
		arg = arg[1:]
	}

	idx := strings.Index(arg, "=")
	if idx >= 0 {
		exploded := strings.Split(arg, "=")
		if exploded[1] == "" {
			return fmt.Errorf("klash: no value provided to %s", exploded[0])
		}
		arg, stringval = exploded[0], exploded[1]
	}

	arg = strings.ToLower(arg)

	if param, ok := ap.Parser.Params[arg]; ok {
		if param.Value.Kind() == reflect.Bool {
			param.Value.Set(reflect.ValueOf(true))
		} else {
			if stringval == "" {
				ap.Idx++
				if ap.Idx >= len(ap.Args) {
					return fmt.Errorf("klash: no value provided to %s", arg)
				}
				stringval = ap.Args[ap.Idx]
			}

			if param.Value.Kind() == reflect.Slice {
				value := reflect.New(param.Value.Type().Elem()).Elem()
				if err := ap.extractVal(stringval, &value); err != nil {
					return err
				}
				param.Value.Set(reflect.Append(param.Value, value))
			} else if err := ap.extractVal(stringval, &param.Value); err != nil {
				return err
			}
		}
	} else {
		return fmt.Errorf("klash: Invalid flag: %s", arg)
	}

	ap.Idx++
	return nil
}
<|endoftext|>


<commit_before>package flv

import (
	"bytes"
	"fmt"
	"io"
	"os"
)

type Header struct {
	Version uint16
	Body    []byte
}

type Frame interface {
	WriteFrame(io.Writer) error
	Stream() uint32
}

type CFrame struct {
	Stream      uint32
	Dts         uint32
	Type        TagType
	Flavor      Flavor
	Position    int64
	Body        []byte
	PrevTagSize uint32
}

func (f CFrame) WriteFrame(w io.Writer) error {
	bl := uint32(len(f.Body))
	var err error
	err = writeType(w, f.Type)
	if err != nil {
		return err
	}
	err = writeBodyLength(w, bl)
	err = writeDts(w, f.Dts)
	err = writeStream(w, f.Stream)
	err = writeBody(w, f.Body)
	prevTagSize := bl + uint32(TAG_HEADER_LENGTH)
	err = writePrevTagSize(w, prevTagSize)
	return nil
}

func writeType(w io.Writer, t TagType) error {
	_, err := w.Write([]byte{byte(t)})
	return err
}

func writeBodyLength(w io.Writer, bl uint32) error {
	_, err := w.Write([]byte{byte(bl >> 16), byte((bl >> 8) & 0xFF), byte(bl & 0xFF)})
	return err
}

func writeDts(w io.Writer, dts uint32) error {
	_, err := w.Write([]byte{byte((dts >> 16) & 0xFF), byte((dts >> 8) & 0xFF), byte(dts & 0xFF)})
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{byte((dts >> 24) & 0xFF)})
	return err
}

func writeStream(w io.Writer, stream uint32) error {
	_, err := w.Write([]byte{byte(stream >> 16), byte((stream >> 8) & 0xFF), byte(stream & 0xFF)})
	return err
}

func writeBody(w io.Writer, body []byte) error {
	_, err := w.Write(body)
	return err
}

func writePrevTagSize(w io.Writer, prevTagSize uint32) error {
	_, err := w.Write([]byte{byte((prevTagSize >> 24) & 0xFF), byte((prevTagSize >> 16) & 0xFF), byte((prevTagSize >> 8) & 0xFF), byte(prevTagSize & 0xFF)})
	return err
}

type VideoFrame struct {
	CFrame
	CodecId VideoCodec
	Width   uint16
	Height  uint16
}

func (f VideoFrame) WriteFrame(w io.Writer) error {
	return f.CFrame.WriteFrame(w)
}
func (f VideoFrame) Stream() uint32 {
	return f.CFrame.Stream
}

type AudioFrame struct {
	CFrame
	CodecId  AudioCodec
	Rate     uint32
	BitSize  AudioSize
	Channels AudioType
}

func (f AudioFrame) WriteFrame(w io.Writer) error {
	return f.CFrame.WriteFrame(w)
}
func (f AudioFrame) Stream() uint32 {
	return f.CFrame.Stream
}

type MetaFrame struct {
	CFrame
}

func (f MetaFrame) WriteFrame(w io.Writer) error {
	return f.CFrame.WriteFrame(w)
}
func (f MetaFrame) Stream() uint32 {
	return f.CFrame.Stream
}

type FlvReader struct {
	InFile *os.File
	width  uint16
	height uint16
}

func NewReader(inFile *os.File) *FlvReader {
	return &FlvReader{
		InFile: inFile,
		width:  0,
		height: 0,
	}
}

type FlvWriter struct {
	OutFile *os.File
}

func NewWriter(outFile *os.File) *FlvWriter {
	return &FlvWriter{
		OutFile: outFile,
	}
}

func (frReader *FlvReader) ReadHeader() (*Header, error) {
	header := make([]byte, HEADER_LENGTH+4)
	_, err := frReader.InFile.Read(header)
	if err != nil {
		return nil, err
	}

	sig := header[0:3]
	if bytes.Compare(sig, []byte(SIG)) != 0 {
		return nil, fmt.Errorf("bad file format")
	}
	version := (uint16(header[3]) << 8) | (uint16(header[4]) << 0)
	//skip := header[4:5]
	//offset := header[5:9]
	//next_id := header[9:13]

	return &Header{Version: version, Body: header}, nil
}

func (frWriter *FlvWriter) WriteHeader(header *Header) error {
	_, err := frWriter.OutFile.Write(header.Body)
	if err != nil {
		return err
	}
	return nil
}

func (frReader *FlvReader) ReadFrame() (fr Frame, e error) {

	var n int
	var err error

	curPos, _ := frReader.InFile.Seek(0, os.SEEK_CUR)

	tagHeaderB := make([]byte, TAG_HEADER_LENGTH)
	n, err = frReader.InFile.Read(tagHeaderB)
	if n == 0 {
		return nil, nil
	}
	if TagSize(n) != TAG_HEADER_LENGTH {
		return nil, fmt.Errorf("bad tag length: %d", n)
	}
	if err != nil {
		return nil, err
	}

	tagType := TagType(tagHeaderB[0])
	bodyLen := (uint32(tagHeaderB[1]) << 16) | (uint32(tagHeaderB[2]) << 8) | (uint32(tagHeaderB[3]) << 0)
	ts := (uint32(tagHeaderB[4]) << 16) | (uint32(tagHeaderB[5]) << 8) | (uint32(tagHeaderB[6]) << 0)
	tsExt := uint32(tagHeaderB[7])
	stream := (uint32(tagHeaderB[8]) << 16) | (uint32(tagHeaderB[9]) << 8) | (uint32(tagHeaderB[10]) << 0)

	var dts uint32
	dts = (tsExt << 24) | ts

	bodyBuf := make([]byte, bodyLen)
	n, err = frReader.InFile.Read(bodyBuf)
	if err != nil {
		return nil, err
	}

	prevTagSizeB := make([]byte, PREV_TAG_SIZE_LENGTH)
	n, err = frReader.InFile.Read(prevTagSizeB)
	if err != nil {
		return nil, err
	}
	prevTagSize := (uint32(prevTagSizeB[0]) << 24) | (uint32(prevTagSizeB[1]) << 16) | (uint32(prevTagSizeB[2]) << 8) | (uint32(prevTagSizeB[3]) << 0)

	pFrame := CFrame{
		Stream:      stream,
		Dts:         dts,
		Type:        tagType,
		Position:    curPos,
		Body:        bodyBuf,
		PrevTagSize: prevTagSize,
	}

	var resFrame Frame

	switch tagType {
	case TAG_TYPE_META:
		pFrame.Flavor = METADATA
		resFrame = MetaFrame{pFrame}
	case TAG_TYPE_VIDEO:
		vft := VideoFrameType(uint8(bodyBuf[0]) >> 4)
		codecId := VideoCodec(uint8(bodyBuf[0]) & 0x0F)
		switch vft {
		case VIDEO_FRAME_TYPE_KEYFRAME:
			pFrame.Flavor = KEYFRAME
			switch codecId {
			case VIDEO_CODEC_ON2VP6:
				hHelper := (uint16(bodyBuf[1]) >> 4) & 0x0F
				wHelper := uint16(bodyBuf[1]) & 0x0F
				w := uint16(bodyBuf[5])
				h := uint16(bodyBuf[6])

				frReader.width = w*16 - wHelper
				frReader.height = h*16 - hHelper
			}
		default:
			pFrame.Flavor = FRAME
		}
		resFrame = VideoFrame{CFrame: pFrame, CodecId: codecId, Width: frReader.width, Height: frReader.height}
	case TAG_TYPE_AUDIO:
		pFrame.Flavor = FRAME
		codecId := AudioCodec(uint8(bodyBuf[0]) >> 4)
		rate := audioRate(AudioRate((uint8(bodyBuf[0]) >> 2) & 0x03))
		bitSize := AudioSize((uint8(bodyBuf[0]) >> 1) & 0x01)
		channels := AudioType(uint8(bodyBuf[0]) & 0x01)
		resFrame = AudioFrame{CFrame: pFrame, CodecId: codecId, Rate: rate, BitSize: bitSize, Channels: channels}
	}

	return resFrame, nil
}

func audioRate(ar AudioRate) uint32 {
	var ret uint32
	switch ar {
	case AUDIO_RATE_5_5:
		ret = 5500
	case AUDIO_RATE_11:
		ret = 11000
	case AUDIO_RATE_22:
		ret = 22000
	case AUDIO_RATE_44:
		ret = 44000
	}
	return ret
}

func (frWriter *FlvWriter) WriteFrame(fr Frame) (e error) {
	return fr.WriteFrame(frWriter.OutFile)
}
<commit_msg>Add String() method to TagType<commit_after>package flv

import (
	"bytes"
	"fmt"
	"io"
	"os"
)

type Header struct {
	Version uint16
	Body    []byte
}

type Frame interface {
	WriteFrame(io.Writer) error
	Stream() uint32
}

type CFrame struct {
	Stream      uint32
	Dts         uint32
	Type        TagType
	Flavor      Flavor
	Position    int64
	Body        []byte
	PrevTagSize uint32
}

func (f CFrame) WriteFrame(w io.Writer) error {
	bl := uint32(len(f.Body))
	var err error
	err = writeType(w, f.Type)
	if err != nil {
		return err
	}
	err = writeBodyLength(w, bl)
	err = writeDts(w, f.Dts)
	err = writeStream(w, f.Stream)
	err = writeBody(w, f.Body)
	prevTagSize := bl + uint32(TAG_HEADER_LENGTH)
	err = writePrevTagSize(w, prevTagSize)
	return nil
}

func writeType(w io.Writer, t TagType) error {
	_, err := w.Write([]byte{byte(t)})
	return err
}

func writeBodyLength(w io.Writer, bl uint32) error {
	_, err := w.Write([]byte{byte(bl >> 16), byte((bl >> 8) & 0xFF), byte(bl & 0xFF)})
	return err
}

func writeDts(w io.Writer, dts uint32) error {
	_, err := w.Write([]byte{byte((dts >> 16) & 0xFF), byte((dts >> 8) & 0xFF), byte(dts & 0xFF)})
	if err != nil {
		return err
	}
	_, err = w.Write([]byte{byte((dts >> 24) & 0xFF)})
	return err
}

func writeStream(w io.Writer, stream uint32) error {
	_, err := w.Write([]byte{byte(stream >> 16), byte((stream >> 8) & 0xFF), byte(stream & 0xFF)})
	return err
}

func writeBody(w io.Writer, body []byte) error {
	_, err := w.Write(body)
	return err
}

func writePrevTagSize(w io.Writer, prevTagSize uint32) error {
	_, err := w.Write([]byte{byte((prevTagSize >> 24) & 0xFF), byte((prevTagSize >> 16) & 0xFF), byte((prevTagSize >> 8) & 0xFF), byte(prevTagSize & 0xFF)})
	return err
}

type VideoFrame struct {
	CFrame
	CodecId VideoCodec
	Width   uint16
	Height  uint16
}

func (f VideoFrame) WriteFrame(w io.Writer) error {
	return f.CFrame.WriteFrame(w)
}
func (f VideoFrame) Stream() uint32 {
	return f.CFrame.Stream
}

type AudioFrame struct {
	CFrame
	CodecId  AudioCodec
	Rate     uint32
	BitSize  AudioSize
	Channels AudioType
}

func (f AudioFrame) WriteFrame(w io.Writer) error {
	return f.CFrame.WriteFrame(w)
}
func (f AudioFrame) Stream() uint32 {
	return f.CFrame.Stream
}

type MetaFrame struct {
	CFrame
}

func (f MetaFrame) WriteFrame(w io.Writer) error {
	return f.CFrame.WriteFrame(w)
}
func (f MetaFrame) Stream() uint32 {
	return f.CFrame.Stream
}

type FlvReader struct {
	InFile *os.File
	width  uint16
	height uint16
}

func NewReader(inFile *os.File) *FlvReader {
	return &FlvReader{
		InFile: inFile,
		width:  0,
		height: 0,
	}
}

type FlvWriter struct {
	OutFile *os.File
}

func NewWriter(outFile *os.File) *FlvWriter {
	return &FlvWriter{
		OutFile: outFile,
	}
}

func (frReader *FlvReader) ReadHeader() (*Header, error) {
	header := make([]byte, HEADER_LENGTH+4)
	_, err := frReader.InFile.Read(header)
	if err != nil {
		return nil, err
	}

	sig := header[0:3]
	if bytes.Compare(sig, []byte(SIG)) != 0 {
		return nil, fmt.Errorf("bad file format")
	}
	version := (uint16(header[3]) << 8) | (uint16(header[4]) << 0)
	//skip := header[4:5]
	//offset := header[5:9]
	//next_id := header[9:13]

	return &Header{Version: version, Body: header}, nil
}

func (frWriter *FlvWriter) WriteHeader(header *Header) error {
	_, err := frWriter.OutFile.Write(header.Body)
	if err != nil {
		return err
	}
	return nil
}

func (frReader *FlvReader) ReadFrame() (fr Frame, e error) {

	var n int
	var err error

	curPos, _ := frReader.InFile.Seek(0, os.SEEK_CUR)

	tagHeaderB := make([]byte, TAG_HEADER_LENGTH)
	n, err = frReader.InFile.Read(tagHeaderB)
	if n == 0 {
		return nil, nil
	}
	if TagSize(n) != TAG_HEADER_LENGTH {
		return nil, fmt.Errorf("bad tag length: %d", n)
	}
	if err != nil {
		return nil, err
	}

	tagType := TagType(tagHeaderB[0])
	bodyLen := (uint32(tagHeaderB[1]) << 16) | (uint32(tagHeaderB[2]) << 8) | (uint32(tagHeaderB[3]) << 0)
	ts := (uint32(tagHeaderB[4]) << 16) | (uint32(tagHeaderB[5]) << 8) | (uint32(tagHeaderB[6]) << 0)
	tsExt := uint32(tagHeaderB[7])
	stream := (uint32(tagHeaderB[8]) << 16) | (uint32(tagHeaderB[9]) << 8) | (uint32(tagHeaderB[10]) << 0)

	var dts uint32
	dts = (tsExt << 24) | ts

	bodyBuf := make([]byte, bodyLen)
	n, err = frReader.InFile.Read(bodyBuf)
	if err != nil {
		return nil, err
	}

	prevTagSizeB := make([]byte, PREV_TAG_SIZE_LENGTH)
	n, err = frReader.InFile.Read(prevTagSizeB)
	if err != nil {
		return nil, err
	}
	prevTagSize := (uint32(prevTagSizeB[0]) << 24) | (uint32(prevTagSizeB[1]) << 16) | (uint32(prevTagSizeB[2]) << 8) | (uint32(prevTagSizeB[3]) << 0)

	pFrame := CFrame{
		Stream:      stream,
		Dts:         dts,
		Type:        tagType,
		Position:    curPos,
		Body:        bodyBuf,
		PrevTagSize: prevTagSize,
	}

	var resFrame Frame

	switch tagType {
	case TAG_TYPE_META:
		pFrame.Flavor = METADATA
		resFrame = MetaFrame{pFrame}
	case TAG_TYPE_VIDEO:
		vft := VideoFrameType(uint8(bodyBuf[0]) >> 4)
		codecId := VideoCodec(uint8(bodyBuf[0]) & 0x0F)
		switch vft {
		case VIDEO_FRAME_TYPE_KEYFRAME:
			pFrame.Flavor = KEYFRAME
			switch codecId {
			case VIDEO_CODEC_ON2VP6:
				hHelper := (uint16(bodyBuf[1]) >> 4) & 0x0F
				wHelper := uint16(bodyBuf[1]) & 0x0F
				w := uint16(bodyBuf[5])
				h := uint16(bodyBuf[6])

				frReader.width = w*16 - wHelper
				frReader.height = h*16 - hHelper
			}
		default:
			pFrame.Flavor = FRAME
		}
		resFrame = VideoFrame{CFrame: pFrame, CodecId: codecId, Width: frReader.width, Height: frReader.height}
	case TAG_TYPE_AUDIO:
		pFrame.Flavor = FRAME
		codecId := AudioCodec(uint8(bodyBuf[0]) >> 4)
		rate := audioRate(AudioRate((uint8(bodyBuf[0]) >> 2) & 0x03))
		bitSize := AudioSize((uint8(bodyBuf[0]) >> 1) & 0x01)
		channels := AudioType(uint8(bodyBuf[0]) & 0x01)
		resFrame = AudioFrame{CFrame: pFrame, CodecId: codecId, Rate: rate, BitSize: bitSize, Channels: channels}
	}

	return resFrame, nil
}

func audioRate(ar AudioRate) uint32 {
	var ret uint32
	switch ar {
	case AUDIO_RATE_5_5:
		ret = 5500
	case AUDIO_RATE_11:
		ret = 11000
	case AUDIO_RATE_22:
		ret = 22000
	case AUDIO_RATE_44:
		ret = 44000
	}
	return ret
}

func (frWriter *FlvWriter) WriteFrame(fr Frame) (e error) {
	return fr.WriteFrame(frWriter.OutFile)
}

func (tt TagType) String() (s string) {
	switch tt {
	case TAG_TYPE_AUDIO: return "audio"
	case TAG_TYPE_VIDEO: return "video"
	case TAG_TYPE_META: return "meta"
	}
	return "UNKNOWN"
}
<|endoftext|>