<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"path"
)

var (
	rootDir = flag.String("rootDir", "",
		"Name of root of directory tree to convert to an object cache")
)

func main() {
	flag.Parse()
	if *rootDir == "" {
		fmt.Fprintf(os.Stderr, "rootDir unspecified\n")
		os.Exit(1)
	}
	subdDirPathname := path.Join(*rootDir, ".subd")
	if err := os.RemoveAll(subdDirPathname); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	objectsDir := path.Join(subdDirPathname, "objects")
	if err := os.MkdirAll(objectsDir, 0750); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := walk(*rootDir, "/", objectsDir); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>Add help information to fs2objectcache.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"path"
)

var (
	rootDir = flag.String("rootDir", "",
		"Name of root of directory tree to convert to an object cache")
)

func init() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr,
			"Usage: fs2objectcache [flags...]")
		fmt.Fprintln(os.Stderr, "Common flags:")
		flag.PrintDefaults()
		fmt.Fprintln(os.Stderr)
		fmt.Fprintln(os.Stderr,
			"This tool will convert a file-system tree to an object cache.")
		fmt.Fprintln(os.Stderr,
			"It is a building block for a secure and fast re-imaging pipeline.")
		fmt.Fprintln(os.Stderr,
			"It should only be run from a trusted boot image.")
	}
}

func main() {
	flag.Parse()
	if *rootDir == "" {
		fmt.Fprintln(os.Stderr, "rootDir unspecified")
		os.Exit(1)
	}
	if *rootDir == "/" {
		fmt.Fprintln(os.Stderr, "Cannot convert root file-system")
		os.Exit(1)
	}
	subdDirPathname := path.Join(*rootDir, ".subd")
	if err := os.RemoveAll(subdDirPathname); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	objectsDir := path.Join(subdDirPathname, "objects")
	if err := os.MkdirAll(objectsDir, 0750); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	if err := walk(*rootDir, "/", objectsDir); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). 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 gopfmt implements the ``gop fmt'' command.
package gopfmt

import (
	"bytes"
	"fmt"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/qiniu/x/log"

	"github.com/goplus/gop/cmd/internal/base"
	"github.com/goplus/gop/format"

	xformat "github.com/goplus/gop/x/format"
)

// Cmd - gop go
var Cmd = &base.Command{
	UsageLine: "gop fmt [-n --smart] path ...",
	Short:     "Format Go+ packages",
}

var (
	flag        = &Cmd.Flag
	flagNotExec = flag.Bool("n", false, "prints commands that would be executed.")
	flagSmart   = flag.Bool("smart", false, "convert Go code style into Go+ style.")
)

func init() {
	Cmd.Run = runCmd
}

var (
	procCnt    = 0
	walkSubDir = false
	extGops    = map[string]struct{}{
		".go":  {},
		".gop": {},
		".spx": {},
		".gmx": {},
	}
	rootDir = ""
)

func gopfmt(path string, smart bool) (err error) {
	src, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}
	var target []byte
	if smart {
		target, err = xformat.Source(src)
	} else {
		target, err = format.Source(src)
	}
	if err != nil {
		return
	}
	if bytes.Equal(src, target) {
		return
	}
	fmt.Println(path)
	return writeFileWithBackup(path, target)
}

func writeFileWithBackup(path string, target []byte) (err error) {
	dir, file := filepath.Split(path)
	f, err := ioutil.TempFile(dir, file)
	if err != nil {
		return
	}
	tmpfile := f.Name()
	_, err = f.Write(target)
	f.Close()
	if err != nil {
		return
	}
	err = os.Remove(path)
	if err != nil {
		return
	}
	return os.Rename(tmpfile, path)
}

func walk(path string, d fs.DirEntry, err error) error {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if d.IsDir() {
		if !walkSubDir && path != rootDir {
			return filepath.SkipDir
		}
	} else {
		ext := filepath.Ext(path)
		if _, ok := extGops[ext]; ok {
			procCnt++
			if *flagNotExec {
				fmt.Println("gop fmt", path)
			} else {
				err = gopfmt(path, *flagSmart && ext != ".go")
			}
		}
	}
	return err
}

func runCmd(cmd *base.Command, args []string) {
	err := flag.Parse(args)
	if err != nil {
		log.Fatalln("parse input arguments failed:", err)
	}
	narg := flag.NArg()
	if narg < 1 {
		cmd.Usage(os.Stderr)
	}
	for i := 0; i < narg; i++ {
		path := flag.Arg(i)
		walkSubDir = strings.HasSuffix(path, "/...")
		if walkSubDir {
			path = path[:len(path)-4]
		}
		procCnt = 0
		rootDir = path
		filepath.WalkDir(path, walk)
		if procCnt == 0 {
			fmt.Println("no Go+ files in", path)
		}
	}
}

// -----------------------------------------------------------------------------
<commit_msg>gop fmt --smart --mvgo<commit_after>/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). 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 gopfmt implements the ``gop fmt'' command.
package gopfmt

import (
	"bytes"
	"fmt"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/qiniu/x/log"

	"github.com/goplus/gop/cmd/internal/base"
	"github.com/goplus/gop/format"

	xformat "github.com/goplus/gop/x/format"
)

// Cmd - gop go
var Cmd = &base.Command{
	UsageLine: "gop fmt [-n --smart --mvgo] path ...",
	Short:     "Format Go+ packages",
}

var (
	flag        = &Cmd.Flag
	flagNotExec = flag.Bool("n", false, "prints commands that would be executed.")
	flagMoveGo  = flag.Bool("mvgo", false, "move .go files to .gop files (only available in `--smart` mode).")
	flagSmart   = flag.Bool("smart", false, "convert Go code style into Go+ style.")
)

func init() {
	Cmd.Run = runCmd
}

var (
	procCnt    = 0
	walkSubDir = false
	extGops    = map[string]struct{}{
		".go":  {},
		".gop": {},
		".spx": {},
		".gmx": {},
	}
	rootDir = ""
)

func gopfmt(path string, smart, mvgo bool) (err error) {
	src, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}
	var target []byte
	if smart {
		target, err = xformat.Source(src)
	} else {
		target, err = format.Source(src)
	}
	if err != nil {
		return
	}
	if bytes.Equal(src, target) {
		return
	}
	fmt.Println(path)
	if mvgo {
		newPath := strings.TrimSuffix(path, ".go") + ".gop"
		if err = os.WriteFile(newPath, target, 0666); err != nil {
			return
		}
		return os.Remove(path)
	}
	return writeFileWithBackup(path, target)
}

func writeFileWithBackup(path string, target []byte) (err error) {
	dir, file := filepath.Split(path)
	f, err := ioutil.TempFile(dir, file)
	if err != nil {
		return
	}
	tmpfile := f.Name()
	_, err = f.Write(target)
	f.Close()
	if err != nil {
		return
	}
	err = os.Remove(path)
	if err != nil {
		return
	}
	return os.Rename(tmpfile, path)
}

func walk(path string, d fs.DirEntry, err error) error {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	} else if d.IsDir() {
		if !walkSubDir && path != rootDir {
			return filepath.SkipDir
		}
	} else {
		ext := filepath.Ext(path)
		if _, ok := extGops[ext]; ok {
			procCnt++
			if *flagNotExec {
				fmt.Println("gop fmt", path)
			} else {
				smart := *flagSmart
				mvgo := smart && *flagMoveGo
				err = gopfmt(path, smart && (mvgo || ext != ".go"), mvgo)
			}
		}
	}
	return err
}

func runCmd(cmd *base.Command, args []string) {
	err := flag.Parse(args)
	if err != nil {
		log.Fatalln("parse input arguments failed:", err)
	}
	narg := flag.NArg()
	if narg < 1 {
		cmd.Usage(os.Stderr)
	}
	for i := 0; i < narg; i++ {
		path := flag.Arg(i)
		walkSubDir = strings.HasSuffix(path, "/...")
		if walkSubDir {
			path = path[:len(path)-4]
		}
		procCnt = 0
		rootDir = path
		filepath.WalkDir(path, walk)
		if procCnt == 0 {
			fmt.Println("no Go+ files in", path)
		}
	}
}

// -----------------------------------------------------------------------------
<|endoftext|>


<commit_before>package command_factory_test

import (
	. "cf/command_factory"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"os"
	"path/filepath"
	"strings"

	"cf/api"
	"cf/manifest"
	"cf/net"
	testconfig "testhelpers/configuration"
	testterm "testhelpers/terminal"
)

var _ = Describe("factory", func() {
	var (
		factory Factory
	)

	BeforeEach(func() {
		fakeUI := &testterm.FakeUI{}
		config := testconfig.NewRepository()
		manifestRepo := manifest.NewManifestDiskRepository()
		repoLocator := api.NewRepositoryLocator(config, map[string]net.Gateway{
			"auth":             net.NewUAAGateway(config),
			"cloud-controller": net.NewCloudControllerGateway(config),
			"uaa":              net.NewUAAGateway(config),
		})

		factory = NewFactory(fakeUI, config, manifestRepo, repoLocator)
	})

	It("provides the metadata for its commands", func() {
		commands := factory.CommandMetadatas()

		err := filepath.Walk("../commands", func(path string, info os.FileInfo, err error) error {
			if strings.HasSuffix(path, "_test.go") || info.IsDir() {
				return nil
			}

			extension := filepath.Ext(info.Name())
			expectedCommandName := strings.Replace(info.Name()[0:len(info.Name())-len(extension)], "_", "-", -1)

			matchingCount := 0
			for _, command := range commands {
				if command.Name == expectedCommandName {
					matchingCount++
				}
			}

			Expect(matchingCount).To(Equal(1))
			return nil
		})

		Expect(err).NotTo(HaveOccurred())
	})
})
<commit_msg>Ignore .test files in factory_test<commit_after>package command_factory_test

import (
	. "cf/command_factory"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"os"
	"path/filepath"
	"strings"

	"cf/api"
	"cf/manifest"
	"cf/net"
	testconfig "testhelpers/configuration"
	testterm "testhelpers/terminal"
)

var _ = Describe("factory", func() {
	var (
		factory Factory
	)

	BeforeEach(func() {
		fakeUI := &testterm.FakeUI{}
		config := testconfig.NewRepository()
		manifestRepo := manifest.NewManifestDiskRepository()
		repoLocator := api.NewRepositoryLocator(config, map[string]net.Gateway{
			"auth":             net.NewUAAGateway(config),
			"cloud-controller": net.NewCloudControllerGateway(config),
			"uaa":              net.NewUAAGateway(config),
		})

		factory = NewFactory(fakeUI, config, manifestRepo, repoLocator)
	})

	It("provides the metadata for its commands", func() {
		commands := factory.CommandMetadatas()

		err := filepath.Walk("../commands", func(path string, info os.FileInfo, err error) error {
			if strings.HasSuffix(path, "_test.go") || info.IsDir() {
				return nil
			}
			if strings.HasSuffix(path, ".test") {
				return nil
			}

			extension := filepath.Ext(info.Name())
			expectedCommandName := strings.Replace(info.Name()[0:len(info.Name())-len(extension)], "_", "-", -1)

			matchingCount := 0
			for _, command := range commands {
				if command.Name == expectedCommandName {
					matchingCount++
				}
			}

			Expect(matchingCount).To(Equal(1), "this command is not tested: "+info.Name())
			return nil
		})

		Expect(err).NotTo(HaveOccurred())
	})
})
<|endoftext|>


<commit_before>// Copyright (c) 2015, Peter Mrekaj. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.txt file.

package honorsclass

import (
	"math/big"
	"math/rand"
	"reflect"
	"sort"
	"testing"

	"github.com/mrekucci/epi/lists"
)

