<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package tools

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/version"

	"github.com/juju/juju/juju/names"
)

// Archive writes the executable files found in the given directory in
// gzipped tar format to w.
func Archive(w io.Writer, dir string) error {
	entries, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	gzw := gzip.NewWriter(w)
	defer closeErrorCheck(&err, gzw)

	tarw := tar.NewWriter(gzw)
	defer closeErrorCheck(&err, tarw)

	for _, ent := range entries {
		h := tarHeader(ent)
		logger.Debugf("adding entry: %#v", h)
		// ignore local umask
		if isExecutable(ent) {
			h.Mode = 0755
		} else {
			h.Mode = 0644
		}
		err := tarw.WriteHeader(h)
		if err != nil {
			return err
		}
		fileName := filepath.Join(dir, ent.Name())
		if err := copyFile(tarw, fileName); err != nil {
			return err
		}
	}
	return nil
}

// archiveAndSHA256 calls Archive with the provided arguments,
// and returns a hex-encoded SHA256 hash of the resulting
// archive.
func archiveAndSHA256(w io.Writer, dir string) (sha256hash string, err error) {
	h := sha256.New()
	if err := Archive(io.MultiWriter(h, w), dir); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), err
}

// copyFile writes the contents of the given file to w.
func copyFile(w io.Writer, file string) error {
	f, err := os.Open(file)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(w, f)
	return err
}

// tarHeader returns a tar file header given the file's stat
// information.
func tarHeader(i os.FileInfo) *tar.Header {
	return &tar.Header{
		Typeflag:   tar.TypeReg,
		Name:       i.Name(),
		Size:       i.Size(),
		Mode:       int64(i.Mode() & 0777),
		ModTime:    i.ModTime(),
		AccessTime: i.ModTime(),
		ChangeTime: i.ModTime(),
		Uname:      "ubuntu",
		Gname:      "ubuntu",
	}
}

// isExecutable returns whether the given info
// represents a regular file executable by (at least) the user.
func isExecutable(i os.FileInfo) bool {
	return i.Mode()&(0100|os.ModeType) == 0100
}

// closeErrorCheck means that we can ensure that
// Close errors do not get lost even when we defer them,
func closeErrorCheck(errp *error, c io.Closer) {
	err := c.Close()
	if *errp == nil {
		*errp = err
	}
}

func setenv(env []string, val string) []string {
	prefix := val[0 : strings.Index(val, "=")+1]
	for i, eval := range env {
		if strings.HasPrefix(eval, prefix) {
			env[i] = val
			return env
		}
	}
	return append(env, val)
}

func findExecutable(execFile string) (string, error) {
	logger.Debugf("looking for: %s", execFile)
	if filepath.IsAbs(execFile) {
		return execFile, nil
	}

	dir, file := filepath.Split(execFile)

	// Now we have two possibilities:
	//   file == path indicating that the PATH was searched
	//   dir != "" indicating that it is a relative path

	if dir == "" {
		path := os.Getenv("PATH")
		for _, name := range filepath.SplitList(path) {
			result := filepath.Join(name, file)
			// Use exec.LookPath() to check if the file exists and is executable`
			f, err := exec.LookPath(result)
			if err == nil {
				return f, nil
			}
		}

		return "", fmt.Errorf("could not find %q in the path", file)
	}
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Clean(filepath.Join(cwd, execFile)), nil
}

func copyExistingJujud(dir string) error {
	// Assume that the user is running juju.
	jujuLocation, err := findExecutable(os.Args[0])
	if err != nil {
		logger.Infof("%v", err)
		return err
	}
	jujudLocation := filepath.Join(filepath.Dir(jujuLocation), names.Jujud)
	logger.Debugf("checking: %s", jujudLocation)
	info, err := os.Stat(jujudLocation)
	if err != nil {
		logger.Infof("couldn't find existing jujud")
		return err
	}
	logger.Infof("found existing jujud")
	// TODO(thumper): break this out into a util function.
	// copy the file into the dir.
	source, err := os.Open(jujudLocation)
	if err != nil {
		logger.Infof("open source failed: %v", err)
		return err
	}
	defer source.Close()
	target := filepath.Join(dir, names.Jujud)
	logger.Infof("target: %v", target)
	destination, err := os.OpenFile(target, os.O_RDWR|os.O_TRUNC|os.O_CREATE, info.Mode())
	if err != nil {
		logger.Infof("open destination failed: %v", err)
		return err
	}
	defer destination.Close()
	_, err = io.Copy(destination, source)
	if err != nil {
		return err
	}
	return nil
}

func buildJujud(dir string) error {
	logger.Infof("building jujud")
	cmds := [][]string{
		{"go", "build", "-gccgoflags=-static-libgo", "-o", filepath.Join(dir, names.Jujud), "github.com/juju/juju/cmd/jujud"},
	}
	for _, args := range cmds {
		cmd := exec.Command(args[0], args[1:]...)
		out, err := cmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("build command %q failed: %v; %s", args[0], err, out)
		}
	}
	return nil
}

// BundleToolsFunc is a function which can bundle all the current juju tools
// in gzipped tar format to the given writer.
type BundleToolsFunc func(w io.Writer, forceVersion *version.Number) (version.Binary, string, error)

// Override for testing.
var BundleTools BundleToolsFunc = bundleTools

// bundleTools bundles all the current juju tools in gzipped tar
// format to the given writer.
// If forceVersion is not nil, a FORCE-VERSION file is included in
// the tools bundle so it will lie about its current version number.
func bundleTools(w io.Writer, forceVersion *version.Number) (tvers version.Binary, sha256Hash string, err error) {
	dir, err := ioutil.TempDir("", "juju-tools")
	if err != nil {
		return version.Binary{}, "", err
	}
	defer os.RemoveAll(dir)

	if err := copyExistingJujud(dir); err != nil {
		logger.Debugf("copy existing failed: %v", err)
		if err := buildJujud(dir); err != nil {
			return version.Binary{}, "", err
		}
	}

	if forceVersion != nil {
		logger.Debugf("forcing version to %s", forceVersion)
		if err := ioutil.WriteFile(filepath.Join(dir, "FORCE-VERSION"), []byte(forceVersion.String()), 0666); err != nil {
			return version.Binary{}, "", err
		}
	}

	tvers, err = getVersionFromJujud(dir)
	if err != nil {
		return version.Binary{}, "", errors.Trace(err)
	}

	sha256hash, err := archiveAndSHA256(w, dir)
	if err != nil {
		return version.Binary{}, "", err
	}
	return tvers, sha256hash, err
}

var execCommand = exec.Command

func getVersionFromJujud(dir string) (version.Binary, error) {
	path := filepath.Join(dir, names.Jujud)
	cmd := execCommand(path, "version")
	stdout := &bytes.Buffer{}
	both := &bytes.Buffer{}
	cmd.Stdout = io.MultiWriter(stdout, both)
	cmd.Stderr = both

	if err := cmd.Run(); err != nil {
		return version.Binary{}, errors.Errorf("cannot get version from %q: %v; %s", path, err, both)
	}
	tvs := strings.TrimSpace(stdout.String())
	tvers, err := version.ParseBinary(tvs)
	if err != nil {
		return version.Binary{}, errors.Errorf("invalid version %q printed by jujud", tvs)
	}
	return tvers, nil
}
<commit_msg>environs/tools: fix data race in getVersionFromJujud<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package tools

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/version"

	"github.com/juju/juju/juju/names"
)

// Archive writes the executable files found in the given directory in
// gzipped tar format to w.
func Archive(w io.Writer, dir string) error {
	entries, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}

	gzw := gzip.NewWriter(w)
	defer closeErrorCheck(&err, gzw)

	tarw := tar.NewWriter(gzw)
	defer closeErrorCheck(&err, tarw)

	for _, ent := range entries {
		h := tarHeader(ent)
		logger.Debugf("adding entry: %#v", h)
		// ignore local umask
		if isExecutable(ent) {
			h.Mode = 0755
		} else {
			h.Mode = 0644
		}
		err := tarw.WriteHeader(h)
		if err != nil {
			return err
		}
		fileName := filepath.Join(dir, ent.Name())
		if err := copyFile(tarw, fileName); err != nil {
			return err
		}
	}
	return nil
}

// archiveAndSHA256 calls Archive with the provided arguments,
// and returns a hex-encoded SHA256 hash of the resulting
// archive.
func archiveAndSHA256(w io.Writer, dir string) (sha256hash string, err error) {
	h := sha256.New()
	if err := Archive(io.MultiWriter(h, w), dir); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", h.Sum(nil)), err
}

// copyFile writes the contents of the given file to w.
func copyFile(w io.Writer, file string) error {
	f, err := os.Open(file)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(w, f)
	return err
}

// tarHeader returns a tar file header given the file's stat
// information.
func tarHeader(i os.FileInfo) *tar.Header {
	return &tar.Header{
		Typeflag:   tar.TypeReg,
		Name:       i.Name(),
		Size:       i.Size(),
		Mode:       int64(i.Mode() & 0777),
		ModTime:    i.ModTime(),
		AccessTime: i.ModTime(),
		ChangeTime: i.ModTime(),
		Uname:      "ubuntu",
		Gname:      "ubuntu",
	}
}

// isExecutable returns whether the given info
// represents a regular file executable by (at least) the user.
func isExecutable(i os.FileInfo) bool {
	return i.Mode()&(0100|os.ModeType) == 0100
}

// closeErrorCheck means that we can ensure that
// Close errors do not get lost even when we defer them,
func closeErrorCheck(errp *error, c io.Closer) {
	err := c.Close()
	if *errp == nil {
		*errp = err
	}
}

func setenv(env []string, val string) []string {
	prefix := val[0 : strings.Index(val, "=")+1]
	for i, eval := range env {
		if strings.HasPrefix(eval, prefix) {
			env[i] = val
			return env
		}
	}
	return append(env, val)
}

func findExecutable(execFile string) (string, error) {
	logger.Debugf("looking for: %s", execFile)
	if filepath.IsAbs(execFile) {
		return execFile, nil
	}

	dir, file := filepath.Split(execFile)

	// Now we have two possibilities:
	//   file == path indicating that the PATH was searched
	//   dir != "" indicating that it is a relative path

	if dir == "" {
		path := os.Getenv("PATH")
		for _, name := range filepath.SplitList(path) {
			result := filepath.Join(name, file)
			// Use exec.LookPath() to check if the file exists and is executable`
			f, err := exec.LookPath(result)
			if err == nil {
				return f, nil
			}
		}

		return "", fmt.Errorf("could not find %q in the path", file)
	}
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Clean(filepath.Join(cwd, execFile)), nil
}

