<commit_before>package main

import (
	"bufio"
	"database/sql"
	"fmt"
	"io"
	olog "log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/kelseyhightower/envconfig"

	"nimona.io/internal/net"
	"nimona.io/internal/version"
	"nimona.io/pkg/blob"
	"nimona.io/pkg/context"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/hyperspace/resolver"
	"nimona.io/pkg/localpeer"
	"nimona.io/pkg/log"
	"nimona.io/pkg/network"
	"nimona.io/pkg/object"
	"nimona.io/pkg/objectmanager"
	"nimona.io/pkg/objectstore"
	"nimona.io/pkg/peer"
	"nimona.io/pkg/sqlobjectstore"

	_ "github.com/arl/statsviz"
)

type fileTransfer struct {
	local         localpeer.LocalPeer
	objectmanager objectmanager.ObjectManager
	objectstore   objectstore.Store
	resolver      resolver.Resolver
	listener      net.Listener
	config        *config
}

// nolint: lll
type config struct {
	Peer struct {
		PrivateKey  crypto.PrivateKey `envconfig:"PRIVATE_KEY"`
		BindAddress string            `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
		Bootstraps  []peer.Shorthand  `envconfig:"BOOTSTRAPS"`
	} `envconfig:"PEER"`

	ReceivedFolder string `envconfig:"RECEIVED_FOLDER" default:"received_files"`
	Debug          struct {
		MetricsPort string `envconfig:"METRICS_PORT"`
	} `envconfig:"DEBUG"`
}

type fileUnloaded struct {
	Metadata object.Metadata `nimona:"metadata:m,omitempty"`
	BlobHash object.Hash     `nimona:"blob:r,omitempty"`
}

func (e *fileUnloaded) Type() string {
	return "nimona.io/File"
}

func main() {
	args := os.Args
	if len(args) != 3 {
		fmt.Printf("usage: %s <get/serve> <hash/file>\n", args[0])
		return
	}

	ctx := context.New(
		context.WithCorrelationID("nimona"),
	)
	logger := log.FromContext(ctx).With(
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	cfg := &config{}
	if err := envconfig.Process("nimona", cfg); err != nil {
		logger.Fatal("error processing config", log.Error(err))
	}

	go func() {
		if cfg.Debug.MetricsPort == "" {
			return
		}

		olog.Println(
			http.ListenAndServe(
				fmt.Sprintf("localhost:%s", cfg.Debug.MetricsPort),
				nil,
			))
	}()

	ft, err := newFileTransfer(ctx, cfg, logger)
	if err != nil {
		logger.Fatal("error initializing", log.Error(err))
	}
	defer ft.close()

	command := args[1]
	param := args[2]

	switch command {
	case "get":
		ft.get(ctx, object.Hash(param))
	case "serve":
		ft.serve(ctx, param)
	default:
		fmt.Println("command not supported")
		return
	}

}

func (ft *fileTransfer) serve(
	ctx context.Context,
	filename string,
) {

	f, err := os.Open(filename)
	if err != nil {
		fmt.Println("failed to open file:", err)
		return
	}

	bl, err := blob.ToBlob(f)
	if err != nil {
		fmt.Println("failed to covert to blob", err)
		return
	}

	fl := &File{}
	fl.Name = filename
	fl.Blob = bl

	_, err = ft.objectmanager.Put(ctx, fl.ToObject())
	if err != nil {
		fmt.Println("failed to store file", err)
		return
	}

	blobj, err := ft.objectmanager.Put(ctx, bl.ToObject())
	if err != nil {
		fmt.Println("failed to store blob", err)
		return
	}
	fmt.Println("blob hash:", blobj.Hash())
	fmt.Println("file hash:", fl.ToObject().Hash().String())

	// register for termination signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// and wait for one
	<-sigs
}

func (ft *fileTransfer) findAndRequest(
	ctx context.Context,
	hash object.Hash,
) (
	*object.Object,
	error,
) {
	peers, err := ft.resolver.Lookup(ctx, resolver.LookupByContentHash(hash))
	if err != nil {
		return nil, err
	}

	obj, err := ft.objectmanager.Request(ctx, hash, peers[0], true)
	if err != nil {
		return nil, err
	}

	return obj, nil
}

func (ft *fileTransfer) get(
	ctx context.Context,
	hash object.Hash,
) {
	fmt.Println("getting file:", hash)

	obj, err := ft.findAndRequest(ctx, hash)
	if err != nil {
		fmt.Println("failed to request file: ", err)
		return
	}

	fl := &File{}
	if err := fl.FromObject(obj); err != nil {
		fmt.Println("object not of type file: ", err)
		return
	}

	flun := &fileUnloaded{}

	err = object.Decode(obj, flun)
	bmgr := blob.NewRequester(
		ctx,
		blob.WithObjectManager(ft.objectmanager),
		blob.WithResolver(ft.resolver),
	)
	if err != nil {
		fmt.Println("failed to decode:", err)
		return
	}

	fmt.Println("getting blob:", flun.BlobHash)
	bl, err := bmgr.Request(ctx, flun.BlobHash)
	if err != nil {
		fmt.Println("failed to request file:", err)
	}

	_ = os.MkdirAll(ft.config.ReceivedFolder, os.ModePerm)
	f, err := os.Create(filepath.Join(ft.config.ReceivedFolder, fl.Name))
	if err != nil {
		fmt.Println("failed to create file:", err)
		return
	}

	fmt.Println("writing file:", fl.Name)
	r := blob.FromBlob(bl)
	bf := bufio.NewReader(r)
	if _, err := io.Copy(f, bf); err != nil {
		fmt.Println("failed to write to file:", err)
	}

	fmt.Println("done")

}

func (ft *fileTransfer) close() {
	if ft.listener != nil {
		ft.listener.Close()
	}
}
func newFileTransfer(
	ctx context.Context,
	cfg *config,
	logger log.Logger,
) (*fileTransfer, error) {
	ft := &fileTransfer{}
	ft.config = cfg
	// construct local peer
	local := localpeer.New()
	// attach peer private key from config
	local.PutPrimaryPeerKey(cfg.Peer.PrivateKey)
	local.PutContentTypes(
		new(File).Type(),
		new(blob.Blob).Type(),
		new(blob.Chunk).Type(),
	)
	ft.local = local

	// construct new network
	net := network.New(
		ctx,
		network.WithLocalPeer(local),
	)

	if cfg.Peer.BindAddress != "" {
		// start listening
		lis, err := net.Listen(
			ctx,
			cfg.Peer.BindAddress,
			network.ListenOnLocalIPs,
			network.ListenOnPrivateIPs,
		)
		if err != nil {
			logger.Fatal("error while listening", log.Error(err))
		}
		ft.listener = lis
	}

	// make sure we have some bootstrap peers to start with
	if len(cfg.Peer.Bootstraps) == 0 {
		cfg.Peer.Bootstraps = []peer.Shorthand{
			"ed25519.CJi6yjjXuNBFDoYYPrp697d6RmpXeW8ZUZPmEce9AgEc@tcps:asimov.bootstrap.nimona.io:22581",
			"ed25519.6fVWVAK2DVGxBhtVBvzNWNKBWk9S83aQrAqGJfrxr75o@tcps:egan.bootstrap.nimona.io:22581",
			"ed25519.7q7YpmPNQmvSCEBWW8ENw8XV8MHzETLostJTYKeaRTcL@tcps:sloan.bootstrap.nimona.io:22581",
		}
	}

	// convert shorthands into peers
	bootstrapPeers := []*peer.Peer{}
	for _, s := range cfg.Peer.Bootstraps {
		bootstrapPeer, err := s.Peer()
		if err != nil {
			logger.Fatal("error parsing bootstrap peer", log.Error(err))
		}
		bootstrapPeers = append(bootstrapPeers, bootstrapPeer)
	}

	// construct new resolver
	res := resolver.New(
		ctx,
		net,
		resolver.WithBoostrapPeers(bootstrapPeers...),
	)
	ft.resolver = res

	logger = logger.With(
		log.String("peer.privateKey", local.GetPrimaryPeerKey().String()),
		log.String("peer.publicKey", local.GetPrimaryPeerKey().PublicKey().String()),
		log.Strings("peer.addresses", local.GetAddresses()),
	)

	// construct object store
	db, err := sql.Open("sqlite3", "file_transfer.db")
	if err != nil {
		logger.Fatal("error opening sql file", log.Error(err))
	}

	str, err := sqlobjectstore.New(db)
	if err != nil {
		logger.Fatal("error starting sql store", log.Error(err))
	}
	ft.objectstore = str

	// construct manager
	man := objectmanager.New(
		ctx,
		net,
		res,
		str,
	)
	ft.objectmanager = man

	return ft, nil

}
<commit_msg>chore(examples/files): update for connectioninfo<commit_after>package main

import (
	"bufio"
	"database/sql"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/kelseyhightower/envconfig"

	"nimona.io/internal/net"
	"nimona.io/internal/version"
	"nimona.io/pkg/blob"
	"nimona.io/pkg/context"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/hyperspace/resolver"
	"nimona.io/pkg/localpeer"
	"nimona.io/pkg/log"
	"nimona.io/pkg/network"
	"nimona.io/pkg/object"
	"nimona.io/pkg/objectmanager"
	"nimona.io/pkg/objectstore"
	"nimona.io/pkg/peer"
	"nimona.io/pkg/sqlobjectstore"

	_ "github.com/arl/statsviz"
)

type fileTransfer struct {
	local         localpeer.LocalPeer
	objectmanager objectmanager.ObjectManager
	objectstore   objectstore.Store
	resolver      resolver.Resolver
	listener      net.Listener
	config        *config
}

// nolint: lll
type config struct {
	Peer struct {
		PrivateKey  crypto.PrivateKey `envconfig:"PRIVATE_KEY"`
		BindAddress string            `envconfig:"BIND_ADDRESS" default:"0.0.0.0:0"`
		Bootstraps  []peer.Shorthand  `envconfig:"BOOTSTRAPS"`
	} `envconfig:"PEER"`

	ReceivedFolder string `envconfig:"RECEIVED_FOLDER" default:"received_files"`
	Debug          struct {
		MetricsPort string `envconfig:"METRICS_PORT"`
	} `envconfig:"DEBUG"`
}

type fileUnloaded struct {
	Metadata object.Metadata `nimona:"metadata:m,omitempty"`
	BlobHash object.Hash     `nimona:"blob:r,omitempty"`
}

func (e *fileUnloaded) Type() string {
	return "nimona.io/File"
}

func main() {
	args := os.Args
	if len(args) != 3 {
		fmt.Printf("usage: %s <get/serve> <hash/file>\n", args[0])
		return
	}

	ctx := context.New(
		context.WithCorrelationID("nimona"),
	)
	logger := log.FromContext(ctx).With(
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	cfg := &config{}
	if err := envconfig.Process("nimona", cfg); err != nil {
		logger.Fatal("error processing config", log.Error(err))
	}

	go func() {
		if cfg.Debug.MetricsPort == "" {
			return
		}

		// nolint: errcheck
		http.ListenAndServe(
			fmt.Sprintf("localhost:%s", cfg.Debug.MetricsPort),
			nil,
		)
	}()

	ft, err := newFileTransfer(ctx, cfg, logger)
	if err != nil {
		logger.Fatal("error initializing", log.Error(err))
	}
	defer ft.close()

	command := args[1]
	param := args[2]

	switch command {
	case "get":
		ft.get(ctx, object.Hash(param))
	case "serve":
		ft.serve(ctx, param)
	default:
		fmt.Println("command not supported")
		return
	}

}

func (ft *fileTransfer) serve(
	ctx context.Context,
	filename string,
) {

	f, err := os.Open(filename)
	if err != nil {
		fmt.Println("failed to open file:", err)
		return
	}

	bl, err := blob.ToBlob(f)
	if err != nil {
		fmt.Println("failed to covert to blob", err)
		return
	}

	fl := &File{}
	fl.Name = filename
	fl.Blob = bl

	_, err = ft.objectmanager.Put(ctx, fl.ToObject())
	if err != nil {
		fmt.Println("failed to store file", err)
		return
	}

	blobj, err := ft.objectmanager.Put(ctx, bl.ToObject())
	if err != nil {
		fmt.Println("failed to store blob", err)
		return
	}
	fmt.Println("blob hash:", blobj.Hash())
	fmt.Println("file hash:", fl.ToObject().Hash().String())

	// register for termination signals
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// and wait for one
	<-sigs
}

func (ft *fileTransfer) findAndRequest(
	ctx context.Context,
	hash object.Hash,
) (
	*object.Object,
	error,
) {
	peers, err := ft.resolver.Lookup(ctx, resolver.LookupByContentHash(hash))
	if err != nil {
		return nil, err
	}

	obj, err := ft.objectmanager.Request(ctx, hash, peers[0], true)
	if err != nil {
		return nil, err
	}

	return obj, nil
}

func (ft *fileTransfer) get(
	ctx context.Context,
	hash object.Hash,
) {
	fmt.Println("getting file:", hash)

	obj, err := ft.findAndRequest(ctx, hash)
	if err != nil {
		fmt.Println("failed to request file: ", err)
		return
	}

	fl := &File{}
	if err := fl.FromObject(obj); err != nil {
		fmt.Println("object not of type file: ", err)
		return
	}

	flun := &fileUnloaded{}

	err = object.Decode(obj, flun)
	bmgr := blob.NewRequester(
		ctx,
		blob.WithObjectManager(ft.objectmanager),
		blob.WithResolver(ft.resolver),
	)
	if err != nil {
		fmt.Println("failed to decode:", err)
		return
	}

	fmt.Println("getting blob:", flun.BlobHash)
	bl, err := bmgr.Request(ctx, flun.BlobHash)
	if err != nil {
		fmt.Println("failed to request file:", err)
	}

	_ = os.MkdirAll(ft.config.ReceivedFolder, os.ModePerm)
	f, err := os.Create(filepath.Join(ft.config.ReceivedFolder, fl.Name))
	if err != nil {
		fmt.Println("failed to create file:", err)
		return
	}

	fmt.Println("writing file:", fl.Name)
	r := blob.FromBlob(bl)
	bf := bufio.NewReader(r)
	if _, err := io.Copy(f, bf); err != nil {
		fmt.Println("failed to write to file:", err)
	}

	fmt.Println("done")

}

func (ft *fileTransfer) close() {
	if ft.listener != nil {
		ft.listener.Close()
	}
}
func newFileTransfer(
	ctx context.Context,
	cfg *config,
	logger log.Logger,
) (*fileTransfer, error) {
	ft := &fileTransfer{}
	ft.config = cfg
	// construct local peer
	local := localpeer.New()
	// attach peer private key from config
	local.PutPrimaryPeerKey(cfg.Peer.PrivateKey)
	local.PutContentTypes(
		new(File).Type(),
		new(blob.Blob).Type(),
		new(blob.Chunk).Type(),
	)
	ft.local = local

	// construct new network
	net := network.New(
		ctx,
		network.WithLocalPeer(local),
	)

	if cfg.Peer.BindAddress != "" {
		// start listening
		lis, err := net.Listen(
			ctx,
			cfg.Peer.BindAddress,
			network.ListenOnLocalIPs,
			network.ListenOnPrivateIPs,
		)
		if err != nil {
			logger.Fatal("error while listening", log.Error(err))
		}
		ft.listener = lis
	}

	// make sure we have some bootstrap peers to start with
	if len(cfg.Peer.Bootstraps) == 0 {
		cfg.Peer.Bootstraps = []peer.Shorthand{
			"ed25519.CJi6yjjXuNBFDoYYPrp697d6RmpXeW8ZUZPmEce9AgEc@tcps:asimov.bootstrap.nimona.io:22581",
			"ed25519.6fVWVAK2DVGxBhtVBvzNWNKBWk9S83aQrAqGJfrxr75o@tcps:egan.bootstrap.nimona.io:22581",
			"ed25519.7q7YpmPNQmvSCEBWW8ENw8XV8MHzETLostJTYKeaRTcL@tcps:sloan.bootstrap.nimona.io:22581",
		}
	}

	// convert shorthands into peers
	bootstrapPeers := []*peer.ConnectionInfo{}
	for _, s := range cfg.Peer.Bootstraps {
		bootstrapPeer, err := s.ConnectionInfo()
		if err != nil {
			logger.Fatal("error parsing bootstrap peer", log.Error(err))
		}
		bootstrapPeers = append(bootstrapPeers, bootstrapPeer)
	}

	// construct new resolver
	res := resolver.New(
		ctx,
		net,
		resolver.WithBoostrapPeers(bootstrapPeers...),
	)
	ft.resolver = res

	logger = logger.With(
		log.String("peer.privateKey", local.GetPrimaryPeerKey().String()),
		log.String("peer.publicKey", local.GetPrimaryPeerKey().PublicKey().String()),
		log.Strings("peer.addresses", local.GetAddresses()),
	)

	// construct object store
	db, err := sql.Open("sqlite3", "file_transfer.db")
	if err != nil {
		logger.Fatal("error opening sql file", log.Error(err))
	}

	str, err := sqlobjectstore.New(db)
	if err != nil {
		logger.Fatal("error starting sql store", log.Error(err))
	}
	ft.objectstore = str

	// construct manager
	man := objectmanager.New(
		ctx,
		net,
		res,
		str,
	)
	ft.objectmanager = man

	return ft, nil

}
<|endoftext|>


<commit_before>package partial

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

const DefaultBlockSize int = 128 * 1024

type PartialHTTPReader struct {
	URL                *url.URL
	length             int64
	blockSize          int
	client             http.Client
	blocks             map[int][]byte
	mutex              sync.RWMutex
	initialized        bool
	etag, lastModified string

	off int64
}

type requestByteRange struct {
	block      int
	start, end int64
}

func (r requestByteRange) String() string {
	return fmt.Sprintf("%d-%d", r.start, r.end)
}

func (r *PartialHTTPReader) downloadRanges(ranges []requestByteRange) {
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = rng.String()
		}
		rangeString := strings.Join(rs, ",")

		req, _ := http.NewRequest("GET", r.URL.String(), nil)
		req.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeString))
		if r.etag != "" {
			req.Header.Set("If-Range", r.etag)
		} else if r.lastModified != "" {
			req.Header.Set("If-Range", r.lastModified)
		}

		resp, _ := r.client.Do(req)
		typ, params, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		defer resp.Body.Close()

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			r.mutex.Lock()
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					rng := ranges[i]
					bn := rng.block
					blocklen := (rng.end - rng.start) + 1
					r.blocks[bn] = make([]byte, blocklen)
					io.ReadFull(part, r.blocks[bn])
					i++
				} else {
					break
				}
			}
			r.mutex.Unlock()
		} else {
			r.mutex.Lock()
			bn := 0
			body := make([]byte, r.length)
			io.ReadFull(resp.Body, body)
			for i := r.length; i > 0; i -= int64(r.blockSize) {
				bs := i
				if bs > int64(r.blockSize) {
					bs = int64(r.blockSize)
				}

				r.blocks[bn] = make([]byte, bs)
				copy(r.blocks[bn], body[bn*r.blockSize:bn*r.blockSize+int(bs)])

				bn++
			}
			r.mutex.Unlock()
		}
	}
}

func (r *PartialHTTPReader) ReadAt(p []byte, off int64) (int, error) {
	if !r.initialized {
		err := r.init()
		if err != nil {
			return 0, err
		}
	}

	l := len(p)
	block := int(off / int64(r.blockSize))
	endBlock := int((off + int64(l)) / int64(r.blockSize))
	endBlockOff := (off + int64(l)) % int64(r.blockSize)
	nblocks := endBlock - block
	if endBlockOff > 0 {
		nblocks++
	}

	ranges := make([]requestByteRange, nblocks)
	nreq := 0
	r.mutex.RLock()
	for i := 0; i < nblocks; i++ {
		bn := block + i
		if _, ok := r.blocks[bn]; ok {
			continue
		}
		ranges[i] = requestByteRange{
			bn,
			int64(bn * r.blockSize),
			int64(((bn + 1) * r.blockSize) - 1),
		}
		if ranges[i].end > r.length {
			ranges[i].end = r.length
		}

		nreq++
	}
	r.mutex.RUnlock()
	ranges = ranges[:nreq]

	r.downloadRanges(ranges)
	return r.copyRangeToBuffer(p, off)
}

func (r *PartialHTTPReader) copyRangeToBuffer(p []byte, off int64) (int, error) {
	remaining := len(p)
	block := int(off / int64(r.blockSize))
	startOffset := off % int64(r.blockSize)
	ncopied := 0

	r.mutex.RLock()
	defer r.mutex.RUnlock()

	for remaining > 0 {
		copylen := r.blockSize
		if copylen > remaining {
			copylen = remaining
		}

		// if we need to copy more bytes than exist in this block
		if startOffset+int64(copylen) > int64(r.blockSize) {
			copylen = int(int64(r.blockSize) - startOffset)
		}

		if _, ok := r.blocks[block]; !ok {
			return 0, errors.New("fu?")
		}
		copy(p[ncopied:ncopied+copylen], r.blocks[block][startOffset:])

		remaining -= copylen
		ncopied += copylen

		block++
		startOffset = 0
	}

	return ncopied, nil
}

func (r *PartialHTTPReader) Length() int64 {
	if !r.initialized {
		r.init()
	}
	return r.length
}

func (r *PartialHTTPReader) Read(p []byte) (int, error) {
	nread, err := r.ReadAt(p, r.off)
	r.off += int64(nread)
	return nread, err
}

func (r *PartialHTTPReader) Seek(off int64, whence int) (int64, error) {
	if off < 0 {
		return 0, errors.New("seek to negative offset!")
	}

	switch whence {
	case 0:
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 1:
		off = r.off + off
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 2:
		off = r.Length() - off
		if off < 0 {
			return 0, errors.New("seek beyond beginning of file")
		}
		r.off = off
	}
	return r.off, nil
}

func (r *PartialHTTPReader) init() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.initialized = true
	r.blocks = make(map[int][]byte)
	if r.blockSize == 0 {
		r.blockSize = DefaultBlockSize
	}

	resp, _ := http.Head(r.URL.String())
	if resp.StatusCode == http.StatusNotFound {
		return errors.New("404")
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.Host + " does not support byte-ranged requests.")
	}

	r.etag = resp.Header.Get("ETag")
	r.lastModified = resp.Header.Get("Last-Modified")
	r.length = resp.ContentLength
	return nil
}

func NewPartialHTTPReader(u *url.URL) (*PartialHTTPReader, error) {
	r := &PartialHTTPReader{
		URL: u,
	}
	err := r.init()
	if err != nil {
		return nil, err
	}
	return r, nil
}
<commit_msg>Bail out for reads beyond the end of the file.<commit_after>package partial

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

const DefaultBlockSize int = 128 * 1024

type PartialHTTPReader struct {
	URL                *url.URL
	length             int64
	blockSize          int
	client             http.Client
	blocks             map[int][]byte
	mutex              sync.RWMutex
	initialized        bool
	etag, lastModified string

	off int64
}

type requestByteRange struct {
	block      int
	start, end int64
}

func (r requestByteRange) String() string {
	return fmt.Sprintf("%d-%d", r.start, r.end)
}

func (r *PartialHTTPReader) downloadRanges(ranges []requestByteRange) {
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = rng.String()
		}
		rangeString := strings.Join(rs, ",")

		req, _ := http.NewRequest("GET", r.URL.String(), nil)
		req.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeString))
		if r.etag != "" {
			req.Header.Set("If-Range", r.etag)
		} else if r.lastModified != "" {
			req.Header.Set("If-Range", r.lastModified)
		}

		resp, _ := r.client.Do(req)
		typ, params, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		defer resp.Body.Close()

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			r.mutex.Lock()
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					rng := ranges[i]
					bn := rng.block
					blocklen := (rng.end - rng.start) + 1
					r.blocks[bn] = make([]byte, blocklen)
					io.ReadFull(part, r.blocks[bn])
					i++
				} else {
					break
				}
			}
			r.mutex.Unlock()
		} else {
			r.mutex.Lock()
			bn := 0
			body := make([]byte, r.length)
			io.ReadFull(resp.Body, body)
			for i := r.length; i > 0; i -= int64(r.blockSize) {
				bs := i
				if bs > int64(r.blockSize) {
					bs = int64(r.blockSize)
				}

				r.blocks[bn] = make([]byte, bs)
				copy(r.blocks[bn], body[bn*r.blockSize:bn*r.blockSize+int(bs)])

				bn++
			}
			r.mutex.Unlock()
		}
	}
}

func (r *PartialHTTPReader) ReadAt(p []byte, off int64) (int, error) {
	if !r.initialized {
		err := r.init()
		if err != nil {
			return 0, err
		}
	}

	l := len(p)

	if off+int64(l) > r.length {
		return 0, errors.New("read beyond end of file")
	}

	block := int(off / int64(r.blockSize))
	endBlock := int((off + int64(l)) / int64(r.blockSize))
	endBlockOff := (off + int64(l)) % int64(r.blockSize)
	nblocks := endBlock - block
	if endBlockOff > 0 {
		nblocks++
	}

	ranges := make([]requestByteRange, nblocks)
	nreq := 0
	r.mutex.RLock()
	for i := 0; i < nblocks; i++ {
		bn := block + i
		if _, ok := r.blocks[bn]; ok {
			continue
		}
		ranges[i] = requestByteRange{
			bn,
			int64(bn * r.blockSize),
			int64(((bn + 1) * r.blockSize) - 1),
		}
		if ranges[i].end > r.length {
			ranges[i].end = r.length
		}

		nreq++
	}
	r.mutex.RUnlock()
	ranges = ranges[:nreq]

	r.downloadRanges(ranges)
	return r.copyRangeToBuffer(p, off)
}

func (r *PartialHTTPReader) copyRangeToBuffer(p []byte, off int64) (int, error) {
	remaining := len(p)
	block := int(off / int64(r.blockSize))
	startOffset := off % int64(r.blockSize)
	ncopied := 0

	r.mutex.RLock()
	defer r.mutex.RUnlock()

	for remaining > 0 {
		copylen := r.blockSize
		if copylen > remaining {
			copylen = remaining
		}

		// if we need to copy more bytes than exist in this block
		if startOffset+int64(copylen) > int64(r.blockSize) {
			copylen = int(int64(r.blockSize) - startOffset)
		}

		if _, ok := r.blocks[block]; !ok {
			return 0, errors.New("fu?")
		}
		copy(p[ncopied:ncopied+copylen], r.blocks[block][startOffset:])

		remaining -= copylen
		ncopied += copylen

		block++
		startOffset = 0
	}

	return ncopied, nil
}

func (r *PartialHTTPReader) Length() int64 {
	if !r.initialized {
		r.init()
	}
	return r.length
}

func (r *PartialHTTPReader) Read(p []byte) (int, error) {
	nread, err := r.ReadAt(p, r.off)
	r.off += int64(nread)
	return nread, err
}

func (r *PartialHTTPReader) Seek(off int64, whence int) (int64, error) {
	if off < 0 {
		return 0, errors.New("seek to negative offset!")
	}

	switch whence {
	case 0:
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 1:
		off = r.off + off
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 2:
		off = r.Length() - off
		if off < 0 {
			return 0, errors.New("seek beyond beginning of file")
		}
		r.off = off
	}
	return r.off, nil
}

func (r *PartialHTTPReader) init() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.initialized = true
	r.blocks = make(map[int][]byte)
	if r.blockSize == 0 {
		r.blockSize = DefaultBlockSize
	}

	resp, _ := http.Head(r.URL.String())
	if resp.StatusCode == http.StatusNotFound {
		return errors.New("404")
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.Host + " does not support byte-ranged requests.")
	}

	r.etag = resp.Header.Get("ETag")
	r.lastModified = resp.Header.Get("Last-Modified")
	r.length = resp.ContentLength
	return nil
}

func NewPartialHTTPReader(u *url.URL) (*PartialHTTPReader, error) {
	r := &PartialHTTPReader{
		URL: u,
	}
	err := r.init()
	if err != nil {
		return nil, err
	}
	return r, nil
}
<|endoftext|>


<commit_before>package jsoncommentstrip

import (
	"bufio"
	"io"
)

type readerState int

type reader struct {
	input *bufio.Reader
	err   error
	buff  string
	state readerState
}

func (reader *reader) checkForError(buff []byte) (count int, err error) {
	if reader.err != nil {
		buffLen := len(buff)
		err = reader.err

		if reader.buff != "" {
			if reader.state == stateOther || reader.state == stateQuotation || reader.state == stateEscape {
				readBuffBytes := []byte(reader.buff)
				readBuffLen := len(readBuffBytes)

				if readBuffLen <= buffLen {
					for i := range readBuffBytes {
						buff[i] = readBuffBytes[i]
					}

					reader.buff = ""
					count = readBuffLen
				} else {
					err = io.ErrShortBuffer
				}
				return
			}

			reader.buff = ""
		}
	}

	return
}

func writeToBuffer(buff []byte, pos int, value string) (count int) {
	for _, b := range []byte(value) {
		buff[pos+count] = b
		count++
	}

	return
}

func (reader *reader) readRuneString() (value string, err error) {
	var char rune
	char, _, err = reader.input.ReadRune()

	if err == nil {
		value = string(char)
	}
	return
}

func (reader *reader) fillBuff() (err error) {
	if reader.buff == "" {
		var buff string
		buff, err = reader.readRuneString()
		if err == nil {
			reader.buff = buff
		}
	}
	return
}

func (reader *reader) processOther(next string, buff []byte, pos int) (count int) {
	str := reader.buff + next
	if str == commentMultiStart {
		reader.state = stateMComment
		reader.buff = ""
		return
	}

	if str == commentSingle {
		reader.state = stateSComment
		reader.buff = ""
		return
	}

	if reader.buff == quoteMark {
		reader.state = stateQuotation
	}

	count = writeToBuffer(buff, pos, reader.buff)
	reader.buff = next
	return
}

func (reader *reader) processMComment(next string, buff []byte, pos int) (count int) {
	str := reader.buff + next
	if str == commentMultiEnd {
		reader.state = stateOther
		reader.buff = ""
		return
	}
	reader.buff = next
	return
}

func (reader *reader) processSComment(next string, buff []byte, pos int) (count int) {
	if reader.buff == "\n" || reader.buff == "\r" && next == "\n" {
		reader.state = stateOther

		// keep line endings
		count += writeToBuffer(buff, pos, reader.buff)
	}
	reader.buff = next
	return
}

func (reader *reader) processQuotation(next string, buff []byte, pos int) (count int) {
	if reader.buff == "\\" {
		reader.state = stateEscape
	} else if reader.buff == quoteMark {
		reader.state = stateOther
	}
	count += writeToBuffer(buff, pos, reader.buff)
	reader.buff = next
	return
}

func (reader *reader) processEscape(next string, buff []byte, pos int) (count int) {
	reader.state = stateQuotation
	count += writeToBuffer(buff, pos, reader.buff)
	reader.buff = next
	return
}

func (reader *reader) processNextRune(next string, buff []byte, pos int) (count int) {
	switch reader.state {
	case stateOther:
		count = reader.processOther(next, buff, pos)
	case stateMComment:
		count = reader.processMComment(next, buff, pos)
	case stateSComment:
		count = reader.processSComment(next, buff, pos)
	case stateQuotation:
		count = reader.processQuotation(next, buff, pos)
	case stateEscape:
		count = reader.processEscape(next, buff, pos)
	}
	return
}

func (reader *reader) Read(buff []byte) (count int, err error) {
	count, err = reader.checkForError(buff)

	if err != nil {
		return
	}

	var next string
	buffLen := len(buff)
	for count < buffLen {
		reader.err = reader.fillBuff()
		if reader.err != nil {
			return
		}

		// check buffer free space
		if buffLen-count < len(reader.buff) {
			if count == 0 {
				err = io.ErrShortBuffer
			}
			return
		}

		// read next rune
		next, reader.err = reader.readRuneString()
		if reader.err != nil {
			return
		}

		count += reader.processNextRune(next, buff, count)
	}

	return
}

// NewReader creates new Reader instance
func NewReader(input io.Reader) Reader {
	return &reader{
		input: bufio.NewReader(input),
		state: stateOther,
	}
}
<commit_msg>Renaming methods<commit_after>package jsoncommentstrip

import (
	"bufio"
	"io"
)

type readerState int

type reader struct {
	input *bufio.Reader
	err   error
	buff  string
	state readerState
}

func (reader *reader) checkForPreviousError(buff []byte) (count int, err error) {
	if reader.err != nil {
		buffLen := len(buff)
		err = reader.err

		if reader.buff != "" {
			if reader.state == stateOther || reader.state == stateQuotation || reader.state == stateEscape {
				readBuffBytes := []byte(reader.buff)
				readBuffLen := len(readBuffBytes)

				if readBuffLen <= buffLen {
					for i := range readBuffBytes {
						buff[i] = readBuffBytes[i]
					}

					reader.buff = ""
					count = readBuffLen
				} else {
					err = io.ErrShortBuffer
				}
				return
			}

			reader.buff = ""
		}
	}

	return
}

func writeStringToBuffer(buff []byte, pos int, value string) (count int) {
	for _, b := range []byte(value) {
		buff[pos+count] = b
		count++
	}

	return
}

func (reader *reader) readRuneAsString() (value string, err error) {
	var char rune
	char, _, err = reader.input.ReadRune()

	if err == nil {
		value = string(char)
	}
	return
}

func (reader *reader) fillBuff() (err error) {
	if reader.buff == "" {
		var buff string
		buff, err = reader.readRuneAsString()
		if err == nil {
			reader.buff = buff
		}
	}
	return
}

func (reader *reader) processOther(next string, buff []byte, pos int) (count int) {
	str := reader.buff + next
	if str == commentMultiStart {
		reader.state = stateMComment
		reader.buff = ""
		return
	}

	if str == commentSingle {
		reader.state = stateSComment
		reader.buff = ""
		return
	}

	if reader.buff == quoteMark {
		reader.state = stateQuotation
	}

	count = writeStringToBuffer(buff, pos, reader.buff)
	reader.buff = next
	return
}

func (reader *reader) processMComment(next string, buff []byte, pos int) (count int) {
	str := reader.buff + next
	if str == commentMultiEnd {
		reader.state = stateOther
		reader.buff = ""
		return
	}
	reader.buff = next
	return
}

func (reader *reader) processSComment(next string, buff []byte, pos int) (count int) {
	if reader.buff == "\n" || reader.buff == "\r" && next == "\n" {
		reader.state = stateOther

		// keep line endings
		count += writeStringToBuffer(buff, pos, reader.buff)
	}
	reader.buff = next
	return
}

func (reader *reader) processQuotation(next string, buff []byte, pos int) (count int) {
	if reader.buff == "\\" {
		reader.state = stateEscape
	} else if reader.buff == quoteMark {
		reader.state = stateOther
	}
	count += writeStringToBuffer(buff, pos, reader.buff)
	reader.buff = next
	return
}

func (reader *reader) processEscape(next string, buff []byte, pos int) (count int) {
	reader.state = stateQuotation
	count += writeStringToBuffer(buff, pos, reader.buff)
	reader.buff = next
	return
}

func (reader *reader) processNextRune(next string, buff []byte, pos int) (count int) {
	switch reader.state {
	case stateOther:
		count = reader.processOther(next, buff, pos)
	case stateMComment:
		count = reader.processMComment(next, buff, pos)
	case stateSComment:
		count = reader.processSComment(next, buff, pos)
	case stateQuotation:
		count = reader.processQuotation(next, buff, pos)
	case stateEscape:
		count = reader.processEscape(next, buff, pos)
	}
	return
}

func (reader *reader) Read(buff []byte) (count int, err error) {
	count, err = reader.checkForPreviousError(buff)

	if err != nil {
		return
	}

	var next string
	buffLen := len(buff)
	for count < buffLen {
		reader.err = reader.fillBuff()
		if reader.err != nil {
			return
		}

		// check buffer free space
		if buffLen-count < len(reader.buff) {
			if count == 0 {
				err = io.ErrShortBuffer
			}
			return
		}

		// read next rune
		next, reader.err = reader.readRuneAsString()
		if reader.err != nil {
			return
		}

		count += reader.processNextRune(next, buff, count)
	}

	return
}

// NewReader creates new Reader instance
func NewReader(input io.Reader) Reader {
	return &reader{
		input: bufio.NewReader(input),
		state: stateOther,
	}
}
<|endoftext|>


<commit_before>package partial

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

const DefaultBlockSize int = 128 * 1024

type PartialHTTPReader struct {
	URL                *url.URL
	length             int64
	blockSize          int
	client             http.Client
	blocks             map[int][]byte
	mutex              sync.RWMutex
	initialized        bool
	etag, lastModified string

	off int64
}

type requestByteRange struct {
	block      int
	start, end int64
}

func (r requestByteRange) String() string {
	return fmt.Sprintf("%d-%d", r.start, r.end)
}

func (r *PartialHTTPReader) readRangeIntoBlock(rng requestByteRange, reader io.Reader) {
	bn := rng.block
	blocklen := (rng.end - rng.start) + 1
	r.blocks[bn] = make([]byte, blocklen)
	io.ReadFull(reader, r.blocks[bn])
}

func (r *PartialHTTPReader) downloadRanges(ranges []requestByteRange) {
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = rng.String()
		}
		rangeString := strings.Join(rs, ",")

		req, _ := http.NewRequest("GET", r.URL.String(), nil)
		req.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeString))
		if r.etag != "" {
			req.Header.Set("If-Range", r.etag)
		} else if r.lastModified != "" {
			req.Header.Set("If-Range", r.lastModified)
		}

		resp, _ := r.client.Do(req)
		typ, params, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		defer resp.Body.Close()

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			r.mutex.Lock()
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					r.readRangeIntoBlock(ranges[i], part)
					i++
				} else {
					break
				}
			}
			r.mutex.Unlock()
		} else {
			r.mutex.Lock()
			for i := 0; i < int(resp.ContentLength)/r.blockSize; i++ {
				r.readRangeIntoBlock(ranges[i], resp.Body)
			}
			r.mutex.Unlock()
		}
	}
}

func (r *PartialHTTPReader) ReadAt(p []byte, off int64) (int, error) {
	if !r.initialized {
		err := r.init()
		if err != nil {
			return 0, err
		}
	}

	l := len(p)
	block := int(off / int64(r.blockSize))
	endBlock := int((off + int64(l)) / int64(r.blockSize))
	endBlockOff := (off + int64(l)) % int64(r.blockSize)
	nblocks := endBlock - block
	if endBlockOff > 0 {
		nblocks++
	}

	ranges := make([]requestByteRange, nblocks)
	nreq := 0
	r.mutex.RLock()
	for i := 0; i < nblocks; i++ {
		bn := block + i
		if _, ok := r.blocks[bn]; ok {
			continue
		}
		ranges[i] = requestByteRange{
			bn,
			int64(bn * r.blockSize),
			int64(((bn + 1) * r.blockSize) - 1),
		}
		if ranges[i].end > r.length {
			ranges[i].end = r.length
		}

		nreq++
	}
	r.mutex.RUnlock()
	ranges = ranges[:nreq]

	r.downloadRanges(ranges)
	return r.copyRangeToBuffer(p, off)
}

func (r *PartialHTTPReader) copyRangeToBuffer(p []byte, off int64) (int, error) {
	remaining := len(p)
	block := int(off / int64(r.blockSize))
	startOffset := off % int64(r.blockSize)
	ncopied := 0

	r.mutex.RLock()
	defer r.mutex.RUnlock()

	for remaining > 0 {
		copylen := r.blockSize
		if copylen > remaining {
			copylen = remaining
		}

		// if we need to copy more bytes than exist in this block
		if startOffset+int64(copylen) > int64(r.blockSize) {
			copylen = int(int64(r.blockSize) - startOffset)
		}

		if _, ok := r.blocks[block]; !ok {
			return 0, errors.New("fu?")
		}
		copy(p[ncopied:ncopied+copylen], r.blocks[block][startOffset:])

		remaining -= copylen
		ncopied += copylen

		block++
		startOffset = 0
	}

	return ncopied, nil
}

func (r *PartialHTTPReader) Length() int64 {
	if !r.initialized {
		r.init()
	}
	return r.length
}

func (r *PartialHTTPReader) Read(p []byte) (int, error) {
	nread, err := r.ReadAt(p, r.off)
	r.off += int64(nread)
	return nread, err
}

func (r *PartialHTTPReader) Seek(off int64, whence int) (int64, error) {
	if off < 0 {
		return 0, errors.New("seek to negative offset!")
	}

	switch whence {
	case 0:
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 1:
		off = r.off + off
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 2:
		off = r.Length() - off
		if off < 0 {
			return 0, errors.New("seek beyond beginning of file")
		}
		r.off = off
	}
	return r.off, nil
}

func (r *PartialHTTPReader) init() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.initialized = true
	r.blocks = make(map[int][]byte)
	if r.blockSize == 0 {
		r.blockSize = DefaultBlockSize
	}

	resp, _ := http.Head(r.URL.String())
	if resp.StatusCode == http.StatusNotFound {
		return errors.New("404")
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.Host + " does not support byte-ranged requests.")
	}

	r.etag = resp.Header.Get("ETag")
	r.lastModified = resp.Header.Get("Last-Modified")
	r.length = resp.ContentLength
	return nil
}

func NewPartialHTTPReader(u *url.URL) (*PartialHTTPReader, error) {
	r := &PartialHTTPReader{
		URL: u,
	}
	err := r.init()
	if err != nil {
		return nil, err
	}
	return r, nil
}
<commit_msg>Revert "If we get a non-ranged response, assume it is the entire file."<commit_after>package partial

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

const DefaultBlockSize int = 128 * 1024

type PartialHTTPReader struct {
	URL                *url.URL
	length             int64
	blockSize          int
	client             http.Client
	blocks             map[int][]byte
	mutex              sync.RWMutex
	initialized        bool
	etag, lastModified string

	off int64
}

type requestByteRange struct {
	block      int
	start, end int64
}

func (r requestByteRange) String() string {
	return fmt.Sprintf("%d-%d", r.start, r.end)
}

func (r *PartialHTTPReader) readRangeIntoBlock(rng requestByteRange, reader io.Reader) {
	bn := rng.block
	blocklen := (rng.end - rng.start) + 1
	r.blocks[bn] = make([]byte, blocklen)
	io.ReadFull(reader, r.blocks[bn])
}

func (r *PartialHTTPReader) downloadRanges(ranges []requestByteRange) {
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = rng.String()
		}
		rangeString := strings.Join(rs, ",")

		req, _ := http.NewRequest("GET", r.URL.String(), nil)
		req.Header.Set("Range", fmt.Sprintf("bytes=%s", rangeString))
		if r.etag != "" {
			req.Header.Set("If-Range", r.etag)
		} else if r.lastModified != "" {
			req.Header.Set("If-Range", r.lastModified)
		}

		resp, _ := r.client.Do(req)
		typ, params, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		defer resp.Body.Close()

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			r.mutex.Lock()
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					r.readRangeIntoBlock(ranges[i], part)
					i++
				} else {
					break
				}
			}
			r.mutex.Unlock()
		} else {
			r.mutex.Lock()
			r.readRangeIntoBlock(ranges[0], resp.Body)
			r.mutex.Unlock()
		}
	}
}

func (r *PartialHTTPReader) ReadAt(p []byte, off int64) (int, error) {
	if !r.initialized {
		err := r.init()
		if err != nil {
			return 0, err
		}
	}

	l := len(p)
	block := int(off / int64(r.blockSize))
	endBlock := int((off + int64(l)) / int64(r.blockSize))
	endBlockOff := (off + int64(l)) % int64(r.blockSize)
	nblocks := endBlock - block
	if endBlockOff > 0 {
		nblocks++
	}

	ranges := make([]requestByteRange, nblocks)
	nreq := 0
	r.mutex.RLock()
	for i := 0; i < nblocks; i++ {
		bn := block + i
		if _, ok := r.blocks[bn]; ok {
			continue
		}
		ranges[i] = requestByteRange{
			bn,
			int64(bn * r.blockSize),
			int64(((bn + 1) * r.blockSize) - 1),
		}
		if ranges[i].end > r.length {
			ranges[i].end = r.length
		}

		nreq++
	}
	r.mutex.RUnlock()
	ranges = ranges[:nreq]

	r.downloadRanges(ranges)
	return r.copyRangeToBuffer(p, off)
}

func (r *PartialHTTPReader) copyRangeToBuffer(p []byte, off int64) (int, error) {
	remaining := len(p)
	block := int(off / int64(r.blockSize))
	startOffset := off % int64(r.blockSize)
	ncopied := 0

	r.mutex.RLock()
	defer r.mutex.RUnlock()

	for remaining > 0 {
		copylen := r.blockSize
		if copylen > remaining {
			copylen = remaining
		}

		// if we need to copy more bytes than exist in this block
		if startOffset+int64(copylen) > int64(r.blockSize) {
			copylen = int(int64(r.blockSize) - startOffset)
		}

		if _, ok := r.blocks[block]; !ok {
			return 0, errors.New("fu?")
		}
		copy(p[ncopied:ncopied+copylen], r.blocks[block][startOffset:])

		remaining -= copylen
		ncopied += copylen

		block++
		startOffset = 0
	}

	return ncopied, nil
}

func (r *PartialHTTPReader) Length() int64 {
	if !r.initialized {
		r.init()
	}
	return r.length
}

func (r *PartialHTTPReader) Read(p []byte) (int, error) {
	nread, err := r.ReadAt(p, r.off)
	r.off += int64(nread)
	return nread, err
}

func (r *PartialHTTPReader) Seek(off int64, whence int) (int64, error) {
	if off < 0 {
		return 0, errors.New("seek to negative offset!")
	}

	switch whence {
	case 0:
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 1:
		off = r.off + off
		if off > r.Length() {
			return 0, errors.New("seek beyond end of file")
		}
		r.off = off
	case 2:
		off = r.Length() - off
		if off < 0 {
			return 0, errors.New("seek beyond beginning of file")
		}
		r.off = off
	}
	return r.off, nil
}

func (r *PartialHTTPReader) init() error {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.initialized = true
	r.blocks = make(map[int][]byte)
	if r.blockSize == 0 {
		r.blockSize = DefaultBlockSize
	}

	resp, _ := http.Head(r.URL.String())
	if resp.StatusCode == http.StatusNotFound {
		return errors.New("404")
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.Host + " does not support byte-ranged requests.")
	}

	r.etag = resp.Header.Get("ETag")
	r.lastModified = resp.Header.Get("Last-Modified")
	r.length = resp.ContentLength
	return nil
}

func NewPartialHTTPReader(u *url.URL) (*PartialHTTPReader, error) {
	r := &PartialHTTPReader{
		URL: u,
	}
	err := r.init()
	if err != nil {
		return nil, err
	}
	return r, nil
}
<|endoftext|>


<commit_before>package worker

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

// type JobStatus string

const (
	// Job statuses
	JobToRun   = "torun"
	JobRunning = "running"
	JobFailed  = "failed"
	JobKilled  = "killed"
	JobRun     = "done"
)

type Job struct {
	Id         uint64
	QueueJobId string

	// unit: minute
	// 0 to run job only once
	Interval uint64

	// zero time value to execute job immediately
	StartAt time.Time

	Cli        string
	WorkerName string
	Status     string
	PID        int

	// RunCounter uint64

	log *os.File
}

func (j *Job) GetWorker() (w *Worker, err error) {
	var ok bool
	if w, ok = workers[j.WorkerName]; !ok {
		err = fmt.Errorf("unknown worker: %s\n", j.WorkerName)
	}

	return
}

func RunJob(jobId uint64) {
	job := &Job{}
	if err := jobDB.Find(job, jobId).Error; err != nil {
		fmt.Printf("job (%d) do not existed\n", jobId)
	} else {
		job.Run()
	}
}

func (j *Job) Run() (err error) {
	parts := strings.Split(j.Cli, " ")
	name := parts[0]
	args := []string{"-job-id", strconv.FormatUint(j.Id, 10)}
	if len(parts) > 1 {
		args = append(args, parts[1:]...)
	}

	err = exec.Command(name, args...).Start()
	return
}

func (j *Job) UpdateStatus(status string) (err error) {
	old := j.Status
	j.Status = status
	if err = jobDB.Save(j).Error; err != nil {
		logger, erro := j.GetLogger()
		if erro == nil {
			fmt.Fprintf(logger, "can't update status from %s to %s: %s\n", old, j.Status, err)
		}

		return
	}

	return
}

var JobLoggerDir = "/tmp"

// TODO: undone
func (j *Job) GetLogger() (rw io.ReadWriter, err error) {
	if j.log == nil {
		path := fmt.Sprintf("%s/%s-%d.log", JobLoggerDir, j.WorkerName, j.Id)
		j.log, err = os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	}
	rw = j.log

	return
}

func (j *Job) SavePID() (err error) {
	j.PID = os.Getpid()
	if err = jobDB.Save(j).Error; err != nil {
		logger, erro := j.GetLogger()
		if erro == nil {
			fmt.Fprintf(logger, "can't save pid for job %d\n", j.Id)
		}

		return
	}

	return
}

func (j *Job) Stop() (err error) { return }

// func (j *Job) Kill() (err error)  { return }
func (j *Job) Start() (err error) { return }
<commit_msg>worker: add job counters<commit_after>package worker

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

// type JobStatus string

const (
	// Job statuses
	JobToRun   = "torun"
	JobRunning = "running"
	JobFailed  = "failed"
	JobKilled  = "killed"
	JobRun     = "done"
)

type Job struct {
	Id         uint64
	QueueJobId string

	// unit: minute
	// 0 to run job only once
	Interval uint64

	// zero time value to execute job immediately
	StartAt time.Time

	Cli        string
	WorkerName string
	Status     string
	PID        int

	RunCounter     uint64
	FailCounter    uint64
	SuccessCounter uint64
	KillCounter    uint64

	log *os.File
}

func (j *Job) GetWorker() (w *Worker, err error) {
	var ok bool
	if w, ok = workers[j.WorkerName]; !ok {
		err = fmt.Errorf("unknown worker: %s\n", j.WorkerName)
	}

	return
}

func RunJob(jobId uint64) {
	job := &Job{}
	if err := jobDB.Find(job, jobId).Error; err != nil {
		fmt.Printf("job (%d) do not existed\n", jobId)
	} else {
		job.Run()
	}
}

func (j *Job) Run() (err error) {
	parts := strings.Split(j.Cli, " ")
	name := parts[0]
	args := []string{"-job-id", strconv.FormatUint(j.Id, 10)}
	if len(parts) > 1 {
		args = append(args, parts[1:]...)
	}

	err = exec.Command(name, args...).Start()
	return
}

func (j *Job) UpdateStatus(status string) (err error) {
	old := j.Status
	j.Status = status
	switch status {
	case JobRunning:
		j.RunCounter++
	case JobFailed:
		j.FailCounter++
	case JobRun:
		j.SuccessCounter++
	case JobKilled:
		j.KillCounter++
	}

	if err = jobDB.Save(j).Error; err != nil {
		logger, erro := j.GetLogger()
		if erro == nil {
			fmt.Fprintf(logger, "can't update status from %s to %s: %s\n", old, j.Status, err)
		}

		return
	}

	return
}

var JobLoggerDir = "/tmp"

// TODO: undone
func (j *Job) GetLogger() (rw io.ReadWriter, err error) {
	if j.log == nil {
		path := fmt.Sprintf("%s/%s-%d.log", JobLoggerDir, j.WorkerName, j.Id)
		j.log, err = os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	}
	rw = j.log

	return
}

func (j *Job) SavePID() (err error) {
	j.PID = os.Getpid()
	if err = jobDB.Save(j).Error; err != nil {
		logger, erro := j.GetLogger()
		if erro == nil {
			fmt.Fprintf(logger, "can't save pid for job %d\n", j.Id)
		}

		return
	}

	return
}

func (j *Job) Stop() (err error) { return }

// func (j *Job) Kill() (err error)  { return }
func (j *Job) Start() (err error) { return }
<|endoftext|>


<commit_before>package winrmtest

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/masterzen/winrm/soap"
	"github.com/masterzen/xmlpath"
)

func Test_create_shell(t *testing.T) {
	w := &wsman{}

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action>
			</env:Header>
			<env:Body>
				<rsp:Shell>
					<rsp:InputStream>stdin</rsp:InputStream>
					<rsp:OutputStreams>stdout stderr</rsp:OutputStreams>
				</rsp:Shell>
			</env:Body>
		</env:Envelope>`))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	if contentType := res.HeaderMap.Get("Content-Type"); contentType != "application/soap+xml" {
		t.Errorf("Expected ContentType application/soap+xml was %s.\n", contentType)
	}

	env, err := xmlpath.Parse(res.Body)
	if err != nil {
		t.Error("Couldn't compile the SOAP response.")
	}

	xpath, _ := xmlpath.CompileWithNamespace(
		"//rsp:ShellId", soap.GetAllNamespaces())

	if _, found := xpath.String(env); !found {
		t.Error("Expected a Shell identifier.")
	}
}