func TestMedianOfSorted(t *testing.T) {
	for _, test := range []struct {
		l    []interface{}
		i    int
		want *big.Rat
		err  error
	}{
		// Test for errors.
		{[]interface{}{"x"}, 0, nil, ErrType},
		{[]interface{}{0, 2, 1, 3}, 0, nil, ErrSort},
		{[]interface{}{0, 1, 2}, -1, nil, ErrNode},

		// Test lists with cycle.
		{[]interface{}{1}, 0, big.NewRat(1, 1), nil},
		{[]interface{}{0, 1}, 0, big.NewRat(1, 2), nil},
		{[]interface{}{0, 1, 2}, 0, big.NewRat(1, 1), nil},
		{[]interface{}{0, 1, 2, 3}, 0, big.NewRat(1+2, 2), nil},
		{[]interface{}{10, 20, 30}, 1, big.NewRat(20, 1), nil},
		{[]interface{}{10, 20, 30, 40}, 2, big.NewRat(20+30, 2), nil},
		{[]interface{}{10, 20, 30, 40, 50}, 4, big.NewRat(30, 1), nil},

		// Test lists without cycle.
		{[]interface{}{}, -1, nil, nil},
		{[]interface{}{0, 1, 2, 3, 4, 5}, -1, big.NewRat(2+3, 2), nil},
		{[]interface{}{10, 20, 30, 40, 50}, -1, big.NewRat(30, 1), nil},
	} {
		l, n := lists.CreateCycle(test.l, test.i)
		if test.err == ErrNode { // Setup an unknown node for an ErrNode test.
			n = &lists.Node{}
		}
		if got, err := MedianOfSorted(l, n); !reflect.DeepEqual(got, test.want) || err != test.err {
			t.Errorf("MedianOfSorted(%v, %v) = %v, %v; want %v, %v", test.l, n, got, err, test.want, test.err)
		}
	}
}

func benchMedianOfSorted(b *testing.B, size int) {
	b.StopTimer()
	if size%2 == 0 { // Decrement size to pickup the median from the middle.
		size--
	}
	for i := 0; i < b.N; i++ {
		ints := rand.New(rand.NewSource(int64(i))).Perm(size)
		sort.Ints(ints)
		var data []interface{}
		for _, n := range ints {
			data = append(data, n)
		}
		l, n := lists.CreateCycle(data, 0)
		b.StartTimer()
		m, err := MedianOfSorted(l, n)
		b.StopTimer()
		if int(m.Num().Int64()) != data[size/2] || err != nil {
			b.Error("MedianOfSorted did not find the median")
		}
	}
}

func BenchmarkMedianOfSorted1e1(b *testing.B) { benchMedianOfSorted(b, 1e1) }
func BenchmarkMedianOfSorted1e2(b *testing.B) { benchMedianOfSorted(b, 1e2) }
func BenchmarkMedianOfSorted1e3(b *testing.B) { benchMedianOfSorted(b, 1e3) }
<commit_msg>Speedup benchmark for honorclass.MedianOfSorted function<commit_after>// Copyright (c) 2015, Peter Mrekaj. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.txt file.

package honorsclass

import (
	"math/big"
	"math/rand"
	"reflect"
	"sort"
	"testing"

	"github.com/mrekucci/epi/lists"
)

func TestMedianOfSorted(t *testing.T) {
	for _, test := range []struct {
		l    []interface{}
		i    int
		want *big.Rat
		err  error
	}{
		// Test for errors.
		{[]interface{}{"x"}, 0, nil, ErrType},
		{[]interface{}{0, 2, 1, 3}, 0, nil, ErrSort},
		{[]interface{}{0, 1, 2}, -1, nil, ErrNode},

		// Test lists with cycle.
		{[]interface{}{1}, 0, big.NewRat(1, 1), nil},
		{[]interface{}{0, 1}, 0, big.NewRat(1, 2), nil},
		{[]interface{}{0, 1, 2}, 0, big.NewRat(1, 1), nil},
		{[]interface{}{0, 1, 2, 3}, 0, big.NewRat(1+2, 2), nil},
		{[]interface{}{10, 20, 30}, 1, big.NewRat(20, 1), nil},
		{[]interface{}{10, 20, 30, 40}, 2, big.NewRat(20+30, 2), nil},
		{[]interface{}{10, 20, 30, 40, 50}, 4, big.NewRat(30, 1), nil},

		// Test lists without cycle.
		{[]interface{}{}, -1, nil, nil},
		{[]interface{}{0, 1, 2, 3, 4, 5}, -1, big.NewRat(2+3, 2), nil},
		{[]interface{}{10, 20, 30, 40, 50}, -1, big.NewRat(30, 1), nil},
	} {
		l, n := lists.CreateCycle(test.l, test.i)
		if test.err == ErrNode { // Setup an unknown node for an ErrNode test.
			n = &lists.Node{}
		}
		if got, err := MedianOfSorted(l, n); !reflect.DeepEqual(got, test.want) || err != test.err {
			t.Errorf("MedianOfSorted(%v, %v) = %v, %v; want %v, %v", test.l, n, got, err, test.want, test.err)
		}
	}
}

func benchMedianOfSorted(b *testing.B, size int) {
	ints := rand.New(rand.NewSource(int64(size))).Perm(size)
	sort.Ints(ints)
	data := make([]interface{}, size)
	for i, n := range ints {
		data[i] = n
	}
	l, n := lists.CreateCycle(data, 0)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		MedianOfSorted(l, n)
	}
}

func BenchmarkMedianOfSorted1e1(b *testing.B) { benchMedianOfSorted(b, 1e1) }
func BenchmarkMedianOfSorted1e2(b *testing.B) { benchMedianOfSorted(b, 1e2) }
func BenchmarkMedianOfSorted1e3(b *testing.B) { benchMedianOfSorted(b, 1e3) }
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"sync"

	"github.com/garyburd/redigo/redis"
)

//NumberOfRecordsPerLBAShard is the fixed length of the LBAShards
const NumberOfRecordsPerLBAShard = 128

//LBAShard is a collection of 128 LBA Records (Hash)
type LBAShard [NumberOfRecordsPerLBAShard]*Hash

//NewLBAShard initializes a new LBAShard an returns a pointer to it
func NewLBAShard() *LBAShard {
	return &LBAShard{}
}

// LBA implements the functionality to lookup block keys through the logical block index.
// The data is persisted to an external metadataserver in shards of 128 keys.
type LBA struct {
	lock        sync.Mutex
	shards      []*LBAShard
	dirtyShards map[int64]*LBAShard

	redisPool *redis.Pool
	volumeID  string
}

//NewLBA creates a new LBA with enough shards to hold the requested numberOfBlocks
func NewLBA(volumeID string, numberOfBlocks uint64, pool *redis.Pool) (lba *LBA) {
	numberOfShards := numberOfBlocks / NumberOfRecordsPerLBAShard
	//If the number of blocks is not aligned on the number of shards, add an extra one
	if (numberOfBlocks % NumberOfRecordsPerLBAShard) != 0 {
		numberOfShards++
	}
	lba = &LBA{
		shards:      make([]*LBAShard, numberOfShards),
		dirtyShards: make(map[int64]*LBAShard),
		redisPool:   pool,
		volumeID:    volumeID,
	}
	return
}

//Set the content hash for a specific block.
// When a key is updated, the shard containing this blockindex is marked as dirty and will be
// stored in the external metadataserver when Flush is called.
func (lba *LBA) Set(blockIndex int64, h *Hash) (err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	//Fetch the appropriate shard
	shardIndex := blockIndex / NumberOfRecordsPerLBAShard
	shard := lba.shards[shardIndex]
	if shard == nil {
		shard, err = lba.getShardFromExternalStorage(shardIndex)
		if err != nil {
			return
		}
		if shard == nil {
			shard = NewLBAShard()
		}
		lba.shards[shardIndex] = shard
	}
	//Update the hash
	(*shard)[blockIndex%NumberOfRecordsPerLBAShard] = h
	//Mark the shard as dirty
	lba.dirtyShards[shardIndex] = shard
	return
}

//Delete the content hash for a specific block.
// When a key is updated, the shard containing this blockindex is marked as dirty and will be
// stored in the external metadaserver when Flush is called
// Deleting means actually that the nilhash will be set for this blockindex.
func (lba *LBA) Delete(blockIndex int64) (err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	//Fetch the appropriate shard
	shardIndex := blockIndex / NumberOfRecordsPerLBAShard
	shard := lba.shards[shardIndex]
	if shard == nil {
		shard, err = lba.getShardFromExternalStorage(shardIndex)
		if err != nil {
			return
		}
		if shard == nil {
			shard = NewLBAShard()
		}
		lba.shards[shardIndex] = shard
	}
	//Update the hash
	(*shard)[blockIndex%NumberOfRecordsPerLBAShard] = nil
	//Mark the shard as dirty
	lba.dirtyShards[shardIndex] = shard
	return
}

//Get returns the hash for a block, nil if no hash registered
// If the shard containing this blockindex is not present, it is fetched from the external metadaserver
func (lba *LBA) Get(blockIndex int64) (h *Hash, err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	shardIndex := blockIndex / NumberOfRecordsPerLBAShard
	shard := lba.shards[shardIndex]
	if shard == nil {
		shard, err = lba.getShardFromExternalStorage(shardIndex)
		if err != nil {
			return
		}
		lba.shards[shardIndex] = shard
	}
	if shard != nil {
		h = (*shard)[blockIndex%NumberOfRecordsPerLBAShard]
	}
	return
}

//Flush stores all dirty shards to the external metadaserver
func (lba *LBA) Flush() (err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	err = lba.storeShardsInExternalStorage(lba.dirtyShards)

	lba.dirtyShards = make(map[int64]*LBAShard)

	return
}

func (lba *LBA) createShardKey(shardIndex int64) string {
	return fmt.Sprintf("%s:%d", lba.volumeID, shardIndex)
}

func (lba *LBA) storeShardsInExternalStorage(shards map[int64]*LBAShard) (err error) {
	conn := lba.redisPool.Get()
	defer conn.Close()

	var key string
	var h *Hash
	var nilHash Hash
	var buffer bytes.Buffer

	// Start Pipe, so that all operations are piped
	if err = conn.Send("MULTI"); err != nil {
		return
	}

	// Collect all sets in output buffer of Redis
	for shardIndex, shard := range shards {
		buffer.Reset()

		key = lba.createShardKey(shardIndex)
		for _, h = range *shard {
			if h == nil {
				if _, err = buffer.Write(nilHash[:]); err != nil {
					return
				}
			} else {
				if _, err = buffer.Write(h[:]); err != nil {
					return
				}
			}
		}

		if err = conn.Send("SET", key, buffer.Bytes()); err != nil {
			return
		}
	}

	// Write all sets in output buffer to Redis at once
	_, err = conn.Do("EXEC")
	return
}

func (lba *LBA) getShardFromExternalStorage(shardIndex int64) (shard *LBAShard, err error) {
	key := lba.createShardKey(shardIndex)

	conn := lba.redisPool.Get()
	defer conn.Close()
	reply, err := conn.Do("GET", key)
	if err != nil || reply == nil {
		return
	}

	shardBytes, err := redis.Bytes(reply, err)
	if err != nil {
		return
	}
	shard = &LBAShard{}
	for i := 0; i < NumberOfRecordsPerLBAShard; i++ {
		var h Hash
		copy(h[:], shardBytes[i*HashSize:])
		(*shard)[i] = &h
	}
	return
}
<commit_msg>Don’t clean the dirtyshards list if an error occurred during flushing<commit_after>package main