func copyExistingJujud(dir string) error {
	// Assume that the user is running juju.
	jujuLocation, err := findExecutable(os.Args[0])
	if err != nil {
		logger.Infof("%v", err)
		return err
	}
	jujudLocation := filepath.Join(filepath.Dir(jujuLocation), names.Jujud)
	logger.Debugf("checking: %s", jujudLocation)
	info, err := os.Stat(jujudLocation)
	if err != nil {
		logger.Infof("couldn't find existing jujud")
		return err
	}
	logger.Infof("found existing jujud")
	// TODO(thumper): break this out into a util function.
	// copy the file into the dir.
	source, err := os.Open(jujudLocation)
	if err != nil {
		logger.Infof("open source failed: %v", err)
		return err
	}
	defer source.Close()
	target := filepath.Join(dir, names.Jujud)
	logger.Infof("target: %v", target)
	destination, err := os.OpenFile(target, os.O_RDWR|os.O_TRUNC|os.O_CREATE, info.Mode())
	if err != nil {
		logger.Infof("open destination failed: %v", err)
		return err
	}
	defer destination.Close()
	_, err = io.Copy(destination, source)
	if err != nil {
		return err
	}
	return nil
}

func buildJujud(dir string) error {
	logger.Infof("building jujud")
	cmds := [][]string{
		{"go", "build", "-gccgoflags=-static-libgo", "-o", filepath.Join(dir, names.Jujud), "github.com/juju/juju/cmd/jujud"},
	}
	for _, args := range cmds {
		cmd := exec.Command(args[0], args[1:]...)
		out, err := cmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("build command %q failed: %v; %s", args[0], err, out)
		}
	}
	return nil
}

// BundleToolsFunc is a function which can bundle all the current juju tools
// in gzipped tar format to the given writer.
type BundleToolsFunc func(w io.Writer, forceVersion *version.Number) (version.Binary, string, error)

// Override for testing.
var BundleTools BundleToolsFunc = bundleTools

// bundleTools bundles all the current juju tools in gzipped tar
// format to the given writer.
// If forceVersion is not nil, a FORCE-VERSION file is included in
// the tools bundle so it will lie about its current version number.
func bundleTools(w io.Writer, forceVersion *version.Number) (tvers version.Binary, sha256Hash string, err error) {
	dir, err := ioutil.TempDir("", "juju-tools")
	if err != nil {
		return version.Binary{}, "", err
	}
	defer os.RemoveAll(dir)

	if err := copyExistingJujud(dir); err != nil {
		logger.Debugf("copy existing failed: %v", err)
		if err := buildJujud(dir); err != nil {
			return version.Binary{}, "", err
		}
	}

	if forceVersion != nil {
		logger.Debugf("forcing version to %s", forceVersion)
		if err := ioutil.WriteFile(filepath.Join(dir, "FORCE-VERSION"), []byte(forceVersion.String()), 0666); err != nil {
			return version.Binary{}, "", err
		}
	}

	tvers, err = getVersionFromJujud(dir)
	if err != nil {
		return version.Binary{}, "", errors.Trace(err)
	}

	sha256hash, err := archiveAndSHA256(w, dir)
	if err != nil {
		return version.Binary{}, "", err
	}
	return tvers, sha256hash, err
}

var execCommand = exec.Command

func getVersionFromJujud(dir string) (version.Binary, error) {
	path := filepath.Join(dir, names.Jujud)
	cmd := execCommand(path, "version")
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr

	if err := cmd.Run(); err != nil {
		return version.Binary{}, errors.Errorf("cannot get version from %q: %v; %s", path, err, stderr.String()+stdout.String())
	}
	tvs := strings.TrimSpace(stdout.String())
	tvers, err := version.ParseBinary(tvs)
	if err != nil {
		return version.Binary{}, errors.Errorf("invalid version %q printed by jujud", tvs)
	}
	return tvers, nil
}
<|endoftext|>


<commit_before>package cache

import (
	"testing"
	"time"
)

func TestCache(t *testing.T) {
	tc := New(0, 0)

	a, found := tc.Get("a")
	if found || a != nil {
		t.Error("Getting A found value that shouldn't exist:", a)
	}

	b, found := tc.Get("b")
	if found || b != nil {
		t.Error("Getting B found value that shouldn't exist:", b)
	}

	c, found := tc.Get("c")
	if found || c != nil {
		t.Error("Getting C found value that shouldn't exist:", c)
	}

	tc.Set("a", 1, 0)
	tc.Set("b", "b", 0)
	tc.Set("c", 3.5, 0)

	x, found := tc.Get("a")
	if !found {
		t.Error("a was not found while getting a2")
	}
	if x == nil {
		t.Error("x for a is nil")
	} else if a2 := x.(int); a2+2 != 3 {
		t.Error("a2 (which should be 1) plus 2 does not equal 3; value:", a2)
	}

	x, found = tc.Get("b")
	if !found {
		t.Error("b was not found while getting b2")
	}
	if x == nil {
		t.Error("x for b is nil")
	} else if b2 := x.(string); b2+"B" != "bB" {
		t.Error("b2 (which should be b) plus B does not equal bB; value:", b2)
	}

	x, found = tc.Get("c")
	if !found {
		t.Error("c was not found while getting c2")
	}
	if x == nil {
		t.Error("x for c is nil")
	} else if c2 := x.(float64); c2+1.2 != 4.7 {
		t.Error("c2 (which should be 3.5) plus 1.2 does not equal 4.7; value:", c2)
	}
}

func TestCacheTimes(t *testing.T) {
	var found bool

	tc := New(50*time.Millisecond, 1*time.Millisecond)
	tc.Set("a", 1, 0)
	tc.Set("b", 2, -1)
	tc.Set("c", 3, 20*time.Millisecond)
	tc.Set("d", 4, 70*time.Millisecond)

	<-time.After(25 * time.Millisecond)
	_, found = tc.Get("c")
	if found {
		t.Error("Found c when it should have been automatically deleted")
	}

	<-time.After(30 * time.Millisecond)
	_, found = tc.Get("a")
	if found {
		t.Error("Found a when it should have been automatically deleted")
	}

	_, found = tc.Get("b")
	if !found {
		t.Error("Did not find b even though it was set to never expire")
	}

	_, found = tc.Get("d")
	if !found {
		t.Error("Did not find d even though it was set to expire later than the default")
	}

	<-time.After(20 * time.Millisecond)
	_, found = tc.Get("d")
	if found {
		t.Error("Found d when it should have been automatically deleted (later than the default)")
	}
}

type TestStruct struct {
	Num int
}

func TestStorePointerToStruct(t *testing.T) {
	tc := New(0, 0)
	tc.Set("foo", &TestStruct{Num: 1}, 0)
	x, found := tc.Get("foo")
	if !found {
		t.Fatal("*TestStruct was not found for foo")
	}
	foo := x.(*TestStruct)
	foo.Num++

	y, found := tc.Get("foo")
	if !found {
		t.Fatal("*TestStruct was not found for foo (second time)")
	}
	bar := y.(*TestStruct)
	if bar.Num != 2 {
		t.Fatal("TestStruct.Num is not 2")
	}
}