func Test_exec_command(t *testing.T) {
	w := &wsman{}
	id := w.HandleCommand("echo tacos", func(out, err io.Writer) int {
		return 0
	})

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/shell/Command</a:Action>
			</env:Header>
			<env:Body>
				<rsp:CommandLine><rsp:Command>"echo tacos"</rsp:Command></rsp:CommandLine>
			</env:Body>
		</env:Envelope>`))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	env, err := xmlpath.Parse(res.Body)
	if err != nil {
		t.Error("Couldn't compile the SOAP response.")
	}

	xpath, _ := xmlpath.CompileWithNamespace(
		"//rsp:CommandId", soap.GetAllNamespaces())

	result, _ := xpath.String(env)
	if result != id {
		t.Error("Expected CommandId=%s but was \"%s\"", id, result)
	}
}

func Test_receive_command(t *testing.T) {
	w := &wsman{}
	id := w.HandleCommand("echo tacos", func(out, err io.Writer) int {
		out.Write([]byte("tacos"))
		return 0
	})

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(fmt.Sprintf(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive</a:Action>
			</env:Header>
			<env:Body>
				<rsp:Receive><rsp:DesiredStream CommandId="%s">stdout stderr</rsp:DesiredStream></rsp:Receive>
			</env:Body>
		</env:Envelope>`, id)))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	env, err := xmlpath.Parse(res.Body)
	if err != nil {
		t.Error("Couldn't compile the SOAP response.")
	}

	xpath, _ := xmlpath.CompileWithNamespace("//rsp:ReceiveResponse", soap.GetAllNamespaces())
	iter := xpath.Iter(env)
	if !iter.Next() {
		t.Error("Expected a ReceiveResponse element.")
	}

	xresp := iter.Node()
	xpath, _ = xmlpath.CompileWithNamespace(
		fmt.Sprintf("rsp:Stream[@CommandId='%s']", id), soap.GetAllNamespaces())
	iter = xpath.Iter(xresp)

	if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stdout") || iter.Node().String() != "dGFjb3M=" {
		t.Error("Expected an stdout Stream with the text \"dGFjb3M=\".")
	}

	if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stdout") || !nodeHasAttribute(iter.Node(), "End", "true") {
		t.Error("Expected an stdout Stream with an \"End\" attribute.")
	}

	if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stderr") || !nodeHasAttribute(iter.Node(), "End", "true") {
		t.Error("Expected an stderr Stream with an \"End\" attribute.")
	}

	xpath, _ = xmlpath.CompileWithNamespace(
		"//rsp:CommandState[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done']",
		soap.GetAllNamespaces())

	if _, found := xpath.String(env); !found {
		t.Error("Expected CommandState=\"Done\"")
	}

	xpath, _ = xmlpath.CompileWithNamespace("//rsp:CommandState/rsp:ExitCode", soap.GetAllNamespaces())
	if code, _ := xpath.String(env); code != "0" {
		t.Errorf("Expected ExitCode=0 but found \"%s\"\n", code)
	}
}