import (
	"bytes"
	"fmt"
	"sync"

	"github.com/garyburd/redigo/redis"
)

//NumberOfRecordsPerLBAShard is the fixed length of the LBAShards
const NumberOfRecordsPerLBAShard = 128

//LBAShard is a collection of 128 LBA Records (Hash)
type LBAShard [NumberOfRecordsPerLBAShard]*Hash

//NewLBAShard initializes a new LBAShard an returns a pointer to it
func NewLBAShard() *LBAShard {
	return &LBAShard{}
}

// LBA implements the functionality to lookup block keys through the logical block index.
// The data is persisted to an external metadataserver in shards of 128 keys.
type LBA struct {
	lock        sync.Mutex
	shards      []*LBAShard
	dirtyShards map[int64]*LBAShard

	redisPool *redis.Pool
	volumeID  string
}

//NewLBA creates a new LBA with enough shards to hold the requested numberOfBlocks
func NewLBA(volumeID string, numberOfBlocks uint64, pool *redis.Pool) (lba *LBA) {
	numberOfShards := numberOfBlocks / NumberOfRecordsPerLBAShard
	//If the number of blocks is not aligned on the number of shards, add an extra one
	if (numberOfBlocks % NumberOfRecordsPerLBAShard) != 0 {
		numberOfShards++
	}
	lba = &LBA{
		shards:      make([]*LBAShard, numberOfShards),
		dirtyShards: make(map[int64]*LBAShard),
		redisPool:   pool,
		volumeID:    volumeID,
	}
	return
}

//Set the content hash for a specific block.
// When a key is updated, the shard containing this blockindex is marked as dirty and will be
// stored in the external metadataserver when Flush is called.
func (lba *LBA) Set(blockIndex int64, h *Hash) (err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	//Fetch the appropriate shard
	shardIndex := blockIndex / NumberOfRecordsPerLBAShard
	shard := lba.shards[shardIndex]
	if shard == nil {
		shard, err = lba.getShardFromExternalStorage(shardIndex)
		if err != nil {
			return
		}
		if shard == nil {
			shard = NewLBAShard()
		}
		lba.shards[shardIndex] = shard
	}
	//Update the hash
	(*shard)[blockIndex%NumberOfRecordsPerLBAShard] = h
	//Mark the shard as dirty
	lba.dirtyShards[shardIndex] = shard
	return
}

//Delete the content hash for a specific block.
// When a key is updated, the shard containing this blockindex is marked as dirty and will be
// stored in the external metadaserver when Flush is called
// Deleting means actually that the nilhash will be set for this blockindex.
func (lba *LBA) Delete(blockIndex int64) (err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	//Fetch the appropriate shard
	shardIndex := blockIndex / NumberOfRecordsPerLBAShard
	shard := lba.shards[shardIndex]
	if shard == nil {
		shard, err = lba.getShardFromExternalStorage(shardIndex)
		if err != nil {
			return
		}
		if shard == nil {
			shard = NewLBAShard()
		}
		lba.shards[shardIndex] = shard
	}
	//Update the hash
	(*shard)[blockIndex%NumberOfRecordsPerLBAShard] = nil
	//Mark the shard as dirty
	lba.dirtyShards[shardIndex] = shard
	return
}

//Get returns the hash for a block, nil if no hash registered
// If the shard containing this blockindex is not present, it is fetched from the external metadaserver
func (lba *LBA) Get(blockIndex int64) (h *Hash, err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	shardIndex := blockIndex / NumberOfRecordsPerLBAShard
	shard := lba.shards[shardIndex]
	if shard == nil {
		shard, err = lba.getShardFromExternalStorage(shardIndex)
		if err != nil {
			return
		}
		lba.shards[shardIndex] = shard
	}
	if shard != nil {
		h = (*shard)[blockIndex%NumberOfRecordsPerLBAShard]
	}
	return
}

//Flush stores all dirty shards to the external metadaserver
func (lba *LBA) Flush() (err error) {
	//TODO: let's see if we really need to lock on such a high level
	lba.lock.Lock()
	defer lba.lock.Unlock()

	if err = lba.storeShardsInExternalStorage(lba.dirtyShards); err != nil {
		return
	}

	lba.dirtyShards = make(map[int64]*LBAShard)

	return
}

func (lba *LBA) createShardKey(shardIndex int64) string {
	return fmt.Sprintf("%s:%d", lba.volumeID, shardIndex)
}

func (lba *LBA) storeShardsInExternalStorage(shards map[int64]*LBAShard) (err error) {
	conn := lba.redisPool.Get()
	defer conn.Close()

	var key string
	var h *Hash
	var nilHash Hash
	var buffer bytes.Buffer

	// Start Pipe, so that all operations are piped
	if err = conn.Send("MULTI"); err != nil {
		return
	}

	// Collect all sets in output buffer of Redis
	for shardIndex, shard := range shards {
		buffer.Reset()

		key = lba.createShardKey(shardIndex)
		for _, h = range *shard {
			if h == nil {
				if _, err = buffer.Write(nilHash[:]); err != nil {
					return
				}
			} else {
				if _, err = buffer.Write(h[:]); err != nil {
					return
				}
			}
		}

		if err = conn.Send("SET", key, buffer.Bytes()); err != nil {
			return
		}
	}

	// Write all sets in output buffer to Redis at once
	_, err = conn.Do("EXEC")
	return
}

func (lba *LBA) getShardFromExternalStorage(shardIndex int64) (shard *LBAShard, err error) {
	key := lba.createShardKey(shardIndex)

	conn := lba.redisPool.Get()
	defer conn.Close()
	reply, err := conn.Do("GET", key)
	if err != nil || reply == nil {
		return
	}

	shardBytes, err := redis.Bytes(reply, err)
	if err != nil {
		return
	}
	shard = &LBAShard{}
	for i := 0; i < NumberOfRecordsPerLBAShard; i++ {
		var h Hash
		copy(h[:], shardBytes[i*HashSize:])
		(*shard)[i] = &h
	}
	return
}
<|endoftext|>


<commit_before><commit_msg>apiextensions: fix conversion tests to enable pruning<commit_after><|endoftext|>


<commit_before>// Package quickfix provides functions for fixing Go ASTs
// that are well typed but "go build" refuses to build.
package quickfix

import (
	"fmt"
	"regexp"
	"strings"

	"go/ast"
	"go/token"
	"go/types"

	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/packages"
)

var (
	declaredNotUsed        = regexp.MustCompile(`^([a-zA-Z0-9_]+) declared but not used$`)
	importedNotUsed        = regexp.MustCompile(`^(".+") imported but not used`)
	noNewVariablesOnDefine = "no new variables on left side of :="
)

// Config for quickfix.
type Config struct {
	Fset     *token.FileSet
	Files    []*ast.File
	Dir      string
	TypeInfo *types.Info
	MaxTries int
}

// QuickFix a file set.
func QuickFix(fset *token.FileSet, files []*ast.File) error {
	config := Config{
		Fset:     fset,
		Files:    files,
		MaxTries: 10,
	}
	return config.QuickFix()
}

// QuickFix rewrites AST files of same package so that they pass go build.
// For example:
//   v declared but not used             -> append `_ = v`
//   "p" imported but not used           -> rewrite to `import _ "p"`
//   no new variables on left side of := -> rewrite `:=` to `=`
//
// TODO implement hardMode, which removes errorneous code rather than adding
func (c Config) QuickFix() (err error) {
	maxTries := 10
	if c.MaxTries > 0 {
		maxTries = c.MaxTries
	}
	for i := 0; i < maxTries; i++ {
		var foundError bool
		foundError, err = c.QuickFixOnce()
		if !foundError {
			return nil
		}
	}

	return
}

type tracedVisitor struct {
	path  []ast.Node
	visit func(ast.Node, []ast.Node) bool
}

func (v tracedVisitor) Visit(node ast.Node) ast.Visitor {
	if v.visit(node, v.path) {
		return tracedVisitor{
			path:  append([]ast.Node{node}, v.path...),
			visit: v.visit,
		}
	}

	return nil
}

func traverseAST(node ast.Node, visit func(ast.Node, []ast.Node) bool) {
	v := tracedVisitor{
		visit: visit,
	}
	ast.Walk(v, node)
}

// pkgsWithSideEffect are set of packages which are known to provide APIs by
// blank identifier import (import _ "p").
var pkgsWithSideEffect = map[string]bool{}

func init() {
	for _, path := range []string{
		"expvar",
		"image/gif",
		"image/jpeg",
		"image/png",
		"net/http/pprof",
		"unsafe",
		"golang.org/x/image/bmp",
		"golang.org/x/image/tiff",
		"golang.org/x/image/vp8",
		"golang.org/x/image/vp81",
		"golang.org/x/image/webp",
		"golang.org/x/tools/go/gcimporter",
	} {
		pkgsWithSideEffect[`"`+path+`"`] = true
	}
}

// RevertQuickFix a file set.
func RevertQuickFix(fset *token.FileSet, files []*ast.File) error {
	config := Config{
		Fset:     fset,
		Files:    files,
		MaxTries: 10,
	}
	return config.RevertQuickFix()
}

// RevertQuickFix reverts possible quickfixes introduced by QuickFix.
// This may result to non-buildable source, and cannot reproduce the original
// code before prior QuickFix.
// For example:
//   `_ = v`        -> removed
//   `import _ "p"` -> rewritten to `import "p"`
func (c Config) RevertQuickFix() (err error) {
	fset := c.Fset
	files := c.Files

	nodeToRemove := map[ast.Node]bool{}

	for _, f := range files {
		ast.Inspect(f, func(node ast.Node) bool {
			if assign, ok := node.(*ast.AssignStmt); ok {
				if len(assign.Lhs) == 1 && isBlankIdent(assign.Lhs[0]) &&
					len(assign.Rhs) == 1 && isIdent(assign.Rhs[0]) {
					// The statement is `_ = v`
					nodeToRemove[node] = true
				}

				return false
			} else if imp, ok := node.(*ast.ImportSpec); ok {
				if isBlankIdent(imp.Name) && !pkgsWithSideEffect[imp.Path.Value] {
					// The spec is `import _ "p"` and p is not a package that
					// provides "side effects"
					imp.Name = nil
				}

				return false
			}

			return true
		})

		for len(nodeToRemove) > 0 {
			traverseAST(f, func(node ast.Node, nodepath []ast.Node) bool {
				if nodeToRemove[node] {
					parent := nodepath[0]
					if removeChildNode(node, parent) == false {
						err = fmt.Errorf(
							"BUG: could not remove node: %s (in: %s)",
							fset.Position(node.Pos()),
							fset.Position(parent.Pos()),
						)
					}
					delete(nodeToRemove, node)
					return false
				}

				return true
			})
		}
	}

	return
}

type pkgsImporter struct {
	dir string
}

func (i pkgsImporter) Import(path string) (*types.Package, error) {
	pkgs, err := packages.Load(&packages.Config{
		Mode: packages.NeedTypes,
		Dir:  i.dir,
	}, path)
	if err != nil {
		return nil, err
	}

	if len(pkgs) == 0 {
		return nil, fmt.Errorf("path %s not found", path)
	}

	return pkgs[0].Types, nil
}