func TestIncrementUint(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint", uint(1), 0)
	err := tc.Increment("tuint", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint")
	if !found {
		t.Error("tuint was not found")
	}
	if x.(uint) != 3 {
		t.Error("tuint is not 3:", x)
	}
}

func TestIncrementUintptr(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuintptr", uintptr(1), 0)
	err := tc.Increment("tuintptr", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuintptr")
	if !found {
		t.Error("tuintptr was not found")
	}
	if x.(uintptr) != 3 {
		t.Error("tuintptr is not 3:", x)
	}
}

func TestIncrementUint8(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint8", uint8(1), 0)
	err := tc.Increment("tuint8", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint8")
	if !found {
		t.Error("tuint8 was not found")
	}
	if x.(uint8) != 3 {
		t.Error("tuint8 is not 3:", x)
	}
}

func TestIncrementUint16(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint16", uint16(1), 0)
	err := tc.Increment("tuint16", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint16")
	if !found {
		t.Error("tuint16 was not found")
	}
	if x.(uint16) != 3 {
		t.Error("tuint16 is not 3:", x)
	}
}

func TestIncrementUint32(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint32", uint32(1), 0)
	err := tc.Increment("tuint32", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint32")
	if !found {
		t.Error("tuint32 was not found")
	}
	if x.(uint32) != 3 {
		t.Error("tuint32 is not 3:", x)
	}
}

func TestIncrementUint64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint64", uint64(1), 0)
	err := tc.Increment("tuint64", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint64")
	if !found {
		t.Error("tuint64 was not found")
	}
	if x.(uint64) != 3 {
		t.Error("tuint64 is not 3:", x)
	}
}

func TestIncrementInt(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint", 1, 0)
	err := tc.Increment("tint", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint")
	if !found {
		t.Error("tint was not found")
	}
	if x.(int) != 3 {
		t.Error("tint is not 3:", x)
	}
}

func TestIncrementInt8(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint8", int8(1), 0)
	err := tc.Increment("tint8", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint8")
	if !found {
		t.Error("tint8 was not found")
	}
	if x.(int8) != 3 {
		t.Error("tint8 is not 3:", x)
	}
}

func TestIncrementInt16(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint16", int16(1), 0)
	err := tc.Increment("tint16", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint16")
	if !found {
		t.Error("tint16 was not found")
	}
	if x.(int16) != 3 {
		t.Error("tint16 is not 3:", x)
	}
}

func TestIncrementInt32(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint32", int32(1), 0)
	err := tc.Increment("tint32", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint32")
	if !found {
		t.Error("tint32 was not found")
	}
	if x.(int32) != 3 {
		t.Error("tint32 is not 3:", x)
	}
}

func TestIncrementInt64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint64", int64(1), 0)
	err := tc.Increment("tint64", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint64")
	if !found {
		t.Error("tint64 was not found")
	}
	if x.(int64) != 3 {
		t.Error("tint64 is not 3:", x)
	}
}

func TestIncrementFloat32(t *testing.T) {
	tc := New(0, 0)
	tc.Set("float32", float32(1.5), 0)
	err := tc.Increment("float32", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("float32")
	if !found {
		t.Error("float32 was not found")
	}
	if x.(float32) != 3.5 {
		t.Error("float32 is not 3.5:", x)
	}
}

func TestIncrementFloat64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("float64", float64(1.5), 0)
	err := tc.Increment("float64", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("float64")
	if !found {
		t.Error("float64 was not found")
	}
	if x.(float64) != 3.5 {
		t.Error("float64 is not 3.5:", x)
	}
}

func TestDecrementInt64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("int64", int64(5), 0)
	err := tc.Decrement("int64", 2)
	if err != nil {
		t.Error("Error decrementing:", err)
	}
	x, found := tc.Get("int64")
	if !found {
		t.Error("int64 was not found")
	}
	if x.(int64) != 3 {
		t.Error("int64 is not 3:", x)
	}
}

func TestAdd(t *testing.T) {
	tc := New(0, 0)
	err := tc.Add("foo", "bar", 0)
	if err != nil {
		t.Error("Couldn't add foo even though it shouldn't exist")
	}
	err = tc.Add("foo", "baz", 0)
	if err == nil {
		t.Error("Successfully added another foo when it should have returned an error")
	}
}

func TestReplace(t *testing.T) {
	tc := New(0, 0)
	err := tc.Replace("foo", "bar", 0)
	if err == nil {
		t.Error("Replaced foo when it shouldn't exist")
	}
	tc.Set("foo", "bar", 0)
	err = tc.Replace("foo", "bar", 0)
	if err != nil {
		t.Error("Couldn't replace existing key foo")
	}
}

func TestDelete(t *testing.T) {
	tc := New(0, 0)
	tc.Set("foo", "bar", 0)
	tc.Delete("foo")
	x, found := tc.Get("foo")
	if found {
		t.Error("foo was found, but it should have been deleted")
	}
	if x != nil {
		t.Error("x is not nil:", x)
	}
}

func TestFlush(t *testing.T) {
	tc := New(0, 0)
	tc.Set("foo", "bar", 0)
	tc.Set("baz", "yes", 0)
	tc.Flush()
	x, found := tc.Get("foo")
	if found {
		t.Error("foo was found, but it should have been deleted")
	}
	if x != nil {
		t.Error("x is not nil:", x)
	}
	x, found = tc.Get("baz")
	if found {
		t.Error("baz was found, but it should have been deleted")
	}
	if x != nil {
		t.Error("x is not nil:", x)
	}
}
<commit_msg>A few benchmarks vs. normal maps<commit_after>package cache

import (
	"testing"
	"time"
)

func TestCache(t *testing.T) {
	tc := New(0, 0)

	a, found := tc.Get("a")
	if found || a != nil {
		t.Error("Getting A found value that shouldn't exist:", a)
	}

	b, found := tc.Get("b")
	if found || b != nil {
		t.Error("Getting B found value that shouldn't exist:", b)
	}

	c, found := tc.Get("c")
	if found || c != nil {
		t.Error("Getting C found value that shouldn't exist:", c)
	}

	tc.Set("a", 1, 0)
	tc.Set("b", "b", 0)
	tc.Set("c", 3.5, 0)

	x, found := tc.Get("a")
	if !found {
		t.Error("a was not found while getting a2")
	}
	if x == nil {
		t.Error("x for a is nil")
	} else if a2 := x.(int); a2+2 != 3 {
		t.Error("a2 (which should be 1) plus 2 does not equal 3; value:", a2)
	}

	x, found = tc.Get("b")
	if !found {
		t.Error("b was not found while getting b2")
	}
	if x == nil {
		t.Error("x for b is nil")
	} else if b2 := x.(string); b2+"B" != "bB" {
		t.Error("b2 (which should be b) plus B does not equal bB; value:", b2)
	}

	x, found = tc.Get("c")
	if !found {
		t.Error("c was not found while getting c2")
	}
	if x == nil {
		t.Error("x for c is nil")
	} else if c2 := x.(float64); c2+1.2 != 4.7 {
		t.Error("c2 (which should be 3.5) plus 1.2 does not equal 4.7; value:", c2)
	}
}

func TestCacheTimes(t *testing.T) {
	var found bool

	tc := New(50*time.Millisecond, 1*time.Millisecond)
	tc.Set("a", 1, 0)
	tc.Set("b", 2, -1)
	tc.Set("c", 3, 20*time.Millisecond)
	tc.Set("d", 4, 70*time.Millisecond)

	<-time.After(25 * time.Millisecond)
	_, found = tc.Get("c")
	if found {
		t.Error("Found c when it should have been automatically deleted")
	}

	<-time.After(30 * time.Millisecond)
	_, found = tc.Get("a")
	if found {
		t.Error("Found a when it should have been automatically deleted")
	}

	_, found = tc.Get("b")
	if !found {
		t.Error("Did not find b even though it was set to never expire")
	}

	_, found = tc.Get("d")
	if !found {
		t.Error("Did not find d even though it was set to expire later than the default")
	}

	<-time.After(20 * time.Millisecond)
	_, found = tc.Get("d")
	if found {
		t.Error("Found d when it should have been automatically deleted (later than the default)")
	}
}

type TestStruct struct {
	Num int
}

func TestStorePointerToStruct(t *testing.T) {
	tc := New(0, 0)
	tc.Set("foo", &TestStruct{Num: 1}, 0)
	x, found := tc.Get("foo")
	if !found {
		t.Fatal("*TestStruct was not found for foo")
	}
	foo := x.(*TestStruct)
	foo.Num++

	y, found := tc.Get("foo")
	if !found {
		t.Fatal("*TestStruct was not found for foo (second time)")
	}
	bar := y.(*TestStruct)
	if bar.Num != 2 {
		t.Fatal("TestStruct.Num is not 2")
	}
}

func TestIncrementUint(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint", uint(1), 0)
	err := tc.Increment("tuint", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint")
	if !found {
		t.Error("tuint was not found")
	}
	if x.(uint) != 3 {
		t.Error("tuint is not 3:", x)
	}
}

func TestIncrementUintptr(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuintptr", uintptr(1), 0)
	err := tc.Increment("tuintptr", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuintptr")
	if !found {
		t.Error("tuintptr was not found")
	}
	if x.(uintptr) != 3 {
		t.Error("tuintptr is not 3:", x)
	}
}

func TestIncrementUint8(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint8", uint8(1), 0)
	err := tc.Increment("tuint8", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint8")
	if !found {
		t.Error("tuint8 was not found")
	}
	if x.(uint8) != 3 {
		t.Error("tuint8 is not 3:", x)
	}
}

func TestIncrementUint16(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint16", uint16(1), 0)
	err := tc.Increment("tuint16", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint16")
	if !found {
		t.Error("tuint16 was not found")
	}
	if x.(uint16) != 3 {
		t.Error("tuint16 is not 3:", x)
	}
}

func TestIncrementUint32(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint32", uint32(1), 0)
	err := tc.Increment("tuint32", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint32")
	if !found {
		t.Error("tuint32 was not found")
	}
	if x.(uint32) != 3 {
		t.Error("tuint32 is not 3:", x)
	}
}

func TestIncrementUint64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tuint64", uint64(1), 0)
	err := tc.Increment("tuint64", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}

	x, found := tc.Get("tuint64")
	if !found {
		t.Error("tuint64 was not found")
	}
	if x.(uint64) != 3 {
		t.Error("tuint64 is not 3:", x)
	}
}

func TestIncrementInt(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint", 1, 0)
	err := tc.Increment("tint", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint")
	if !found {
		t.Error("tint was not found")
	}
	if x.(int) != 3 {
		t.Error("tint is not 3:", x)
	}
}

func TestIncrementInt8(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint8", int8(1), 0)
	err := tc.Increment("tint8", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint8")
	if !found {
		t.Error("tint8 was not found")
	}
	if x.(int8) != 3 {
		t.Error("tint8 is not 3:", x)
	}
}

func TestIncrementInt16(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint16", int16(1), 0)
	err := tc.Increment("tint16", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint16")
	if !found {
		t.Error("tint16 was not found")
	}
	if x.(int16) != 3 {
		t.Error("tint16 is not 3:", x)
	}
}

func TestIncrementInt32(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint32", int32(1), 0)
	err := tc.Increment("tint32", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint32")
	if !found {
		t.Error("tint32 was not found")
	}
	if x.(int32) != 3 {
		t.Error("tint32 is not 3:", x)
	}
}

func TestIncrementInt64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("tint64", int64(1), 0)
	err := tc.Increment("tint64", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("tint64")
	if !found {
		t.Error("tint64 was not found")
	}
	if x.(int64) != 3 {
		t.Error("tint64 is not 3:", x)
	}
}

func TestIncrementFloat32(t *testing.T) {
	tc := New(0, 0)
	tc.Set("float32", float32(1.5), 0)
	err := tc.Increment("float32", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("float32")
	if !found {
		t.Error("float32 was not found")
	}
	if x.(float32) != 3.5 {
		t.Error("float32 is not 3.5:", x)
	}
}

func TestIncrementFloat64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("float64", float64(1.5), 0)
	err := tc.Increment("float64", 2)
	if err != nil {
		t.Error("Error incrementing:", err)
	}
	x, found := tc.Get("float64")
	if !found {
		t.Error("float64 was not found")
	}
	if x.(float64) != 3.5 {
		t.Error("float64 is not 3.5:", x)
	}
}

func TestDecrementInt64(t *testing.T) {
	tc := New(0, 0)
	tc.Set("int64", int64(5), 0)
	err := tc.Decrement("int64", 2)
	if err != nil {
		t.Error("Error decrementing:", err)
	}
	x, found := tc.Get("int64")
	if !found {
		t.Error("int64 was not found")
	}
	if x.(int64) != 3 {
		t.Error("int64 is not 3:", x)
	}
}

func TestAdd(t *testing.T) {
	tc := New(0, 0)
	err := tc.Add("foo", "bar", 0)
	if err != nil {
		t.Error("Couldn't add foo even though it shouldn't exist")
	}
	err = tc.Add("foo", "baz", 0)
	if err == nil {
		t.Error("Successfully added another foo when it should have returned an error")
	}
}

func TestReplace(t *testing.T) {
	tc := New(0, 0)
	err := tc.Replace("foo", "bar", 0)
	if err == nil {
		t.Error("Replaced foo when it shouldn't exist")
	}
	tc.Set("foo", "bar", 0)
	err = tc.Replace("foo", "bar", 0)
	if err != nil {
		t.Error("Couldn't replace existing key foo")
	}
}

func TestDelete(t *testing.T) {
	tc := New(0, 0)
	tc.Set("foo", "bar", 0)
	tc.Delete("foo")
	x, found := tc.Get("foo")
	if found {
		t.Error("foo was found, but it should have been deleted")
	}
	if x != nil {
		t.Error("x is not nil:", x)
	}
}

func TestFlush(t *testing.T) {
	tc := New(0, 0)
	tc.Set("foo", "bar", 0)
	tc.Set("baz", "yes", 0)
	tc.Flush()
	x, found := tc.Get("foo")
	if found {
		t.Error("foo was found, but it should have been deleted")
	}
	if x != nil {
		t.Error("x is not nil:", x)
	}
	x, found = tc.Get("baz")
	if found {
		t.Error("baz was found, but it should have been deleted")
	}
	if x != nil {
		t.Error("x is not nil:", x)
	}
}

func BenchmarkCache(b *testing.B) {
	tc := New(0, 0)
	tc.Set("foo", "bar", 0)
	for i := 0; i < b.N; i++ {
		tc.Get("foo")
	}
}

func BenchmarkMap(b *testing.B) {
	m := map[string]string{
		"foo": "bar",
	}
	for i := 0; i < b.N; i++ {
		_, _ = m["foo"]
	}
}

func BenchmarkCacheSet(b *testing.B) {
	tc := New(0, 0)
	for i := 0; i < b.N; i++ {
		tc.Set("foo", "bar", 0)
	}
}

func BenchmarkMapSet(b *testing.B) {
	m := map[string]string{}
	for i := 0; i < b.N; i++ {
		m["foo"] = "bar"
	}
}

func BenchmarkCacheSetDelete(b *testing.B) {
	tc := New(0, 0)
	for i := 0; i < b.N; i++ {
		tc.Set("foo", "bar", 0)
		tc.Delete("foo")
	}
}

func BenchmarkMapSetDelete(b *testing.B) {
	m := map[string]string{}
	for i := 0; i < b.N; i++ {
		m["foo"] = "bar"
		delete(m, "foo")
	}
}
<|endoftext|>


<commit_before>package caseformat

import (
	"unicode"
)

func ToUpperCamel(str string) string {
	s := ""
	for _, buf := range tokenize(str) {
		buf[0] = unicode.ToUpper(buf[0])
		s += string(buf)
	}
	return s
}

func ToLowerCamel(str string) string {
	s := ""
	for i, buf := range tokenize(str) {
		if i > 0 {
			buf[0] = unicode.ToUpper(buf[0])
		}
		s += string(buf)
	}
	return s
}

func ToLowerUnderscore(str string) string {
	s := ""
	for i, buf := range tokenize(str) {
		if i > 0 {
			s += "_"
		}
		s += string(buf)
	}
	return s
}

func ToUpperUnderscore(str string) string {
	s := ""
	for i, buf := range tokenize(str) {
		if i > 0 {
			s += "_"
		}
		for i, char := range buf {
			buf[i] = unicode.ToUpper(char)
		}
		s += string(buf)
	}
	return s
}

func tokenize(str string) [][]rune {
	parts := make([][]rune, 0)
	buf := []rune(str)
	bufLen := len(buf)
	current := make([]rune, 0)
	for i := 0; i < bufLen; i += 1 {
		canLookBehind, canLookAhead1, canLookAhead2 := i > 0, i < bufLen-1, i < bufLen-2
		aLower, bLower := unicode.IsLower(buf[i]), canLookAhead1 && unicode.IsLower(buf[i+1])
		aLetter, bLetter := unicode.IsLetter(buf[i]), canLookAhead1 && unicode.IsLetter(buf[i+1])
		behindLower, ahead2Lower := canLookBehind && unicode.IsLower(buf[i-1]), canLookAhead2 && unicode.IsLower(buf[i+2])
		split := !bLetter || (aLower && !bLower) || (!aLower && !bLower && (behindLower || ahead2Lower))
		if aLetter {
			current = append(current, unicode.ToLower(buf[i]))
			if split {
				parts = append(parts, current)
				current = make([]rune, 0)
			}
		}
	}
	if len(current) > 0 {
		parts = append(parts, current)
	}
	return parts
}
<commit_msg>handle zero case<commit_after>package caseformat

import (
	"unicode"
)

func ToUpperCamel(str string) string {
	s := ""
	for _, buf := range tokenize(str) {
		buf[0] = unicode.ToUpper(buf[0])
		s += string(buf)
	}
	return s
}

func ToLowerCamel(str string) string {
	s := ""
	for i, buf := range tokenize(str) {
		if i > 0 {
			buf[0] = unicode.ToUpper(buf[0])
		}
		s += string(buf)
	}
	return s
}

func ToLowerUnderscore(str string) string {
	s := ""
	for i, buf := range tokenize(str) {
		if i > 0 {
			s += "_"
		}
		s += string(buf)
	}
	return s
}

func ToUpperUnderscore(str string) string {
	s := ""
	for i, buf := range tokenize(str) {
		if i > 0 {
			s += "_"
		}
		for i, char := range buf {
			buf[i] = unicode.ToUpper(char)
		}
		s += string(buf)
	}
	return s
}

func tokenize(str string) [][]rune {

	parts := make([][]rune, 0)
	buf := []rune(str)
	bufLen := len(buf)
	current := make([]rune, 0)

	for i := 0; i < bufLen; i += 1 {
		canLookBehind, canLookAhead1, canLookAhead2 := i > 0, i < bufLen-1, i < bufLen-2
		aLower, bLower := unicode.IsLower(buf[i]), canLookAhead1 && unicode.IsLower(buf[i+1])
		aLetter, bLetter := unicode.IsLetter(buf[i]), canLookAhead1 && unicode.IsLetter(buf[i+1])
		behindLower, ahead2Lower := canLookBehind && unicode.IsLower(buf[i-1]), canLookAhead2 && unicode.IsLower(buf[i+2])
		split := !bLetter || (aLower && !bLower) || (!aLower && !bLower && (behindLower || ahead2Lower || i == 0))
		if aLetter {
			current = append(current, unicode.ToLower(buf[i]))
			if split {
				parts = append(parts, current)
				current = make([]rune, 0)
			}
		}
	}

	if len(current) > 0 {
		parts = append(parts, current)
	}

	return parts
}
<|endoftext|>


<commit_before>package interactive

import (
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
)

// A Session is an interactive shell. The New() function should be used to
// obtain a new Session instance.
type Session struct {
	// Action is the actual application logic that is looped until the
	// application gets terminated.
	Action ActionFunc
	// After is run AFTER the action function, BEFORE the session is closed.
	// It is invoked by context.Close().
	After AfterFunc
	// Before is run BEFORE the action function.
	Before BeforeFunc

	context *Context
	fd      int
	state   *terminal.State
	term    *terminal.Terminal
}

// New spawns an interactive session in the current terminal. A prompt character
// needs to be provided which will be printed when user input is awaited.
func New(prompt string) *Session {
	// Save old state and set terminal into raw mode.
	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		panic(err)
	}

	// Satisfies the ReadWriter interface and serves as I/O for the new terminal.
	shell := &Shell{
		r: os.Stdin,
		w: os.Stdout,
	}

	// Create new terminal with desired prompt sign.
	term := terminal.NewTerminal(shell, strings.Trim(prompt, " ")+" ")

	// Finally create the session.
	s := &Session{
		Action: dummyAction,
		fd:     fd,
		state:  oldState,
		term:   term,
	}
	s.context = &Context{session: s}

	// Set up Ctrl^C listener.
	term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
		if key == '\x03' {
			s.close(0)
		}
		return "", 0, false
	}

	return s
}

// Run is a blocking method that executes the actual logic.
func (s *Session) Run() {
	// Run Before function if present. Close session if an error occurs.
	if s.Before != nil {
		if err := s.Before(s.context); err != nil {
			s.writeLine(err.Error())
			s.close(1)
		}
	}

	// Loop root action. Close session if an error occurs.
	for {
		if err := s.Action(s.context); err != nil {
			s.writeLine(err.Error())
			s.close(1)
		}
	}
}

func (s *Session) close(exitCode int) {
	// Run After function if present.
	if s.After != nil {
		if err := s.After(s.context); err != nil {
			s.writeLine(err.Error())
		}
	}

	// Restore terminal.
	terminal.Restore(s.fd, s.state)
	os.Exit(exitCode)
}

func (s *Session) readLine() string {
	text, err := s.term.ReadLine()
	if err != nil {
		// Close session on Ctrl^D.
		if err == io.EOF {
			s.close(0)
		}
		panic(err)
	}
	return text
}

func (s *Session) writeLine(text string) {
	s.term.Write([]byte(text + "\n"))
}

func dummyAction(c *Context) error {
	c.WriteLine("No Action defined!")
	c.Close()
	return nil
}
<commit_msg>Add method to return the sessions underlying shell<commit_after>package interactive

import (
	"io"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"
)

// A Session is an interactive shell. The New() function should be used to
// obtain a new Session instance.
type Session struct {
	// Action is the actual application logic that is looped until the
	// application gets terminated.
	Action ActionFunc
	// After is run AFTER the action function, BEFORE the session is closed.
	// It is invoked by context.Close().
	After AfterFunc
	// Before is run BEFORE the action function.
	Before BeforeFunc

	context *Context
	fd      int
	shell   *Shell
	state   *terminal.State
	term    *terminal.Terminal
}

// New spawns an interactive session in the current terminal. A prompt character
// needs to be provided which will be printed when user input is awaited.
func New(prompt string) *Session {
	// Save old state and set terminal into raw mode.
	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		panic(err)
	}

	// Satisfies the ReadWriter interface and serves as I/O for the new terminal.
	shell := &Shell{
		r: os.Stdin,
		w: os.Stdout,
	}

	// Create new terminal with desired prompt sign.
	term := terminal.NewTerminal(shell, strings.Trim(prompt, " ")+" ")

	// Finally create the session.
	s := &Session{
		Action: dummyAction,
		fd:     fd,
		shell:  shell,
		state:  oldState,
		term:   term,
	}
	s.context = &Context{session: s}

	// Set up Ctrl^C listener.
	term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
		if key == '\x03' {
			s.close(0)
		}
		return "", 0, false
	}

	return s
}

// Run is a blocking method that executes the actual logic.
func (s *Session) Run() {
	// Run Before function if present. Close session if an error occurs.
	if s.Before != nil {
		if err := s.Before(s.context); err != nil {
			s.writeLine(err.Error())
			s.close(1)
		}
	}

	// Loop root action. Close session if an error occurs.
	for {
		if err := s.Action(s.context); err != nil {
			s.writeLine(err.Error())
			s.close(1)
		}
	}
}

// Shell returns the sessions underlying shell (ReadWriter).
func (s *Session) Shell() *Shell {
	return s.shell
}

func (s *Session) close(exitCode int) {
	// Run After function if present.
	if s.After != nil {
		if err := s.After(s.context); err != nil {
			s.writeLine(err.Error())
		}
	}

	// Restore terminal.
	terminal.Restore(s.fd, s.state)
	os.Exit(exitCode)
}

func (s *Session) readLine() string {
	text, err := s.term.ReadLine()
	if err != nil {
		// Close session on Ctrl^D.
		if err == io.EOF {
			s.close(0)
		}
		panic(err)
	}
	return text
}

func (s *Session) writeLine(text string) {
	s.term.Write([]byte(text + "\n"))
}

func dummyAction(c *Context) error {
	c.WriteLine("No Action defined!")
	c.Close()
	return nil
}
<|endoftext|>


<commit_before>package quic

import (
	"bytes"
	"errors"
	"fmt"
	"net"

	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"

	"github.com/lucas-clemente/quic-go/crypto"
	"github.com/lucas-clemente/quic-go/handshake"
	"github.com/lucas-clemente/quic-go/protocol"
)

// A Session is a QUIC session
type Session struct {
	ConnectionID protocol.ConnectionID
	ServerConfig *ServerConfig

	Connection        *net.UDPConn
	CurrentRemoteAddr *net.UDPAddr

	aead crypto.AEAD

	Entropy EntropyAccumulator

	lastSentPacketNumber protocol.PacketNumber
}

// NewSession makes a new session
func NewSession(conn *net.UDPConn, connectionID protocol.ConnectionID, sCfg *ServerConfig) *Session {
	return &Session{
		Connection:   conn,
		ConnectionID: connectionID,
		ServerConfig: sCfg,
		aead:         &crypto.NullAEAD{},
	}
}

// HandlePacket handles a packet
func (s *Session) HandlePacket(addr *net.UDPAddr, publicHeaderBinary []byte, publicHeader *PublicHeader, r *bytes.Reader) error {
	// TODO: Only do this after authenticating
	if addr != s.CurrentRemoteAddr {
		s.CurrentRemoteAddr = addr
	}

	r, err := s.aead.Open(publicHeader.PacketNumber, publicHeaderBinary, r)
	if err != nil {
		return err
	}

	privateFlag, err := r.ReadByte()
	if err != nil {
		return err
	}
	s.Entropy.Add(publicHeader.PacketNumber, privateFlag&0x01 > 0)

	frameCounter := 0

	// read all frames in the packet
	for r.Len() > 0 {
		typeByte, err := r.ReadByte()
		if err != nil {
			fmt.Println("No more frames in this packet.")
			break
		}

		frameCounter++
		fmt.Printf("Reading frame %d\n", frameCounter)

		if typeByte&0x80 == 0x80 { // STREAM
			fmt.Println("Detected STREAM")
			frame, err := ParseStreamFrame(r, typeByte)
			if err != nil {
				return err
			}
			fmt.Printf("Got %d bytes for stream %d\n", len(frame.Data), frame.StreamID)

			if frame.StreamID == 0 {
				return errors.New("Session: 0 is not a valid Stream ID")
			}

			// TODO: Switch stream here
			if frame.StreamID == 1 {
				s.HandleCryptoHandshake(frame)
			} else {
				h2r := bytes.NewReader(frame.Data)
				h2framer := http2.NewFramer(nil, h2r)
				h2framer.ReadMetaHeaders = hpack.NewDecoder(1024, nil)
				h2frame, err := h2framer.ReadFrame()
				if err != nil {
					return err
				}
				h2headersFrame := h2frame.(*http2.MetaHeadersFrame)
				fmt.Printf("%#v\n", h2headersFrame)
				panic("streamid not 1")
			}
		} else if typeByte&0xC0 == 0x40 { // ACK
			fmt.Println("Detected ACK")
			frame, err := ParseAckFrame(r, typeByte)
			if err != nil {
				return err
			}

			fmt.Printf("%#v\n", frame)

			continue // not yet implemented
		} else if typeByte&0xE0 == 0x20 { // CONGESTION_FEEDBACK
			return errors.New("Detected CONGESTION_FEEDBACK")
		} else if typeByte&0x06 == 0x06 { // STOP_WAITING
			fmt.Println("Detected STOP_WAITING")
			r.ReadByte()
			r.ReadByte()
		} else {
			return errors.New("Session: invalid Frame Type Field")
		}
	}
	return nil
}

// SendFrames sends a number of frames to the client
func (s *Session) SendFrames(frames []Frame) error {
	var framesData bytes.Buffer
	framesData.WriteByte(0) // TODO: entropy
	for _, f := range frames {
		if err := f.Write(&framesData); err != nil {
			return err
		}
	}

	s.lastSentPacketNumber++

	var fullReply bytes.Buffer
	responsePublicHeader := PublicHeader{ConnectionID: s.ConnectionID, PacketNumber: s.lastSentPacketNumber}
	fmt.Printf("Sending packet # %d\n", responsePublicHeader.PacketNumber)
	if err := responsePublicHeader.WritePublicHeader(&fullReply); err != nil {
		return err
	}

	s.aead.Seal(s.lastSentPacketNumber, &fullReply, fullReply.Bytes(), framesData.Bytes())

	_, err := s.Connection.WriteToUDP(fullReply.Bytes(), s.CurrentRemoteAddr)
	return err
}

// HandleCryptoHandshake handles the crypto handshake
func (s *Session) HandleCryptoHandshake(frame *StreamFrame) error {
	messageTag, cryptoData, err := handshake.ParseHandshakeMessage(frame.Data)
	if err != nil {
		panic(err)
	}

	// TODO: Switch client messages here
	if messageTag != handshake.TagCHLO {
		return errors.New("Session: expected CHLO")
	}

	if _, ok := cryptoData[handshake.TagSCID]; ok {
		var sharedSecret []byte
		sharedSecret, err = s.ServerConfig.kex.CalculateSharedKey(cryptoData[handshake.TagPUBS])
		if err != nil {
			return err
		}
		s.aead, err = crypto.DeriveKeysChacha20(sharedSecret, cryptoData[handshake.TagNONC], s.ConnectionID, frame.Data, s.ServerConfig.Get(), s.ServerConfig.kd.GetCertUncompressed())
		if err != nil {
			return err
		}
		s.SendFrames([]Frame{&AckFrame{
			Entropy:         s.Entropy.Get(),
			LargestObserved: 2,
		}})
		return nil
	}

	proof, err := s.ServerConfig.Sign(frame.Data)
	if err != nil {
		return err
	}
	var serverReply bytes.Buffer
	handshake.WriteHandshakeMessage(&serverReply, handshake.TagREJ, map[handshake.Tag][]byte{
		handshake.TagSCFG: s.ServerConfig.Get(),
		handshake.TagCERT: s.ServerConfig.GetCertData(),
		handshake.TagPROF: proof,
	})

	return s.SendFrames([]Frame{
		&AckFrame{
			Entropy:         s.Entropy.Get(),
			LargestObserved: 1,
		},
		&StreamFrame{
			StreamID: 1,
			Data:     serverReply.Bytes(),
		},
	})
}
<commit_msg>add support for PAD frames<commit_after>package quic

import (
	"bytes"
	"errors"
	"fmt"
	"net"

	"golang.org/x/net/http2"
	"golang.org/x/net/http2/hpack"

	"github.com/lucas-clemente/quic-go/crypto"
	"github.com/lucas-clemente/quic-go/handshake"
	"github.com/lucas-clemente/quic-go/protocol"
)

// A Session is a QUIC session
type Session struct {
	ConnectionID protocol.ConnectionID
	ServerConfig *ServerConfig

	Connection        *net.UDPConn
	CurrentRemoteAddr *net.UDPAddr

	aead crypto.AEAD

	Entropy EntropyAccumulator

	lastSentPacketNumber protocol.PacketNumber
}

// NewSession makes a new session
func NewSession(conn *net.UDPConn, connectionID protocol.ConnectionID, sCfg *ServerConfig) *Session {
	return &Session{
		Connection:   conn,
		ConnectionID: connectionID,
		ServerConfig: sCfg,
		aead:         &crypto.NullAEAD{},
	}
}

// HandlePacket handles a packet
func (s *Session) HandlePacket(addr *net.UDPAddr, publicHeaderBinary []byte, publicHeader *PublicHeader, r *bytes.Reader) error {
	// TODO: Only do this after authenticating
	if addr != s.CurrentRemoteAddr {
		s.CurrentRemoteAddr = addr
	}

	r, err := s.aead.Open(publicHeader.PacketNumber, publicHeaderBinary, r)
	if err != nil {
		return err
	}

	privateFlag, err := r.ReadByte()
	if err != nil {
		return err
	}
	s.Entropy.Add(publicHeader.PacketNumber, privateFlag&0x01 > 0)

	frameCounter := 0

	// read all frames in the packet
	for r.Len() > 0 {
		typeByte, err := r.ReadByte()
		if err != nil {
			fmt.Println("No more frames in this packet.")
			break
		}

		frameCounter++
		fmt.Printf("Reading frame %d\n", frameCounter)

		if typeByte&0x80 == 0x80 { // STREAM
			fmt.Println("Detected STREAM")
			frame, err := ParseStreamFrame(r, typeByte)
			if err != nil {
				return err
			}
			fmt.Printf("Got %d bytes for stream %d\n", len(frame.Data), frame.StreamID)

			if frame.StreamID == 0 {
				return errors.New("Session: 0 is not a valid Stream ID")
			}

			// TODO: Switch stream here
			if frame.StreamID == 1 {
				s.HandleCryptoHandshake(frame)
			} else {
				h2r := bytes.NewReader(frame.Data)
				h2framer := http2.NewFramer(nil, h2r)
				h2framer.ReadMetaHeaders = hpack.NewDecoder(1024, nil)
				h2frame, err := h2framer.ReadFrame()
				if err != nil {
					return err
				}
				h2headersFrame := h2frame.(*http2.MetaHeadersFrame)
				fmt.Printf("%#v\n", h2headersFrame)
				panic("streamid not 1")
			}
		} else if typeByte&0xC0 == 0x40 { // ACK
			fmt.Println("Detected ACK")
			frame, err := ParseAckFrame(r, typeByte)
			if err != nil {
				return err
			}

			fmt.Printf("%#v\n", frame)

			continue // not yet implemented
		} else if typeByte&0xE0 == 0x20 { // CONGESTION_FEEDBACK
			return errors.New("Detected CONGESTION_FEEDBACK")
		} else if typeByte&0x06 == 0x06 { // STOP_WAITING
			fmt.Println("Detected STOP_WAITING")
			r.ReadByte()
			r.ReadByte()
		} else if typeByte == 0 {
			// PAD
			return nil
		} else {
			return errors.New("Session: invalid Frame Type Field")
		}
	}
	return nil
}

// SendFrames sends a number of frames to the client
func (s *Session) SendFrames(frames []Frame) error {
	var framesData bytes.Buffer
	framesData.WriteByte(0) // TODO: entropy
	for _, f := range frames {
		if err := f.Write(&framesData); err != nil {
			return err
		}
	}

	s.lastSentPacketNumber++

	var fullReply bytes.Buffer
	responsePublicHeader := PublicHeader{ConnectionID: s.ConnectionID, PacketNumber: s.lastSentPacketNumber}
	fmt.Printf("Sending packet # %d\n", responsePublicHeader.PacketNumber)
	if err := responsePublicHeader.WritePublicHeader(&fullReply); err != nil {
		return err
	}

	s.aead.Seal(s.lastSentPacketNumber, &fullReply, fullReply.Bytes(), framesData.Bytes())

	_, err := s.Connection.WriteToUDP(fullReply.Bytes(), s.CurrentRemoteAddr)
	return err
}

// HandleCryptoHandshake handles the crypto handshake
func (s *Session) HandleCryptoHandshake(frame *StreamFrame) error {
	messageTag, cryptoData, err := handshake.ParseHandshakeMessage(frame.Data)
	if err != nil {
		panic(err)
	}

	// TODO: Switch client messages here
	if messageTag != handshake.TagCHLO {
		return errors.New("Session: expected CHLO")
	}

	if _, ok := cryptoData[handshake.TagSCID]; ok {
		var sharedSecret []byte
		sharedSecret, err = s.ServerConfig.kex.CalculateSharedKey(cryptoData[handshake.TagPUBS])
		if err != nil {
			return err
		}
		s.aead, err = crypto.DeriveKeysChacha20(sharedSecret, cryptoData[handshake.TagNONC], s.ConnectionID, frame.Data, s.ServerConfig.Get(), s.ServerConfig.kd.GetCertUncompressed())
		if err != nil {
			return err
		}
		s.SendFrames([]Frame{&AckFrame{
			Entropy:         s.Entropy.Get(),
			LargestObserved: 2,
		}})
		return nil
	}

	proof, err := s.ServerConfig.Sign(frame.Data)
	if err != nil {
		return err
	}
	var serverReply bytes.Buffer
	handshake.WriteHandshakeMessage(&serverReply, handshake.TagREJ, map[handshake.Tag][]byte{
		handshake.TagSCFG: s.ServerConfig.Get(),
		handshake.TagCERT: s.ServerConfig.GetCertData(),
		handshake.TagPROF: proof,
	})

	return s.SendFrames([]Frame{
		&AckFrame{
			Entropy:         s.Entropy.Get(),
			LargestObserved: 1,
		},
		&StreamFrame{
			StreamID: 1,
			Data:     serverReply.Bytes(),
		},
	})
}
<|endoftext|>


<commit_before>package issuer

import (
	"fmt"
	"testing"

	"github.com/mundipagg/boleto-api/test"
	"github.com/stretchr/testify/assert"
)

var barCodeParameter = []test.Parameter{
	{Input: Issuer{barCode: "03391877100003841119"}, Expected: true},
	{Input: Issuer{barCode: "03391877100003841119794611200000957223350101"}, Expected: true},
	{Input: Issuer{barCode: "0339187710000384111979461120000095722335010103391877100003841119794611200000957223350101"}, Expected: true},
	{Input: Issuer{barCode: ""}, Expected: false},
	{Input: Issuer{barCode: "     "}, Expected: false},
	{Input: Issuer{barCode: "abcdefghijklmnopqrstuvxwyz"}, Expected: false},
	{Input: Issuer{barCode: "a03391877100003841119"}, Expected: false},
	{Input: Issuer{barCode: "03391877100003841119b"}, Expected: false},
	{Input: Issuer{barCode: "033918771C00003841119"}, Expected: false},
	{Input: Issuer{barCode: "033 9187 71000 03841 119"}, Expected: false},
	{Input: Issuer{barCode: "*03391877100003841???1197946112´00000957223350101"}, Expected: false},
}

var digitableLineParameter = []test.Parameter{
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 877"}, Expected: true},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: true},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290452564156412"}, Expected: true},
	{Input: Issuer{digitableLine: ""}, Expected: false},
	{Input: Issuer{digitableLine: "     "}, Expected: false},
	{Input: Issuer{digitableLine: "a23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.b69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 400c04.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617d383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 110e00.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.1809FFF0f8 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9g 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 877200000h07290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "2372.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.6937 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 4004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.61383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 1100.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.18008 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908  87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9"}, Expected: false},
	{Input: Issuer{digitableLine: "^23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
}

var barCodeAndDigitableLineParameters = []test.Parameter{
	{Input: Issuer{barCode: "03391877100003841119794611200000957223350101", digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: true},
	{Input: Issuer{barCode: "0339187abc7100003841119794611200000957223350101", digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{barCode: "03391877100003841119794611200000957223350101", digitableLine: "23a792.693b07 400c04.61d7383 1100e0.180f908 9g 8772000h0007290"}, Expected: false},
	{Input: Issuer{barCode: "a0339187710000v,^^´[´p3841119794611", digitableLine: "11000.180908 9 87720000007290"}, Expected: false},
}

func TestNewIssuer(t *testing.T) {
	expectedBarCode := "0339187710000384111794611200000957223350101"
	expectedDigitableLine := "2379.69307 40004.617383 11000.180908 9 87720000007290"

	issuer := NewIssuer(expectedBarCode, expectedDigitableLine)

	assert.Equal(t, expectedBarCode, issuer.barCode, "O barcode não foi atribuído corretamente")
	assert.Equal(t, expectedDigitableLine, issuer.digitableLine, "A digitableline não foi atribuído corretamnte")
}

func TestIsValidBarCode(t *testing.T) {
	for _, fact := range barCodeParameter {
		issuer := fact.Input.(Issuer)
		result := issuer.IsValidBarCode()

		assert.Equal(t, fact.Expected, result, fmt.Sprintf("O barCode não é válido %v ", fact.Input))
	}
}

func TestIsValidDigitableLine(t *testing.T) {
	for _, fact := range digitableLineParameter {
		issuer := fact.Input.(Issuer)
		result := issuer.IsValidDigitableLine()

		assert.Equal(t, fact.Expected, result, fmt.Sprintf("A linha digitável não é válida %v ", fact.Input))
	}
}

func TestIsValidDigitableLineAndBarCode(t *testing.T) {
	for _, fact := range barCodeAndDigitableLineParameters {
		issuer := fact.Input.(Issuer)
		result := issuer.IsValidBarCode() && issuer.IsValidDigitableLine()

		assert.Equal(t, fact.Expected, result, fmt.Sprintf("A linha digitávl ou o código de barras não são válidos %v", fact.Input))
	}
}
<commit_msg>style: corrige palavras escritas erradas<commit_after>package issuer

import (
	"fmt"
	"testing"

	"github.com/mundipagg/boleto-api/test"
	"github.com/stretchr/testify/assert"
)

var barCodeParameter = []test.Parameter{
	{Input: Issuer{barCode: "03391877100003841119"}, Expected: true},
	{Input: Issuer{barCode: "03391877100003841119794611200000957223350101"}, Expected: true},
	{Input: Issuer{barCode: "0339187710000384111979461120000095722335010103391877100003841119794611200000957223350101"}, Expected: true},
	{Input: Issuer{barCode: ""}, Expected: false},
	{Input: Issuer{barCode: "     "}, Expected: false},
	{Input: Issuer{barCode: "abcdefghijklmnopqrstuvxwyz"}, Expected: false},
	{Input: Issuer{barCode: "a03391877100003841119"}, Expected: false},
	{Input: Issuer{barCode: "03391877100003841119b"}, Expected: false},
	{Input: Issuer{barCode: "033918771C00003841119"}, Expected: false},
	{Input: Issuer{barCode: "033 9187 71000 03841 119"}, Expected: false},
	{Input: Issuer{barCode: "*03391877100003841???1197946112´00000957223350101"}, Expected: false},
}

var digitableLineParameter = []test.Parameter{
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 877"}, Expected: true},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: true},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290452564156412"}, Expected: true},
	{Input: Issuer{digitableLine: ""}, Expected: false},
	{Input: Issuer{digitableLine: "     "}, Expected: false},
	{Input: Issuer{digitableLine: "a23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.b69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 400c04.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617d383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 110e00.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.1809FFF0f8 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9g 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9 877200000h07290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "2372.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.6937 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 4004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.61383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 1100.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.18008 9 87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908  87720000007290"}, Expected: false},
	{Input: Issuer{digitableLine: "23792.69307 40004.617383 11000.180908 9"}, Expected: false},
	{Input: Issuer{digitableLine: "^23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
}

var barCodeAndDigitableLineParameters = []test.Parameter{
	{Input: Issuer{barCode: "03391877100003841119794611200000957223350101", digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: true},
	{Input: Issuer{barCode: "0339187abc7100003841119794611200000957223350101", digitableLine: "23792.69307 40004.617383 11000.180908 9 87720000007290"}, Expected: false},
	{Input: Issuer{barCode: "03391877100003841119794611200000957223350101", digitableLine: "23a792.693b07 400c04.61d7383 1100e0.180f908 9g 8772000h0007290"}, Expected: false},
	{Input: Issuer{barCode: "a0339187710000v,^^´[´p3841119794611", digitableLine: "11000.180908 9 87720000007290"}, Expected: false},
}

func TestNewIssuer(t *testing.T) {
	expectedBarCode := "0339187710000384111794611200000957223350101"
	expectedDigitableLine := "2379.69307 40004.617383 11000.180908 9 87720000007290"

	issuer := NewIssuer(expectedBarCode, expectedDigitableLine)

	assert.Equal(t, expectedBarCode, issuer.barCode, "O barcode não foi atribuído corretamente")
	assert.Equal(t, expectedDigitableLine, issuer.digitableLine, "A digitableline não foi atribuída corretamente")
}

func TestIsValidBarCode(t *testing.T) {
	for _, fact := range barCodeParameter {
		issuer := fact.Input.(Issuer)
		result := issuer.IsValidBarCode()

		assert.Equal(t, fact.Expected, result, fmt.Sprintf("O barCode não é válido %v ", fact.Input))
	}
}

func TestIsValidDigitableLine(t *testing.T) {
	for _, fact := range digitableLineParameter {
		issuer := fact.Input.(Issuer)
		result := issuer.IsValidDigitableLine()

		assert.Equal(t, fact.Expected, result, fmt.Sprintf("A linha digitável não é válida %v ", fact.Input))
	}
}

func TestIsValidDigitableLineAndBarCode(t *testing.T) {
	for _, fact := range barCodeAndDigitableLineParameters {
		issuer := fact.Input.(Issuer)
		result := issuer.IsValidBarCode() && issuer.IsValidDigitableLine()

		assert.Equal(t, fact.Expected, result, fmt.Sprintf("A linha digitável ou o código de barras não são válidos %v", fact.Input))
	}
}
<|endoftext|>


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

package gocql

import (
	"errors"
	"fmt"
	"io"
	"sync"
	"time"
)

// Session is the interface used by users to interact with the database.
//
// It's safe for concurrent use by multiple goroutines and a typical usage
// scenario is to have one global session object to interact with the
// whole Cassandra cluster.
//
// This type extends the Node interface by adding a convinient query builder
// and automatically sets a default consinstency level on all operations
// that do not have a consistency level set.
type Session struct {
	Node     Node
	cons     Consistency
	pageSize int
	prefetch float64
	trace    Tracer
	mu       sync.RWMutex
	cfg      ClusterConfig
}

// NewSession wraps an existing Node.
func NewSession(c *clusterImpl) *Session {
	return &Session{Node: c, cons: Quorum, prefetch: 0.25, cfg: c.cfg}
}

// SetConsistency sets the default consistency level for this session. This
// setting can also be changed on a per-query basis and the default value
// is Quorum.
func (s *Session) SetConsistency(cons Consistency) {
	s.mu.Lock()
	s.cons = cons
	s.mu.Unlock()
}

// SetPageSize sets the default page size for this session. A value <= 0 will
// disable paging. This setting can also be changed on a per-query basis.
func (s *Session) SetPageSize(n int) {
	s.mu.Lock()
	s.pageSize = n
	s.mu.Unlock()
}

// SetPrefetch sets the default threshold for pre-fetching new pages. If
// there are only p*pageSize rows remaining, the next page will be requested
// automatically. This value can also be changed on a per-query basis and
// the default value is 0.25.
func (s *Session) SetPrefetch(p float64) {
	s.mu.Lock()
	s.prefetch = p
	s.mu.Unlock()
}

// SetTrace sets the default tracer for this session. This setting can also
// be changed on a per-query basis.
func (s *Session) SetTrace(trace Tracer) {
	s.mu.Lock()
	s.trace = trace
	s.mu.Unlock()
}

// Query generates a new query object for interacting with the database.
// Further details of the query may be tweaked using the resulting query
// value before the query is executed.
func (s *Session) Query(stmt string, values ...interface{}) *Query {
	s.mu.RLock()
	qry := &Query{stmt: stmt, values: values, cons: s.cons,
		session: s, pageSize: s.pageSize, trace: s.trace,
		prefetch: s.prefetch, rt: s.cfg.RetryPolicy}
	s.mu.RUnlock()
	return qry
}

// Close closes all connections. The session is unusable after this
// operation.
func (s *Session) Close() {
	s.Node.Close()
}

func (s *Session) executeQuery(qry *Query) *Iter {
	var itr *Iter
	count := 0
	for count <= qry.rt.NumRetries {
		conn := s.Node.Pick(nil)
		//Assign the error unavailable to the iterator
		if conn == nil {
			itr = &Iter{err: ErrUnavailable}
			break
		}
		itr = conn.executeQuery(qry)
		//Exit for loop if the query was successful
		if itr.err == nil {
			break
		}
		count++
	}
	return itr
}

// ExecuteBatch executes a batch operation and returns nil if successful
// otherwise an error is returned describing the failure.
func (s *Session) ExecuteBatch(batch *Batch) error {
	// Prevent the execution of the batch if greater than the limit
	// Currently batches have a limit of 65536 queries.
	// https://datastax-oss.atlassian.net/browse/JAVA-229
	if batch.Size() > BatchSizeMaximum {
		return ErrTooManyStmts
	}
	var err error
	count := 0
	for count <= batch.rt.NumRetries {
		conn := s.Node.Pick(nil)
		//Assign the error unavailable and break loop
		if conn == nil {
			err = ErrUnavailable
			break
		}
		err = conn.executeBatch(batch)
		//Exit loop if operation executed correctly
		if err == nil {
			break
		}
		count++
	}
	return err
}

// Query represents a CQL statement that can be executed.
type Query struct {
	stmt      string
	values    []interface{}
	cons      Consistency
	pageSize  int
	pageState []byte
	prefetch  float64
	trace     Tracer
	session   *Session
	rt        RetryPolicy
}

// Consistency sets the consistency level for this query. If no consistency
// level have been set, the default consistency level of the cluster
// is used.
func (q *Query) Consistency(c Consistency) *Query {
	q.cons = c
	return q
}

// Trace enables tracing of this query. Look at the documentation of the
// Tracer interface to learn more about tracing.
func (q *Query) Trace(trace Tracer) *Query {
	q.trace = trace
	return q
}

// PageSize will tell the iterator to fetch the result in pages of size n.
// This is useful for iterating over large result sets, but setting the
// page size to low might decrease the performance. This feature is only
// available in Cassandra 2 and onwards.
func (q *Query) PageSize(n int) *Query {
	q.pageSize = n
	return q
}

// SetPrefetch sets the default threshold for pre-fetching new pages. If
// there are only p*pageSize rows remaining, the next page will be requested
// automatically.
func (q *Query) Prefetch(p float64) *Query {
	q.prefetch = p
	return q
}

// RetryPolicy sets the policy to use when retrying the query.
func (q *Query) RetryPolicy(r RetryPolicy) *Query {
	q.rt = r
	return q
}

// Exec executes the query without returning any rows.
func (q *Query) Exec() error {
	iter := q.session.executeQuery(q)
	return iter.err
}

// Iter executes the query and returns an iterator capable of iterating
// over all results.
func (q *Query) Iter() *Iter {
	return q.session.executeQuery(q)
}

// Scan executes the query, copies the columns of the first selected
// row into the values pointed at by dest and discards the rest. If no rows
// were selected, ErrNotFound is returned.
func (q *Query) Scan(dest ...interface{}) error {
	iter := q.Iter()
	if iter.err != nil {
		return iter.err
	}
	if len(iter.rows) == 0 {
		return ErrNotFound
	}
	iter.Scan(dest...)
	return iter.Close()
}

// ScanCAS executes a lightweight transaction (i.e. an UPDATE or INSERT
// statement containing an IF clause). If the transaction fails because
// the existing values did not match, the previos values will be stored
// in dest.
func (q *Query) ScanCAS(dest ...interface{}) (applied bool, err error) {
	iter := q.Iter()
	if iter.err != nil {
		return false, iter.err
	}
	if len(iter.rows) == 0 {
		return false, ErrNotFound
	}
	if len(iter.Columns()) > 1 {
		dest = append([]interface{}{&applied}, dest...)
		iter.Scan(dest...)
	} else {
		iter.Scan(&applied)
	}
	return applied, iter.Close()
}

// Iter represents an iterator that can be used to iterate over all rows that
// were returned by a query. The iterator might send additional queries to the
// database during the iteration if paging was enabled.
type Iter struct {
	err     error
	pos     int
	rows    [][][]byte
	columns []ColumnInfo
	next    *nextIter
}

// Columns returns the name and type of the selected columns.
func (iter *Iter) Columns() []ColumnInfo {
	return iter.columns
}

// Scan consumes the next row of the iterator and copies the columns of the
// current row into the values pointed at by dest. Use nil as a dest value
// to skip the corresponding column. Scan might send additional queries
// to the database to retrieve the next set of rows if paging was enabled.
//
// Scan returns true if the row was successfully unmarshaled or false if the
// end of the result set was reached or if an error occurred. Close should
// be called afterwards to retrieve any potential errors.
func (iter *Iter) Scan(dest ...interface{}) bool {
	if iter.err != nil {
		return false
	}
	if iter.pos >= len(iter.rows) {
		if iter.next != nil {
			*iter = *iter.next.fetch()
			return iter.Scan(dest...)
		}
		return false
	}
	if iter.next != nil && iter.pos == iter.next.pos {
		go iter.next.fetch()
	}
	if len(dest) != len(iter.columns) {
		iter.err = errors.New("count mismatch")
		return false
	}
	for i := 0; i < len(iter.columns); i++ {
		if dest[i] == nil {
			continue
		}
		err := Unmarshal(iter.columns[i].TypeInfo, iter.rows[iter.pos][i], dest[i])
		if err != nil {
			iter.err = err
			return false
		}
	}
	iter.pos++
	return true
}

// Close closes the iterator and returns any errors that happened during
// the query or the iteration.
func (iter *Iter) Close() error {
	return iter.err
}

type nextIter struct {
	qry  Query
	pos  int
	once sync.Once
	next *Iter
}

func (n *nextIter) fetch() *Iter {
	n.once.Do(func() {
		n.next = n.qry.session.executeQuery(&n.qry)
	})
	return n.next
}

type Batch struct {
	Type    BatchType
	Entries []BatchEntry
	Cons    Consistency
	rt      RetryPolicy
}

// NewBatch creates a new batch operation without defaults from the cluster
func NewBatch(typ BatchType) *Batch {
	return &Batch{Type: typ}
}

// NewBatch creates a new batch operation using defaults defined in the cluster
func (s *Session) NewBatch(typ BatchType) *Batch {
	return &Batch{Type: typ, rt: s.cfg.RetryPolicy}
}

// Query adds the query to the batch operation
func (b *Batch) Query(stmt string, args ...interface{}) {
	b.Entries = append(b.Entries, BatchEntry{Stmt: stmt, Args: args})
}

// RetryPolicy sets the retry policy to use when executing the batch operation
func (b *Batch) RetryPolicy(r RetryPolicy) *Batch {
	b.rt = r
	return b
}

// Size returns the number of batch statements to be executed by the batch operation.
func (b *Batch) Size() int {
	return len(b.Entries)
}

type BatchType int

const (
	LoggedBatch   BatchType = 0
	UnloggedBatch BatchType = 1
	CounterBatch  BatchType = 2
)

type BatchEntry struct {
	Stmt string
	Args []interface{}
}

type Consistency int

const (
	Any Consistency = 1 + iota
	One
	Two
	Three
	Quorum
	All
	LocalQuorum
	EachQuorum
	Serial
	LocalSerial
)

var consinstencyNames = []string{
	0:           "default",
	Any:         "any",
	One:         "one",
	Two:         "two",
	Three:       "three",
	Quorum:      "quorum",
	All:         "all",
	LocalQuorum: "localquorum",
	EachQuorum:  "eachquorum",
	Serial:      "serial",
	LocalSerial: "localserial",
}

func (c Consistency) String() string {
	return consinstencyNames[c]
}

type ColumnInfo struct {
	Keyspace string
	Table    string
	Name     string
	TypeInfo *TypeInfo
}

// Tracer is the interface implemented by query tracers. Tracers have the
// ability to obtain a detailed event log of all events that happened during
// the execution of a query from Cassandra. Gathering this information might
// be essential for debugging and optimizing queries, but this feature should
// not be used on production systems with very high load.
type Tracer interface {
	Trace(traceId []byte)
}

type traceWriter struct {
	session *Session
	w       io.Writer
	mu      sync.Mutex
}

// NewTraceWriter returns a simple Tracer implementation that outputs
// the event log in a textual format.
func NewTraceWriter(session *Session, w io.Writer) Tracer {
	return traceWriter{session: session, w: w}
}

func (t traceWriter) Trace(traceId []byte) {
	var (
		coordinator string
		duration    int
	)
	t.session.Query(`SELECT coordinator, duration
			FROM system_traces.sessions
			WHERE session_id = ?`, traceId).
		Consistency(One).Scan(&coordinator, &duration)

	iter := t.session.Query(`SELECT event_id, activity, source, source_elapsed
			FROM system_traces.events
			WHERE session_id = ?`, traceId).
		Consistency(One).Iter()
	var (
		timestamp time.Time
		activity  string
		source    string
		elapsed   int
	)
	t.mu.Lock()
	defer t.mu.Unlock()
	fmt.Fprintf(t.w, "Tracing session %016x (coordinator: %s, duration: %v):\n",
		traceId, coordinator, time.Duration(duration)*time.Microsecond)
	for iter.Scan(&timestamp, &activity, &source, &elapsed) {
		fmt.Fprintf(t.w, "%s: %s (source: %s, elapsed: %d)\n",
			timestamp.Format("2006/01/02 15:04:05.999999"), activity, source, elapsed)
	}
	if err := iter.Close(); err != nil {
		fmt.Fprintln(t.w, "Error:", err)
	}
}

type Error struct {
	Code    int
	Message string
}

func (e Error) Error() string {
	return e.Message
}

var (
	ErrNotFound     = errors.New("not found")
	ErrUnavailable  = errors.New("unavailable")
	ErrProtocol     = errors.New("protocol error")
	ErrUnsupported  = errors.New("feature not supported")
	ErrTooManyStmts = errors.New("too many statements")
)

const BatchSizeMaximum = 65536
<commit_msg>Reduced BatchSizeMaximum by 1 as 65535 is the true maximum. More than this number in statements to execute will cause the batch to fail.<commit_after>// Copyright (c) 2012 The gocql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocql

import (
	"errors"
	"fmt"
	"io"
	"sync"
	"time"
)

// Session is the interface used by users to interact with the database.
//
// It's safe for concurrent use by multiple goroutines and a typical usage
// scenario is to have one global session object to interact with the
// whole Cassandra cluster.
//
// This type extends the Node interface by adding a convinient query builder
// and automatically sets a default consinstency level on all operations
// that do not have a consistency level set.
type Session struct {
	Node     Node
	cons     Consistency
	pageSize int
	prefetch float64
	trace    Tracer
	mu       sync.RWMutex
	cfg      ClusterConfig
}

// NewSession wraps an existing Node.
func NewSession(c *clusterImpl) *Session {
	return &Session{Node: c, cons: Quorum, prefetch: 0.25, cfg: c.cfg}
}

// SetConsistency sets the default consistency level for this session. This
// setting can also be changed on a per-query basis and the default value
// is Quorum.
func (s *Session) SetConsistency(cons Consistency) {
	s.mu.Lock()
	s.cons = cons
	s.mu.Unlock()
}

// SetPageSize sets the default page size for this session. A value <= 0 will
// disable paging. This setting can also be changed on a per-query basis.
func (s *Session) SetPageSize(n int) {
	s.mu.Lock()
	s.pageSize = n
	s.mu.Unlock()
}

// SetPrefetch sets the default threshold for pre-fetching new pages. If
// there are only p*pageSize rows remaining, the next page will be requested
// automatically. This value can also be changed on a per-query basis and
// the default value is 0.25.
func (s *Session) SetPrefetch(p float64) {
	s.mu.Lock()
	s.prefetch = p
	s.mu.Unlock()
}

// SetTrace sets the default tracer for this session. This setting can also
// be changed on a per-query basis.
func (s *Session) SetTrace(trace Tracer) {
	s.mu.Lock()
	s.trace = trace
	s.mu.Unlock()
}

// Query generates a new query object for interacting with the database.
// Further details of the query may be tweaked using the resulting query
// value before the query is executed.
func (s *Session) Query(stmt string, values ...interface{}) *Query {
	s.mu.RLock()
	qry := &Query{stmt: stmt, values: values, cons: s.cons,
		session: s, pageSize: s.pageSize, trace: s.trace,
		prefetch: s.prefetch, rt: s.cfg.RetryPolicy}
	s.mu.RUnlock()
	return qry
}

// Close closes all connections. The session is unusable after this
// operation.
func (s *Session) Close() {
	s.Node.Close()
}

func (s *Session) executeQuery(qry *Query) *Iter {
	var itr *Iter
	count := 0
	for count <= qry.rt.NumRetries {
		conn := s.Node.Pick(nil)
		//Assign the error unavailable to the iterator
		if conn == nil {
			itr = &Iter{err: ErrUnavailable}
			break
		}
		itr = conn.executeQuery(qry)
		//Exit for loop if the query was successful
		if itr.err == nil {
			break
		}
		count++
	}
	return itr
}

// ExecuteBatch executes a batch operation and returns nil if successful
// otherwise an error is returned describing the failure.
func (s *Session) ExecuteBatch(batch *Batch) error {
	// Prevent the execution of the batch if greater than the limit
	// Currently batches have a limit of 65536 queries.
	// https://datastax-oss.atlassian.net/browse/JAVA-229
	if batch.Size() > BatchSizeMaximum {
		return ErrTooManyStmts
	}
	var err error
	count := 0
	for count <= batch.rt.NumRetries {
		conn := s.Node.Pick(nil)
		//Assign the error unavailable and break loop
		if conn == nil {
			err = ErrUnavailable
			break
		}
		err = conn.executeBatch(batch)
		//Exit loop if operation executed correctly
		if err == nil {
			break
		}
		count++
	}
	return err
}

// Query represents a CQL statement that can be executed.
type Query struct {
	stmt      string
	values    []interface{}
	cons      Consistency
	pageSize  int
	pageState []byte
	prefetch  float64
	trace     Tracer
	session   *Session
	rt        RetryPolicy
}

// Consistency sets the consistency level for this query. If no consistency
// level have been set, the default consistency level of the cluster
// is used.
func (q *Query) Consistency(c Consistency) *Query {
	q.cons = c
	return q
}

// Trace enables tracing of this query. Look at the documentation of the
// Tracer interface to learn more about tracing.
func (q *Query) Trace(trace Tracer) *Query {
	q.trace = trace
	return q
}

// PageSize will tell the iterator to fetch the result in pages of size n.
// This is useful for iterating over large result sets, but setting the
// page size to low might decrease the performance. This feature is only
// available in Cassandra 2 and onwards.
func (q *Query) PageSize(n int) *Query {
	q.pageSize = n
	return q
}

// SetPrefetch sets the default threshold for pre-fetching new pages. If
// there are only p*pageSize rows remaining, the next page will be requested
// automatically.
func (q *Query) Prefetch(p float64) *Query {
	q.prefetch = p
	return q
}

// RetryPolicy sets the policy to use when retrying the query.
func (q *Query) RetryPolicy(r RetryPolicy) *Query {
	q.rt = r
	return q
}

// Exec executes the query without returning any rows.
func (q *Query) Exec() error {
	iter := q.session.executeQuery(q)
	return iter.err
}

// Iter executes the query and returns an iterator capable of iterating
// over all results.
func (q *Query) Iter() *Iter {
	return q.session.executeQuery(q)
}

// Scan executes the query, copies the columns of the first selected
// row into the values pointed at by dest and discards the rest. If no rows
// were selected, ErrNotFound is returned.
func (q *Query) Scan(dest ...interface{}) error {
	iter := q.Iter()
	if iter.err != nil {
		return iter.err
	}
	if len(iter.rows) == 0 {
		return ErrNotFound
	}
	iter.Scan(dest...)
	return iter.Close()
}

// ScanCAS executes a lightweight transaction (i.e. an UPDATE or INSERT
// statement containing an IF clause). If the transaction fails because
// the existing values did not match, the previos values will be stored
// in dest.
func (q *Query) ScanCAS(dest ...interface{}) (applied bool, err error) {
	iter := q.Iter()
	if iter.err != nil {
		return false, iter.err
	}
	if len(iter.rows) == 0 {
		return false, ErrNotFound
	}
	if len(iter.Columns()) > 1 {
		dest = append([]interface{}{&applied}, dest...)
		iter.Scan(dest...)
	} else {
		iter.Scan(&applied)
	}
	return applied, iter.Close()
}

// Iter represents an iterator that can be used to iterate over all rows that
// were returned by a query. The iterator might send additional queries to the
// database during the iteration if paging was enabled.
type Iter struct {
	err     error
	pos     int
	rows    [][][]byte
	columns []ColumnInfo
	next    *nextIter
}

// Columns returns the name and type of the selected columns.
func (iter *Iter) Columns() []ColumnInfo {
	return iter.columns
}

// Scan consumes the next row of the iterator and copies the columns of the
// current row into the values pointed at by dest. Use nil as a dest value
// to skip the corresponding column. Scan might send additional queries
// to the database to retrieve the next set of rows if paging was enabled.
//
// Scan returns true if the row was successfully unmarshaled or false if the
// end of the result set was reached or if an error occurred. Close should
// be called afterwards to retrieve any potential errors.
func (iter *Iter) Scan(dest ...interface{}) bool {
	if iter.err != nil {
		return false
	}
	if iter.pos >= len(iter.rows) {
		if iter.next != nil {
			*iter = *iter.next.fetch()
			return iter.Scan(dest...)
		}
		return false
	}
	if iter.next != nil && iter.pos == iter.next.pos {
		go iter.next.fetch()
	}
	if len(dest) != len(iter.columns) {
		iter.err = errors.New("count mismatch")
		return false
	}
	for i := 0; i < len(iter.columns); i++ {
		if dest[i] == nil {
			continue
		}
		err := Unmarshal(iter.columns[i].TypeInfo, iter.rows[iter.pos][i], dest[i])
		if err != nil {
			iter.err = err
			return false
		}
	}
	iter.pos++
	return true
}

// Close closes the iterator and returns any errors that happened during
// the query or the iteration.
func (iter *Iter) Close() error {
	return iter.err
}

type nextIter struct {
	qry  Query
	pos  int
	once sync.Once
	next *Iter
}

func (n *nextIter) fetch() *Iter {
	n.once.Do(func() {
		n.next = n.qry.session.executeQuery(&n.qry)
	})
	return n.next
}

type Batch struct {
	Type    BatchType
	Entries []BatchEntry
	Cons    Consistency
	rt      RetryPolicy
}

// NewBatch creates a new batch operation without defaults from the cluster
func NewBatch(typ BatchType) *Batch {
	return &Batch{Type: typ}
}

// NewBatch creates a new batch operation using defaults defined in the cluster
func (s *Session) NewBatch(typ BatchType) *Batch {
	return &Batch{Type: typ, rt: s.cfg.RetryPolicy}
}

// Query adds the query to the batch operation
func (b *Batch) Query(stmt string, args ...interface{}) {
	b.Entries = append(b.Entries, BatchEntry{Stmt: stmt, Args: args})
}

// RetryPolicy sets the retry policy to use when executing the batch operation
func (b *Batch) RetryPolicy(r RetryPolicy) *Batch {
	b.rt = r
	return b
}

// Size returns the number of batch statements to be executed by the batch operation.
func (b *Batch) Size() int {
	return len(b.Entries)
}

type BatchType int

const (
	LoggedBatch   BatchType = 0
	UnloggedBatch BatchType = 1
	CounterBatch  BatchType = 2
)

type BatchEntry struct {
	Stmt string
	Args []interface{}
}

type Consistency int

const (
	Any Consistency = 1 + iota
	One
	Two
	Three
	Quorum
	All
	LocalQuorum
	EachQuorum
	Serial
	LocalSerial
)

var consinstencyNames = []string{
	0:           "default",
	Any:         "any",
	One:         "one",
	Two:         "two",
	Three:       "three",
	Quorum:      "quorum",
	All:         "all",
	LocalQuorum: "localquorum",
	EachQuorum:  "eachquorum",
	Serial:      "serial",
	LocalSerial: "localserial",
}

func (c Consistency) String() string {
	return consinstencyNames[c]
}

type ColumnInfo struct {
	Keyspace string
	Table    string
	Name     string
	TypeInfo *TypeInfo
}

// Tracer is the interface implemented by query tracers. Tracers have the
// ability to obtain a detailed event log of all events that happened during
// the execution of a query from Cassandra. Gathering this information might
// be essential for debugging and optimizing queries, but this feature should
// not be used on production systems with very high load.
type Tracer interface {
	Trace(traceId []byte)
}

type traceWriter struct {
	session *Session
	w       io.Writer
	mu      sync.Mutex
}

// NewTraceWriter returns a simple Tracer implementation that outputs
// the event log in a textual format.
func NewTraceWriter(session *Session, w io.Writer) Tracer {
	return traceWriter{session: session, w: w}
}

func (t traceWriter) Trace(traceId []byte) {
	var (
		coordinator string
		duration    int
	)
	t.session.Query(`SELECT coordinator, duration
			FROM system_traces.sessions
			WHERE session_id = ?`, traceId).
		Consistency(One).Scan(&coordinator, &duration)

	iter := t.session.Query(`SELECT event_id, activity, source, source_elapsed
			FROM system_traces.events
			WHERE session_id = ?`, traceId).
		Consistency(One).Iter()
	var (
		timestamp time.Time
		activity  string
		source    string
		elapsed   int
	)
	t.mu.Lock()
	defer t.mu.Unlock()
	fmt.Fprintf(t.w, "Tracing session %016x (coordinator: %s, duration: %v):\n",
		traceId, coordinator, time.Duration(duration)*time.Microsecond)
	for iter.Scan(&timestamp, &activity, &source, &elapsed) {
		fmt.Fprintf(t.w, "%s: %s (source: %s, elapsed: %d)\n",
			timestamp.Format("2006/01/02 15:04:05.999999"), activity, source, elapsed)
	}
	if err := iter.Close(); err != nil {
		fmt.Fprintln(t.w, "Error:", err)
	}
}

type Error struct {
	Code    int
	Message string
}

func (e Error) Error() string {
	return e.Message
}

var (
	ErrNotFound     = errors.New("not found")
	ErrUnavailable  = errors.New("unavailable")
	ErrProtocol     = errors.New("protocol error")
	ErrUnsupported  = errors.New("feature not supported")
	ErrTooManyStmts = errors.New("too many statements")
)

// BatchSizeMaximum is the maximum number of statements a batch operation can have.
// This limit is set by cassandra and could change in the future.
const BatchSizeMaximum = 65535
<|endoftext|>