func Test_delete_shell(t *testing.T) {
	w := &wsman{}

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action>
			</env:Header>
		</env:Envelope>`))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	if res.Body.Len() != 0 {
		t.Errorf("Expected body to be empty but was \"%v\".", res.Body)
	}
}

func nodeHasAttribute(n *xmlpath.Node, name, value string) bool {
	xpath := xmlpath.MustCompile("attribute::" + name)
	if result, found := xpath.String(n); found {
		return result == value
	}

	return false
}
<commit_msg>Update test names<commit_after>package winrmtest

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/masterzen/winrm/soap"
	"github.com/masterzen/xmlpath"
)

func Test_creating_a_shell(t *testing.T) {
	w := &wsman{}

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action>
			</env:Header>
			<env:Body>
				<rsp:Shell>
					<rsp:InputStream>stdin</rsp:InputStream>
					<rsp:OutputStreams>stdout stderr</rsp:OutputStreams>
				</rsp:Shell>
			</env:Body>
		</env:Envelope>`))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	if contentType := res.HeaderMap.Get("Content-Type"); contentType != "application/soap+xml" {
		t.Errorf("Expected ContentType application/soap+xml was %s.\n", contentType)
	}

	env, err := xmlpath.Parse(res.Body)
	if err != nil {
		t.Error("Couldn't compile the SOAP response.")
	}

	xpath, _ := xmlpath.CompileWithNamespace(
		"//rsp:ShellId", soap.GetAllNamespaces())

	if _, found := xpath.String(env); !found {
		t.Error("Expected a Shell identifier.")
	}
}