// QuickFixOnce apply the fixes once.
func (c Config) QuickFixOnce() (bool, error) {
	fset := c.Fset
	files := c.Files

	errs := []error{}
	config := &types.Config{
		Error: func(err error) {
			errs = append(errs, err)
		},
		Importer: pkgsImporter{dir: c.Dir},
	}

	_, err := config.Check("_quickfix", fset, files, c.TypeInfo)
	if err == nil {
		return false, nil
	}

	// apply fixes on AST later so that we won't break funcs that inspect AST by positions
	fixes := map[error]func() bool{}
	unhandled := ErrorList{}

	foundError := len(errs) > 0

	for _, err := range errs {
		err, ok := err.(types.Error)
		if !ok {
			unhandled = append(unhandled, err)
			continue
		}

		f := findFile(c.Files, err.Pos)
		if f == nil {
			e := ErrCouldNotLocate{
				Err:  err,
				Fset: fset,
			}
			unhandled = append(unhandled, e)
			continue
		}

		nodepath, _ := astutil.PathEnclosingInterval(f, err.Pos, err.Pos)

		var fix func() bool

		// - "%s declared but not used"
		// - "%q imported but not used" (+ " as %s")
		// - "label %s declared but not used" TODO
		// - "no new variables on left side of :="
		if m := declaredNotUsed.FindStringSubmatch(err.Msg); m != nil {
			identName := m[1]
			fix = func() bool {
				return fixDeclaredNotUsed(nodepath, identName)
			}
		} else if m := importedNotUsed.FindStringSubmatch(err.Msg); m != nil {
			pkgPath := m[1] // quoted string, but it's okay because this will be compared to ast.BasicLit.Value.
			fix = func() bool {
				return fixImportedNotUsed(nodepath, pkgPath)
			}
		} else if err.Msg == noNewVariablesOnDefine {
			fix = func() bool {
				return fixNoNewVariables(nodepath)
			}
		} else {
			unhandled = append(unhandled, err)
		}

		if fix != nil {
			fixes[err] = fix
		}
	}

	for err, fix := range fixes {
		if fix() == false {
			unhandled = append(unhandled, err)
		}
	}

	return foundError, unhandled.any()
}

func fixDeclaredNotUsed(nodepath []ast.Node, identName string) bool {
	// insert "_ = x" to supress "declared but not used" error
	stmt := &ast.AssignStmt{
		Lhs: []ast.Expr{ast.NewIdent("_")},
		Tok: token.ASSIGN,
		Rhs: []ast.Expr{ast.NewIdent(identName)},
	}
	return appendStmt(nodepath, stmt)
}

func fixImportedNotUsed(nodepath []ast.Node, pkgPath string) bool {
	for _, node := range nodepath {
		if f, ok := node.(*ast.File); ok {
			for _, imp := range f.Imports {
				if imp.Path.Value == pkgPath {
					// make this import spec anonymous one
					imp.Name = ast.NewIdent("_")
					return true
				}
			}
		}
	}
	return false
}

func fixNoNewVariables(nodepath []ast.Node) bool {
	for _, node := range nodepath {
		switch node := node.(type) {
		case *ast.AssignStmt:
			if node.Tok == token.DEFINE {
				node.Tok = token.ASSIGN
				return true
			}

		case *ast.RangeStmt:
			if node.Tok == token.DEFINE {
				node.Tok = token.ASSIGN
				return true
			}
		}
	}
	return false
}

// ErrorList represents a collection of errors.
type ErrorList []error

func (errs ErrorList) any() error {
	if len(errs) == 0 {
		return nil
	}

	return errs
}

func (errs ErrorList) Error() string {
	s := []string{fmt.Sprintf("%d error(s):", len(errs))}
	for _, e := range errs {
		s = append(s, fmt.Sprintf("- %s", e))
	}
	return strings.Join(s, "\n")
}

func appendStmt(nodepath []ast.Node, stmt ast.Stmt) bool {
	for _, node := range nodepath {
		switch node := node.(type) {
		case *ast.BlockStmt:
			if node.List == nil {
				node.List = []ast.Stmt{}
			}
			node.List = append(node.List, stmt)

		case *ast.CaseClause:
			if node.Body == nil {
				node.Body = []ast.Stmt{}
			}
			node.Body = append(node.Body, stmt)

		case *ast.CommClause:
			if node.Body == nil {
				node.Body = []ast.Stmt{}
			}
			node.Body = append(node.Body, stmt)

		case *ast.RangeStmt:
			if node.Body == nil {
				node.Body = &ast.BlockStmt{}
			}
			if node.Body.List == nil {
				node.Body.List = []ast.Stmt{}
			}
			node.Body.List = append(node.Body.List, stmt)

		default:
			continue
		}

		return true
	}

	return false
}

func removeChildNode(child, parent ast.Node) bool {
	switch parent := parent.(type) {
	case *ast.BlockStmt:
		removeFromStmtList(child, parent.List)
		return true
	case *ast.CaseClause:
		removeFromStmtList(child, parent.Body)
		return true
	case *ast.CommClause:
		removeFromStmtList(child, parent.Body)
		return true
	case *ast.RangeStmt:
		removeFromStmtList(child, parent.Body.List)
		return true
	}

	return false
}

// removeFromStmtList remove node from slice of statements list. This function
// modifies list in-place and pads rest of the slice with ast.EmptyStmt.
func removeFromStmtList(node ast.Node, list []ast.Stmt) bool {
	for i, s := range list {
		if s == node {
			for ; i < len(list)-1; i++ {
				list[i] = list[i+1]
			}
			list[len(list)-1] = &ast.EmptyStmt{}
			return true
		}
	}

	return false
}

func findFile(files []*ast.File, pos token.Pos) *ast.File {
	for _, f := range files {
		if f.Pos() <= pos && pos < f.End() {
			return f
		}
	}

	return nil
}

func isIdent(node ast.Node) bool {
	if node == nil {
		return false
	}

	_, ok := node.(*ast.Ident)
	return ok
}

func isBlankIdent(node ast.Node) bool {
	if node == nil {
		return false
	}

	ident, ok := node.(*ast.Ident)
	return ok && ident != nil && ident.Name == "_"
}

// ErrCouldNotLocate represents a file not found error.
type ErrCouldNotLocate struct {
	Err  types.Error
	Fset *token.FileSet
}

func (e ErrCouldNotLocate) Error() string {
	return fmt.Sprintf("cannot find file for error %q: %s (%d)", e.Err.Error(), e.Fset.Position(e.Err.Pos), e.Err.Pos)
}
<commit_msg>require packages.NeedDeps<commit_after>// Package quickfix provides functions for fixing Go ASTs
// that are well typed but "go build" refuses to build.
package quickfix

import (
	"fmt"
	"regexp"
	"strings"

	"go/ast"
	"go/token"
	"go/types"

	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/packages"
)

var (
	declaredNotUsed        = regexp.MustCompile(`^([a-zA-Z0-9_]+) declared but not used$`)
	importedNotUsed        = regexp.MustCompile(`^(".+") imported but not used`)
	noNewVariablesOnDefine = "no new variables on left side of :="
)

// Config for quickfix.
type Config struct {
	Fset     *token.FileSet
	Files    []*ast.File
	Dir      string
	TypeInfo *types.Info
	MaxTries int
}

// QuickFix a file set.
func QuickFix(fset *token.FileSet, files []*ast.File) error {
	config := Config{
		Fset:     fset,
		Files:    files,
		MaxTries: 10,
	}
	return config.QuickFix()
}

// QuickFix rewrites AST files of same package so that they pass go build.
// For example:
//   v declared but not used             -> append `_ = v`
//   "p" imported but not used           -> rewrite to `import _ "p"`
//   no new variables on left side of := -> rewrite `:=` to `=`
//
// TODO implement hardMode, which removes errorneous code rather than adding
func (c Config) QuickFix() (err error) {
	maxTries := 10
	if c.MaxTries > 0 {
		maxTries = c.MaxTries
	}
	for i := 0; i < maxTries; i++ {
		var foundError bool
		foundError, err = c.QuickFixOnce()
		if !foundError {
			return nil
		}
	}

	return
}

type tracedVisitor struct {
	path  []ast.Node
	visit func(ast.Node, []ast.Node) bool
}

func (v tracedVisitor) Visit(node ast.Node) ast.Visitor {
	if v.visit(node, v.path) {
		return tracedVisitor{
			path:  append([]ast.Node{node}, v.path...),
			visit: v.visit,
		}
	}

	return nil
}

func traverseAST(node ast.Node, visit func(ast.Node, []ast.Node) bool) {
	v := tracedVisitor{
		visit: visit,
	}
	ast.Walk(v, node)
}

// pkgsWithSideEffect are set of packages which are known to provide APIs by
// blank identifier import (import _ "p").
var pkgsWithSideEffect = map[string]bool{}

func init() {
	for _, path := range []string{
		"expvar",
		"image/gif",
		"image/jpeg",
		"image/png",
		"net/http/pprof",
		"unsafe",
		"golang.org/x/image/bmp",
		"golang.org/x/image/tiff",
		"golang.org/x/image/vp8",
		"golang.org/x/image/vp81",
		"golang.org/x/image/webp",
		"golang.org/x/tools/go/gcimporter",
	} {
		pkgsWithSideEffect[`"`+path+`"`] = true
	}
}

// RevertQuickFix a file set.
func RevertQuickFix(fset *token.FileSet, files []*ast.File) error {
	config := Config{
		Fset:     fset,
		Files:    files,
		MaxTries: 10,
	}
	return config.RevertQuickFix()
}

// RevertQuickFix reverts possible quickfixes introduced by QuickFix.
// This may result to non-buildable source, and cannot reproduce the original
// code before prior QuickFix.
// For example:
//   `_ = v`        -> removed
//   `import _ "p"` -> rewritten to `import "p"`
func (c Config) RevertQuickFix() (err error) {
	fset := c.Fset
	files := c.Files

	nodeToRemove := map[ast.Node]bool{}

	for _, f := range files {
		ast.Inspect(f, func(node ast.Node) bool {
			if assign, ok := node.(*ast.AssignStmt); ok {
				if len(assign.Lhs) == 1 && isBlankIdent(assign.Lhs[0]) &&
					len(assign.Rhs) == 1 && isIdent(assign.Rhs[0]) {
					// The statement is `_ = v`
					nodeToRemove[node] = true
				}

				return false
			} else if imp, ok := node.(*ast.ImportSpec); ok {
				if isBlankIdent(imp.Name) && !pkgsWithSideEffect[imp.Path.Value] {
					// The spec is `import _ "p"` and p is not a package that
					// provides "side effects"
					imp.Name = nil
				}

				return false
			}

			return true
		})

		for len(nodeToRemove) > 0 {
			traverseAST(f, func(node ast.Node, nodepath []ast.Node) bool {
				if nodeToRemove[node] {
					parent := nodepath[0]
					if removeChildNode(node, parent) == false {
						err = fmt.Errorf(
							"BUG: could not remove node: %s (in: %s)",
							fset.Position(node.Pos()),
							fset.Position(parent.Pos()),
						)
					}
					delete(nodeToRemove, node)
					return false
				}

				return true
			})
		}
	}

	return
}

type pkgsImporter struct {
	dir string
}

func (i pkgsImporter) Import(path string) (*types.Package, error) {
	pkgs, err := packages.Load(&packages.Config{
		Mode: packages.NeedTypes | packages.NeedDeps,
		Dir:  i.dir,
	}, path)
	if err != nil {
		return nil, err
	}

	if len(pkgs) == 0 {
		return nil, fmt.Errorf("path %s not found", path)
	}

	return pkgs[0].Types, nil
}