func Test_executing_a_command(t *testing.T) {
	w := &wsman{}
	id := w.HandleCommand("echo tacos", func(out, err io.Writer) int {
		return 0
	})

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/shell/Command</a:Action>
			</env:Header>
			<env:Body>
				<rsp:CommandLine><rsp:Command>"echo tacos"</rsp:Command></rsp:CommandLine>
			</env:Body>
		</env:Envelope>`))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	env, err := xmlpath.Parse(res.Body)
	if err != nil {
		t.Error("Couldn't compile the SOAP response.")
	}

	xpath, _ := xmlpath.CompileWithNamespace(
		"//rsp:CommandId", soap.GetAllNamespaces())

	result, _ := xpath.String(env)
	if result != id {
		t.Error("Expected CommandId=%s but was \"%s\"", id, result)
	}
}

func Test_receiving_command_results(t *testing.T) {
	w := &wsman{}
	id := w.HandleCommand("echo tacos", func(out, err io.Writer) int {
		out.Write([]byte("tacos"))
		return 0
	})

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(fmt.Sprintf(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive</a:Action>
			</env:Header>
			<env:Body>
				<rsp:Receive><rsp:DesiredStream CommandId="%s">stdout stderr</rsp:DesiredStream></rsp:Receive>
			</env:Body>
		</env:Envelope>`, id)))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	env, err := xmlpath.Parse(res.Body)
	if err != nil {
		t.Error("Couldn't compile the SOAP response.")
	}

	xpath, _ := xmlpath.CompileWithNamespace("//rsp:ReceiveResponse", soap.GetAllNamespaces())
	iter := xpath.Iter(env)
	if !iter.Next() {
		t.Error("Expected a ReceiveResponse element.")
	}

	xresp := iter.Node()
	xpath, _ = xmlpath.CompileWithNamespace(
		fmt.Sprintf("rsp:Stream[@CommandId='%s']", id), soap.GetAllNamespaces())
	iter = xpath.Iter(xresp)

	if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stdout") || iter.Node().String() != "dGFjb3M=" {
		t.Error("Expected an stdout Stream with the text \"dGFjb3M=\".")
	}

	if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stdout") || !nodeHasAttribute(iter.Node(), "End", "true") {
		t.Error("Expected an stdout Stream with an \"End\" attribute.")
	}

	if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stderr") || !nodeHasAttribute(iter.Node(), "End", "true") {
		t.Error("Expected an stderr Stream with an \"End\" attribute.")
	}

	xpath, _ = xmlpath.CompileWithNamespace(
		"//rsp:CommandState[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done']",
		soap.GetAllNamespaces())

	if _, found := xpath.String(env); !found {
		t.Error("Expected CommandState=\"Done\"")
	}

	xpath, _ = xmlpath.CompileWithNamespace("//rsp:CommandState/rsp:ExitCode", soap.GetAllNamespaces())
	if code, _ := xpath.String(env); code != "0" {
		t.Errorf("Expected ExitCode=0 but found \"%s\"\n", code)
	}
}

func Test_deleting_a_shell(t *testing.T) {
	w := &wsman{}

	res := httptest.NewRecorder()
	req, _ := http.NewRequest("POST", "", strings.NewReader(`
		<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">
			<env:Header>
				<a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action>
			</env:Header>
		</env:Envelope>`))

	w.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Errorf("Expected 200 OK but was %d.\n", res.Code)
	}

	if res.Body.Len() != 0 {
		t.Errorf("Expected body to be empty but was \"%v\".", res.Body)
	}
}

func nodeHasAttribute(n *xmlpath.Node, name, value string) bool {
	xpath := xmlpath.MustCompile("attribute::" + name)
	if result, found := xpath.String(n); found {
		return result == value
	}

	return false
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))

	if err != nil {
		//do nothing
	}
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	// added by chenlin@20170308
	if len(args) != 0 {
		return nil, errors.New("incorrect args")
	}
	// adminCert, err := stub.GetCallerMetadata()
	// if err != nil {
	// 	return nil, errors.New("failed getting metadata")
	// }
	// if len(adminCert) == 0 {
	// 	return nil, errors.New("invalid admin certificate. Empty")
	// }
	stub.PutState("admin", []byte("hahahahaha"))
	err := stub.CreateTable("AssetsOwnership", []*shim.ColumnDefinition{
		&shim.ColumnDefinition{Name: "Asset", Type: shim.ColumnDefinition_STRING, Key: true},
		&shim.ColumnDefinition{Name: "Owner", Type: shim.ColumnDefinition_BYTES, Key: false},
	})
	if err != nil {
		return nil, errors.New("Failed creating AssetsOnwership table.")
	}
	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "putState":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		value := []byte(args[1])
		err := stub.PutState(key, value)
		if err != nil {
			return nil, err
		}
		return value, err
	case "delState":
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		err := stub.DelState(key)
		if err != nil {
			return nil, err
		}
		result := key + "has been deleted"
		return []byte(result), err

	case "createTable":
		err := stub.CreateTable("AssetsOwnership", []*shim.ColumnDefinition{
			&shim.ColumnDefinition{Name: "Asset", Type: shim.ColumnDefinition_STRING, Key: true},
			&shim.ColumnDefinition{Name: "Owner", Type: shim.ColumnDefinition_BYTES, Key: false},
		})
		if err != nil {
			return nil, errors.New("Failed creating AssetsOnwership table.")
		}
		return nil, nil

	case "insertTable":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		asset := args[0]
		owner := []byte(args[1])
		row := shim.Row{
			Columns: []*shim.Column{
				&shim.Column{Value: &shim.Column_String_{String_: asset}},
				&shim.Column{Value: &shim.Column_Bytes{Bytes: owner}}}}
		ok, err := stub.InsertRow("AssetsOwnership", row)
		if !ok && err == nil {
			return nil, errors.New("asset was already assigned")
		}
		return nil, nil //end of insertTable

	}
	return nil, nil
}