// QuickFixOnce apply the fixes once.
func (c Config) QuickFixOnce() (bool, error) {
	fset := c.Fset
	files := c.Files

	errs := []error{}
	config := &types.Config{
		Error: func(err error) {
			errs = append(errs, err)
		},
		Importer: pkgsImporter{dir: c.Dir},
	}

	_, err := config.Check("_quickfix", fset, files, c.TypeInfo)
	if err == nil {
		return false, nil
	}

	// apply fixes on AST later so that we won't break funcs that inspect AST by positions
	fixes := map[error]func() bool{}
	unhandled := ErrorList{}

	foundError := len(errs) > 0

	for _, err := range errs {
		err, ok := err.(types.Error)
		if !ok {
			unhandled = append(unhandled, err)
			continue
		}

		f := findFile(c.Files, err.Pos)
		if f == nil {
			e := ErrCouldNotLocate{
				Err:  err,
				Fset: fset,
			}
			unhandled = append(unhandled, e)
			continue
		}

		nodepath, _ := astutil.PathEnclosingInterval(f, err.Pos, err.Pos)

		var fix func() bool

		// - "%s declared but not used"
		// - "%q imported but not used" (+ " as %s")
		// - "label %s declared but not used" TODO
		// - "no new variables on left side of :="
		if m := declaredNotUsed.FindStringSubmatch(err.Msg); m != nil {
			identName := m[1]
			fix = func() bool {
				return fixDeclaredNotUsed(nodepath, identName)
			}
		} else if m := importedNotUsed.FindStringSubmatch(err.Msg); m != nil {
			pkgPath := m[1] // quoted string, but it's okay because this will be compared to ast.BasicLit.Value.
			fix = func() bool {
				return fixImportedNotUsed(nodepath, pkgPath)
			}
		} else if err.Msg == noNewVariablesOnDefine {
			fix = func() bool {
				return fixNoNewVariables(nodepath)
			}
		} else {
			unhandled = append(unhandled, err)
		}

		if fix != nil {
			fixes[err] = fix
		}
	}

	for err, fix := range fixes {
		if fix() == false {
			unhandled = append(unhandled, err)
		}
	}

	return foundError, unhandled.any()
}

func fixDeclaredNotUsed(nodepath []ast.Node, identName string) bool {
	// insert "_ = x" to supress "declared but not used" error
	stmt := &ast.AssignStmt{
		Lhs: []ast.Expr{ast.NewIdent("_")},
		Tok: token.ASSIGN,
		Rhs: []ast.Expr{ast.NewIdent(identName)},
	}
	return appendStmt(nodepath, stmt)
}

func fixImportedNotUsed(nodepath []ast.Node, pkgPath string) bool {
	for _, node := range nodepath {
		if f, ok := node.(*ast.File); ok {
			for _, imp := range f.Imports {
				if imp.Path.Value == pkgPath {
					// make this import spec anonymous one
					imp.Name = ast.NewIdent("_")
					return true
				}
			}
		}
	}
	return false
}

func fixNoNewVariables(nodepath []ast.Node) bool {
	for _, node := range nodepath {
		switch node := node.(type) {
		case *ast.AssignStmt:
			if node.Tok == token.DEFINE {
				node.Tok = token.ASSIGN
				return true
			}

		case *ast.RangeStmt:
			if node.Tok == token.DEFINE {
				node.Tok = token.ASSIGN
				return true
			}
		}
	}
	return false
}

// ErrorList represents a collection of errors.
type ErrorList []error

func (errs ErrorList) any() error {
	if len(errs) == 0 {
		return nil
	}

	return errs
}

func (errs ErrorList) Error() string {
	s := []string{fmt.Sprintf("%d error(s):", len(errs))}
	for _, e := range errs {
		s = append(s, fmt.Sprintf("- %s", e))
	}
	return strings.Join(s, "\n")
}

func appendStmt(nodepath []ast.Node, stmt ast.Stmt) bool {
	for _, node := range nodepath {
		switch node := node.(type) {
		case *ast.BlockStmt:
			if node.List == nil {
				node.List = []ast.Stmt{}
			}
			node.List = append(node.List, stmt)

		case *ast.CaseClause:
			if node.Body == nil {
				node.Body = []ast.Stmt{}
			}
			node.Body = append(node.Body, stmt)

		case *ast.CommClause:
			if node.Body == nil {
				node.Body = []ast.Stmt{}
			}
			node.Body = append(node.Body, stmt)

		case *ast.RangeStmt:
			if node.Body == nil {
				node.Body = &ast.BlockStmt{}
			}
			if node.Body.List == nil {
				node.Body.List = []ast.Stmt{}
			}
			node.Body.List = append(node.Body.List, stmt)

		default:
			continue
		}

		return true
	}

	return false
}

func removeChildNode(child, parent ast.Node) bool {
	switch parent := parent.(type) {
	case *ast.BlockStmt:
		removeFromStmtList(child, parent.List)
		return true
	case *ast.CaseClause:
		removeFromStmtList(child, parent.Body)
		return true
	case *ast.CommClause:
		removeFromStmtList(child, parent.Body)
		return true
	case *ast.RangeStmt:
		removeFromStmtList(child, parent.Body.List)
		return true
	}

	return false
}

// removeFromStmtList remove node from slice of statements list. This function
// modifies list in-place and pads rest of the slice with ast.EmptyStmt.
func removeFromStmtList(node ast.Node, list []ast.Stmt) bool {
	for i, s := range list {
		if s == node {
			for ; i < len(list)-1; i++ {
				list[i] = list[i+1]
			}
			list[len(list)-1] = &ast.EmptyStmt{}
			return true
		}
	}

	return false
}

func findFile(files []*ast.File, pos token.Pos) *ast.File {
	for _, f := range files {
		if f.Pos() <= pos && pos < f.End() {
			return f
		}
	}

	return nil
}

func isIdent(node ast.Node) bool {
	if node == nil {
		return false
	}

	_, ok := node.(*ast.Ident)
	return ok
}

func isBlankIdent(node ast.Node) bool {
	if node == nil {
		return false
	}

	ident, ok := node.(*ast.Ident)
	return ok && ident != nil && ident.Name == "_"
}

// ErrCouldNotLocate represents a file not found error.
type ErrCouldNotLocate struct {
	Err  types.Error
	Fset *token.FileSet
}

func (e ErrCouldNotLocate) Error() string {
	return fmt.Sprintf("cannot find file for error %q: %s (%d)", e.Err.Error(), e.Fset.Position(e.Err.Pos), e.Err.Pos)
}
<|endoftext|>


<commit_before>package r

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"

	"github.com/gorilla/mux"
	"github.com/pkg/errors"
)

type Server struct {
	keys     chan string
	sessions chan *Session
}

var cache map[string]string
var cachingEnabled bool
var cacheMutex *sync.Mutex

func (s *Server) Call(pkg, fun, args string) (string, error) {
	session := <-s.sessions
	res, err := session.Call(pkg, fun, args)
	if err != nil {
		var new_err error
		session.cmd.Process.Kill()
		session, new_err = NewSession(session.id)
		if new_err != nil {
			return "", errors.Wrap(err, "Could not start new R Session")
		}
	}
	s.sessions <- session
	return res, err
}

func (s Server) Get(key, format string) ([]byte, error) {
	session := <-s.sessions
	res, err := session.Get(key, format)
	if err != nil {
		var new_err error
		session.cmd.Process.Kill()
		session, new_err = NewSession(session.id)
		if new_err != nil {
			return []byte{}, errors.Wrap(err, "Could not start new R Session")
		}
	}
	s.sessions <- session
	return res, err
}