func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "getTxID":
		txID := stub.GetTxID()
		result := []byte(txID)
		return result, nil
	case "getTxTimestamp":
		time, err := stub.GetTxTimestamp()
		result := []byte(time.String()) //时间转换为字符串，time.String()
		return result, err
	case "getStringArgs":
		strList := stub.GetStringArgs()
		var result string
		for index := 0; index < len(strList); index++ {
			result += "***" + strList[index] + "***"
		}
		return []byte(result), nil

	case "getState":
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		result, err := stub.GetState(key)

		if err != nil {
			return nil, errors.New("Failed in function getState")
		}
		return result, err

	case "getCallerCert":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetCallerCertificate()
		if err != nil {
			return nil, errors.New("Failed in function getCallerCert")
		}
		return result, err

	case "getCallerMetadata":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetCallerMetadata()
		if err != nil {
			return nil, errors.New("Failed in function getCallerMetadata")
		}
		fmt.Println(result)
		return result, err
		// return []byte("getCallerMetadata"), err
	case "getBinding":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetBinding()

		if err != nil {
			return nil, errors.New("Failed in function getBinding")
		}
		fmt.Println(result)
		return result, err
		// return []byte("getBinding"), err
	case "getPayload":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetPayload()

		if err != nil {
			return nil, errors.New("Failed in function getPayload")
		}
		fmt.Println(result)
		return result, err
	// return []byte("getPayload"), err
	case "getRow":
		fmt.Println("*********************")
		fmt.Println("*********************")
		fmt.Println("*********************")
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		asset := args[0]
		var columns []shim.Column
		col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
		columns = append(columns, col1)

		row, err := stub.GetRow("AssetsOwnership", columns)
		if err != nil {
			return nil, errors.New("failed in function getRow")
		}

		val0 := row.Columns[0].GetString_()
		val1 := row.Columns[1].GetString_()
		result := "****" + val0 + "****" + val1
		return []byte(result), err
	//end of "getRow"

	case "getRowDefinition":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		asset := args[0]
		owner := []byte(args[1])

		col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
		col2 := shim.Column{Value: &shim.Column_Bytes{Bytes: owner}}
		var columns []shim.Column
		columns = append(columns, col1)
		columns = append(columns, col2)

		// val0 := columns[0].GetString_()
		// val1 := columns[1].GetString_()
		result := asset + "********" + string(owner) + "*****end of input ****"
		result += col1.GetString_() + "********" + col2.GetString_()
		return []byte(result), nil
	}

	return nil, nil
}
<commit_msg>updated<commit_after>package main

import (
	"errors"
	"fmt"

	"github.com/hyperledger/fabric/core/chaincode/shim"
)

type SimpleChaincode struct {
}

func main() {
	err := shim.Start(new(SimpleChaincode))

	if err != nil {
		//do nothing
	}
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	// added by chenlin@20170308
	if len(args) != 0 {
		return nil, errors.New("incorrect args")
	}
	// adminCert, err := stub.GetCallerMetadata()
	// if err != nil {
	// 	return nil, errors.New("failed getting metadata")
	// }
	// if len(adminCert) == 0 {
	// 	return nil, errors.New("invalid admin certificate. Empty")
	// }
	stub.PutState("admin", []byte("hahahahaha"))
	err := stub.CreateTable("AssetsOwnership", []*shim.ColumnDefinition{
		&shim.ColumnDefinition{Name: "Asset", Type: shim.ColumnDefinition_STRING, Key: true},
		&shim.ColumnDefinition{Name: "Owner", Type: shim.ColumnDefinition_BYTES, Key: false},
	})
	if err != nil {
		return nil, errors.New("Failed creating AssetsOnwership table.")
	}
	return nil, nil
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "putState":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		value := []byte(args[1])
		err := stub.PutState(key, value)
		if err != nil {
			return nil, err
		}
		return value, err
	case "delState":
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		err := stub.DelState(key)
		if err != nil {
			return nil, err
		}
		result := key + "has been deleted"
		return []byte(result), err

	case "createTable":
		err := stub.CreateTable("AssetsOwnership", []*shim.ColumnDefinition{
			&shim.ColumnDefinition{Name: "Asset", Type: shim.ColumnDefinition_STRING, Key: true},
			&shim.ColumnDefinition{Name: "Owner", Type: shim.ColumnDefinition_BYTES, Key: false},
		})
		if err != nil {
			return nil, errors.New("Failed creating AssetsOnwership table.")
		}
		return nil, nil

	case "insertTable":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		asset := args[0]
		owner := []byte(args[1])
		row := shim.Row{
			Columns: []*shim.Column{
				&shim.Column{Value: &shim.Column_String_{String_: asset}},
				&shim.Column{Value: &shim.Column_Bytes{Bytes: owner}}}}
		ok, err := stub.InsertRow("AssetsOwnership", row)
		if !ok && err == nil {
			return nil, errors.New("asset was already assigned")
		}
		return nil, nil //end of insertTable

	}
	return nil, nil
}

func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	switch function {
	case "getTxID":
		txID := stub.GetTxID()
		result := []byte(txID)
		return result, nil
	case "getTxTimestamp":
		time, err := stub.GetTxTimestamp()
		result := []byte(time.String()) //时间转换为字符串，time.String()
		return result, err
	case "getStringArgs":
		strList := stub.GetStringArgs()
		var result string
		for index := 0; index < len(strList); index++ {
			result += "***" + strList[index] + "***"
		}
		return []byte(result), nil

	case "getState":
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		key := args[0]
		result, err := stub.GetState(key)

		if err != nil {
			return nil, errors.New("Failed in function getState")
		}
		return result, err

	case "getCallerCert":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetCallerCertificate()
		if err != nil {
			return nil, errors.New("Failed in function getCallerCert")
		}
		return result, err

	case "getCallerMetadata":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetCallerMetadata()
		if err != nil {
			return nil, errors.New("Failed in function getCallerMetadata")
		}
		fmt.Println(result)
		return result, err
		// return []byte("getCallerMetadata"), err
	case "getBinding":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetBinding()

		if err != nil {
			return nil, errors.New("Failed in function getBinding")
		}
		fmt.Println(result)
		return result, err
		// return []byte("getBinding"), err
	case "getPayload":
		if len(args) != 0 {
			return nil, errors.New("incorrect args")
		}
		result, err := stub.GetPayload()

		if err != nil {
			return nil, errors.New("Failed in function getPayload")
		}
		fmt.Println(result)
		return result, err
	// return []byte("getPayload"), err
	case "getRow":
		fmt.Println("*********************")
		fmt.Println("*********************")
		fmt.Println("*********************")
		if len(args) != 1 {
			return nil, errors.New("incorrect args")
		}
		asset := args[0]
		var columns []shim.Column
		col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
		columns = append(columns, col1)

		row, err := stub.GetRow("AssetsOwnership", columns)
		if err != nil {
			return nil, errors.New("failed in function getRow")
		}

		val0 := row.Columns[0].GetString_()
		val1 := row.Columns[1].GetString_()
		result := "****" + val0 + "****" + val1
		return []byte(result), err
	//end of "getRow"

	case "getRowDefinition":
		if len(args) != 2 {
			return nil, errors.New("incorrect args")
		}
		// asset := args[0]
		owner := []byte(args[1])

		// col1 := shim.Column{Value: &shim.Column_String_{String_: asset}}
		// col2 := shim.Column{Value: &shim.Column_Bytes{Bytes: owner}}

		col2 := shim.Column{Value: &shim.Column_Bytes{Bytes: owner}}
		// var columns []shim.Column
		// columns = append(columns, col1)
		// columns = append(columns, col2)

		// val0 := columns[0].GetString_()
		// val1 := columns[1].GetString_()
		// result := asset + "********" + string(owner) + "*****end of input ****"
		// result += col1.GetString_() + "****" + col2.GetString_()
		result := col2.GetBytes()
		return result, nil
	}

	return nil, nil
}
<|endoftext|>


<commit_before>// detox.go
package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"unicode"
)

const lastModified = "18 Sep 20"

/*
  REVISION HISTORY
  ----------------
  14 Sep 20 -- First version, based on code from fromfx.go.
  16 Sep 20 -- Fixed when it changed '-' to '_'.  And I added IsPunct so it would change comma and other punctuations.
  18 Sep 20 -- I had it ignore '~' and not change it, as it was included as a punctuation mark by IsPunct.
*/

func main() {
	//var e error
    var globPattern string

    fmt.Println()

	if len(os.Args) <= 1 { // this means no arguments on line, as the program name is always first argument passed in os.Args
                fmt.Println(" Usage: detox globPattern")
                os.Exit(1)
	}

	globPattern = os.Args[1]
	startDirectory, _ := os.Getwd() // startDirectory is a string
	fmt.Println()
	fmt.Printf(" detox.go lastModified is %s, will use globbing pattern of %q and will start in %s. \n", lastModified, globPattern, startDirectory)
	fmt.Println()

	files := myReadDirNames(startDirectory)
    ctr := 0
	for _, fn := range files {
		name := strings.ToLower(fn)
		if BOOL, _ := filepath.Match(globPattern, name); BOOL {
			detoxedName, toxic := detoxFilename(fn)
			if toxic {
				err := os.Rename(fn, detoxedName)
				if err != nil {
					//fmt.Fprintf(os.Stderr, " Error from rename function for name %s -> %s: %v \n", fn, detoxedName, err)
					fmt.Fprintln(os.Stderr, err)
				}
				ctr++
			fmt.Printf(" filename %q -> %q \n", fn, detoxedName)
			}
		}
	}
	if ctr > 1 {
		fmt.Printf("\n Total of %d files were renamed. \n", ctr)
	} else if ctr == 1 {
		fmt.Printf("\n One file was renamed. \n")
	} else {
		fmt.Println(" No files were renamed.")
	}
} // end main

//---------------------------------------------------------------------------------------------------
func detoxFilename (fname string) (string, bool) {
    var toxic bool

	buf := bytes.NewBufferString(fname)

    byteslice := make([]byte,0,100)

    for {
		r, size, err := buf.ReadRune()
		if err == io.EOF {  // only valid exit from this loop
			name := string(byteslice)
			return name, toxic
		} else if err != nil {
			fmt.Fprintln(os.Stderr, err )
			return "", false // returning toxic as false to not do anything with this name as it got an error of some type.
		}
		if size > 1 {
			toxic = true
			byteslice = append(byteslice,'_')
		} else if unicode.IsSpace(r) {
			toxic = true
			byteslice = append(byteslice,'_')
		} else if unicode.IsControl(r) {
			toxic = true
			byteslice = append(byteslice,'_')
		} else if r == '.' || r == '_' || r == '-' || r == '~' {
			byteslice = append(byteslice, byte(r))
		} else if unicode.IsSymbol(r) || unicode.IsPunct(r) {
			toxic = true
			byteslice = append(byteslice,'_')
		} else {
			byteslice = append(byteslice, byte(r))
		}
    }
} // end detoxFilename

// ------------------------------- myReadDirNames -----------------------------------
func myReadDirNames(dir string) []string { // based on the code from dsrt and descendents

	dirname, err := os.Open(dir)
	if err != nil {
		return nil
	}
	defer dirname.Close()

	names, err := dirname.Readdirnames(0) // zero means read all names into the returned []string
	if err != nil {
		return nil
	}
    dirname.Close()
    return names
} // myReadDirNames

// END detox.go
<commit_msg>09/23/2020 07:18:25 AM detox/detox.go<commit_after>// detox.go
package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"unicode"
)

const lastModified = "22 Sep 20"

/*
  REVISION HISTORY
  ----------------
  14 Sep 20 -- First version, based on code from fromfx.go.
  16 Sep 20 -- Fixed when it changed '-' to '_'.  And I added IsPunct so it would change comma and other punctuations.
  18 Sep 20 -- I had it ignore '~' and not change it, as it was included as a punctuation mark by IsPunct.
  22 Sep 20 -- Fixed case issue by converting pattern to all lower case also.  I forgot that before.  And I will allow no pattern to be entered.
*/

func main() {
	//var e error
	var globPattern string

	fmt.Println()

	if len(os.Args) <= 1 { // this means no arguments on line, as the program name is always first argument passed in os.Args
		globPattern = "*"
	} else {
		globPattern = strings.ToLower(os.Args[1])
	}

	startDirectory, _ := os.Getwd() // startDirectory is a string
	fmt.Println()
	fmt.Printf(" detox.go lastModified is %s, will use globbing pattern of %q and will start in %s. \n", lastModified, globPattern, startDirectory)
	fmt.Println()

	files := myReadDirNames(startDirectory)
	ctr := 0
	for _, fn := range files {
		name := strings.ToLower(fn)
		if BOOL, _ := filepath.Match(globPattern, name); BOOL {
			detoxedName, toxic := detoxFilename(fn)
			if toxic {
				err := os.Rename(fn, detoxedName)
				if err != nil {
					//fmt.Fprintf(os.Stderr, " Error from rename function for name %s -> %s: %v \n", fn, detoxedName, err)
					fmt.Fprintln(os.Stderr, err)
				}
				ctr++
				fmt.Printf(" filename %q -> %q \n", fn, detoxedName)
			}
		}
	}
	if ctr > 1 {
		fmt.Printf("\n Total of %d files were renamed. \n", ctr)
	} else if ctr == 1 {
		fmt.Printf("\n One file was renamed. \n")
	} else {
		fmt.Println(" No files were renamed.")
	}
} // end main

//---------------------------------------------------------------------------------------------------
func detoxFilename(fname string) (string, bool) {
	var toxic bool

	buf := bytes.NewBufferString(fname)

	byteslice := make([]byte, 0, 100)

	for {
		r, size, err := buf.ReadRune()
		if err == io.EOF { // only valid exit from this loop
			name := string(byteslice)
			return name, toxic
		} else if err != nil {
			fmt.Fprintln(os.Stderr, err)
			return "", false // returning toxic as false to not do anything with this name as it got an error of some type.
		}
		if size > 1 {
			toxic = true
			byteslice = append(byteslice, '_')
		} else if unicode.IsSpace(r) {
			toxic = true
			byteslice = append(byteslice, '_')
		} else if unicode.IsControl(r) {
			toxic = true
			byteslice = append(byteslice, '_')
		} else if r == '.' || r == '_' || r == '-' || r == '~' {
			byteslice = append(byteslice, byte(r))
		} else if unicode.IsSymbol(r) || unicode.IsPunct(r) {
			toxic = true
			byteslice = append(byteslice, '_')
		} else {
			byteslice = append(byteslice, byte(r))
		}
	}
} // end detoxFilename

// ------------------------------- myReadDirNames -----------------------------------
func myReadDirNames(dir string) []string { // based on the code from dsrt and descendents

	dirname, err := os.Open(dir)
	if err != nil {
		return nil
	}
	defer dirname.Close()

	names, err := dirname.Readdirnames(0) // zero means read all names into the returned []string
	if err != nil {
		return nil
	}
	dirname.Close()
	return names
} // myReadDirNames

// END detox.go
<|endoftext|>


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

// I can rip off tests from the standard library I guess
// as long as i keep that header, I think that's what
// BSD means, but I'm not sure... :-)

package bigbyte_test

import (
    . "github.com/bjarneh/bigbyte"
    "testing"
    "io/ioutil"
    "bytes"
    "os"
    "path"
)

var haystack    []byte

func init(){

    var err os.Error
    var srcroot, me string

    srcroot = os.Getenv("SRCROOT")
    me  = "github.com/bjarneh/bigbyte"

    // we are testing with godag
    if srcroot != "" {
        haystack, err = ioutil.ReadFile(path.Join(srcroot,me,"wild-duck-no-utf8.txt"))
    }else{// we are testing with Makefile
        haystack, err = ioutil.ReadFile("wild-duck-no-utf8.txt")
    }

    if err != nil {
        panic("could not read file needed for Benchmark")
    }
}

type BinOpTest struct {
	a string
	b string
	i int
}

var indexTests = []BinOpTest{
	{"", "", 0},
	{"", "a", -1},
	{"", "foo", -1},
	{"fo", "foo", -1},
	{"foo", "foo", 0},
	{"oofofoofooo", "f", 2},
	{"oofofoofooo", "foo", 4},
	{"barfoobarfoo", "foo", 3},
	{"foo", "", 0},
	{"foo", "o", 1},
	{"abcABCabc", "A", 3},
	// cases with one byte strings - test IndexByte and special case in Index()
	{"", "a", -1},
	{"x", "a", -1},
	{"x", "x", 0},
	{"abc", "a", 0},
	{"abc", "b", 1},
	{"abc", "c", 2},
	{"abc", "x", -1},
	{"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33},
	{"foofyfoobarfoobar", "y", 4},
	{"oooooooooooooooooooooo", "r", -1},
}

// Execute f on each test case.  funcName should be the name of f; it's used
// in failure reports.
func runIndexTests(t *testing.T, f func(s, sep []byte) int, funcName string, testCases []BinOpTest) {
	for _, test := range testCases {
		a := []byte(test.a)
		b := []byte(test.b)
		actual := f(a, b)
		if actual != test.i {
			t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, b, actual, test.i)
		}
	}
}

func TestIndexBMH(t *testing.T)  { runIndexTests(t, IndexBMH, "IndexBMH", indexTests) }

// using the same philosophy as the one the Go developers used for testing,
// only now for benching :-)

type BenchTest struct {
    find string
    where int
}

var benchTests = []BenchTest{
	{"lkjlxkcj lsdjfl s", -1},
	{"strækker armene ud og mumler.  Lovet være herren;", 276159},
	{"her viser jo hjertelag,", 16573},
	{"I alt det strå?  går mod den øverste dør til", 58757},
	{"LKJoisdjflksjdf iOJJDFjl skdfjls df", -1},
}

func BenchmarkIndexBMH(b *testing.B){
    runBenchTests(b, IndexBMH, "bigbyte.IndexBMH", benchTests)
}

func BenchmarkIndex(b *testing.B){
    runBenchTests(b, bytes.Index, "bytes.Index", benchTests)
}

func runBenchTests(b *testing.B, f func(s, sep []byte) int, funcName string, testCases []BenchTest){

    var needle []byte
    var result int

    for _, bt := range testCases {
        needle = []byte(bt.find)
        result = f(haystack, needle)
        if result != bt.where {
            panic("function: "+funcName+", does not work properly")
        }
        for i := 0; i < b.N; i++ {
            _ = f(haystack, needle)
        }
    }
}

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

// I can rip off tests from the standard library I guess
// as long as i keep that header, I think that's what
// BSD means, but I'm not sure... :-)

package bigbyte_test

import (
	"bytes"
	. "github.com/bjarneh/bigbyte"
	"io/ioutil"
	"os"
	"path"
	"testing"
)

var haystack []byte

func init() {

	var err error
	var srcroot, me string

	srcroot = os.Getenv("SRCROOT")
	me = "github.com/bjarneh/bigbyte"

	// we are testing with godag
	if srcroot != "" {
		haystack, err = ioutil.ReadFile(path.Join(srcroot, me, "wild-duck-no-utf8.txt"))
	} else { // we are testing with Makefile
		haystack, err = ioutil.ReadFile("wild-duck-no-utf8.txt")
	}

	if err != nil {
		panic("could not read file needed for Benchmark")
	}
}

type BinOpTest struct {
	a string
	b string
	i int
}

var indexTests = []BinOpTest{
	{"", "", 0},
	{"", "a", -1},
	{"", "foo", -1},
	{"fo", "foo", -1},
	{"foo", "foo", 0},
	{"oofofoofooo", "f", 2},
	{"oofofoofooo", "foo", 4},
	{"barfoobarfoo", "foo", 3},
	{"foo", "", 0},
	{"foo", "o", 1},
	{"abcABCabc", "A", 3},
	// cases with one byte strings - test IndexByte and special case in Index()
	{"", "a", -1},
	{"x", "a", -1},
	{"x", "x", 0},
	{"abc", "a", 0},
	{"abc", "b", 1},
	{"abc", "c", 2},
	{"abc", "x", -1},
	{"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33},
	{"foofyfoobarfoobar", "y", 4},
	{"oooooooooooooooooooooo", "r", -1},
}

// Execute f on each test case.  funcName should be the name of f; it's used
// in failure reports.
func runIndexTests(t *testing.T, f func(s, sep []byte) int, funcName string, testCases []BinOpTest) {
	for _, test := range testCases {
		a := []byte(test.a)
		b := []byte(test.b)
		actual := f(a, b)
		if actual != test.i {
			t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, b, actual, test.i)
		}
	}
}

func TestIndexBMH(t *testing.T) { runIndexTests(t, IndexBMH, "IndexBMH", indexTests) }

// using the same philosophy as the one the Go developers used for testing,
// only now for benching :-)

type BenchTest struct {
	find  string
	where int
}

var benchTests = []BenchTest{
	{"lkjlxkcj lsdjfl s", -1},
	{"strækker armene ud og mumler.  Lovet være herren;", 276159},
	{"her viser jo hjertelag,", 16573},
	{"I alt det strå?  går mod den øverste dør til", 58757},
	{"LKJoisdjflksjdf iOJJDFjl skdfjls df", -1},
}

func BenchmarkIndexBMH(b *testing.B) {
	runBenchTests(b, IndexBMH, "bigbyte.IndexBMH", benchTests)
}

func BenchmarkIndex(b *testing.B) {
	runBenchTests(b, bytes.Index, "bytes.Index", benchTests)
}

func runBenchTests(b *testing.B, f func(s, sep []byte) int, funcName string, testCases []BenchTest) {

	var needle []byte
	var result int

	for _, bt := range testCases {
		needle = []byte(bt.find)
		result = f(haystack, needle)
		if result != bt.where {
			panic("function: " + funcName + ", does not work properly")
		}
		for i := 0; i < b.N; i++ {
			_ = f(haystack, needle)
		}
	}
}
<|endoftext|>


<commit_before>package vsolver

import (
	"fmt"

	"github.com/armon/go-radix"
)