func (s *Server) CallHandler(w http.ResponseWriter, r *http.Request) {

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Println("Call read body failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	call := Call{}
	err = json.Unmarshal(body, &call)
	if err != nil {
		fmt.Println("Call unmarshal failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	log("Call:", call.Package, call.Function, call.Arguments)

	if cachingEnabled {
		key := call.cacheKey()
		res := cache[key]
		if res != "" {
			log("Cache hit")
			w.Write([]byte(res))
			return
		}
		log("Cache miss")
	} else {
		log("No cache")
	}

	res, err := s.Call(call.Package, call.Function, call.Arguments)
	if err != nil {
		http.Error(w, "Call failed: "+err.Error(), 500)
		return
	}
	w.Write([]byte(res))
	if cachingEnabled {
		key := call.cacheKey()
		cacheMutex.Lock()
		cache[key] = res
		cacheMutex.Unlock()
	}

	return
}

func (s *Server) GetHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	key := vars["key"]
	format := vars["format"]

	log("Get", key, format)

	res, err := s.Get(key, format)
	if err != nil {
		fmt.Println("Get failed", err)
		http.Error(w, "Get failed."+err.Error(), 500)
		return
	}
	w.Write(res)
}

func (s *Server) EnableCaching() {
	cachingEnabled = true
	cache = make(map[string]string, 0)
	cacheMutex = &sync.Mutex{}
}

func (s *Server) Start(port string) error {

	router := mux.NewRouter()
	router.HandleFunc("/call", s.CallHandler)
	router.HandleFunc("/get/{key}/{format}", s.GetHandler)
	http.Handle("/", router)

	return http.ListenAndServe(port, router)

}
<commit_msg>bugfix: acquire mutex to read cache<commit_after>package r

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"

	"github.com/gorilla/mux"
	"github.com/pkg/errors"
)

type Server struct {
	keys     chan string
	sessions chan *Session
}

var cache map[string]string
var cachingEnabled bool
var cacheMutex *sync.Mutex

func (s *Server) Call(pkg, fun, args string) (string, error) {
	session := <-s.sessions
	res, err := session.Call(pkg, fun, args)
	if err != nil {
		var new_err error
		session.cmd.Process.Kill()
		session, new_err = NewSession(session.id)
		if new_err != nil {
			return "", errors.Wrap(err, "Could not start new R Session")
		}
	}
	s.sessions <- session
	return res, err
}

func (s Server) Get(key, format string) ([]byte, error) {
	session := <-s.sessions
	res, err := session.Get(key, format)
	if err != nil {
		var new_err error
		session.cmd.Process.Kill()
		session, new_err = NewSession(session.id)
		if new_err != nil {
			return []byte{}, errors.Wrap(err, "Could not start new R Session")
		}
	}
	s.sessions <- session
	return res, err
}

func (s *Server) CallHandler(w http.ResponseWriter, r *http.Request) {

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Println("Call read body failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	call := Call{}
	err = json.Unmarshal(body, &call)
	if err != nil {
		fmt.Println("Call unmarshal failed", err)
		http.Error(w, "Call failed."+err.Error(), 500)
		return
	}

	log("Call:", call.Package, call.Function, call.Arguments)

	if cachingEnabled {
		key := call.cacheKey()
		cacheMutex.Lock()
		res := cache[key]
		cacheMutex.Unlock()

		if res != "" {
			log("Cache hit")
			w.Write([]byte(res))
			return
		}
		log("Cache miss")
	} else {
		log("No cache")
	}

	res, err := s.Call(call.Package, call.Function, call.Arguments)
	if err != nil {
		http.Error(w, "Call failed: "+err.Error(), 500)
		return
	}
	w.Write([]byte(res))
	if cachingEnabled {
		key := call.cacheKey()
		cacheMutex.Lock()
		cache[key] = res
		cacheMutex.Unlock()
	}

	return
}

func (s *Server) GetHandler(w http.ResponseWriter, r *http.Request) {

	vars := mux.Vars(r)
	key := vars["key"]
	format := vars["format"]

	log("Get", key, format)

	res, err := s.Get(key, format)
	if err != nil {
		fmt.Println("Get failed", err)
		http.Error(w, "Get failed."+err.Error(), 500)
		return
	}
	w.Write(res)
}

func (s *Server) EnableCaching() {
	cachingEnabled = true
	cache = make(map[string]string, 0)
	cacheMutex = &sync.Mutex{}
}

func (s *Server) Start(port string) error {

	router := mux.NewRouter()
	router.HandleFunc("/call", s.CallHandler)
	router.HandleFunc("/get/{key}/{format}", s.GetHandler)
	http.Handle("/", router)

	return http.ListenAndServe(port, router)

}
<|endoftext|>


<commit_before>package spf

import (
	"net"

	"github.com/zaccone/goSPF/dns"
)

type SPFResult int

const (
	Illegal SPFResult = iota

	None
	Neutral
	Pass
	Fail
	Softfail
	Temperror
	Permerror

	// end of SPF
	SPFEnd
)

func (spf *SPFResult) String() string {
	switch *spf {
	case None:
		return "None"
	case Neutral:
		return "Neutral"
	case Pass:
		return "Pass"
	case Fail:
		return "Fail"
	case Temperror:
		return "Temperror"
	case Permerror:
		return "Permerror"
	default:
		return "Permerror"
	}
}

// CheckHost is a main entrypoint function evaluating e-mail with regard to SPF
// As per RFC 7208 it will accept 3 parameters (strings):
// <ip> - IP{4,6} address of the connected client
// <domain> - domain portion of the MAIL FROM or HELO identity
// <sender> - MAIL FROM or HELO identity
// All the parameters should be parsed and dereferenced from real email fields.
// This means domain should already be extracted from MAIL FROM field so this
// function can focus on the core part.
func checkHost(ip net.IP, domain, sender string) (SPFResult, error) {

	// TODO(zaccone) s/_/spfRecord/
	_, dnsErr := dns.LookupSPF(domain)

	if dnsErr != nil {
		switch dnsErr.(type) {
		// as per RFC7208 section 4.4, and DNS query errors result in Temperror
		//result immediately
		case *net.DNSError:
			return Temperror, dnsErr
		default:
			return Permerror, dnsErr

		}
	}
	query := "v=spf1 a -all"
	parser := NewParser(sender, domain, ip, query)

	var result SPFResult = Neutral
	var err error

	if result, err = parser.Parse(); err != nil {
		// handle error, something went wrong.
		// Let's set PermError for now.
		return Permerror, nil
	}

	// check if there is redirect and recursively call check_host with redirect
	// as domain
	if parser.Redirect != nil {
		result, err = checkHost(ip, domain, sender)
	}

	// return SPF evaluation result
	return result, nil
}
<commit_msg>Change SPFResult String().<commit_after>package spf

import (
	"net"

	"github.com/zaccone/goSPF/dns"
)

type SPFResult int

const (
	Illegal SPFResult = iota

	None
	Neutral
	Pass
	Fail
	Softfail
	Temperror
	Permerror

	// end of SPF
	SPFEnd
)

func (spf SPFResult) String() string {
	switch *spf {
	case None:
		return "None"
	case Neutral:
		return "Neutral"
	case Pass:
		return "Pass"
	case Fail:
		return "Fail"
	case Temperror:
		return "Temperror"
	case Permerror:
		return "Permerror"
	default:
		return "Permerror"
	}
}

// CheckHost is a main entrypoint function evaluating e-mail with regard to SPF
// As per RFC 7208 it will accept 3 parameters (strings):
// <ip> - IP{4,6} address of the connected client
// <domain> - domain portion of the MAIL FROM or HELO identity
// <sender> - MAIL FROM or HELO identity
// All the parameters should be parsed and dereferenced from real email fields.
// This means domain should already be extracted from MAIL FROM field so this
// function can focus on the core part.
func checkHost(ip net.IP, domain, sender string) (SPFResult, error) {

	// TODO(zaccone) s/_/spfRecord/
	_, dnsErr := dns.LookupSPF(domain)

	if dnsErr != nil {
		switch dnsErr.(type) {
		// as per RFC7208 section 4.4, and DNS query errors result in Temperror
		//result immediately
		case *net.DNSError:
			return Temperror, dnsErr
		default:
			return Permerror, dnsErr

		}
	}
	query := "v=spf1 a -all"
	parser := NewParser(sender, domain, ip, query)

	var result SPFResult = Neutral
	var err error

	if result, err = parser.Parse(); err != nil {
		// handle error, something went wrong.
		// Let's set PermError for now.
		return Permerror, nil
	}

	// check if there is redirect and recursively call check_host with redirect
	// as domain
	if parser.Redirect != nil {
		result, err = checkHost(ip, domain, sender)
	}

	// return SPF evaluation result
	return result, nil
}
<|endoftext|>


<commit_before>// 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 spinner is a simple package to add a spinner / progress indicator to any terminal application.
package spinner

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// validColors holds an array of the only colors allowed
var validColors = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	for _, i := range validColors {
		if c == i {
			return true
		}
	}
	return false
}

// Spinner struct to hold the provided options
type Spinner struct {
	Delay          time.Duration                 // Delay is the speed of the indicator
	chars          []string                      // chars holds the chosen character set
	Prefix         string                        // Prefix is the text preppended to the indicator
	Suffix         string                        // Suffix is the text appended to the indicator
	FinalMSG       string                        // string displayed after Stop() is called
	lastOutput     string                        // last character(set) written
	color          func(a ...interface{}) string // default color is white
	lock           *sync.RWMutex                 // Lock useed for
	Writer         io.Writer                     // to make testing better, exported so users have access
	active         bool                          // active holds the state of the spinner
	stopChan       chan struct{}                 // stopChan is a channel used to stop the indicator
	lastOutputChan chan string                   // allows main to safely get the last output from the spinner goroutine
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(cs []string, d time.Duration) *Spinner {
	return &Spinner{
		Delay:          d,
		chars:          cs,
		color:          color.New(color.FgWhite).SprintFunc(),
		lock:           &sync.RWMutex{},
		Writer:         color.Output,
		active:         false,
		stopChan:       make(chan struct{}, 1),
		lastOutputChan: make(chan string, 1),
	}
}

// Start will start the indicator
func (s *Spinner) Start() {
	if s.active {
		return
	}
	s.active = true

	go func() {
		for {
			for i := 0; i < len(s.chars); i++ {
				select {
				case <-s.stopChan:
					s.erase(<-s.lastOutputChan)
					return
				default:
					fmt.Fprint(s.Writer, fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix))
					out := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
					s.lastOutput = out
					s.lock.RLock()
					time.Sleep(s.Delay)
					s.lock.RUnlock()
					s.erase(out)
				}
			}
		}
	}()
}

// Stop stops the indicator
func (s *Spinner) Stop() {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.active {
		s.active = false
		if s.FinalMSG != "" {
			fmt.Fprintf(s.Writer, s.FinalMSG)
		}
		s.stopChan <- struct{}{}
	}
}

// Restart will stop and start the indicator
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to the indicator
func (s *Spinner) Reverse() {
	s.lock.Lock()
	defer s.lock.Unlock()
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(c string) error {
	if validColor(c) {
		switch c {
		case "red":
			s.color = color.New(color.FgRed).SprintFunc()
			s.Restart()
		case "yellow":
			s.color = color.New(color.FgYellow).SprintFunc()
			s.Restart()
		case "green":
			s.color = color.New(color.FgGreen).SprintFunc()
			s.Restart()
		case "magenta":
			s.color = color.New(color.FgMagenta).SprintFunc()
			s.Restart()
		case "blue":
			s.color = color.New(color.FgBlue).SprintFunc()
			s.Restart()
		case "cyan":
			s.color = color.New(color.FgCyan).SprintFunc()
			s.Restart()
		case "white":
			s.color = color.New(color.FgWhite).SprintFunc()
			s.Restart()
		default:
			return errInvalidColor
		}
	}
	return nil
}

// UpdateSpeed will set the indicator delay to the given value
func (s *Spinner) UpdateSpeed(d time.Duration) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.Delay = d
}

// UpdateCharSet will change the current character set to the given one
func (s *Spinner) UpdateCharSet(cs []string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.chars = cs
}

// erase deletes written characters
func (s *Spinner) erase(a string) {
	n := utf8.RuneCountInString(a)
	s.lock.Lock()
	defer s.lock.Unlock()
	for i := 0; i < n; i++ {
		fmt.Fprintf(s.Writer, "\b")
	}
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	numSeq := make([]string, length)
	for i := 0; i < length; i++ {
		numSeq[i] = strconv.Itoa(i)
	}
	return numSeq
}
<commit_msg>Clean up on Stop()<commit_after>// 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 spinner is a simple package to add a spinner / progress indicator to any terminal application.
package spinner

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// validColors holds an array of the only colors allowed
var validColors = []string{"red", "green", "yellow", "blue", "magenta", "cyan", "white"}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	for _, i := range validColors {
		if c == i {
			return true
		}
	}
	return false
}