// mkbmu - "make bimodal universe"
//
// Assembles a universe of projects and packages - that is, a slice of depspecs -
// from discrete project and package declarations.
//
// Projects must be declared before any containing packages, or this function
// will panic. Projects cannot be declared more than once (else panic). Projects
// cannot be nested. A project does not imply an importable package; both must
// be explicitly declared.
func mkbmu(list ...bmelem) (ret []depspec) {
	xt := radix.New()

	var rootname string
	for k, elem := range list {
		switch p := elem.(type) {
		case depspec:
			if k == 0 {
				rootname = string(p.Name())
			}
			xt.WalkPath(string(p.Name()), func(s string, v interface{}) bool {
				panic(fmt.Sprintf("Got bmproj with name %s, but already had %s. Do not duplicate or declare projects relative to each other.", p.Name(), s))
			})

			xt.Insert(string(p.Name()), p)
		case tpkg:
			var success bool
			xt.WalkPath(p.path, func(s string, v interface{}) bool {
				if proj, ok := v.(depspec); ok {
					proj.pkgs = append(proj.pkgs, p)
					_, ok = xt.Insert(string(proj.Name()), proj)
					success = true
					//if !ok {
					//panic(fmt.Sprintf("Failed to reinsert updated bmproj %s", proj.Name()))
					//}
				}
				return false
			})
			if !success {
				panic(fmt.Sprintf("Couldn't find parent project for %s. mkbmu is sensitive to parameter order; always declare the root project first.", p.path))
			}
		default:
			panic(fmt.Sprintf("Unrecognized bmelem type %T", elem))
		}
	}

	// Ensure root always goes in first
	val, _ := xt.Get(rootname)
	ret = append(ret, val.(depspec))
	for _, pi := range xt.ToMap() {
		if p, ok := pi.(depspec); ok {
			ret = append(ret, p)
		}
	}

	return
}

// mkpkr - "make package" - makes a tpkg appropriate for use in bimodal testing
func mkpkg(path string, imports ...string) tpkg {
	return tpkg{
		path:    path,
		imports: imports,
	}
}

func init() {
	for k, fix := range bimodalFixtures {
		// Assign the name into the fixture itself
		fix.n = k
		bimodalFixtures[k] = fix
	}
}

// Fixtures that rely on simulated bimodal (project and package-level)
// analysis for correct operation. These all have some extra work done on
// them down in init().
var bimodalFixtures = map[string]bimodalFixture{
	// Simple case, ensures that we do the very basics of picking up and
	// including a single, simple import that is expressed an import
	"simple bimodal add": {
		ds: mkbmu(
			dsv("root 0.0.0"),
			mkpkg("root", "a"),
			dsv("a 1.0.0"),
			mkpkg("a"),
		),
		r: mkresults(
			"a 1.0.0",
		),
	},
	// Ensure it works when the import jump is not from the package with the
	// same path as root, but from a subpkg
	"subpkg bimodal add": {
		ds: mkbmu(
			dsv("root 0.0.0"),
			mkpkg("root", "root/foo"),
			mkpkg("root/foo", "a"),
			dsv("a 1.0.0"),
			mkpkg("a"),
		),
		r: mkresults(
			"a 1.0.0",
		),
	},
	// Ensure that if a constraint is expressed, but no actual import exists,
	// then the constraint is disregarded - the project named in the constraint
	// is not part of the solution.
	"ignore constraint without import": {
		ds: mkbmu(
			dsv("root 0.0.0", "a 1.0.0"),
			mkpkg("root", "root/foo"),
			dsv("a 1.0.0"),
			mkpkg("a"),
		),
		r: mkresults(),
	},
}

//type bmproj struct {
//n       ProjectName
//v       Version
//deps    []ProjectDep
//devdeps []ProjectDep
//pkgs    []tpkg
//}

//var _ Manifest = bmproj{}

//// impl Spec interface
//func (p bmproj) GetDependencies() []ProjectDep {
//return p.deps
//}

//// impl Spec interface
//func (p bmproj) GetDevDependencies() []ProjectDep {
//return p.devdeps
//}

//// impl Spec interface
//func (p bmproj) Name() ProjectName {
//return p.n
//}

type tpkg struct {
	// Full import path of this package
	path string
	// Slice of full paths to its virtual imports
	imports []string
}

type bmelem interface {
	_bmelem()
}

func (p tpkg) _bmelem() {}

type bimodalFixture struct {
	// name of this fixture datum
	n string
	// bimodal project. first is always treated as root project
	ds []depspec
	// results; map of name/version pairs
	r map[string]Version
	// max attempts the solver should need to find solution. 0 means no limit
	maxAttempts int
	// Use downgrade instead of default upgrade sorter
	downgrade bool
	// lock file simulator, if one's to be used at all
	l fixLock
	// projects expected to have errors, if any
	errp []string
	// request up/downgrade to all projects
	changeall bool
}

func (f bimodalFixture) name() string {
	return f.n
}

func (f bimodalFixture) specs() []depspec {
	return f.ds
}

func (f bimodalFixture) maxTries() int {
	return f.maxAttempts
}

func (f bimodalFixture) expectErrs() []string {
	return f.errp
}

func (f bimodalFixture) result() map[string]Version {
	return f.r
}

type bmSourceManager struct {
	depspecSourceManager
}

var _ SourceManager = &bmSourceManager{}

func newbmSM(ds []depspec) *bmSourceManager {
	sm := &bmSourceManager{}
	sm.specs = ds
	sm.rm = computeBimodalExternalMap(ds)

	return sm
}

func (sm *bmSourceManager) ExternalReach(n ProjectName, v Version) (map[string][]string, error) {
	for _, ds := range sm.specs {
		if ds.n == n && v.Matches(ds.v) {
			rm := make(map[string][]string)
			for _, pkg := range ds.pkgs {
				rm[pkg.path] = pkg.imports
			}

			return rm, nil
		}
	}

	// TODO proper solver errs
	return nil, fmt.Errorf("No reach data for %s at version %s", n, v)
}

func computeBimodalExternalMap(ds []depspec) map[pident][]string {
	rm := make(map[pident][]string)

	for _, d := range ds {
		exmap := make(map[string]struct{})

		for _, pkg := range d.pkgs {
			for _, ex := range pkg.imports {
				exmap[ex] = struct{}{}
			}
		}

		var list []string
		for ex := range exmap {
			list = append(list, ex)
		}
		id := pident{
			n: d.n,
			v: d.v,
		}
		rm[id] = list
	}

	return rm
}
<commit_msg>Don't double-add the root spec<commit_after>package vsolver

import (
	"fmt"
	"strings"

	"github.com/armon/go-radix"
)

// mkbmu - "make bimodal universe"
//
// Assembles a universe of projects and packages - that is, a slice of depspecs -
// from discrete project and package declarations.
//
// Projects must be declared before any containing packages, or this function
// will panic. Projects cannot be declared more than once (else panic). Projects
// cannot be nested. A project does not imply an importable package; both must
// be explicitly declared.
func mkbmu(list ...bmelem) (ret []depspec) {
	xt := radix.New()

	var rootname string
	for k, elem := range list {
		switch p := elem.(type) {
		case depspec:
			if k == 0 {
				rootname = string(p.Name())
			}
			xt.WalkPath(string(p.Name()), func(s string, v interface{}) bool {
				panic(fmt.Sprintf("Got bmproj with name %s, but already had %s. Do not duplicate or declare projects relative to each other.", p.Name(), s))
			})

			xt.Insert(string(p.Name()), p)
		case tpkg:
			var success bool
			xt.WalkPath(p.path, func(s string, v interface{}) bool {
				if proj, ok := v.(depspec); ok {
					proj.pkgs = append(proj.pkgs, p)
					_, ok = xt.Insert(string(proj.Name()), proj)
					success = true
					//if !ok {
					//panic(fmt.Sprintf("Failed to reinsert updated bmproj %s", proj.Name()))
					//}
				}
				return false
			})
			if !success {
				panic(fmt.Sprintf("Couldn't find parent project for %s. mkbmu is sensitive to parameter order; always declare the root project first.", p.path))
			}
		default:
			panic(fmt.Sprintf("Unrecognized bmelem type %T", elem))
		}
	}

	// Ensure root always goes in first
	val, _ := xt.Get(rootname)
	ret = append(ret, val.(depspec))
	for _, pi := range xt.ToMap() {
		if p, ok := pi.(depspec); ok && string(p.n) != rootname {
			ret = append(ret, p)
		}
	}

	return
}

// mkpkr - "make package" - makes a tpkg appropriate for use in bimodal testing
func mkpkg(path string, imports ...string) tpkg {
	return tpkg{
		path:    path,
		imports: imports,
	}
}

func init() {
	for k, fix := range bimodalFixtures {
		// Assign the name into the fixture itself
		fix.n = k
		bimodalFixtures[k] = fix
	}
}

// Fixtures that rely on simulated bimodal (project and package-level)
// analysis for correct operation. These all have some extra work done on
// them down in init().
var bimodalFixtures = map[string]bimodalFixture{
	// Simple case, ensures that we do the very basics of picking up and
	// including a single, simple import that is expressed an import
	"simple bimodal add": {
		ds: mkbmu(
			dsv("root 0.0.0"),
			mkpkg("root", "a"),
			dsv("a 1.0.0"),
			mkpkg("a"),
		),
		r: mkresults(
			"a 1.0.0",
		),
	},
	// Ensure it works when the import jump is not from the package with the
	// same path as root, but from a subpkg
	"subpkg bimodal add": {
		ds: mkbmu(
			dsv("root 0.0.0"),
			mkpkg("root", "root/foo"),
			mkpkg("root/foo", "a"),
			dsv("a 1.0.0"),
			mkpkg("a"),
		),
		r: mkresults(
			"a 1.0.0",
		),
	},
	// Ensure that if a constraint is expressed, but no actual import exists,
	// then the constraint is disregarded - the project named in the constraint
	// is not part of the solution.
	"ignore constraint without import": {
		ds: mkbmu(
			dsv("root 0.0.0", "a 1.0.0"),
			mkpkg("root", "root/foo"),
			dsv("a 1.0.0"),
			mkpkg("a"),
		),
		r: mkresults(),
	},
}

//type bmproj struct {
//n       ProjectName
//v       Version
//deps    []ProjectDep
//devdeps []ProjectDep
//pkgs    []tpkg
//}

//var _ Manifest = bmproj{}

//// impl Spec interface
//func (p bmproj) GetDependencies() []ProjectDep {
//return p.deps
//}

//// impl Spec interface
//func (p bmproj) GetDevDependencies() []ProjectDep {
//return p.devdeps
//}

//// impl Spec interface
//func (p bmproj) Name() ProjectName {
//return p.n
//}

type tpkg struct {
	// Full import path of this package
	path string
	// Slice of full paths to its virtual imports
	imports []string
}

type bmelem interface {
	_bmelem()
}

func (p tpkg) _bmelem() {}

type bimodalFixture struct {
	// name of this fixture datum
	n string
	// bimodal project. first is always treated as root project
	ds []depspec
	// results; map of name/version pairs
	r map[string]Version
	// max attempts the solver should need to find solution. 0 means no limit
	maxAttempts int
	// Use downgrade instead of default upgrade sorter
	downgrade bool
	// lock file simulator, if one's to be used at all
	l fixLock
	// projects expected to have errors, if any
	errp []string
	// request up/downgrade to all projects
	changeall bool
}

func (f bimodalFixture) name() string {
	return f.n
}

func (f bimodalFixture) specs() []depspec {
	return f.ds
}

func (f bimodalFixture) maxTries() int {
	return f.maxAttempts
}

func (f bimodalFixture) expectErrs() []string {
	return f.errp
}

func (f bimodalFixture) result() map[string]Version {
	return f.r
}

type bmSourceManager struct {
	depspecSourceManager
}

var _ SourceManager = &bmSourceManager{}

func newbmSM(ds []depspec) *bmSourceManager {
	sm := &bmSourceManager{}
	sm.specs = ds
	sm.rm = computeBimodalExternalMap(ds)

	return sm
}

func (sm *bmSourceManager) ExternalReach(n ProjectName, v Version) (map[string][]string, error) {
	for _, ds := range sm.specs {
		if ds.n == n && v.Matches(ds.v) {
			rm := make(map[string][]string)
			for _, pkg := range ds.pkgs {
				rm[pkg.path] = pkg.imports
			}

			return rm, nil
		}
	}

	// TODO proper solver errs
	return nil, fmt.Errorf("No reach data for %s at version %s", n, v)
}

func computeBimodalExternalMap(ds []depspec) map[pident][]string {
	rm := make(map[pident][]string)

	for _, d := range ds {
		exmap := make(map[string]struct{})

		for _, pkg := range d.pkgs {
			for _, ex := range pkg.imports {
				exmap[ex] = struct{}{}
			}
		}

		var list []string
		for ex := range exmap {
			list = append(list, ex)
		}
		id := pident{
			n: d.n,
			v: d.v,
		}
		rm[id] = list
	}

	return rm
}
<|endoftext|>