// Spinner struct to hold the provided options
type Spinner struct {
	Delay      time.Duration                 // Delay is the speed of the indicator
	chars      []string                      // chars holds the chosen character set
	Prefix     string                        // Prefix is the text preppended to the indicator
	Suffix     string                        // Suffix is the text appended to the indicator
	FinalMSG   string                        // string displayed after Stop() is called
	lastOutput string                        // last character(set) written
	color      func(a ...interface{}) string // default color is white
	lock       *sync.RWMutex                 //
	Writer     io.Writer                     // to make testing better, exported so users have access
	active     bool                          // active holds the state of the spinner
	stopChan   chan struct{}                 // stopChan is a channel used to stop the indicator
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(cs []string, d time.Duration) *Spinner {
	return &Spinner{
		Delay:    d,
		chars:    cs,
		color:    color.New(color.FgWhite).SprintFunc(),
		lock:     &sync.RWMutex{},
		Writer:   color.Output,
		active:   false,
		stopChan: make(chan struct{}, 1),
	}
}

// Start will start the indicator
func (s *Spinner) Start() {
	if s.active {
		return
	}
	s.active = true

	go func() {
		for {
			for i := 0; i < len(s.chars); i++ {
				select {
				case <-s.stopChan:
					return
				default:
					s.lock.Lock()
					s.erase()
					outColor := fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
					outPlain := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
					fmt.Fprint(s.Writer, outColor)
					s.lastOutput = outPlain
					delay := s.Delay
					s.lock.Unlock()

					time.Sleep(delay)
				}
			}
		}
	}()
}

// Stop stops the indicator
func (s *Spinner) Stop() {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.active {
		s.active = false
		s.erase()
		if s.FinalMSG != "" {
			fmt.Fprintf(s.Writer, s.FinalMSG)
		}
		s.stopChan <- struct{}{}
	}
}

// Restart will stop and start the indicator
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to the indicator
func (s *Spinner) Reverse() {
	s.lock.Lock()
	defer s.lock.Unlock()
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(c string) error {
	if validColor(c) {
		switch c {
		case "red":
			s.color = color.New(color.FgRed).SprintFunc()
			s.Restart()
		case "yellow":
			s.color = color.New(color.FgYellow).SprintFunc()
			s.Restart()
		case "green":
			s.color = color.New(color.FgGreen).SprintFunc()
			s.Restart()
		case "magenta":
			s.color = color.New(color.FgMagenta).SprintFunc()
			s.Restart()
		case "blue":
			s.color = color.New(color.FgBlue).SprintFunc()
			s.Restart()
		case "cyan":
			s.color = color.New(color.FgCyan).SprintFunc()
			s.Restart()
		case "white":
			s.color = color.New(color.FgWhite).SprintFunc()
			s.Restart()
		default:
			return errInvalidColor
		}
	}
	return nil
}

// UpdateSpeed will set the indicator delay to the given value
func (s *Spinner) UpdateSpeed(d time.Duration) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.Delay = d
}

// UpdateCharSet will change the current character set to the given one
func (s *Spinner) UpdateCharSet(cs []string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.chars = cs
}

// erase deletes written characters
//
// Caller must already hold s.lock.
func (s *Spinner) erase() {
	n := utf8.RuneCountInString(s.lastOutput)
	for i := 0; i < n; i++ {
		fmt.Fprintf(s.Writer, "\b")
	}
	s.lastOutput = ""
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	numSeq := make([]string, length)
	for i := 0; i < length; i++ {
		numSeq[i] = strconv.Itoa(i)
	}
	return numSeq
}
<|endoftext|>


<commit_before>package sql // import "eriol.xyz/piken/sql"

import (
	"database/sql"
	"os"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

const (
	createTablesQuery = `
	CREATE TABLE last_update (
		filename TEXT NOT NULL PRIMARY KEY,
		date TEXT
	);
	CREATE TABLE unicode_data (
		id TEXT NOT NULL PRIMARY KEY,
		name TEXT NOT NULL,
		category TEXT NOT NULL,
		canonical_class NUMERIC NOT NULL,
		bidi_class TEXT NOT NULL,
		decomposition_type TEXT,
		numeric_value_1 TEXT,
		numeric_value_2 TEXT,
		numeric_value_3 TEXT,
		bidi_mirrored TEXT,
		unicode_1_name TEXT,
		iso_comment TEXT,
		simple_uppercase_mapping TEXT,
		simple_lowercase_mapping TEXT,
		simple_titlecase_mapping TEXT
	);`
	insertUnicodeDataQuery = `INSERT INTO unicode_data (
		id,
		name,
		category,
		canonical_class,
		bidi_class,
		decomposition_type,
		numeric_value_1,
		numeric_value_2,
		numeric_value_3,
		bidi_mirrored,
		unicode_1_name,
		iso_comment,
		simple_uppercase_mapping,
		simple_lowercase_mapping,
		simple_titlecase_mapping)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
	getLastUpdateQuery = `SELECT date FROM last_update WHERE filename = ?`
)

type Store struct {
	db *sql.DB
}

// Open SQLite 3 database used by piken or create it if it doesn't exist yet.
func (s *Store) Open(database string) error {

	db, err := sql.Open("sqlite3", database)
	if err != nil {
		return err
	}

	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			if err := s.createDatabase(); err != nil {
				return err
			}
		}
	}

	return nil

}

// Close SQLite3 database used by piken.
func (s *Store) Close() error {
	return s.db.Close()
}

func (s *Store) createDatabase() error {

	_, err := s.db.Exec(createTablesQuery)
	if err != nil {
		return err
	}
	return nil
}

// Load multiple records into piken database.
func (s *Store) LoadFromRecords(records [][]string) error {

	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(insertUnicodeDataQuery)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, record := range records {

		args := make([]interface{}, len(record))
		for i, v := range record {
			args[i] = v
		}

		if _, err = stmt.Exec(args...); err != nil {
			return err
		}
	}
	tx.Commit()

	return nil
}

// Get latest update for given file.
func (s *Store) GetLastUpdate(filename string) (time.Time, error) {
	var t string

	s.db.QueryRow(getLastUpdateQuery, filename).Scan(&t)

	// If the query is empty return the beginning of time.
	if t == "" {
		return time.Unix(0, 0), nil
	}

	tp, err := time.Parse(time.RFC3339Nano, t)
	if err != nil {
		return time.Unix(0, 0), err
	}

	return tp, nil

}
<commit_msg>Create latest update entry for given file<commit_after>package sql // import "eriol.xyz/piken/sql"

import (
	"database/sql"
	"os"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

const (
	createTablesQuery = `
	CREATE TABLE last_update (
		filename TEXT NOT NULL PRIMARY KEY,
		date TEXT
	);
	CREATE TABLE unicode_data (
		id TEXT NOT NULL PRIMARY KEY,
		name TEXT NOT NULL,
		category TEXT NOT NULL,
		canonical_class NUMERIC NOT NULL,
		bidi_class TEXT NOT NULL,
		decomposition_type TEXT,
		numeric_value_1 TEXT,
		numeric_value_2 TEXT,
		numeric_value_3 TEXT,
		bidi_mirrored TEXT,
		unicode_1_name TEXT,
		iso_comment TEXT,
		simple_uppercase_mapping TEXT,
		simple_lowercase_mapping TEXT,
		simple_titlecase_mapping TEXT
	);`
	insertUnicodeDataQuery = `INSERT INTO unicode_data (
		id,
		name,
		category,
		canonical_class,
		bidi_class,
		decomposition_type,
		numeric_value_1,
		numeric_value_2,
		numeric_value_3,
		bidi_mirrored,
		unicode_1_name,
		iso_comment,
		simple_uppercase_mapping,
		simple_lowercase_mapping,
		simple_titlecase_mapping)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
	createLastUpdateQuery = `INSERT INTO last_update (filename, date) VALUES (?, ?)`
	getLastUpdateQuery    = `SELECT date FROM last_update WHERE filename = ?`
)

type Store struct {
	db *sql.DB
}

// Open SQLite 3 database used by piken or create it if it doesn't exist yet.
func (s *Store) Open(database string) error {

	db, err := sql.Open("sqlite3", database)
	if err != nil {
		return err
	}

	s.db = db

	if _, err := os.Stat(database); err != nil {
		if os.IsNotExist(err) {
			if err := s.createDatabase(); err != nil {
				return err
			}
		}
	}

	return nil

}

// Close SQLite3 database used by piken.
func (s *Store) Close() error {
	return s.db.Close()
}

func (s *Store) createDatabase() error {

	_, err := s.db.Exec(createTablesQuery)
	if err != nil {
		return err
	}
	return nil
}

// Load multiple records into piken database.
func (s *Store) LoadFromRecords(records [][]string) error {

	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(insertUnicodeDataQuery)
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, record := range records {

		args := make([]interface{}, len(record))
		for i, v := range record {
			args[i] = v
		}

		if _, err = stmt.Exec(args...); err != nil {
			return err
		}
	}
	tx.Commit()

	return nil
}

// Create latest update entry for given file.
func (s *Store) CreateLastUpdate(filename string, t time.Time) error {

	ts := t.Format(time.RFC3339)

	_, err := s.db.Exec(createLastUpdateQuery, filename, ts)

	return err
}

// Get latest update for given file.
func (s *Store) GetLastUpdate(filename string) (time.Time, error) {
	var t string

	s.db.QueryRow(getLastUpdateQuery, filename).Scan(&t)

	// If the query is empty return the beginning of time.
	if t == "" {
		return time.Unix(0, 0), nil
	}

	tp, err := time.Parse(time.RFC3339Nano, t)
	if err != nil {
		return time.Unix(0, 0), err
	}

	return tp, nil

}
<|endoftext|>


<commit_before>package util

import (
	"bytes"
	"io"
)

const (
	defaultReaderBufSize = 4096
	minReaderBufferSize  = 16
)

// Type implementing a robust line reader.
type Reader struct {
	rd  io.Reader
	buf []byte
	err error
	p   int // points to first unscanned byte in buffer
	n   int // points to location after last byte in buffer
}

// Create new reader with default buffer size.
func NewReader(rd io.Reader) *Reader {
	return NewReaderSize(rd, defaultReaderBufSize)
}

// Create new reader with specified buffer size.
func NewReaderSize(rd io.Reader, size int) *Reader {
	if size < minReaderBufferSize {
		size = minReaderBufferSize
	}
	r := Reader{
		buf: make([]byte, size),
		rd:  rd,
		n:   2,
	}
	r.buf[0] = '\r' // in case first line is empty
	r.buf[1] = '\n' //
	return &r
}

// Same as ReadLine(), but returns string instead of []byte.
// Result stays valid.
func (r *Reader) ReadLineString() (line string, err error) {
	b, e := r.ReadLine()
	return string(b), e
}

// Read single line from Reader, without EOL.
// EOL can be any of: \n \r \r\n \n\r.
// Last line without EOL is allowed.
// Result is only valid until next call to ReadLine() or ReadLineString()
func (r *Reader) ReadLine() (line []byte, err error) {
	var lines [][]byte
	beginning := true
	for {

		// Fill buffer if it is nearly empty, unless error was already received.
		// Part after the '||' is not necessary, but makes it (perhaps) more
		// efficient by avoiding the use of partial saves as much as possble.
		if r.n-r.p < 2 && r.err == nil ||
			r.p < r.n && r.err == nil && bytes.IndexByte(r.buf[r.p:r.n], '\n') < 0 && bytes.IndexByte(r.buf[r.p:r.n], '\r') < 0 {
			if r.p > 0 {
				// make room
				copy(r.buf, r.buf[r.p:r.n])
				r.n -= r.p
				r.p = 0
			}
			var i int
			i, r.err = r.rd.Read(r.buf[r.n:])
			r.n += i
		}

		// if at beginning of line, skip EOL of previous line
		if beginning {
			var c byte
			for i := 0; i < 2; i++ {
				// 1 or 2 times \r or \n, but not \r\r or \n\n
				if r.p < r.n && (r.buf[r.p] == '\n' || r.buf[r.p] == '\r') && r.buf[r.p] != c {
					c = r.buf[r.p]
					r.p += 1
				} else {
					break
				}
			}
			beginning = false
		}

		// if error and no more unscanned data, return saved parts if they exist, else return error
		if r.p == r.n && r.err != nil {
			if len(lines) > 0 {
				return buildline(lines, r.buf[0:0]), nil
			} else {
				return r.buf[0:0], r.err
			}
		}

		if r.p < r.n {
			// find next EOL, can start with \n or \r
			i := bytes.IndexByte(r.buf[r.p:r.n], '\n')
			if i < 0 {
				i = r.n
			}
			j := bytes.IndexByte(r.buf[r.p:r.n], '\r')
			if j < 0 {
				j = r.n
			}
			if j < i {
				i = j
			}
			if i == r.n { // no EOL found
				buf := make([]byte, r.n-r.p)
				copy(buf, r.buf[r.p:r.n])
				lines = append(lines, buf)
				r.p = r.n
			} else { // EOL found
				p := r.p
				r.p += i
				return buildline(lines, r.buf[p:r.p]), nil
			}
		}

	}
	panic("not reached")
}

func buildline(lines [][]byte, last []byte) []byte {
	if len(lines) == 0 {
		return last
	}

	i := len(last)
	for _, line := range lines {
		i += len(line)
	}
	buf := make([]byte, i)
	i = 0
	for _, line := range lines {
		copy(buf[i:], line)
		i += len(line)
	}
	copy(buf[i:], last)
	return buf
}
<commit_msg>Minor optimization in ReadLine()<commit_after>package util

import (
	"bytes"
	"io"
)

const (
	defaultReaderBufSize = 4096
	minReaderBufferSize  = 16
)

// Type implementing a robust line reader.
type Reader struct {
	rd  io.Reader
	buf []byte
	err error
	p   int // points to first unscanned byte in buffer
	n   int // points to location after last byte in buffer
}

// Create new reader with default buffer size.
func NewReader(rd io.Reader) *Reader {
	return NewReaderSize(rd, defaultReaderBufSize)
}

// Create new reader with specified buffer size.
func NewReaderSize(rd io.Reader, size int) *Reader {
	if size < minReaderBufferSize {
		size = minReaderBufferSize
	}
	r := Reader{
		buf: make([]byte, size),
		rd:  rd,
		n:   2,
	}
	r.buf[0] = '\r' // in case first line is empty
	r.buf[1] = '\n' //
	return &r
}

// Same as ReadLine(), but returns string instead of []byte.
// Result stays valid.
func (r *Reader) ReadLineString() (line string, err error) {
	b, e := r.ReadLine()
	return string(b), e
}

// Read single line from Reader, without EOL.
// EOL can be any of: \n \r \r\n \n\r.
// Last line without EOL is allowed.
// Result is only valid until next call to ReadLine() or ReadLineString()
func (r *Reader) ReadLine() (line []byte, err error) {
	var lines [][]byte
	beginning := true
	for {

		// Fill buffer if it is nearly empty, unless error was already received.
		// Part after the '||' is not necessary, but makes it (perhaps) more
		// efficient by avoiding the use of partial saves as much as possble.
		if r.n-r.p < 2 && r.err == nil ||
			r.p < r.n && r.err == nil && bytes.IndexByte(r.buf[r.p+2:r.n], '\n') < 0 && bytes.IndexByte(r.buf[r.p+2:r.n], '\r') < 0 {
			if r.p > 0 {
				// make room
				copy(r.buf, r.buf[r.p:r.n])
				r.n -= r.p
				r.p = 0
			}
			var i int
			i, r.err = r.rd.Read(r.buf[r.n:])
			r.n += i
		}

		// if at beginning of line, skip EOL of previous line
		if beginning {
			var c byte
			for i := 0; i < 2; i++ {
				// 1 or 2 times \r or \n, but not \r\r or \n\n
				if r.p < r.n && (r.buf[r.p] == '\n' || r.buf[r.p] == '\r') && r.buf[r.p] != c {
					c = r.buf[r.p]
					r.p += 1
				} else {
					break
				}
			}
			beginning = false
		}

		// if error and no more unscanned data, return saved parts if they exist, else return error
		if r.p == r.n && r.err != nil {
			if len(lines) > 0 {
				return buildline(lines, r.buf[0:0]), nil
			} else {
				return r.buf[0:0], r.err
			}
		}

		if r.p < r.n {
			// find next EOL, can start with \n or \r
			i := bytes.IndexByte(r.buf[r.p:r.n], '\n')
			if i < 0 {
				i = r.n
			}
			j := bytes.IndexByte(r.buf[r.p:r.n], '\r')
			if j < 0 {
				j = r.n
			}
			if j < i {
				i = j
			}
			if i == r.n { // no EOL found
				buf := make([]byte, r.n-r.p)
				copy(buf, r.buf[r.p:r.n])
				lines = append(lines, buf)
				r.p = r.n
			} else { // EOL found
				p := r.p
				r.p += i
				return buildline(lines, r.buf[p:r.p]), nil
			}
		}

	}
	panic("not reached")
}

func buildline(lines [][]byte, last []byte) []byte {
	if len(lines) == 0 {
		return last
	}

	i := len(last)
	for _, line := range lines {
		i += len(line)
	}
	buf := make([]byte, i)
	i = 0
	for _, line := range lines {
		copy(buf[i:], line)
		i += len(line)
	}
	copy(buf[i:], last)
	return buf
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Xuyuan Pang <xuyuanp@gmail.com>
 *
 * 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 glogger

import "sync"

// register is a thread-safe map
type register struct {
	mapper map[string]interface{}
	mu     sync.RWMutex
}

// NewRegister returns a new register.
func NewRegister() *register {
	return &register{
		mapper: make(map[string]interface{}),
	}
}

// Register binds the interface and the name. If this name has been registerd, it panics.
func (r *register) Register(name string, v interface{}) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, dup := r.mapper[name]; dup {
		panic("register name: " + name + " twice")
	}
	r.mapper[name] = v
}

// Unregister unbinds the interface and the name. It returns the interface or nil
func (r *register) Unregister(name string) interface{} {
	r.mu.Lock()
	defer r.mu.Unlock()
	if v, ok := r.mapper[nam]; ok {
		delete(r.mapper, name)
		return v
	}
	return nil
}

// Get return an interface registerd with this name.
func (r *register) Get(name string) interface{} {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.mapper[name]
}
<commit_msg>fix spelling mistake<commit_after>/*
 * Copyright 2014 Xuyuan Pang <xuyuanp@gmail.com>
 *
 * 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 glogger

import "sync"

// register is a thread-safe map
type register struct {
	mapper map[string]interface{}
	mu     sync.RWMutex
}

// NewRegister returns a new register.
func NewRegister() *register {
	return &register{
		mapper: make(map[string]interface{}),
	}
}

// Register binds the interface and the name. If this name has been registerd, it panics.
func (r *register) Register(name string, v interface{}) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, dup := r.mapper[name]; dup {
		panic("register name: " + name + " twice")
	}
	r.mapper[name] = v
}

// Unregister unbinds the interface and the name. It returns the interface or nil
func (r *register) Unregister(name string) interface{} {
	r.mu.Lock()
	defer r.mu.Unlock()
	if v, ok := r.mapper[name]; ok {
		delete(r.mapper, name)
		return v
	}
	return nil
}

// Get return an interface registerd with this name.
func (r *register) Get(name string) interface{} {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.mapper[name]
}
<|endoftext|>


<commit_before>package proctl

import (
	"debug/elf"
	"debug/gosym"
	"fmt"
	"os"
	"sync"
	"syscall"

	"github.com/derekparker/delve/dwarf/frame"
)

const (
	STATUS_SLEEPING   = 'S'
	STATUS_RUNNING    = 'R'
	STATUS_TRACE_STOP = 't'
)

func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) {
	err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
	if err == syscall.ESRCH {
		_, _, err = wait(tid, 0)
		if err != nil {
			return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
		}

		err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
		if err != nil {
			return nil, fmt.Errorf("could not set options for new traced thread %d %s", tid, err)
		}
	}

	dbp.Threads[tid] = &ThreadContext{
		Id:      tid,
		Process: dbp,
	}

	return dbp.Threads[tid], nil
}

func stopped(pid int) bool {
	f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
	if err != nil {
		return false
	}
	defer f.Close()

	var (
		p     int
		comm  string
		state rune
	)
	fmt.Fscanf(f, "%d %s %c", &p, &comm, &state)
	if state == STATUS_TRACE_STOP {
		return true
	}
	return false
}

// Finds the executable from /proc/<pid>/exe and then
// uses that to parse the following information:
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
func (dbp *DebuggedProcess) LoadInformation() error {
	var (
		wg  sync.WaitGroup
		exe *elf.File
		err error
	)

	exe, err = dbp.findExecutable()
	if err != nil {
		return err
	}

	wg.Add(2)
	go dbp.parseDebugFrame(exe, &wg)
	go dbp.obtainGoSymbols(exe, &wg)

	wg.Wait()

	return nil
}

func (dbp *DebuggedProcess) findExecutable() (*elf.File, error) {
	procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)

	f, err := os.OpenFile(procpath, 0, os.ModePerm)
	if err != nil {
		return nil, err
	}

	elffile, err := elf.NewFile(f)
	if err != nil {
		return nil, err
	}

	data, err := elffile.DWARF()
	if err != nil {
		return nil, err
	}
	dbp.Dwarf = data

	return elffile, nil
}

func (dbp *DebuggedProcess) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
	defer wg.Done()

	if sec := exe.Section(".debug_frame"); sec != nil {
		debugFrame, err := exe.Section(".debug_frame").Data()
		if err != nil {
			fmt.Println("could not get .debug_frame section", err)
			os.Exit(1)
		}
		dbp.FrameEntries = frame.Parse(debugFrame)
	} else {
		fmt.Println("No debug symbols found")
		os.Exit(1)
	}
}

func (dbp *DebuggedProcess) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
	defer wg.Done()

	var (
		symdat  []byte
		pclndat []byte
		err     error
	)

	if sec := exe.Section(".gosymtab"); sec != nil {
		symdat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gosymtab section", err)
			os.Exit(1)
		}
	}

	if sec := exe.Section(".gopclntab"); sec != nil {
		pclndat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gopclntab section", err)
			os.Exit(1)
		}
	}

	pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
	tab, err := gosym.NewTable(symdat, pcln)
	if err != nil {
		fmt.Println("could not get initialize line table", err)
		os.Exit(1)
	}

	dbp.GoSymTable = tab
}
<commit_msg>Update error message for missing .debug_frame<commit_after>package proctl

import (
	"debug/elf"
	"debug/gosym"
	"fmt"
	"os"
	"sync"
	"syscall"

	"github.com/derekparker/delve/dwarf/frame"
)

const (
	STATUS_SLEEPING   = 'S'
	STATUS_RUNNING    = 'R'
	STATUS_TRACE_STOP = 't'
)

func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) {
	err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
	if err == syscall.ESRCH {
		_, _, err = wait(tid, 0)
		if err != nil {
			return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
		}

		err := syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE)
		if err != nil {
			return nil, fmt.Errorf("could not set options for new traced thread %d %s", tid, err)
		}
	}

	dbp.Threads[tid] = &ThreadContext{
		Id:      tid,
		Process: dbp,
	}

	return dbp.Threads[tid], nil
}

func stopped(pid int) bool {
	f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
	if err != nil {
		return false
	}
	defer f.Close()

	var (
		p     int
		comm  string
		state rune
	)
	fmt.Fscanf(f, "%d %s %c", &p, &comm, &state)
	if state == STATUS_TRACE_STOP {
		return true
	}
	return false
}

// Finds the executable from /proc/<pid>/exe and then
// uses that to parse the following information:
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
func (dbp *DebuggedProcess) LoadInformation() error {
	var (
		wg  sync.WaitGroup
		exe *elf.File
		err error
	)

	exe, err = dbp.findExecutable()
	if err != nil {
		return err
	}

	wg.Add(2)
	go dbp.parseDebugFrame(exe, &wg)
	go dbp.obtainGoSymbols(exe, &wg)

	wg.Wait()

	return nil
}

func (dbp *DebuggedProcess) findExecutable() (*elf.File, error) {
	procpath := fmt.Sprintf("/proc/%d/exe", dbp.Pid)

	f, err := os.OpenFile(procpath, 0, os.ModePerm)
	if err != nil {
		return nil, err
	}

	elffile, err := elf.NewFile(f)
	if err != nil {
		return nil, err
	}

	data, err := elffile.DWARF()
	if err != nil {
		return nil, err
	}
	dbp.Dwarf = data

	return elffile, nil
}

func (dbp *DebuggedProcess) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
	defer wg.Done()

	if sec := exe.Section(".debug_frame"); sec != nil {
		debugFrame, err := exe.Section(".debug_frame").Data()
		if err != nil {
			fmt.Println("could not get .debug_frame section", err)
			os.Exit(1)
		}
		dbp.FrameEntries = frame.Parse(debugFrame)
	} else {
		fmt.Println("could not find .debug_frame section in binary")
		os.Exit(1)
	}
}

func (dbp *DebuggedProcess) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
	defer wg.Done()

	var (
		symdat  []byte
		pclndat []byte
		err     error
	)

	if sec := exe.Section(".gosymtab"); sec != nil {
		symdat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gosymtab section", err)
			os.Exit(1)
		}
	}

	if sec := exe.Section(".gopclntab"); sec != nil {
		pclndat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gopclntab section", err)
			os.Exit(1)
		}
	}

	pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
	tab, err := gosym.NewTable(symdat, pcln)
	if err != nil {
		fmt.Println("could not get initialize line table", err)
		os.Exit(1)
	}

	dbp.GoSymTable = tab
}
<|endoftext|>


<commit_before>// This project does not use godoc. Instead there is extensive
//  and detailed description of the language features maintained
//  on the wiki. See the following link.
//
// https://github.com/glycerine/zygomys/wiki
//
package repl
<commit_msg>update doc.go structure<commit_after>/*
 This project does not use godoc. Instead there is extensive
  and detailed description of the language features maintained
  on the wiki. See the following link.

 https:github.com/glycerine/zygomys/wiki
*/
package repl
<|endoftext|>